| 04-04-2004, 12:35 PM | #1 |
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:
Bugs found and corrected:
NOTE: This first post will be updated. You might want to revisit it. |
| 04-04-2004, 12:37 PM | #2 |
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
endfunctionStart 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 |
| 04-04-2004, 01:06 PM | #3 |
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" )
endfunctionDon'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 |
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" )
endfunctionThe 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 |
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" )
endfunctionThis 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 |
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
endfunctionThe 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? ;-)"
endfunctionAnother 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.
endfunctionWe 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 |
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" )
endfunctionMaybe 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 |
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)
endifHey, 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" )
endifThis 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" )
endifBy 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" )
endifloop 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.
endloopLoops 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
endloopfor 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;
}Code:
initStatement
loop
exitwhen not (condition)
stuff
endOfIterationStatement
endloopCode:
for (int i = 0; i < 10; i++) {
printf("We need to say it 10 times");
}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
endloopwhile 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 |
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 |
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 |
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 [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.
endifSo 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 |
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 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 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.
endfunctionsCode:
call resourceManager( 5 ) 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" )
endfunctionMake 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 |
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 |
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 |
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")
endfunctionTry making a few humans fight against each other with this script... (oops... the post got kind of long I guess...) |
