Benchmark UnitAlive vs UNIT_TYPE_DEAD

Bribe

vJass errors are legion
Reaction score
67
Comparison:
[LJASS]UnitAlive[/LJASS] vs [LJASS]UNIT_TYPE_DEAD[/LJASS]​

Approximate Results & Conclusions:
  • Tested on latest Warcraft III Version.
  • [LJASS]UnitAlive[/LJASS] is 50% faster than [LJASS]UNIT_TYPE_DEAD[/LJASS].

Comments & Personal Criticism:
  • Nestharus thought UnitAlive was a wrapper for UNIT_TYPE_DEAD, this strongly differs.

Code:

JASS:
library Benchmark initializer OnInit
    ///////////////////////////////////////////////
    // Native declarations for stopwatch natives //
    //  - Requires no modified common.j import   //
    ///////////////////////////////////////////////
    native StopWatchCreate  takes nothing returns integer
    native StopWatchMark    takes integer stopwatch returns real
    native StopWatchDestroy takes integer stopwatch returns nothing
    
    native UnitAlive takes unit id returns boolean
    
    /////////////////////////
    // Benchmarking script //
    /////////////////////////
    
    // Initialisation
    globals
        // ...
        unit u
    endglobals

    ////////////
    private function Init takes nothing returns nothing
        // things required to be performed once before your test
        set u = CreateUnit(Player(0), 'hfoo', 0,0,0)
    endfunction
    
    // Tests
    
    globals
    private constant string TITLE_A="UnitAlive"
    //! textmacro Benchmark__TestA
        call UnitAlive(u)
        call UnitAlive(u)
        call UnitAlive(u)
        call UnitAlive(u)
        call UnitAlive(u)
        call UnitAlive(u)
        call UnitAlive(u)
        call UnitAlive(u)
        call UnitAlive(u)
        call UnitAlive(u)
    //! endtextmacro
    private constant string TITLE_B="IsUnitType"
    //! textmacro Benchmark__TestB
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
        call IsUnitType(u, UNIT_TYPE_DEAD)
    //! endtextmacro
    endglobals
    
    // execution
    private function TestA1000 takes nothing returns nothing
        local integer i = 100 // hence 1,000 execs
        loop
            exitwhen(i == 0)
            set i = i - 1
            // Repeat x10
            //! runtextmacro Benchmark__TestA() // 1
            //! runtextmacro Benchmark__TestA() // 2
            //! runtextmacro Benchmark__TestA() // 3
            //! runtextmacro Benchmark__TestA() // 4
            //! runtextmacro Benchmark__TestA() // 5
            //! runtextmacro Benchmark__TestA() // 6
            //! runtextmacro Benchmark__TestA() // 7
            //! runtextmacro Benchmark__TestA() // 8
            //! runtextmacro Benchmark__TestA() // 9
            //! runtextmacro Benchmark__TestA() // 10
        endloop
    endfunction
    private function TestB1000 takes nothing returns nothing
        local integer i = 100
        loop
            exitwhen(i == 0) // hence 1,000 execs
            set i = i - 1
            // Repeat x10
            //! runtextmacro Benchmark__TestB() // 1
            //! runtextmacro Benchmark__TestB() // 2
            //! runtextmacro Benchmark__TestB() // 3
            //! runtextmacro Benchmark__TestB() // 4
            //! runtextmacro Benchmark__TestB() // 5
            //! runtextmacro Benchmark__TestB() // 6
            //! runtextmacro Benchmark__TestB() // 7
            //! runtextmacro Benchmark__TestB() // 8
            //! runtextmacro Benchmark__TestB() // 9
            //! runtextmacro Benchmark__TestB() // 10
        endloop
    endfunction
    
    private function OnEsc takes nothing returns nothing
        local integer sw = StopWatchCreate()
        local integer i = 0
        
        loop
            set i = i + 1
            call TestA1000.evaluate() // x10 - 10,000 executions altogether.
            exitwhen(i == 10)
        endloop
        call BJDebugMsg("|cff66ddff"+TITLE_A+" : "+R2S(StopWatchMark(sw)*100)+"|r")
        call StopWatchDestroy(sw)
        
        set i = 0
        set sw = StopWatchCreate()
        loop
            set i = i + 1
            call TestB1000.evaluate() // x10 - 10,000 executions altogether.
            exitwhen(i == 10)
        endloop
        call BJDebugMsg("|cffddff66"+TITLE_B+" : "+R2S(StopWatchMark(sw)*100)+"|r")
        call StopWatchDestroy(sw)
    endfunction
    
    ///////////////////////////////
    // Registers the OnEsc event //
    ///////////////////////////////
    private function OnInit takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(t,function OnEsc)
        call Init()
    endfunction
endlibrary
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
Eh, I forgot the exact reason why UNIT_TYPE_DEAD isn't used. I think it is because it doesn't consider whether or not the unit is reincarnating. So +1 for usage of UnitAlive. :thup:
 

Azlier

Old World Ghost
Reaction score
461
Good show, old bean.

I always knew it had to be faster!
 

Romek

Super Moderator
Reaction score
963
I didn't really care much for the speed of this; it's a better check in that it has less chance of error. I guess it being that extra bit faster is a bonus though.

> Nestharus thought UnitAlive was a wrapper for UNIT_TYPE_DEAD, this strongly differs.
...Lol.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Where is [ljass]native UnitAlive takes unit id returns boolean[/ljass] defined actually?
 

Jedi

New Member
Reaction score
63
common.ai

If you have jasshelper you can declare it in a library or map script, else you need to import a common.j to your map.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Tnx Jedi.

>else you need to import a common.j to your map.
So I edit common.j and just add the line native UnitAilive...
And this works for all the other common.ai functions right?
 

Jedi

New Member
Reaction score
63
I am not sure about other common.ai natives.GetUnitGoldCost-WoodCost are working too(unless you use them on a hero).I have never needed others.
 

Laiev

Hey Listen!!
Reaction score
188
JASS:
//============================================================================
//  common.ai
//============================================================================
native DebugS               takes string str                            returns nothing
native DebugFI              takes string str, integer val               returns nothing
native DebugUnitID          takes string str, integer val               returns nothing
native DisplayText          takes integer p, string str                 returns nothing
native DisplayTextI         takes integer p, string str, integer val    returns nothing
native DoAiScriptDebug      takes nothing                               returns boolean

native GetAiPlayer          takes nothing                               returns integer
native GetHeroId            takes nothing                               returns integer
native GetHeroLevelAI       takes nothing                               returns integer

native GetUnitCount         takes integer unitid                        returns integer
native GetPlayerUnitTypeCount takes player p, integer unitid            returns integer
native GetUnitCountDone     takes integer unitid                        returns integer
native GetTownUnitCount     takes integer id, integer tn, boolean dn    returns integer
native GetUnitGoldCost      takes integer unitid                        returns integer
native GetUnitWoodCost      takes integer unitid                        returns integer

native GetMinesOwned        takes nothing                               returns integer
native GetGoldOwned         takes nothing                               returns integer
native TownWithMine         takes nothing                               returns integer
native TownHasMine          takes integer townid                        returns boolean
native TownHasHall          takes integer townid                        returns boolean

native GetUpgradeLevel      takes integer id                            returns integer
native GetUpgradeGoldCost   takes integer id                            returns integer
native GetUpgradeWoodCost   takes integer id                            returns integer
native GetNextExpansion     takes nothing                               returns integer
native GetMegaTarget        takes nothing                               returns unit
native GetBuilding          takes player p                              returns unit
native GetEnemyPower        takes nothing                               returns integer
native SetAllianceTarget    takes unit id                               returns nothing
native GetAllianceTarget    takes nothing                               returns unit

native SetProduce           takes integer qty, integer id, integer town returns boolean
native Unsummon             takes unit unitid                           returns nothing
native SetExpansion         takes unit peon, integer id                 returns boolean
native SetUpgrade           takes integer id                            returns boolean
native SetHeroLevels        takes code func                             returns nothing
native SetNewHeroes         takes boolean state                         returns nothing
native PurchaseZeppelin     takes nothing                               returns nothing

native MergeUnits           takes integer qty, integer a, integer b, integer make returns boolean

native SetCampaignAI        takes nothing                               returns nothing
native SetMeleeAI           takes nothing                               returns nothing
native SetTargetHeroes      takes boolean state                         returns nothing
native SetPeonsRepair       takes boolean state                         returns nothing
native SetHeroesFlee        takes boolean state                         returns nothing
native SetWatchMegaTargets  takes boolean state                         returns nothing
native SetIgnoreInjured     takes boolean state                         returns nothing
native SetHeroesTakeItems   takes boolean state                         returns nothing
native SetUnitsFlee         takes boolean state                         returns nothing
native SetGroupsFlee        takes boolean state                         returns nothing
native SetSlowChopping      takes boolean state                         returns nothing
native SetCaptainChanges    takes boolean allow                         returns nothing
native SetSmartArtillery    takes boolean state                         returns nothing
native SetReplacementCount  takes integer qty                           returns nothing
native GroupTimedLife       takes boolean allow                         returns nothing
native RemoveInjuries       takes nothing                               returns nothing
native RemoveSiege          takes nothing                               returns nothing

native InitAssault          takes nothing                               returns nothing
native AddAssault           takes integer qty, integer id               returns boolean
native AddDefenders         takes integer qty, integer id               returns boolean

native GetCreepCamp         takes integer min, integer max, boolean flyers_ok returns unit

native StartGetEnemyBase    takes nothing                               returns nothing
native WaitGetEnemyBase     takes nothing                               returns boolean
native GetEnemyBase         takes nothing                               returns unit
native GetExpansionFoe      takes nothing                               returns unit
native GetEnemyExpansion    takes nothing                               returns unit
native GetExpansionX        takes nothing                               returns integer
native GetExpansionY        takes nothing                               returns integer
native SetStagePoint        takes real x, real y                        returns nothing
native AttackMoveKill       takes unit target                           returns nothing
native AttackMoveXY         takes integer x, integer y                  returns nothing
native SuicidePlayer        takes player id, boolean check_full         returns boolean
native CaptainInCombat      takes boolean attack_captain                returns boolean
native IsTowered            takes unit target                           returns boolean

native ClearHarvestAI       takes nothing                               returns nothing
native HarvestGold          takes integer town, integer peons           returns nothing
native HarvestWood          takes integer town, integer peons           returns nothing
native GetExpansionPeon     takes nothing                               returns unit

native StopGathering        takes nothing                               returns nothing
native AddGuardPost         takes integer id, real x, real y            returns nothing
native FillGuardPosts       takes nothing                               returns nothing
native ReturnGuardPosts     takes nothing                               returns nothing
native CreateCaptains       takes nothing                               returns nothing
native SetCaptainHome       takes integer which, real x, real y         returns nothing
native ShiftTownSpot        takes real x, real y                        returns nothing
native TeleportCaptain      takes real x, real y                        returns nothing
native ClearCaptainTargets  takes nothing                               returns nothing
native CaptainAttack        takes real x, real y                        returns nothing
native CaptainVsUnits       takes player id                             returns nothing
native CaptainVsPlayer      takes player id                             returns nothing
native CaptainGoHome        takes nothing                               returns nothing
native CaptainIsHome        takes nothing                               returns boolean
native CaptainIsFull        takes nothing                               returns boolean
native CaptainIsEmpty       takes nothing                               returns boolean
native CaptainGroupSize     takes nothing                               returns integer
native CaptainReadiness     takes nothing                               returns integer
native CaptainRetreating    takes nothing                               returns boolean
native CaptainReadinessHP   takes nothing                               returns integer
native CaptainReadinessMa   takes nothing                               returns integer
native CaptainAtGoal        takes nothing                               returns boolean
native CreepsOnMap          takes nothing                               returns boolean
native SuicideUnit          takes integer count, integer unitid         returns nothing
native SuicideUnitEx        takes integer ct, integer uid, integer pid  returns nothing
native StartThread          takes code func                             returns nothing
native Sleep                takes real seconds                          returns nothing
native UnitAlive            takes unit id                               returns boolean
native IgnoredUnits         takes integer unitid                        returns integer
native TownThreated         takes nothing                               returns boolean

native CommandsWaiting      takes nothing                               returns integer
native GetLastCommand       takes nothing                               returns integer
native GetLastData          takes nothing                               returns integer
native PopLastCommand       takes nothing                               returns nothing

//============================================================================
//  Globals for all AI scripts
//============================================================================
globals

    //--------------------------------------------------------------------
    //  HUMANS
    //--------------------------------------------------------------------

    // human heroes
    constant integer ARCHMAGE           = 'Hamg'
    constant integer PALADIN            = 'Hpal'
    constant integer MTN_KING           = 'Hmkg'

    // human hero abilities
    constant integer AVATAR             = 'AHav'
    constant integer BASH               = 'AHbh'
    constant integer THUNDER_BOLT       = 'AHtb'
    constant integer THUNDER_CLAP       = 'AHtc'

    constant integer DEVOTION_AURA      = 'AHad'
    constant integer DIVINE_SHIELD      = 'AHds'
    constant integer HOLY_BOLT          = 'AHhb'
    constant integer RESURRECTION       = 'AHre'

    constant integer BLIZZARD           = 'AHbz'
    constant integer BRILLIANCE_AURA    = 'AHab'
    constant integer MASS_TELEPORT      = 'AHmt'
    constant integer WATER_ELEMENTAL    = 'AHwe'

    // special human heroes
    constant integer JAINA              = 'Hjai'
	constant integer MURADIN            = 'Hmbr'

    // human units                      
    constant integer COPTER             = 'hgyr'
    constant integer ELEMENTAL          = 'hwat'
    constant integer FOOTMAN            = 'hfoo'
    constant integer FOOTMEN            =  FOOTMAN
    constant integer GRYPHON            = 'hgry'
    constant integer KNIGHT             = 'hkni'
    constant integer MORTAR             = 'hmtm'
    constant integer PEASANT            = 'hpea'
    constant integer PRIEST             = 'hmpr'
    constant integer RIFLEMAN           = 'hrif'
    constant integer RIFLEMEN           =  RIFLEMAN
    constant integer SORCERESS          = 'hsor'
    constant integer TANK               = 'hmtt'
    constant integer MILITIA            = 'hmil'

    // human buildings
    constant integer AVIARY             = 'hgra'
    constant integer BARRACKS           = 'hbar'
    constant integer BLACKSMITH         = 'hbla'
    constant integer CANNON_TOWER       = 'hctw'
    constant integer CASTLE             = 'hcas'
    constant integer CHURCH             = 'htws'
    constant integer MAGE_TOWER         =  CHURCH
    constant integer GUARD_TOWER        = 'hgtw'
    constant integer HOUSE              = 'hhou'
    constant integer HUMAN_ALTAR        = 'halt'
    constant integer KEEP               = 'hkee'
    constant integer LUMBER_MILL        = 'hlum'
    constant integer SANCTUM            = 'hars'
    constant integer TOWN_HALL          = 'htow'
    constant integer WATCH_TOWER        = 'hwtw'
    constant integer WORKSHOP           = 'harm'

    // human upgrades
    constant integer UPG_MELEE          = 'Rhme'
    constant integer UPG_RANGED         = 'Rhra'
    constant integer UPG_ARTILLERY      = 'Rhaa'
    constant integer UPG_ARMOR          = 'Rhar'
    constant integer UPG_GOLD           = 'Rhmi'
    constant integer UPG_MASONRY        = 'Rhac'
    constant integer UPG_SIGHT          = 'Rhss'
    constant integer UPG_DEFEND         = 'Rhde'
    constant integer UPG_BREEDING       = 'Rhan'
    constant integer UPG_PRAYING        = 'Rhpt'
    constant integer UPG_SORCERY        = 'Rhst'
    constant integer UPG_LEATHER        = 'Rhla'
    constant integer UPG_GUN_RANGE      = 'Rhri'
    constant integer UPG_WOOD           = 'Rhlh'
    constant integer UPG_SENTINEL	    = 'Rhse'
    constant integer UPG_SCATTER		= 'Rhsr'
	constant integer UPG_BOMBS			= 'Rhgb'
	constant integer UPG_HAMMERS		= 'Rhhb'

    //--------------------------------------------------------------------
    //  ORCS
    //--------------------------------------------------------------------

    // orc heroes
    constant integer BLADE_MASTER       = 'Obla'
    constant integer FAR_SEER           = 'Ofar'
    constant integer TAUREN_CHIEF       = 'Otch'

    // special orc heroes
    constant integer GROM               = 'Ogrh'
    constant integer THRALL             = 'Othr'

    // orc hero abilities
    constant integer CRITICAL_STRIKE    = 'AOcr'
    constant integer MIRROR_IMAGE       = 'AOmi'
    constant integer BLADE_STORM        = 'AOww'
    constant integer WIND_WALK          = 'AOwk'

    constant integer CHAIN_LIGHTNING    = 'AOcl'
    constant integer EARTHQUAKE         = 'AOeq'
    constant integer FAR_SIGHT          = 'AOfs'
    constant integer SPIRIT_WOLF        = 'AOsf'

    constant integer ENDURANE_AURA      = 'AOae'
    constant integer REINCARNATION      = 'AOre'
    constant integer SHOCKWAVE          = 'AOsh'
    constant integer WAR_STOMP          = 'AOws'

    // orc units
    constant integer GUARDIAN           = 'oang'
    constant integer CATAPULT           = 'ocat'
    constant integer WITCH_DOCTOR       = 'odoc'
    constant integer GRUNT              = 'ogru'
    constant integer HEAD_HUNTER        = 'ohun'
    constant integer KODO_BEAST         = 'okod'
    constant integer PEON               = 'opeo'
    constant integer RAIDER             = 'orai'
    constant integer SHAMAN             = 'oshm'
    constant integer TAUREN             = 'otau'
    constant integer WYVERN             = 'owyv'

    // orc buildings
    constant integer ORC_ALTAR          = 'oalt'
    constant integer ORC_BARRACKS       = 'obar'
    constant integer BESTIARY           = 'obea'
    constant integer FORGE              = 'ofor'
    constant integer FORTRESS           = 'ofrt'
    constant integer GREAT_HALL         = 'ogre'
    constant integer LODGE              = 'osld'
    constant integer STRONGHOLD         = 'ostr'
    constant integer BURROW             = 'otrb'
    constant integer TOTEM              = 'otto'
    constant integer ORC_WATCH_TOWER    = 'owtw'

    // orc upgrades
    constant integer UPG_ORC_MELEE      = 'Rome'
    constant integer UPG_ORC_RANGED     = 'Rora'
    constant integer UPG_ORC_ARTILLERY  = 'Roaa'
    constant integer UPG_ORC_ARMOR      = 'Roar'
    constant integer UPG_ORC_WAR_DRUMS  = 'Rwdm'
    constant integer UPG_ORC_PILLAGE    = 'Ropg'
    constant integer UPG_ORC_BERSERK    = 'Robs'
    constant integer UPG_ORC_PULVERIZE  = 'Rows'
    constant integer UPG_ORC_ENSNARE    = 'Roen'
    constant integer UPG_ORC_WYVERNS    = 'Rowt'
    constant integer UPG_ORC_VENOM      = 'Rovs'
    constant integer UPG_ORC_DOCS       = 'Rowd'
    constant integer UPG_ORC_SHAMAN     = 'Rost'
    constant integer UPG_ORC_SPIKES     = 'Rosp'
    constant integer UPG_ORC_REGEN      = 'Rotr'

    // Warcraft 2 orc units
    constant integer OGRE_MAGI          = 'nomg'
    constant integer ORC_DRAGON         = 'nrwm'
    constant integer SAPPER             = 'ngsp'
    constant integer ZEPPLIN            = 'nzep'
    constant integer ZEPPELIN           =  ZEPPLIN
    constant integer W2_WARLOCK         = 'nw2w'
    constant integer PIG_FARM           = 'npgf'

    // special orc units
    constant integer CHAOS_GRUNT        = 'nchg'
    constant integer CHAOS_WARLOCK      = 'nchw'
    constant integer CHAOS_RAIDER       = 'nchr'
	constant integer CHAOS_PEON			= 'ncpn'
	constant integer CHAOS_KODO			= 'nckb'

    //--------------------------------------------------------------------
    //  UNDEAD
    //--------------------------------------------------------------------

    // undead heroes
    constant integer DEATH_KNIGHT       = 'Udea'
    constant integer DREAD_LORD         = 'Udre'
    constant integer LICH               = 'Ulic'

    // special undead heroes
    constant integer MALGANIS           = 'Umal'
    constant integer TICHONDRIUS        = 'Utic'
    constant integer PIT_LORD           = 'Npld'

    // undead hero abilities
    constant integer SLEEP              = 'AUsl'
    constant integer VAMP_AURA          = 'AUav'
    constant integer CARRION_SWARM      = 'AUcs'
    constant integer INFERNO            = 'AUin'

    constant integer DARK_RITUAL        = 'AUdr'
    constant integer DEATH_DECAY        = 'AUdd'
    constant integer FROST_ARMOR        = 'AUfa'
    constant integer FROST_NOVA         = 'AUfn'

    constant integer ANIM_DEAD          = 'AUan'
    constant integer DEATH_COIL         = 'AUdc'
    constant integer DEATH_PACT         = 'AUdp'
    constant integer UNHOLY_AURA        = 'AUau'

    // undead units
    constant integer ABOMINATION        = 'uabo'
    constant integer ACOLYTE            = 'uaco'
    constant integer BANSHEE            = 'uban'
    constant integer PIT_FIEND          = 'ucry'
    constant integer CRYPT_FIEND        =  PIT_FIEND
    constant integer FROST_WYRM         = 'ufro'
    constant integer GARGOYLE           = 'ugar'
    constant integer GARGOYLE_MORPH     = 'ugrm'
    constant integer GHOUL              = 'ugho'
    constant integer MEAT_WAGON         = 'umtw'
    constant integer NECRO              = 'unec'
    constant integer SKEL_WARRIOR       = 'uske'
    constant integer SHADE              = 'ushd'

    // undead buildings
    constant integer UNDEAD_MINE        = 'ugol'
    constant integer UNDEAD_ALTAR       = 'uaod'
    constant integer BONEYARD           = 'ubon'
    constant integer GARG_SPIRE         = 'ugsp'
    constant integer NECROPOLIS_1       = 'unpl'    // normal
    constant integer NECROPOLIS_2       = 'unp1'    // upgraded once
    constant integer NECROPOLIS_3       = 'unp2'    // full upgrade
    constant integer SAC_PIT            = 'usap'
    constant integer CRYPT              = 'usep'
    constant integer SLAUGHTERHOUSE     = 'uslh'
    constant integer DAMNED_TEMPLE      = 'utod'
    constant integer ZIGGURAT_1         = 'uzig'    // normal
    constant integer ZIGGURAT_2         = 'uzg1'    // upgraded
    constant integer GRAVEYARD          = 'ugrv'

    // undead upgrades
    constant integer UPG_UNHOLY_STR     = 'Rume'
    constant integer UPG_CR_ATTACK      = 'Rura'
    constant integer UPG_UNHOLY_ARMOR   = 'Ruar'
    constant integer UPG_CANNIBALIZE    = 'Ruac'
    constant integer UPG_GHOUL_FRENZY   = 'Rugf'
    constant integer UPG_FIEND_WEB      = 'Ruwb'
    constant integer UPG_ABOM           = 'Ruab'
    constant integer UPG_STONE_FORM     = 'Rusf'
    constant integer UPG_NECROS         = 'Rune'
    constant integer UPG_BANSHEE        = 'Ruba'
    constant integer UPG_MEAT_WAGON     = 'Rump'
    constant integer UPG_WYRM_BREATH    = 'Rufb'
    constant integer UPG_SKEL_LIFE      = 'Rusl'
    constant integer UPG_SACRIFICE      = 'Rurs'
    constant integer UPG_ABOM_EXPL      = 'Ruax'
    constant integer UPG_CR_ARMOR       = 'Rucr'
    constant integer UPG_PLAGUE         = 'Rupc'

    //--------------------------------------------------------------------
    //  ELVES
    //--------------------------------------------------------------------

    // elf heroes
    constant integer DEMON_HUNTER       = 'Edem'
    constant integer DEMON_HUNTER_M     = 'Edmm'
    constant integer KEEPER             = 'Ekee'
    constant integer MOON_CHICK         = 'Emoo'
    constant integer MOON_BABE          =  MOON_CHICK
    constant integer MOON_HONEY         =  MOON_CHICK

    // special elf heroes
    constant integer SYLVANUS           = 'Hvwd'
    constant integer CENARIUS           = 'Ecen'

    // elf hero abilities
    constant integer FORCE_NATURE       = 'AEfn'
    constant integer ENT_ROOTS          = 'AEer'
    constant integer THORNS_AURA        = 'AEah'
    constant integer TRANQUILITY        = 'AEtq'

    constant integer EVASION            = 'AEev'
    constant integer IMMOLATION         = 'AEim'
    constant integer MANA_BURN          = 'AEmb'
    constant integer METAMORPHOSIS      = 'AEme'

    constant integer SEARING_ARROWS     = 'AHfa'
    constant integer SCOUT              = 'AEst'
    constant integer STARFALL           = 'AEsf'
    constant integer TRUESHOT           = 'AEar'

    // elf units
    constant integer WISP               = 'ewsp'
    constant integer ARCHER             = 'earc'
    constant integer DRUID_TALON        = 'edot'
    constant integer DRUID_TALON_M      = 'edtm'
    constant integer BALLISTA           = 'ebal'
    constant integer DRUID_CLAW         = 'edoc'
    constant integer DRUID_CLAW_M       = 'edcm'
    constant integer DRYAD              = 'edry'
    constant integer HIPPO              = 'ehip'
    constant integer HIPPO_RIDER        = 'ehpr'
    constant integer HUNTRESS           = 'esen'
    constant integer CHIMAERA           = 'echm'
    constant integer ENT                = 'efon'

    // special elf units
    constant integer HIGH_ARCHER        = 'nhea'
    constant integer HIGH_FOOTMAN       = 'hcth'
    constant integer HIGH_FOOTMEN       =  HIGH_FOOTMAN
    constant integer HIGH_SWORDMAN      = 'hhes'
    constant integer DRAGON_HAWK        = 'nws1'
	constant integer CORRUPT_TREANT     = 'nenc'
	constant integer POISON_TREANT      = 'nenp'
	constant integer PLAGUE_TREANT      = 'nepl'
    constant integer SHANDRIS           = 'eshd'

    // elf buildings
    constant integer ANCIENT_LORE       = 'eaoe'
    constant integer ANCIENT_WAR        = 'eaom'
    constant integer ANCIENT_WIND       = 'eaow'
    constant integer TREE_AGES          = 'etoa'
    constant integer TREE_ETERNITY      = 'etoe'
    constant integer TREE_LIFE          = 'etol'
    constant integer ANCIENT_PROTECT    = 'etrp'
    constant integer ELF_ALTAR          = 'eate'
    constant integer BEAR_DEN           = 'edol'
    constant integer CHIMAERA_ROOST     = 'edos'
    constant integer HUNTERS_HALL       = 'edob'
    constant integer MOON_WELL          = 'emow'
    constant integer ELF_MINE           = 'egol'

    // special elf buildings
    constant integer ELF_FARM           = 'nefm'
    constant integer ELF_GUARD_TOWER    = 'negt'
	constant integer HIGH_SKY           = 'negm'
    constant integer HIGH_EARTH         = 'negf'
    constant integer HIGH_TOWER         = 'negt'
    constant integer ELF_HIGH_BARRACKS  = 'nheb'
	constant integer CORRUPT_LIFE		= 'nctl'
	constant integer CORRUPT_WELL		= 'ncmw'
	constant integer CORRUPT_PROTECTOR	= 'ncap'
	constant integer CORRUPT_WAR		= 'ncaw'

    // elf upgrades
    constant integer UPG_STR_MOON       = 'Resm'
    constant integer UPG_STR_WILD       = 'Resw'
    constant integer UPG_MOON_ARMOR     = 'Rema'
    constant integer UPG_HIDES          = 'Rerh'
    constant integer UPG_ULTRAVISION    = 'Reuv'
    constant integer UPG_BLESSING       = 'Renb'
    constant integer UPG_SCOUT          = 'Resc'
    constant integer UPG_GLAIVE         = 'Remg'
    constant integer UPG_BOWS           = 'Reib'
    constant integer UPG_MARKSMAN       = 'Remk'
    constant integer UPG_DRUID_TALON    = 'Redt'
    constant integer UPG_DRUID_CLAW     = 'Redc'
    constant integer UPG_ABOLISH		= 'Resi'
    constant integer UPG_CHIM_ACID      = 'Recb'
    constant integer UPG_HIPPO_TAME     = 'Reht'
	constant integer UPG_BOLT			= 'Repd'

    //--------------------------------------------------------------------
    // Neutral
    //--------------------------------------------------------------------
    constant integer DEMON_GATE         = 'ndmg'
    constant integer FELLHOUND          = 'nfel'
    constant integer INFERNAL           = 'ninf'
    constant integer DOOMGUARD          = 'nbal'
	constant integer SATYR		        = 'nsty'
	constant integer TRICKSTER          = 'nsat'
	constant integer SHADOWDANCER       = 'nsts'
	constant integer SOULSTEALER		= 'nstl'
	constant integer HELLCALLER         = 'nsth'
	constant integer SKEL_ARCHER        = 'nska'
	constant integer SKEL_MARKSMAN      = 'nskm'
	constant integer SKEL_BURNING       = 'nskf'
	constant integer SKEL_GIANT			= 'nskg'
	constant integer FURBOLG			= 'nfrl'
	constant integer FURBOLG_TRACKER	= 'nfrb'
	constant integer FURBOLG_SHAMAN		= 'nfrs'
	constant integer FURBOLG_CHAMP		= 'nfrg'
	constant integer FURBOLG_ELDER		= 'nfre'

    //--------------------------------------------------------------------
    constant integer M1                 =    60
    constant integer M2                 =  2*60
    constant integer M3                 =  3*60
    constant integer M4                 =  4*60
    constant integer M5                 =  5*60
    constant integer M6                 =  6*60
    constant integer M7                 =  7*60
    constant integer M8                 =  8*60
    constant integer M9                 =  9*60
    constant integer M10                = 10*60
    constant integer M11                = 11*60
    constant integer M12                = 12*60
    constant integer M13                = 13*60
    constant integer M14                = 14*60
    constant integer M15                = 15*60

    constant integer EASY               = 1
    constant integer NORMAL             = 2
    constant integer HARD               = 3
    constant integer INSANE             = 4

    constant integer ATTACK_CAPTAIN     = 1
    constant integer DEFENSE_CAPTAIN    = 2
    constant integer BOTH_CAPTAINS      = 3

    constant integer BUILD_UNIT         = 1
    constant integer BUILD_UPGRADE      = 2
    constant integer BUILD_EXPAND       = 3

    //--------------------------------------------------------------------

    player  ai_player

    integer sleep_seconds
    integer total_gold              = 0
    integer total_wood              = 0
    integer gold_buffer             = 0 // usually for potion money
    integer difficulty              = NORMAL
    integer racial_farm             = 'hhou'
    integer hero_id                 = 'Hamg'
    integer hero_id2                = 'Hmkg'
    integer array skills1
    integer array skills2
    integer max_hero_level          = 0

    integer array harass_qty
    integer array harass_max
    integer array harass_units
    integer harass_length           = 0

    integer array defense_qty
    integer array defense_units
    integer defense_length          = 0

    integer array build_qty
    integer array build_type
    integer array build_item
    integer array build_town
    integer build_length            = 0

    integer campaign_gold_peons     = 5
    integer campaign_wood_peons     = 3
    integer campaign_basics_speed   = 5

    boolean harvest_town1           = true
    boolean harvest_town2           = true
    boolean harvest_town3           = true
    boolean do_campaign_farms       = true
    boolean two_heroes              = false
    boolean allow_air_creeps        = false
    boolean take_exp                = false
    boolean allow_signal_abort      = false
    boolean ready_for_zeppelin      = true
    boolean get_zeppelin            = false

    boolean build_campaign_attackers = true

    boolean do_debug_cheats         = false
    boolean trace_on                = true
endglobals

//============================================================================
function Trace takes string message returns nothing
    if trace_on then
        call DisplayText(GetAiPlayer(),message)
    endif
endfunction

//============================================================================
function TraceI takes string message, integer val returns nothing
    if trace_on then
        call DisplayTextI(GetAiPlayer(),message,val)
    endif
endfunction
 
//============================================================================
function InitAI takes nothing returns nothing
    set ai_player = Player(GetAiPlayer())
    set sleep_seconds = 0
    call StopGathering()
endfunction

//============================================================================
function StandardAI takes code heroes, code peons, code attacks returns nothing
    call InitAI()
    call SetTargetHeroes(true)
    call SetMeleeAI()
    call SetHeroesFlee(true)
    call SetWatchMegaTargets(true)
    call SetHeroesTakeItems(true)
    call SetIgnoreInjured(true)
    call SetUnitsFlee(true)
    call SetGroupsFlee(true)
    call SetPeonsRepair(true)
    call CreateCaptains()
    call SetSmartArtillery(true)
    call SetHeroLevels(heroes)
    call Sleep(0.1)
    call StartThread(peons)
    call StartThread(attacks)
endfunction

//============================================================================
//  Utility Functions
//============================================================================
function SuicideSleep takes integer seconds returns nothing
    set sleep_seconds = sleep_seconds - seconds
    loop
        exitwhen seconds <= 0
        exitwhen allow_signal_abort and CommandsWaiting() != 0

        if seconds >= 5 then
            call Sleep(5)
            set seconds = seconds - 5
        else
            call Sleep(seconds)
            set seconds = 0
        endif
    endloop
endfunction

//============================================================================
function WaitForSignal takes nothing returns integer
    local integer cmd
    loop
        exitwhen CommandsWaiting() != 0
        call Sleep(2)
    endloop

    set cmd = GetLastCommand()
    call PopLastCommand()
    return cmd
endfunction

//============================================================================
function GetMinorCreep takes nothing returns unit
    return GetCreepCamp(0,9,false)
endfunction

//============================================================================
function GetMajorCreep takes nothing returns unit
    return GetCreepCamp(10,100,allow_air_creeps)
endfunction

//============================================================================
function GetGold takes nothing returns integer
    return GetPlayerState(ai_player,PLAYER_STATE_RESOURCE_GOLD)
endfunction

//============================================================================
function GetWood takes nothing returns integer
    return GetPlayerState(ai_player,PLAYER_STATE_RESOURCE_LUMBER)
endfunction

//============================================================================
function InitBuildArray takes nothing returns nothing
    set build_length = 0
endfunction

//============================================================================
function InitAssaultGroup takes nothing returns nothing
    set harass_length = 0
endfunction

//============================================================================
function InitDefenseGroup takes nothing returns nothing
    set defense_length = 0
endfunction

//============================================================================
function InitMeleeGroup takes nothing returns nothing
    call InitAssaultGroup()
    call RemoveInjuries()
    call RemoveSiege()
endfunction

//============================================================================
function PrepFullSuicide takes nothing returns nothing
    call InitAssaultGroup()
    call InitDefenseGroup()
    set campaign_gold_peons = 0
    set campaign_wood_peons = 0
endfunction

//============================================================================
function SetReplacements takes integer easy, integer med, integer hard returns nothing
    if difficulty == EASY then
        call SetReplacementCount(easy)
    elseif difficulty == NORMAL then
        call SetReplacementCount(med)
    else
        call SetReplacementCount(hard)
    endif
endfunction

//============================================================================
function StartTownBuilder takes code func returns nothing
    call StartThread(func)
endfunction

//============================================================================
function SetBuildAll takes integer t, integer qty, integer unitid, integer town returns nothing
    if qty > 0 then
        set build_qty[build_length] = qty
        set build_type[build_length] = t
        set build_item[build_length] = unitid
        set build_town[build_length] = town
        set build_length = build_length + 1
    endif
endfunction

//============================================================================
function SetBuildUnit takes integer qty, integer unitid returns nothing
    call SetBuildAll(BUILD_UNIT,qty,unitid,-1)
endfunction

//============================================================================
function SetBuildUnitEx takes integer easy, integer med, integer hard, integer unitid returns nothing
    if difficulty == EASY then
        call SetBuildAll(BUILD_UNIT,easy,unitid,-1)
    elseif difficulty == NORMAL then
        call SetBuildAll(BUILD_UNIT,med,unitid,-1)
    else
        call SetBuildAll(BUILD_UNIT,hard,unitid,-1)
    endif
endfunction

//============================================================================
function SecondaryTown takes integer town, integer qty, integer unitid returns nothing
    call SetBuildAll(BUILD_UNIT,qty,unitid,town)
endfunction

//============================================================================
function SecTown takes integer town, integer qty, integer unitid returns nothing
    call SetBuildAll(BUILD_UNIT,qty,unitid,town)
endfunction

//============================================================================
function SetBuildUpgr takes integer qty, integer unitid returns nothing
    call SetBuildAll(BUILD_UPGRADE,qty,unitid,-1)
endfunction

//============================================================================
function SetBuildUpgrEx takes integer easy, integer med, integer hard, integer unitid returns nothing
    if difficulty == EASY then
        call SetBuildAll(BUILD_UPGRADE,easy,unitid,-1)
    elseif difficulty == NORMAL then
        call SetBuildAll(BUILD_UPGRADE,med,unitid,-1)
    else
        call SetBuildAll(BUILD_UPGRADE,hard,unitid,-1)
    endif
endfunction

//============================================================================
function SetBuildExpa takes integer qty, integer unitid returns nothing
    call SetBuildAll(BUILD_EXPAND,qty,unitid,-1)
endfunction

//============================================================================
function StartUpgrade takes integer level, integer upgid returns boolean
    local integer gold_cost
    local integer wood_cost

    if GetUpgradeLevel(upgid) >= level then
        return true
    endif

    set gold_cost = GetUpgradeGoldCost(upgid)
    if total_gold < gold_cost then
        return false
    endif

    set wood_cost = GetUpgradeWoodCost(upgid)
    if total_wood < wood_cost then
        return false
    endif

    return SetUpgrade(upgid)
endfunction

//============================================================================
function BuildFactory takes integer unitid returns nothing
    if GetGold() > 1000 and GetWood() > 500 then
        call SetBuildUnit( 2, unitid )
    else
        call SetBuildUnit( 1, unitid )
    endif
endfunction

//============================================================================
function HallsCompleted takes integer unitid returns boolean
    return GetUnitCount(unitid) == GetUnitCountDone(unitid) 
endfunction

//============================================================================
function GuardSecondary takes integer townid, integer qty, integer unitid returns nothing
    if TownHasHall(townid) and TownHasMine(townid) then
        call SecondaryTown( townid, qty, unitid )
    endif
endfunction

//============================================================================
function GetUnitCountEx takes integer unitid, boolean only_done, integer townid returns integer
    if townid == -1 then
        if only_done then
            return GetUnitCountDone(unitid)
        else
            return GetUnitCount(unitid)
        endif
    else
        return GetTownUnitCount(unitid,townid,only_done)
    endif
endfunction

//============================================================================
function TownCountEx takes integer unitid, boolean only_done, integer townid returns integer

    local integer have_qty = GetUnitCountEx(unitid,only_done,townid)

    if unitid == TOWN_HALL then
        set have_qty = have_qty + GetUnitCountEx(KEEP,false,townid) + GetUnitCountEx(CASTLE,false,townid)
    elseif unitid == KEEP then
        set have_qty = have_qty  + GetUnitCountEx(CASTLE,false,townid)

    elseif unitid == WATCH_TOWER then
        set have_qty = have_qty + GetUnitCountEx(GUARD_TOWER,false,townid) + GetUnitCountEx(CANNON_TOWER,false,townid)

    elseif unitid == PEASANT then
        set have_qty = have_qty + GetUnitCountEx(MILITIA,false,townid)

    elseif unitid == GREAT_HALL then
        set have_qty = have_qty + GetUnitCountEx(STRONGHOLD,false,townid) + GetUnitCountEx(FORTRESS,false,townid)
    elseif unitid == STRONGHOLD then
        set have_qty = have_qty + GetUnitCountEx(FORTRESS,false,townid)

    elseif unitid == NECROPOLIS_1 then
        set have_qty = have_qty + GetUnitCountEx(NECROPOLIS_2,false,townid) + GetUnitCountEx(NECROPOLIS_3,false,townid)
    elseif unitid == NECROPOLIS_2 then
        set have_qty = have_qty + GetUnitCountEx(NECROPOLIS_3,false,townid)

    elseif unitid == ZIGGURAT_1 then
        set have_qty = have_qty + GetUnitCountEx(ZIGGURAT_2,false,townid)

    elseif unitid == GARGOYLE then
        set have_qty = have_qty + GetUnitCountEx(GARGOYLE_MORPH,false,townid)

    elseif unitid == TREE_LIFE then
        set have_qty = have_qty + GetUnitCountEx(TREE_AGES,false,townid) + GetUnitCountEx(TREE_ETERNITY,false,townid)
    elseif unitid == TREE_AGES then
        set have_qty = have_qty + GetUnitCountEx(TREE_ETERNITY,false,townid)

    elseif unitid == DRUID_TALON then
        set have_qty = have_qty + GetUnitCountEx(DRUID_TALON_M,false,townid)
    elseif unitid == DRUID_TALON_M then
        set have_qty = have_qty + GetUnitCountEx(DRUID_TALON,only_done,townid)

    elseif unitid == DRUID_CLAW then
        set have_qty = have_qty + GetUnitCountEx(DRUID_CLAW_M,false,townid)
    elseif unitid == DRUID_CLAW_M then
        set have_qty = have_qty + GetUnitCountEx(DRUID_CLAW,only_done,townid)
    endif

    return have_qty
endfunction

//============================================================================
function TownCountDone takes integer base returns integer
    return TownCountEx(base,true,-1)
endfunction

//============================================================================
function TownCount takes integer base returns integer
    return TownCountEx(base,false,-1)
endfunction

//============================================================================
function BasicExpansion takes boolean build_it, integer unitid returns nothing
    if build_it and GetGold() < 2000 and HallsCompleted(unitid) then
        call SetBuildExpa( TownCount(unitid)+1, unitid )
    endif
endfunction

//============================================================================
function UpgradeAll takes integer baseid, integer newid returns nothing
    call SetBuildUnit( TownCountDone(baseid), newid )
endfunction

//============================================================================
function TownCountTown takes integer base, integer townid returns integer
    return TownCountEx(base,false,townid)
endfunction

//============================================================================
//  FoodPool
//============================================================================
function FoodPool takes integer food, boolean weak, integer id1, integer use1, boolean strong, integer id2, integer use2 returns nothing
    if strong then
        call SetBuildUnit( (food - use1 * TownCount(id1)) / use2, id2 )
    elseif weak then
        call SetBuildUnit( (food - use2 * TownCount(id2)) / use1, id1 )
    endif
endfunction

//============================================================================
//  MeleeTownHall
//============================================================================
function MeleeTownHall takes integer townid, integer unitid returns nothing
    if TownHasMine(townid) and not TownHasHall(townid) then
        call SecondaryTown ( townid, 1, unitid )
    endif
endfunction

//============================================================================
function WaitForUnits takes integer unitid, integer qty returns nothing
    loop
        exitwhen TownCountDone(unitid) == qty
        call Sleep(2)
    endloop
endfunction

//============================================================================
function StartUnit takes integer ask_qty, integer unitid, integer town returns boolean
    local integer have_qty
    local integer need_qty
    local integer afford_gold
    local integer afford_wood
    local integer afford_qty
    local integer gold_cost
    local integer wood_cost

    //------------------------------------------------------------------------
    // if we have all we're asking for then make nothing
    //
    if town == -1 then
        set have_qty = TownCount(unitid)
    else
        set have_qty = TownCountTown(unitid,town)
    endif

    if have_qty >= ask_qty then
        return true
    endif
    set need_qty = ask_qty - have_qty

    //------------------------------------------------------------------------
    // limit the qty we're requesting to the amount of resources available
    //
    set gold_cost = GetUnitGoldCost(unitid)
    set wood_cost = GetUnitWoodCost(unitid)

    if gold_cost == 0 then
        set afford_gold = need_qty
    else
        set afford_gold = total_gold / gold_cost
    endif
    if afford_gold < need_qty then
        set afford_qty = afford_gold
    else
        set afford_qty = need_qty
    endif

    if wood_cost == 0 then
        set afford_wood = need_qty
    else
        set afford_wood = total_wood / wood_cost
    endif
    if afford_wood < afford_qty then
        set afford_qty = afford_wood
    endif

    // if we're waiting on gold/wood; pause build orders
    if afford_qty < 1 then
        return false
    endif

    //------------------------------------------------------------------------
    // whether we make right now what we're requesting or not, assume we will
    // and deduct the cost of the units from our fake gold total right away
    //
    set total_gold = total_gold - gold_cost * need_qty
    set total_wood = total_wood - wood_cost * need_qty

    if total_gold < 0 then
        set total_gold = 0
    endif
    if total_wood < 0 then
        set total_wood = 0
    endif

    //------------------------------------------------------------------------
    // give the AI a chance to make the units (it may not be able to right now
    // but that doesn't stop us from trying other units after this as long
    // as we have enough money to make this AND the needed, unbuilt ones)
    //
    return SetProduce(afford_qty,unitid,town)
endfunction

//============================================================================
function WaitForTown takes integer towns, integer townid returns nothing
    local integer i = 0
    loop
        call Sleep(10)
        exitwhen TownCount(townid) >= towns
        set i = i + 1
        exitwhen i == 12
    endloop
endfunction

//============================================================================
function StartExpansion takes integer qty, integer hall returns boolean
    local integer count
    local integer town
    local unit    peon
    local integer gold_cost

    set count = TownCount(hall)
    if count >= qty then
        return true
    endif

    set town = GetNextExpansion()
    if town == -1 then
        return true
    endif

    set take_exp = true

    set gold_cost = GetUnitGoldCost(hall)
    if gold_cost > total_gold then
        return false
    endif
    set total_gold = total_gold - gold_cost

    if GetExpansionFoe() != null then
        return true
    endif

    set peon = GetExpansionPeon()
    if peon != null then
        return SetExpansion(peon,hall)
    endif

    return true
endfunction

//============================================================================
function OneBuildLoop takes nothing returns nothing
    local integer index = 0
    local integer qty
    local integer id
    local integer tp

    set total_gold   = GetGold() - gold_buffer
    set total_wood   = GetWood()

    loop
        exitwhen index == build_length

        set qty  = build_qty[index]
        set id   = build_item[index]
        set tp   = build_type[index]

        //--------------------------------------------------------------------
        if tp == BUILD_UNIT then
            if not StartUnit(qty,id,build_town[index]) then
                return
            endif

        //--------------------------------------------------------------------
        elseif tp == BUILD_UPGRADE then
            call StartUpgrade(qty,id)

        //--------------------------------------------------------------------
        else // tp == BUILD_EXPAND
            if not StartExpansion(qty,id) then
                return
            endif
        endif

        set index = index + 1
    endloop
endfunction

//============================================================================
function StaggerSleep takes real base, real spread returns nothing
    call Sleep(base + spread * I2R(GetAiPlayer()) / I2R(GetPlayers()))
endfunction

//============================================================================
function BuildLoop takes nothing returns nothing
    call OneBuildLoop()
    call StaggerSleep(1,2)
    loop
        call OneBuildLoop()
        call Sleep(2)
    endloop
endfunction

//============================================================================
function StartBuildLoop takes nothing returns nothing
    call StartThread(function BuildLoop)
endfunction

//============================================================================
function SetInitialWave takes integer seconds returns nothing
    set sleep_seconds = seconds
endfunction

//============================================================================
function AddSleepSeconds takes integer seconds returns nothing
    set sleep_seconds = sleep_seconds + seconds
endfunction

//============================================================================
function SleepForever takes nothing returns nothing
    loop
        call Sleep(100)
    endloop
endfunction

//============================================================================
function PlayGame takes nothing returns nothing
    call StartBuildLoop()
    call SleepForever()
endfunction

//============================================================================
function Conversions takes integer desire, integer unitid returns nothing
    if unitid == HIPPO_RIDER then
        call MergeUnits(desire,ARCHER,HIPPO,HIPPO_RIDER)
    endif
endfunction

//============================================================================
function SetAssaultGroup takes integer qty, integer max, integer unitid returns nothing
    call Conversions(max,unitid)

    if qty <= 0 and TownCountDone(unitid) == 0 then
        return
    endif
    set harass_qty[harass_length] = qty
    set harass_max[harass_length] = max
    set harass_units[harass_length] = unitid
    set harass_length = harass_length + 1
endfunction

//============================================================================
function Interleave3 takes integer e1, integer m1, integer h1, integer u1, integer e2, integer m2, integer h2, integer u2, integer e3, integer m3, integer h3, integer u3 returns nothing
    local integer i1 = 1
    local integer i2 = 1
    local integer i3 = 1
    local integer q1
    local integer q2
    local integer q3

    if difficulty == EASY then
        set q1 = e1
        set q2 = e2
        set q3 = e3
    elseif difficulty == NORMAL then
        set q1 = m1
        set q2 = m2
        set q3 = m3
    else // difficulty == HARD
        set q1 = h1
        set q2 = h2
        set q3 = h3
    endif

    loop
        exitwhen q1<=0 and q2<=0 and q3<=0

        if q1 > 0 then
            call SetAssaultGroup(i1,i1,u1)
            set q1 = q1 - 1
            set i1 = i1 + 1
        endif

        if q2 > 0 then
            call SetAssaultGroup(i2,i2,u2)
            set q2 = q2 - 1
            set i2 = i2 + 1
        endif

        if q3 > 0 then
            call SetAssaultGroup(i3,i3,u3)
            set q3 = q3 - 1
            set i3 = i3 + 1
        endif
    endloop
endfunction

//============================================================================
function SetMeleeGroup takes integer unitid returns nothing
    if unitid == hero_id then
        call SetAssaultGroup(1,9,unitid)
    else
        call SetAssaultGroup((TownCountDone(unitid)*3)/4,12,unitid)
    endif
endfunction

//============================================================================
function CampaignDefender takes integer level, integer qty, integer unitid returns nothing
    if qty > 0 and difficulty >= level then
        set defense_qty[defense_length] = qty
        set defense_units[defense_length] = unitid
        set defense_length = defense_length + 1
        call Conversions(qty,unitid)
        call SetBuildUnit(qty,unitid)
    endif
endfunction

//============================================================================
function CampaignDefenderEx takes integer easy, integer med, integer hard, integer unitid returns nothing
    if difficulty == EASY then
        call CampaignDefender(EASY,easy,unitid)
    elseif difficulty == NORMAL then
        call CampaignDefender(NORMAL,med,unitid)
    else
        call CampaignDefender(HARD,hard,unitid)
    endif
endfunction


continue \/
 

Laiev

Hey Listen!!
Reaction score
188
JASS:
//============================================================================
function CampaignAttacker takes integer level, integer qty, integer unitid returns nothing
    if qty > 0 and difficulty >= level then 
        call SetAssaultGroup(qty,qty,unitid)
    endif
endfunction

//============================================================================
function CampaignAttackerEx takes integer easy, integer med, integer hard, integer unitid returns nothing
    if difficulty == EASY then
        call CampaignAttacker(EASY,easy,unitid)
    elseif difficulty == NORMAL then
        call CampaignAttacker(NORMAL,med,unitid)
    else
        call CampaignAttacker(HARD,hard,unitid)
    endif
endfunction

//============================================================================
function FormGroup takes integer seconds, boolean testReady returns nothing
    local integer index
    local integer count
    local integer unitid
    local integer desire
    local integer readyPercent

    // normally test for CaptainReadiness() of 50%
    if testReady == true then
        set readyPercent = 50
    else
        set readyPercent = 0
    endif

    loop
        call SuicideSleep(seconds)
        call InitAssault()

        set index = 0
        loop
            exitwhen index == harass_length

            set unitid = harass_units[index]
            set desire = harass_max[index]
            set count  = TownCountDone(unitid)

            call Conversions(desire,unitid)

            if count >= desire then
                call AddAssault(desire,unitid)
            else
                set desire = harass_qty[index]

                if count < desire then
                    call AddAssault(desire,unitid)
                else
                    call AddAssault(count,unitid)
                endif
            endif

            set index = index + 1
        endloop

        // time out and send group anyway if time has already expired
        exitwhen sleep_seconds < -60
        exitwhen CaptainInCombat(true)
        exitwhen CaptainIsFull() and CaptainReadiness() >= readyPercent
    endloop
endfunction

//============================================================================
function WavePrepare takes integer unitid returns integer
    if unitid=='ebal' or unitid=='hgry' or unitid=='hkni' or unitid=='ocat' or unitid=='uabo' or unitid=='umtw' or unitid=='otau' or unitid=='nfel' then
        return 50
    endif
    if unitid=='hmtt' or unitid=='echm' or unitid=='ufro' or unitid=='etrp' or unitid=='ninf' then
        return 80
    endif
    return 40
endfunction

//============================================================================
function PrepTime takes nothing returns integer
    local integer unitid
    local integer missing
    local integer prep
    local integer count
    local integer largest = 30
    local integer index = 0

    loop
        exitwhen index == harass_length

        set unitid  = harass_units[index]
        set missing = harass_qty[index] + IgnoredUnits(unitid) - TownCount(unitid)
        set prep    = WavePrepare(unitid) * missing

        if prep > largest then
            set largest = prep
        endif

        set index = index + 1
    endloop

    return largest
endfunction

//============================================================================
function SuicideOnPlayer takes integer seconds, player p returns nothing
    local integer wave_prep = PrepTime()
    local integer save_peons
    local integer save_length

    set save_length = harass_length
    set harass_length = 0

    call AddSleepSeconds(seconds)
    if sleep_seconds-wave_prep > 0 then
        call SuicideSleep(sleep_seconds-wave_prep)
    endif

    set harass_length = save_length

    if harass_length < 1 then
        return
    endif

    set save_peons = campaign_wood_peons
    set campaign_wood_peons = 0
    loop
        exitwhen allow_signal_abort and CommandsWaiting() != 0

        loop
            exitwhen allow_signal_abort and CommandsWaiting() != 0

            call FormGroup(5, true)
            exitwhen sleep_seconds <= 0
        endloop

        exitwhen SuicidePlayer(p,sleep_seconds >= -60)
        call SuicideSleep(5)
    endloop

    set campaign_wood_peons = save_peons
    set harass_length = 0

    loop
        exitwhen allow_signal_abort and CommandsWaiting() != 0

        exitwhen CaptainInCombat(true)
        exitwhen CaptainIsEmpty()
        call SuicideSleep(10)
        exitwhen sleep_seconds < -300
    endloop

    loop
        exitwhen allow_signal_abort and CommandsWaiting() != 0

        exitwhen CaptainIsEmpty()
        call SuicideSleep(10)
        exitwhen sleep_seconds < -300
    endloop
endfunction

//============================================================================
function SuicideUntilSignal takes integer seconds, player p returns nothing
    local integer save
    local integer wave_prep = PrepTime()

    loop
        call AddSleepSeconds(seconds)
        if sleep_seconds-wave_prep > 0 then
            call SuicideSleep(sleep_seconds-wave_prep)
        endif

        set save = campaign_wood_peons
        set campaign_wood_peons = 0
        loop
            loop
                call FormGroup(5, true)
                exitwhen sleep_seconds <= 0
                exitwhen CommandsWaiting() != 0
            endloop
            exitwhen SuicidePlayer(p,sleep_seconds >= -60)
            exitwhen CommandsWaiting() != 0
            call SuicideSleep(3)
        endloop
        set campaign_wood_peons = save

        loop
            exitwhen CaptainIsEmpty()
            exitwhen CommandsWaiting() != 0
            call SuicideSleep(5)
        endloop
        exitwhen CommandsWaiting() != 0
    endloop
endfunction

//============================================================================
function SuicideUnitA takes integer unitid returns nothing
    if unitid != 0 then
        call SuicideUnit(1,unitid)
    endif
    call Sleep(0.1)
endfunction

//============================================================================
function SuicideUnits takes integer u1, integer u2, integer u3, integer u4, integer u5, integer u6, integer u7, integer u8, integer u9, integer uA returns nothing
    call PrepFullSuicide()
    loop
        call SuicideUnitA(u1)
        call SuicideUnitA(u2)
        call SuicideUnitA(u3)
        call SuicideUnitA(u4)
        call SuicideUnitA(u5)
        call SuicideUnitA(u6)
        call SuicideUnitA(u7)
        call SuicideUnitA(u8)
        call SuicideUnitA(u9)
        call SuicideUnitA(uA)
    endloop
endfunction

//============================================================================
function SuicideOnPlayerEx takes integer easy, integer med, integer hard, player p returns nothing
    if difficulty == EASY then
        call SuicideOnPlayer(easy,p)
    elseif difficulty == NORMAL then
        call SuicideOnPlayer(med,p)
    else
        call SuicideOnPlayer(hard,p)
    endif
endfunction

//============================================================================
function ForeverSuicideOnPlayer takes integer seconds, player p returns nothing
    local integer length = harass_length
    loop
        exitwhen allow_signal_abort and CommandsWaiting() != 0
        call SuicideOnPlayer(seconds,p)
        set harass_length = length
    endloop
endfunction

//============================================================================
function SleepInCombat takes nothing returns nothing
    local integer count = 0
    loop
        loop
            exitwhen not CaptainInCombat(true)  // goal is cleared
            call SuicideSleep(1)
        endloop

        set count = count + 1
        exitwhen count >= 8
    endloop
endfunction

//============================================================================
function CommonSleepUntilTargetDead takes unit target, boolean reform returns nothing
    loop
        exitwhen CaptainRetreating()
        exitwhen CaptainReadinessHP() <= 40

        exitwhen not UnitAlive(target)  // target killed
        call AttackMoveKill(target)

        call SuicideSleep(3)

        if reform and sleep_seconds < -40 then
            if CaptainInCombat(true) then
                set sleep_seconds = sleep_seconds + 5
            else
                set sleep_seconds = 0
                call FormGroup(1,false)
            endif
        endif
    endloop
endfunction

//============================================================================
function SleepUntilTargetDead takes unit target returns nothing
    call CommonSleepUntilTargetDead(target,false)
endfunction

//============================================================================
function ReformUntilTargetDead takes unit target returns nothing
    call CommonSleepUntilTargetDead(target,true)
endfunction

//============================================================================
function SleepUntilAtGoal takes nothing returns nothing
    loop
        exitwhen CaptainRetreating()
        exitwhen CaptainAtGoal()        // reached goal
        exitwhen CaptainIsHome()        // failed to path and returned home
        exitwhen CaptainIsEmpty()       // all units died
        call SuicideSleep(3)
    endloop
endfunction

//============================================================================
function AttackMoveKillA takes unit target returns nothing
    if target == null then
        call SuicideSleep(3)
        return
    endif

    call AttackMoveKill(target)
    call ReformUntilTargetDead(target)
    call SleepInCombat()
endfunction

//============================================================================
function AttackMoveXYA takes integer x, integer y returns nothing
    call AttackMoveXY(x,y)
    call SleepUntilAtGoal()
    call SleepInCombat()
endfunction

//============================================================================
function MinorCreepAttack takes nothing returns nothing
    local unit target = GetMinorCreep()
    call SetAllianceTarget(target)
    call FormGroup(3, true)
    call AttackMoveKillA(target)
endfunction

//============================================================================
function MajorCreepAttack takes nothing returns nothing
    local unit target = GetMajorCreep()
    call SetAllianceTarget(target)
    call FormGroup(3,true)
    call AttackMoveKillA(target)
endfunction

//============================================================================
function AnyPlayerAttack takes nothing returns nothing
    local unit hall

    set hall = GetEnemyExpansion()
    if hall == null then
        call StartGetEnemyBase()
        loop
            exitwhen not WaitGetEnemyBase()
            call SuicideSleep(1)
        endloop
        set hall = GetEnemyBase()
    endif

    call SetAllianceTarget(hall)
    call FormGroup(3,true)
    call AttackMoveKillA(hall)
endfunction

//============================================================================
function ExpansionAttack takes nothing returns nothing
    local unit creep = GetExpansionFoe()
    local integer x

    call FormGroup(3, true)
    if creep == null then
        set x = GetExpansionX()
        if x != -1 then
            call AttackMoveXYA(x,GetExpansionY())
        endif
    else
        call AttackMoveKillA(creep)
    endif
endfunction

//============================================================================
//  AddSiege
//============================================================================
function AddSiege takes nothing returns nothing
    call SetAssaultGroup( 0, 9, SHADE       )
    call SetAssaultGroup( 0, 9, MEAT_WAGON  )
    call SetAssaultGroup( 0, 9, MORTAR      )
    call SetAssaultGroup( 0, 9, TANK        )
    call SetAssaultGroup( 0, 9, BALLISTA    )
    call SetAssaultGroup( 0, 9, CATAPULT    )
endfunction

//============================================================================
//  SingleMeleeAttack
//============================================================================
function SingleMeleeAttack takes boolean needs_exp, boolean has_siege, boolean major_ok, boolean air_units returns nothing
    local boolean can_siege
    local real daytime 
    local unit hall
    local unit mega
    local unit creep
    local unit common

    if TownThreated() then
        call Sleep(2)
        return
    endif

    // purchase zeppelins
    //
    if get_zeppelin and GetGold() > 300 and GetWood() > 100 then
        call PurchaseZeppelin()
        set get_zeppelin = false
        set ready_for_zeppelin = false
        return
    endif
    set ready_for_zeppelin = true

    // coordinate with allies
    //
    set common = GetAllianceTarget()
    if common != null then
        if GetMegaTarget() != null then
            call AddSiege()
        endif
        call FormGroup(3,true)
        call AttackMoveKillA(common)
        call SetAllianceTarget(null)
        return
    endif

    // take expansions as needed
    //
    if needs_exp then
        set creep = GetExpansionFoe()
        if creep != null then
            call SetAllianceTarget(creep)
            call FormGroup(3,true)
            call AttackMoveKillA(creep)
            call Sleep(20)
            set take_exp = false
            return
        endif
    endif

    // all-out attack if the player is down
    //
    set mega = GetMegaTarget()
    if mega != null then
        call AddSiege()
        call FormGroup(3,true)
        call AttackMoveKillA(mega)
        return
    endif

    // deny player an expansion
    //
    set hall = GetEnemyExpansion()
    set daytime = GetFloatGameState(GAME_STATE_TIME_OF_DAY)
    set can_siege = has_siege and (air_units or (daytime>=4 and daytime<=12))

    if hall!=null and (can_siege or not IsTowered(hall)) then
        call AddSiege()
        call SetAllianceTarget(hall)
        call FormGroup(3,true)
        call AttackMoveKillA(hall)
        return
    endif

    // attack player's main base when siege is available
    //
    if can_siege then
        call AddSiege()
        call AnyPlayerAttack()
        return
    endif

    // nothing better to do, so kill a creep camp
    //
    if major_ok then
        call MajorCreepAttack()
        return
    endif

    call MinorCreepAttack()
endfunction

//============================================================================
function GetZeppelin takes nothing returns nothing
    if ready_for_zeppelin then
        set get_zeppelin = true
    endif
endfunction

//============================================================================
function FoodUsed takes nothing returns integer
    return GetPlayerState(ai_player,PLAYER_STATE_RESOURCE_FOOD_USED)
endfunction

//============================================================================
function FoodAvail takes integer base returns integer
    return GetFoodMade(racial_farm) * TownCount(racial_farm) + GetFoodMade(base) * TownCount(base)
endfunction

//============================================================================
function BuildAttackers takes nothing returns nothing
    local integer index = 0
    local integer unitid
    local integer desire
    local integer count

    loop
        exitwhen index == harass_length

        set unitid = harass_units[index]
        set desire = harass_qty[index] + IgnoredUnits(unitid)
        set count  = TownCount(unitid)

        if count != desire then
            if not StartUnit(desire,unitid,-1) then
                return
            endif
        endif

        set index = index + 1
    endloop
endfunction

//============================================================================
function BuildDefenders takes nothing returns nothing
    local integer index = 0
    local integer unitid
    local integer qty
    loop
        exitwhen index == defense_length

        set unitid = defense_units[index]
        set qty = defense_qty[index]

        call Conversions(qty,unitid)
        call AddDefenders(qty,unitid)

        set index = index + 1
    endloop
endfunction

//============================================================================
function CampaignBasicsA takes nothing returns nothing
    local integer food_each = GetFoodMade(racial_farm)
    local integer on_wood

    call ClearHarvestAI()

    if CaptainInCombat(false) then
        set on_wood = 0
    else
        set on_wood = campaign_wood_peons
    endif

    call HarvestGold(0,campaign_gold_peons)
    call HarvestWood(0,on_wood)

    if harvest_town1 then
        call HarvestGold(1,campaign_gold_peons)
        call HarvestWood(1,on_wood)
    endif

    if harvest_town2 then
        call HarvestGold(2,campaign_gold_peons)
        call HarvestWood(2,on_wood)
    endif

    if harvest_town3 then
        call HarvestGold(3,campaign_gold_peons)
        call HarvestWood(3,on_wood)
    endif

    if do_campaign_farms and FoodUsed()+food_each-1 > food_each*(TownCount(racial_farm)+1) then
        call StartUnit(TownCount(racial_farm)+1,racial_farm,-1)
    endif

    if build_campaign_attackers then
        call BuildAttackers()
    endif

    if not CaptainInCombat(false) then
        call BuildDefenders()
    endif

    call FillGuardPosts()
    call ReturnGuardPosts()
endfunction

//============================================================================
function CampaignBasics takes nothing returns nothing
    call Sleep(1)
    call CampaignBasicsA()
    call StaggerSleep(1,5)
    loop
        call CampaignBasicsA()
        call Sleep(campaign_basics_speed)
    endloop
endfunction

//============================================================================
function CampaignAI takes integer farms, code heroes returns nothing
    if GetGameDifficulty() == MAP_DIFFICULTY_EASY then
        set difficulty = EASY

        call SetTargetHeroes(false)
        call SetUnitsFlee(false)

    elseif GetGameDifficulty() == MAP_DIFFICULTY_NORMAL then
        set difficulty = NORMAL

        call SetTargetHeroes(false)
        call SetUnitsFlee(false)

    elseif GetGameDifficulty() == MAP_DIFFICULTY_HARD then
        set difficulty = HARD

        call SetPeonsRepair(true)
    else
        set difficulty = INSANE
    endif

    call InitAI()
    call InitBuildArray()
    call InitAssaultGroup()
    call CreateCaptains()

    call SetNewHeroes(false)
    if heroes != null then
        call SetHeroLevels(heroes)
    endif

    call SetHeroesFlee(false)
    call SetGroupsFlee(false)
    call SetSlowChopping(true)
    call GroupTimedLife(false)
    call SetCampaignAI()
	call Sleep(0.1)

    set racial_farm = farms
    call StartThread(function CampaignBasics)
    call StartBuildLoop()
endfunction

//============================================================================
function UnsummonAll takes nothing returns nothing
    local unit bldg
    loop
        set bldg = GetBuilding(ai_player)
        exitwhen bldg==null
        call Unsummon(bldg)
        call Sleep(2)
    endloop
endfunction

//============================================================================
//  SkillArrays
//============================================================================
function SkillArrays takes nothing returns integer
    local integer level = GetHeroLevelAI()

    if level > max_hero_level then
        set max_hero_level = level
    endif

    if GetHeroId() == hero_id then
        return skills1[level]
    else
        return skills2[level]
    endif
endfunction

//============================================================================
//  AwaitMeleeHeroes
//============================================================================
function AwaitMeleeHeroes takes nothing returns nothing
    if GetUnitCountDone(hero_id2) > 0 then
        set two_heroes = true
    endif
    loop
        exitwhen GetUnitCountDone(hero_id)>0 and (take_exp or (not two_heroes or GetUnitCountDone(hero_id2)>0))
        call Sleep(1)
    endloop
endfunction

//============================================================================
//  PickMeleeHero 
//============================================================================
function PickMeleeHero takes race raceid returns integer
    local integer hero_roll = GetRandomInt(1,10)

    //------------------------------------------------------------------------
    if raceid == RACE_HUMAN then
    //------------------------------------------------------------------------
        if hero_roll == 1 then
            set hero_id  = MTN_KING
            set hero_id2 = ARCHMAGE

        elseif hero_roll <= 4 then
            set hero_id  = MTN_KING
            set hero_id2 = PALADIN

        elseif hero_roll <= 7 then
            set hero_id  = ARCHMAGE
            set hero_id2 = PALADIN
        else
            set hero_id  = PALADIN
            set hero_id2 = ARCHMAGE
        endif

    //------------------------------------------------------------------------
    elseif raceid == RACE_ORC then
    //------------------------------------------------------------------------
        if hero_roll == 1 then
            set hero_id  = BLADE_MASTER
            set hero_id2 = TAUREN_CHIEF

        elseif hero_roll <= 4 then
            set hero_id  = BLADE_MASTER
            set hero_id2 = FAR_SEER

        elseif hero_roll <= 7 then
            set hero_id  = FAR_SEER
            set hero_id2 = TAUREN_CHIEF
        else
            set hero_id  = TAUREN_CHIEF
            set hero_id2 = FAR_SEER
        endif

    //------------------------------------------------------------------------
    elseif raceid == RACE_NIGHTELF then
    //------------------------------------------------------------------------
        if hero_roll == 1 then
            set hero_id  = DEMON_HUNTER
            set hero_id2 = MOON_BABE

        elseif hero_roll <= 4 then
            set hero_id  = MOON_CHICK
            set hero_id2 = KEEPER

        elseif hero_roll <= 7 then
            set hero_id  = KEEPER
            set hero_id2 = DEMON_HUNTER
        else
            set hero_id  = DEMON_HUNTER
            set hero_id2 = KEEPER
        endif

    //------------------------------------------------------------------------
    elseif raceid == RACE_UNDEAD then
    //------------------------------------------------------------------------
        if hero_roll == 1 then
            set hero_id  = DREAD_LORD
            set hero_id2 = LICH

        elseif hero_roll <= 4 then
            set hero_id  = LICH
            set hero_id2 = DEATH_KNIGHT

        elseif hero_roll <= 7 then
            set hero_id  = DEATH_KNIGHT
            set hero_id2 = DREAD_LORD
        else
            set hero_id  = DREAD_LORD
            set hero_id2 = DEATH_KNIGHT
        endif
    else
        set hero_id = 0
    endif

    return hero_id
endfunction


:) over.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
How do you setup this stuff so you can use for example the [ljass]native GetUnitGoldCost takes integer unitid returns integer[/ljass]
function. Here's what I do:
1. Copy-Paste the above code line in the last row in common.j
2. Extract wc3map.j and edit it to use GetUnitGoldCost()
3. Import common.j to a map then remove the path from wc3imported/common.j to just common.j.
4. Import wc3map.j to a map then remove the path from wc3imported/wc3map.j to just wc3map.j.
5. Compile the map (no errors) then start the map but the game goes to Main Menu only (does not load the map) :/
What am I doing wrong?(I want to set this manually.)
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
Even if i don't care about the speed gain here, i want to say that you result is probably not enough accurate.
For such lightnings functions i would use more checks in a same thread, at very least 100 calls.
I would even go for 500 to 1000 on a same thread.
cJass is handy for that (macro).

EDIT : Ok you already do it for 100, but there are some extra operations also because of the loop.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
@Sgqvur
Import path should be
Scripts\common.j
Ok did this but still I can't make it passed the Main Menu.
If I try to directly compile
call BJDebugMsg(I2S(GetUnitGoldCost(gg_unit_hfoo_0001))) <- some error name
If I edit war3map.j with this line and import it to the root folder of the map the game goes to Main Menu. Any ideas?
 

Bribe

vJass errors are legion
Reaction score
67
Why aren't you just using JassHelper and placing the native in one of the triggers? That's got to be 100 times easier.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
tnx for all the help Jedi. It seamed I've been using an old common.j that doesn 't contain the new Hashtable natives and that was the cause of all the 4-6K errors. It works now xD!
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top