HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

AI Script Tutorial: Creating a basic (Melee) AI

04-04-2004, 12:35 PM#1
Vidstige
This tutorial will step by step show how to build a basic melee AI the .ai script way. If you think that this tutorial will teach you how to make an AI that is better than AMAI, you might want to re-think. Instead it will try to teach you the basic Jass, built-in native functions and concepts that you need to know to be able to start your career as a melee AI scripter.

This tutorial will (unfortunately) not be about triggering at all (sorry Alfryd). If there is demand (and I feel like it), I might create one about using triggers for creating maps with special game dynamics.

If you have any problems with the scripts in this tutorial, or have any other thoughts, feel free to reply to this thread. Your feedback will improve this tutorial. Good luck and have fun.
/Vidstige
------------------------------------------
The contents so far:
  1. Hello World!
  2. Harvest resources
  3. Train more workers
  4. Building farms to support more workers
  5. Some Jass theory: main, variable and types
  6. Producing a barrack and training some soldiers
  7. More Jass theory: flow control, conditions and loops
  8. Functions: The ones we have used so far
  9. When should we build what [ code to be added ]
  10. Functions: Creating them and using them
Coming up soon:
  • Functions: An overview of the natives
  • Syntax checkers
  • Creeping
  • Your wish (might come true)
-----------------------------
Bugs found and corrected:
  • Variable post: local has now been added in the declarations.

NOTE: This first post will be updated. You might want to revisit it.
04-04-2004, 12:37 PM#2
Vidstige
Let us start with making sure that you know how to get a simple Hello World! script up and running.

Code:
function main takes nothing returns nothing  
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Hello World!" )  // F12 wont help you
endfunction
Paste the above code into a file named human.ai .
Start the Word Editor.
Open your favourite melee-map and save it under another name, or (preferable) another directory.
Press F12 (or open the Modules menu and select the Import manager) .
Select File-> Import file in the Import manager.
Select the newly created file human.ai.
Right click the imported file and select Modify file properties.
Change the location of the file to \Scripts\human.ai .
Save the map (you might also need to close the map).
Start WarCraft III, choose single game -> custom game -> YourNiceMap.
Make sure that you play against a computer of the human race.
Later you will want to select that the hole map should be visible.
Start the game and look for a Hello World! on the screen (it will quickly dissapear though).

If you think it dissapear to quick, or you are not sure it was there at all, you might want to try this version instead.
Code:
function main takes nothing returns nothing
    call Sleep( 2 )  // <--- To make sure that the game is ready to record the greeting
    call DisplayTimedTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 3600, "Hello World!" )
endfunction
If you are away from screen for more than one hour (3600 secs), you can look at the greeting by pressing F12.
04-04-2004, 01:06 PM#3
Vidstige
Everyone know that harvesting resources is an important activity when playing WarCraft. So why not look at how an harvesting AI script can be done.
Code:
function main takes nothing returns nothing
    call Sleep( 1.0 )  // Sleep 1 secund
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop   // start eternal loop
        call ClearHarvestAI()   // First reset the built in harvest-manager and
        call HarvestGold( 0, 4 )   // then tell it to have 4 workers harvest gold
        call HarvestWood( 0, 1 )   // and 1 worker harvest wood.
        call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )
        call Sleep( 1.0 )  // If we do not sleep sometimes, the engine will kill us.
    endloop

    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction
Try out this script the same way you did with the Hello World! one above. Instead of removing an earlier imported file, and then adding the new version, you can (int the import manager) right-click a previously added file and select replace.

Don't forget to set always visible on the advanced map settings before starting the game, otherwise you won't as easily see what the AI player is up to.

Why do we reset and reassign the harvest manager all the time then? What happens if a worker that is chopping wood gets killed? If we don't reset the harvest manager, the worker might never get replaced.
04-04-2004, 02:44 PM#4
Vidstige
If we have only 5 workers it will take forever to get enough resources to raise an army though, so let's train some more workers.

Code:
function main takes nothing returns nothing
    call Sleep( 1.0 )  // Sleep 1 secund
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop   // start eternal loop
        call ClearHarvestAI()   // First reset the built in harvest-manager and
        call HarvestGold( 0, 4 )   // then tell it to have 4 workers harvest gold
        call HarvestWood( 0, GetUnitCountDone('hpea')-4 )   // and the rest to harvest wood.
        call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )

        call SetProduce(1, 'hpea', 0)  // Try to start training a peasant

        call Sleep( 3.0 )  // If we do not sleep sometimes, the engine will kill us. 
    endloop
    
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

The call to GetUnitCountDone('hpea') counts how many Human PEAsants that this player controls. It is used to calculate how many peasants that are not harvesting gold and send them off to chop wood. Why don't you observer the computer building tons of workers?

Another way to start a map, that is useful for observing the AI players we develop, are to create a LAN game. If you set the advanced options to allow full observers and make yourself an observer, then you can study how much resources the AI player have by selecting on of its units or buildings.

Have you tried? Did it build tons of units? No? Did it need more food? We will soon make it build some farms. ;)
04-04-2004, 06:57 PM#5
Vidstige
Let us build some farms so we can train even more workers and get even more gold. ;)
If we produce less than 3 extra food, then we will start building a new farm.

Code:
function main takes nothing returns nothing
    call Sleep( 1.0 )  // Sleep 1 secund
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop   
        call ClearHarvestAI()   
        call HarvestGold( 0, 4 )   
        call HarvestWood( 0, GetUnitCountDone( 'hpea' )-4 )   
        call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )

        // If we already train a peasant, the below SetProduce fails, which is alright for now.
        call SetProduce( 1, 'hpea', 0 )  // Train Human PEAsant

        // Our base produces food for 12 people and every base for 6 people
        // Compare the produced food with the number of people and decide
        // if it is time to build a new farm (Human HOUse).

        if ( 12 + 6 * GetUnitCount('hhou') - GetUnitCount('hpea') < 3 ) then
            call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a house" )
            call SetProduce( 1, 'hhou', 0 )   // Produce a Human HOUse
        endif

        call Sleep( 3.0 )
    endloop

    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

This should get us lots of workers and lots of resources, but shouldn't we try to spend the resources on something fun? If we create some more buildings, maybe those buildings can help us train an army. Wouldn't that be something? Be alert for the next post about buildings, it might come even earlier than you expect.
04-04-2004, 08:04 PM#6
Vidstige
So sorry to dissapoint you, but I feel that we should do some meditation over Jass before we create more buildings. It might do our work easier. If you already know this stuff, lean back and see if you can detect any errrors of mine. If it seems strange, maybe you should reply or pm me about the strangeness, so that I can improve this tutorial. Get ready for some Jass theory.

main
Code:
function main takes nothing returns nothing
    // A lot of code
endfunction
All the action in an AI script start when the game-engine calls the main function.
The main function must be exactly as above, or the script will fail.

Variables
Sometimes it is good to remember things. In Jass you remember things by storing values into variables. Different kinds of variables can remember different kinds of things.
Code:
function main takes nothing returns nothing
local integer number          // Tell the engine (declare) that we want to be able to store a number
local integer anotherNumber   // and (declare) another one
local float decimalNumber      // and declare a decimal one
local string someText            // and declare some text

    set number = 42
    set anotherNumber = 6 * 9
    set decimalNumber = 3.14
    set someText = "Is this to easy for you? ;-)"
endfunction

Another way to achieve exacly the same thing would be:
Code:
function main takes nothing returns nothing
local integer number = 42  // declare and immediatly assign, one line is better than two, right? 
local integer anotherNumber = 6 * 9
local float decimalNumber = 3.14
local string someText = "Is this to easy for you? ;-)"
endfunction

One thing to remember is that at the beginning of the function we need to declare (tell the engine) about all variables that we are going to use. The following example is not legal.

Code:
function main takes nothing returns nothing
local integer number = 42  // declare and immediatly assign, one line is better than two, right? 
local integer anotherNumber = 6 * 9
local float decimalNumber = 3.14
    set anotherNumber = number  // Not legal, the engine will probably explode with disgust.
local string someText = "Is this to easy for you? ;-)"
    set anotherNumber = number  // This line is legal at this place though.
endfunction

We put local first on every line we declare a variable. Local means that the variable only exist inside this function. It is not optional though. We will later look at how global variables can be used.

Now take a deep breath and say after me: "Variables are our friends". :>

Yikes, this theory section grew longer than I thought. Maybe it is time to go and construct a building before we continue. Maybe we could construct a barrack, and would't it be something if we could make it train a soldier? Our very own soldier. (Or rather the AI's own soldier. But you get my point anyway I hope.)
04-04-2004, 09:01 PM#7
Vidstige
Now is the time to take our first steps towards training an army which will crush all resistance. Let us start with constructing a barrack and train some foot soldiers.

Code:
function main takes nothing returns nothing
    call Sleep( 1.0 )
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop
        call ClearHarvestAI()   
        call HarvestGold( 0, 4 )
        call HarvestWood( 0, GetUnitCountDone( 'hpea' )-4 )
        call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )

        call SetProduce( 1, 'hpea', 0 )  // Train Human PEAsant

        // Our base produces food for 12 people and every base for 6 people
        // Compare the produced food with the number of people and decide
        // if it is time to build a new farm (Human HOUse).

        if ( 12 + 6 * GetUnitCount('hhou') - GetUnitCount('hpea') < 3 ) then
            call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a house" )
            call SetProduce( 1, 'hhou', 0 )   // Produce a Human HOUse

        endif

        if ( GetUnitCount( 'hbar' ) < 1 ) then  // Do we have any Human BARrack?
            call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a barrack" )
            call SetProduce( 1, 'hbar', 0 )   // Create a Human BARrack (in town 0).
        else
            call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to train a footman" )
            call SetProduce( 1, 'hfoo', 0 )   // Create a Human FOOtman (in town 0).
        endif

        call Sleep( 3.0 )  // If we do not sleep sometimes, the engine will kill us. 
    endloop
    
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

Maybe you noticed that the AI stopped both farm construction and unit building after a while. Can you figure out why. Are we not building more farms when we are about to run out of food. Maybe we are confused about how much food we are using. Maybe you could try to fix that problem yourself, before I show a solution to the problem. (HINT: There is a problem in one of the conditions.)

Before we extend the code anymore, like creating different kinds of building, training different kinds of military units, and maybe we could even let the units fight something. Wouldn't that be something, but that is probably to hard and way out of scope for this basic tutorial.

Before trying to figure out if it is to hard, it is time to learn some more Jass. It is time to learn the usefulness of having several functions. It might even be time to learn where all those weird functions we have used come from, and how they really work. Don't go away. ;)
04-04-2004, 09:21 PM#8
Vidstige
Flow control is about how to make it possible to go different ways in a program. Jass supports this with its if and loop constructs. So let's have a look at them.

if

There exist a few variations of how if can be used. But the basic idea is that if something is true then we want to do some things, otherwise we want to do something else (or maybe nothing at all).

Code:
if condition then
    doSomething
    doSomethingMore
endif

if GetUnitCount( 'hpea' ) < 12 then   // If we have less than 12 peasants
    call setProduce( 1, 'hpea', 0)       // Build one more peasant (in our starting base)
endif

Hey, but if we we to do something else if we have all the peasants?

Code:
if condition then
    doSomething
else
    doSomethingElse
endif

if GetUnitCount( 'hpea' ) < 12 then   // If we have less than 12 peasants
    call setProduce( 1, 'hpea', 0)       // Build one more peasant (in our starting base)
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
endif

This works fine if there is only two possibilities, but what if we need choose between three separate actions?

Code:
if condition then
    doSomething
elseif condition2
    doSomethingElse
else
    doSomeOtherThing
endif

if GetUnitCount( 'hpea' ) < 12 then   // If we have less than 12 peasants
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need more peasants" )
    call setProduce( 1, 'hpea', 0)       // Build one more peasant (in our starting base)
elseif GetUnitCount( 'hpea' ) < 20 then
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We have to many peasants" )
endif

By inserting more elseifs you can make the program choose between several different actions. Just make sure that you put your conditions in a proper order. The following would be a bad order.

Code:
if GetUnitCount( 'hpea' ) < 20 then
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
elseif GetUnitCount( 'hpea' ) < 12 then   // If we have less than 12 peasants
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need more peasants" )
    call setProduce( 1, 'hpea', 0)       // Build one more peasant (in our starting base)
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We have to many peasants" )
endif
If we have one peasant, this code would say that we need no more peasants. This is because the conditions is checked from top to bottom.

loop
Loops are about repeating thing over and over again. If you have used loops in different languages, you might feel uncomfortable with Jass' loops. There is a way to map between whiles and fors to Jass' loops though.

Let's first look at a loop that will run forever (or to the game-engine kills it at least).
Code:
loop
    doSomethingForever
endloop

loop
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Let's spam the user forever")
    call Sleep( 1.0 )    // It wouldn't be forever with no Sleep, because the engine would have killed us for being to troublesome.
endloop

Loops that run forever have their uses, but they are not very flexible. So let's introduce a way get out of the loop.

Code:
loop
    doSomething
    exitwhen condition
    doSomethingMore
endloop

local integer i = 1
loop
    call Sleep( 1.0 )    // It wouldn't be forever with no Sleep, because the engine would 
have killed us for being to troublesome.
    exitwhen i > 10
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Let's spam the user 10 times")
    set i = i + 1
endloop

for
We can't have for loops in Jass, but we can make a loop behave like a for loop. :>
Code:
for ( initStatement; condition; endOfIterationStatement ) {
    stuff;    
}
could be made in Jass as
Code:
initStatement
loop
    exitwhen not (condition) 
    stuff  
    endOfIterationStatement  
endloop
And now let's look at a more concrete example.
Code:
for (int i = 0; i < 10; i++) {
    printf("We need to say it 10 times");
}
would in Jass become
Code:
local integer i = 0
loop
    exitwhen not ( i < 10 )    // exitwhen i >= 10
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need to say it 10 times")
    set i = i + 1
endloop

while
Maybe you should try to figure this one out by yourself. If you are not used to using while loops, just ignore them, Jass know nothing about them anyway.
04-04-2004, 10:43 PM#9
Alfryd
It seems fine as far as it goes. It would be a good idea to list some of the more common combinations/uses of functions as well as simply listing them, though, and while you're at it you may want to explain the basics of triggering.
04-05-2004, 12:20 AM#10
Vidstige
This post looks at the functions we have used so far, how they work, what they do, and why we want them to do just that. This post is dedicated to Alfryd, hope you will like it. ;)
For each of the functions we have used, I will list how it is declared in the sourcefiles, explain the arguments, what it does and what it returns.

native Sleep takes real seconds returns nothing
call Sleep( 3.5 ) // will make our script sleep 3.5 secunds
If we don't sleep sometimes, the engine will get mad and kill the script after a while.

constant native GetLocalPlayer takes nothing returns player
local Player p = GetLocalPlayer() // Gives us a reference to the player at the local computer
We use this reference when we display text (see below).

native DisplayTextToPlayer takes player toPlayer, real x, real y, string message returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Hello local player!" )
We need to tell the function what to write and to whom. By changing the zeroes to something else, we can also control where the text appears on screen. Feel free to experiment, zeroes usually works good though. This message will go away after a little while. The time depends on the length on the message.

native DisplayTimedTextToPlayer takes player toPlayer, real x, real y, real duration, string message returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 60, "Hello for 60 secs!" )
The same as above, but now we control how long the text shall be shown.

native ClearHarvestAI takes nothing returns nothing
call ClearHarvestAI() // orders the harvest AI to stop harvesting.
This is used with the two functions below to control how many workers that harvest gold and lumber.

native HarvestGold takes integer town, integer peons returns nothing
call HarvestGold( 0, 5 ) // Tells 5 workers to start harvesting gold in town 0, which is the starting town.
If this same call is made twise, without a ClearHarvestAI, 10 workers instead of 5 will harvest gold.

native HarvestWood takes integer town, integer peons returns nothing
call HarvestWood( 0, 10 )
Works exactly as the onw above for gold, but for wood instead.

native SetProduce takes integer qty, integer id, integer town returns boolean
local boolean success = SetProduce( 1, 'hpea', 0 ) // Tries to produce one peasant at town 0 (the starting town).

call SetProduce( 1, 'hpea', 0 ) // Do like this if you don't care if it succeds or not, or have another way of checking it.
I don't fully trust the return value, but that's maybe because I am a bit sceptical at times.

native GetUnitCount takes integer unitid returns integer
set numberOfPeasants = GetUnitCount( 'hpea' ) // Count the number of peasants the AI player have.
This value includes those currently in training.

native GetUnitCountDone takes integer unitid returns integer
set numberOfPeasants = GetUnitCount( 'hpea' ) // Count the number of peasants the AI player have.
This value does not include those in training.


This is all very well, you probably say, but how do I build or count other stuff? We will soon look on what secret values are used to construct other things.
04-05-2004, 11:00 AM#11
Vidstige
This post was going to be about the construction of our very own functions, but that would probably be to much theory in a row. Therefore we will consider how we can know when we should build new buildings and train new units. It is not very clever to try to train riflemen if we have a barrack but don't have a blacksmith.

The simplest way to make the AI create buildings would be to just let it try, ignoring all dependensies. If we put all the buildorders in a loop, it will eventually build all we have ordered it to (if we haven't forget to include something that something else depends on :-). It wouldn't work very well though, we would have got several of the same buildings, like 3 blacksmiths (which is a bit of an overkill). How should this be solved then?

If we re-check section 4 (Building farms to support more workers) and 6 (Producing a barrack and training some soldiers), we see that there are a few if-statements, rules, which decided what shall be done. If we think about how we play the game ourselves, we should be able to construct a few rules which should make the AI construct all tier 1 buildings at least. So, what are we waiting for, let's try it! :>

Rule 1: If we have to few peasants then train more peasants.
Rule 2: If we have to little food then build more farms.
Rule 3: If we don't have a barrack then build a barrack.
Rule 4: If we have a barrack then train footmen.
Rule 5: If we have a barrack then build a blacksmith.
Rule 6: If we don't have an altar then build an altar.
Rule 7: If we have an altar and we don't have a hero then train a hero.

Let's look at these rules, which hopefully will give us an army of footmen and riflemen, lead by a hero. Why don't you consider how you would convert these to Jass, before you read on? (Section 7 containing if statements might help.)

To lazy to try on your own, eh? Alright, I will leade you trough a few of them.
Let's see how we could map rule 1 to Jass, it look like it could fit into a Jass if-statement:
Code:
if "we have to few peasant" then 
  "train more peasants"
endif
What does to few mean, and how many are more? Let's restate the rule as:
[b]Rule 1b: If we have less than 10 peasants then start training 1 peasant./B]
And try to map this new rule to Jass.
Code:
if GetUnitCount('hpea') < 10 then 
  call SetProduce( 1, 'hpea', 0 ) // 0 is for town 0 our starting town.
endif
Nice! :>

So let's do the same thing with the other rules. I will list the reformated rule and the code. Try it yourself before you peak. ;)

Rule 1b: If we have less than 10 peasants then start training 1 peasant.
Rule 2b: If we have less than 6 extra food then start building 1 farm.
Rule 3b: If we have less than 1 barrack then start building 1 barrack.
Rule 4b: If we have more than 0 barracks then start training 1 footman.
Rule 5b: If we have more than 0 barracks and we have less than 1 blacksmith then start building 1 blacksmith.
Rule 6b: If we have less than 1 altar then start building 1 altar.
Rule 7b: If we have more than 0 altar and we have less than 1 hero then start training 1 hero.
Rule 8b: If we have more than 0 unjassed rules then start converting 1 rule to Jass :8

Code:
These rules will be inserted in the code from section 6. 
Please be patient.
04-07-2004, 09:12 AM#12
Vidstige
Functions... what are they good for, why do we want them? Well... we want someone else to have written a lot of nice functions, for us to call, so we don't have to create them ourselves. And we want to create a few of them ourselves, so we can prevent our code to look like a total mess. Divide and conquer. Divide your code into several functions, and then conquer each function. :>

Let us look at a minimal function:
Code:
function myFunc takes nothing returns nothing
endfunction
This function does nothing at all and does noone happy. It does however contain a few parts that every function must have. It starts with the keyword function and ends with the keyword [/b]endfunction[/b]. After function comes the name of the function, myFunc in this case. Next comes the word takes and thereafter a list with all argument that the function takes. This function takes nothing. It also returns nothing. What does all this gives us? It gives us a function with the name myFunc, which takes no input arguments/parameters and has no return value.

Let us now try to make a function that actually does something, takes some arguments and returns a value.
Code:
function add takes integer a, integer b returns // sum of a and b
  return a + b
endfunction
Pretty obvious what it does, right? And also not very useful, we could have just used + from the beginning without a function. Let's make a more useful function, one that makes sure that all peasants harvest something.
Code:
function resourceManager takes goldPeasants returns nothing
  call ClearHarvestAI()   // First reset the built in harvest-manager and
  call HarvestGold( 0, goldPeasants )   // then tell it to have [i]goldPeasants[/i] workers harvest gold
  call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )   // and the rest to harvest wood.
endfunctions
This function takes as an argument how many peasants that should harvest gold (the rest is made to harvest wood. It can be called from another function (like main) like this:
Code:
call resourceManager( 5 )
which will make the function put 5 peasants working in the mine and the rest in the forest. By using this function in the previous programs, we could have replaced several lines of code from the main function with a call to the resourceManager. The program from section 2. Harvest resources, can now be written like:
Code:
function resourceManager takes goldPeasants returns nothing
  call ClearHarvestAI()   // First reset the built in harvest-manager and
  call HarvestGold( 0, goldPeasants )   // then tell it to have [i]goldPeasants[/i] workers harvest gold
  call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )   // and the rest to harvest wood.
endfunctions

function main takes nothing returns nothing
    call Sleep( 1.0 )  // Sleep 1 secund
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop   // start eternal loop
        call resourceManager( 4 )  // 4 peasants in the mine and the rest in the forest.
        call Sleep( 1.0 )  // If we do not sleep sometimes, the engine will kill us.
    endloop

    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

Make sure that the utilty function, the one that gets called, are before the one that calls it. Otherwise the WarCraft III thinks like this: What strange function is this? Have never heard of it. I better kill this script, it looks like its broken.

Another useful utility function is this:
Code:
function print takes string message returns nothing
  call DisplayTimedTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 3600, message )
endfunction

Alright, alright you say... but these function doesn't raise us an army. Well... soon we will create a few that will help us raise a large army. Patience please.
04-12-2004, 07:26 PM#13
Vidstige
How useful is this tutorial in its current state?
Have you any idea how it can get more useful?

I will try to make some more updates tomorrow.
/V.

[Edit: This wont be updated for a while... sorry,
but I am quite busy right now...]
04-12-2004, 08:08 PM#14
PitzerMike
Neat tutorial! I'll add it to the important threads

Maybe AI commands could be added (how to use them ...)
05-08-2004, 02:36 PM#15
Vidstige
This post will take a giant leap forward. Let's first rewrite the code from section 6. Producing a barrack and training some soldiers.

To increase the readablity and make the managing of the code easier the code is split up in different managers which handles resources, training of units and construction of buildings.

Code:
function resourceManager takes nothing returns nothing 
  local integer goldPeasants = 5
  local integer peasantsDone = GetUnitCountDone('hpea')
  if peasantsDone < 6 then
    set goldPeasants = peasantsDone - 1
  endif

  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "In manager")
  call ClearHarvestAI() // First reset the built in harvest-manager and
  call HarvestGold( 0, goldPeasants ) // then tell it to have
                           // goldPeasants workers harvest gold   
  call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )
                      // and the rest to harvest wood. 
endfunction 

function unitManager takes nothing returns nothing 
  if GetUnitCount('hpea') < 12 then
    call SetProduce(1, 'hpea', -1) // Train one peasant anywhere possible
  endif
  call SetProduce(1, 'hfoo', -1) // Train one foot soldier anywhere possible
endfunction 

function buildingManager takes nothing returns nothing 
  local integer produced = 12 + 6 * GetUnitCount('hhou') 
  local integer used = 1 * GetUnitCount('hpea') + 2 * GetUnitCount('hfoo')
  local integer foodSpace = produced - used 
  local integer barrack = GetUnitCount('hbar')

  if foodSpace < 6 then 
    call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,"Building house") 
    call SetProduce(1,'hhou',-1) 
  endif

  if barrack < 1 then
    call SetProduce(1,'hbar',-1)     
  endif
endfunction 

function main takes nothing returns nothing 
  call Sleep( 1.0 )  // Sleep 1 second 
  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script started") 

  loop   // start eternal loop
  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "in loop") 
    call resourceManager() 
    call unitManager() 
    call buildingManager() 
    call Sleep( 1.0 ) //If we do not sleep sometimes, the engine will kill us.      
  endloop 

  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script exiting") 
endfunction 

This code will produce a horde of footmen, which just stand about doing nothing. But that should soon be taken care of... Let start off with creating an AI that randomly wanders the map with its footmen. Hopefully it will find something to kill... We shall soon also consider how the built in AI solves the commanding of its troop by cheating...
Code:
globals  // variables that can be reached from all functions in the AI script.
  unit scoutUnit = null
  real scoutX = 0
  real scoutY = 0
  real scoutHealth
endglobals
//=========================================================
//  resourceManager
//
//  Collects resources by commanding peasants
//=========================================================
function resourceManager takes nothing returns nothing 
  local integer goldPeasants = 5
  local integer peasantsDone = GetUnitCountDone('hpea')
  if peasantsDone < 6 then
    set goldPeasants = peasantsDone - 1
  endif

//  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "In resourceManager")
  call ClearHarvestAI() // First reset the built in harvest-manager and
  call HarvestGold( 0, goldPeasants ) // then tell it to have
                           // goldPeasants workers harvest gold   
  call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )
                      // and the rest to harvest wood. 
endfunction 
//=========================================================
//  unitManager
//
//  Trains units
//=========================================================
function unitManager takes nothing returns nothing 
  if GetUnitCount('hpea') < 12 then
    call SetProduce(1, 'hpea', -1) // Train one peasant anywhere possible
  endif
  if GetUnitCount('hfoo') < 15 then
    call SetProduce(1, 'hfoo', -1) // Train one foot soldier anywhere possible
  endif
endfunction 
//=========================================================
//  buildingManager
//
//  Constructs buildings
//=========================================================
function buildingManager takes nothing returns nothing 
  local integer produced = 12 + 6 * GetUnitCount('hhou') 
  local integer used = 1 * GetUnitCount('hpea') + 2 * GetUnitCount('hfoo')
  local integer foodSpace = produced - used 
  local integer barrack = GetUnitCount('hbar')

  if foodSpace < 6 then 
//    call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,"Building house") 
    call SetProduce(1,'hhou',-1) 
  endif

  if barrack < 2 then
    call SetProduce(1,'hbar',-1)     
  endif
endfunction 
//=========================================================
//  allocateScout
//
//  Selects one unit as a scout, which will lead the troops.
//=========================================================
function allocateScout takes nothing returns nothing
  local group grp = CreateGroup()
  call GroupEnumUnitsOfType(grp, "footman", null)
  set scoutUnit = FirstOfGroup(grp)
  call RemoveGuardPosition(scoutUnit)
  set scoutX = GetUnitX(scoutUnit)
  set scoutY = GetUnitY(scoutUnit)
  set scoutHealth = GetUnitState(scoutUnit, UNIT_STATE_LIFE)
endfunction
//=========================================================
//  init
//
//  Initializes script...
//=========================================================
function init takes nothing returns nothing
endfunction
//=========================================================
//  walk
//
//  Orders the unit u to move distance forward.
//=========================================================
function walk takes unit u, real distance returns nothing
  local real dir = GetUnitFacing(u) * 3.14 / 180
  local real x = GetUnitX(u) + distance * Cos(dir)
  local real y = GetUnitY(u) + distance * Sin(dir)
  call IssuePointOrder( u, "move", x, y)
endfunction
//=========================================================
//  walk
//
//  Orders the unit u to move distance in direction.
//=========================================================
function walkDir takes unit u, real distance, real direction returns nothing
  local real dir = direction * 3.14 / 180
  local real x = GetUnitX(u) + distance * Cos(dir)
  local real y = GetUnitY(u) + distance * Sin(dir)
  call IssuePointOrder( u, "move", x, y)
endfunction
//=========================================================
//  helpScout
//
//  Sends units that follows in the scouts tracks
//=========================================================
function helpScout takes nothing returns nothing
  local group grp = CreateGroup()
  local unit u
  call GroupEnumUnitsOfType(grp, "footman", null)
  set u = FirstOfGroup(grp)
  loop
    exitwhen u == null
    if u != scoutUnit then
      call IssuePointOrder( u, "attack", scoutX, scoutY)    
    endif
    call GroupRemoveUnit(grp, u)
    set u = FirstOfGroup(grp)
  endloop
  call DestroyGroup(grp)
endfunction
//=========================================================
//  scout
//
//  Controls the scout that wanders randomly on map.
//=========================================================
function scout takes nothing returns nothing
  local real R = 1024
  local real x = GetUnitX(scoutUnit)
  local real y = GetUnitY(scoutUnit)

  if scoutX == x and scoutY == y then
//    call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,"== Bump ==")   
    call walkDir(scoutUnit, R, GetUnitFacing(scoutUnit) + GetRandomReal(90, 270))
  else
    set scoutX = x
    set scoutY = y
    call walk(scoutUnit, R)
  endif
endfunction
//=========================================================
//  combatManager
//
//  Controls the army by having them follow the scout...
//=========================================================
function combatManager takes nothing returns nothing
//  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "In combatManager")
  if scoutUnit == null or not UnitAlive(scoutUnit) then
    call allocateScout()
  endif
  if GetUnitCountDone('hfoo') > 4 and scoutUnit != null then
    call scout()
    call helpScout()
  endif
endfunction
//=========================================================
//  main
//
//  Main function that runs the AI.
//=========================================================
function main takes nothing returns nothing 
  call Sleep( 1.0 )  // Sleep 1 second 
  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script started") 
  call init()

  loop   // start eternal loop
//    call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "in loop") 
    call resourceManager() 
    call unitManager() 
    call buildingManager() 
    call combatManager()
    call Sleep( 5.0 ) //If we do not sleep sometimes, the engine will kill us.      
  endloop 

  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script exiting") 
endfunction

Try making a few humans fight against each other with this script...
(oops... the post got kind of long I guess...)