Jump to content


Photo

[Gen - ZH] Step By Step Guide to Building an AI


  • Please log in to reply
2 replies to this topic

#1 Calamity_Jones

Calamity_Jones

    Boomers Disposable Minion

  • Project Team
  • 2,399 posts
  • Location:East Yorkshire - England
  • Projects:Only War 2
  •  The Lurker-Turned-Poster-Turned-Lurker

Posted 19 March 2007 - 06:47 PM

Admins - Don't merge my posts because I'm exceeding the maximum post length and as such my tutorial is getting cut off...

I've found that a lot of mods for ZH and Generals don't have an AI due to the difficulty in finding people who are capable of making an AI, and willing to do so. It's time consuming, dull, frustrating and difficult, so you can see why :p I'm making an AI for the All Stars mod, and whilst I do it, I'll add parts to this post, showing the process of building the AI bit by bit.

Another note is that THIS IS NOT EASY. You cannot simply copy another AI to make it work for your mod. You NEED to play around with scripts until you have a good understanding of the basics. If you have never used worldbuilder before, close this webpage. Read this tutorial and go make a few maps. Once you know how wb works and feel comfortable, make a plain map, add a player, and mess around with scripts... try to make some units spawn, move the camera, things like that.

If you want any more help, e-mail me.
My address is calamity_jones[AT]hotmail.com
I would also really appreciate feedback on this tutorial!




####Step by Step Guide to Building an AI By Calamity_Jones####




####Part 1####

This first part describes a few ideas and things like that. Stuff you'll need to know if you're building an AI. Firstly, we'll look at the files that are required:

-AIData.INI ... everyone seems to thing this is the AI and nothing else... actually it just tells the AI where to build, what to build and what generals powers to use as well as a few other tidbits of information.)

-SkirmishScripts.scb ... THIS is the AI, once you have scripted your AI, you need to export your scripts to a file with this name and place it in Data.

-Trigger areas and waypoints on every map ... These tell the AI where to go basically.

Now, by far the most difficult bit of AI making is the scripts as you need a pretty damn good grip on how to script to make an AI. I'll only focus on the scripts as that's the bit people need help with. There are many ways to make an AI, this is just how I did it.

1) Script General Knowledge

A script is a small chunk of Boolean algebra that tells to computer to carry out an operation once a certain condition has been met. You could make it do something like "IF (my enemy has 3 tanks) THEN (build 5 rocket soldiers)" Simple huh? Scripts have several variants that are simply made by checking tickboxes:

-Subroutines are scripts that are assigned to teams and are executed when the team reaches a condition and the script reaches its condition.

-Active scripts will check their conditions constantly until they are met, then execute and either shut off or continue executing. Therefore an inactive script will do nothing at all even if its conditions are met untill it's activated by another script.

-Deactivate on success makes the script turn off once it has met its condition and executed. If you deselect this, the script will keep executing until its condition isn't met anymore. These kind of scripts are the stuff for build conditions.

-Evaluation, this determines how often the computer checks to see if a scripts conditions have been met. The default is every frame, but can be changed for every n seconds.

-A sequential script is the same as any other script, except that another script tells the computer to run the script sequentially. This means it will execute the first action, then the second, then the thrid etc one after the other until the end or forever if you so wish.

In the example above, the script that says "IF (my enemy has 3 tanks) THEN (build 5 rocket soldiers)" would be a deactivating subroutine as it would check all the time to see if the enemy has three tanks, and as soon as it does, it would tell the AI to build a team of 5 rocket soldiers then switch off.

2) Team General Knowledge

A team is a group of units that the AI builds to attack you with, they can be assigned a huge plethora of scripts and conditions that alter them in certain ways. I'll take you through the various panels of a teams window. First, the Identity window:

-You need to select the unit types in this window, and the AI will make a random number of the selected units (if it can) between the minimum and maximum numbers set. Naturally, if you set the same number for min and max, it'll produce the same numbers every time.

-The priority of a team tells the AI how important a team is to it to be built, the success and failiure amounts are what the teams priority is reduced or increased by. A script needs to do this as the AI needs to know what is counted as success and failiure!

-The production condition is what determines wether the AI should build a team, these and all scripts that a team runs must be subroutines.

-The execute associated actions checkbox tells the AI to run the scripts on the generic tab.

Now the Reinforcement one:

-If you write a script that tells the computer to spawn a team somewhere, you can tell it to drop them off in a transport vehicle.

The Behaviour tab:

-This tab runs scripts when a condition is met by the team as well as setting their attitude.

Generic:

-These scripts will only run if the "execute associated actions" checkbox is ticked on identity.

For example, the 5 rocket soldiers would be a team of, say min:5, max: 5 AmericaInfantryMissileDefender. the script that says "IF (my enemy has 3 tanks) THEN (build 5 rocket soldiers)" would be the production condition. You would then set a priority script to run on the generic tab which would tell the AI to look for tanks, and instructions to hunt or something along them lines on the "on create" line in behaviour.

3) Counters

The AI needs to know the time! It may sound silly, but the AI needs a timer or counter that tells it what to do and when. The best way to do this is with a counter that has several possible values controlled by corresponding non-deactivating scripts. For example, in my AI the counter detects when the AI has lost a number of units, and eventually loops back to the original value of 1 when it has lost so many units. This counter controlls everything the AI does. For example, the counter reaches 4 when the AI reaches generals level 3 (and thus unlocks new units). When the counter reaches this value, the AI recieves cash, expands its base and begins mass producing its brand new units.

For example, a condition in the build condition script for our rocket launchers could tell the AI to only build them when the counter is at a certain value. If you have a fancy uber tank that specialises in killing other tanks, the counter could determine if the AI is able to build them things, and as soon as it is, stop the AI making rocket soldiers and them instead.

4) Base Building

The AI needs a few scripts that tell it when and what to build. It won't simply build its base automatically, you need to tell it to ;) You'll have to customise this based on your mods prerequesits. Assuming that you need power and a barracks to build a warfactory. For example, the first script should be something like "this player builds a Barracks" and a second line that tells it to build a powerplant. Then you set up another script that has a condition to check if the player has a Barracks AND a Power plant with the action of building a warfactory.

5) Setup Scripts

-You need to set up a few things for the AI. All these scripts should be true and deactivate right away. They set up things like flags, timers, counters, priorities and crap like that.

To do priorities you need to first define object types, then make a script that sets for a certain priority, a certain value for a certain object type.

6) Team Building Conditions

-These are scripts that tell the computer to build a team when a condition is met, you need a LOT of build condition scripts for different difficulty levels and numerous teams. You could make a generic team that has an IF (true) condition which would simply make the AI mass produce the exact same team and make it do the exact same thing over and over. This would be very boring so you need to make a shed load of teams and conditions.

The general structure of these scripts should be a condition that relies on the main timer and perhaps some other flags or counters, then a command as the action that tells <This Team> to guard. For example, you could have a script that executes if the player has less than 10 red guard. You could then set this script as the build condition for a team of, say, 6 red guard. Therefore, if the player has less than 10 red guard, it would build that team and continue to do so until it has more than 10 (therefore making the scripts condition false.)

These scripts should be SAnD i.e. Subrouting, Active, non Deactivate. Set the evaluation to a certain number of seconds, NOT per frame as with a good hundred build conditions for the computer to evaluate so many times every second for each AI you'll get unnecessary lag. I generally set the evaluations to somewhere between 1 and 10 seconds, depending on how likley the script is to go true and tell the computer to build the team. For Example, a team that consists of high tech super units that you only have acess to late in the game should evaluate less often as there's less chance you'll get them :p

If the script is SAnD, the computer will check to see if it's true. If it isn't it'll ignore it (unless you set a false behaviour). It'll keep evaluating until it is true, then as soon as it is, it'll produce the team constantly until it is false again.

7) Team behaviours

-These define what a team does when attacked, when made, when destroyed, when idle etc. You need to
make scripts that tell the AI to :
-Send units along certain waypoints to attack the enemy.
-Attack the enemy.
-Respond to threats.
-Avoid Generals Powers.
etc.

8) General Tips

Always use the <This Team> and <This Player> tags, things often go wrong otherwise... For example, if you have a script that says "the player builds an upgrade" instead of having the script say "Player SkirmishUSA build advanced armour upgrade or whatever, have Player <This Player>, as long as it's in the USA root, it will work.

IF False actions on scripts can be used to make "sensors". In other words, the script does something all the time and flips between states depending on the condition. For Example, you could have a condition that counts the enemies money, and see's if it's greater than 10k. The true action could set a flag to true, and the false would set the flag to false. That way you have a sensor that checks the AI's opponents cash situation ;)

Edited by Calamity_Jones, 20 March 2007 - 10:24 PM.

Posted Image

#2 Calamity_Jones

Calamity_Jones

    Boomers Disposable Minion

  • Project Team
  • 2,399 posts
  • Location:East Yorkshire - England
  • Projects:Only War 2
  •  The Lurker-Turned-Poster-Turned-Lurker

Posted 20 March 2007 - 10:28 PM

####Part 2####

Ok, lets start with the map you'll be working with. You'll need to make a map as follows: (Indeed, every map you use should have a basic trigger area order like this)

Cals__s_AI_Tutorial_01.jpg

TA means Trigger Area and WP means Waypoint. The asterix indicates things that are pre-named in worldbuilder, so if you make a trigger area, the name "OuterPerimiter1" will be available in a drop-down list. The map should be fairly large too, 500x500 with borders of 50 is what I used.

The names should all be exactly as they appear in the diagram, with the hashes replaced with the player numbers. MAKE SURE THAT NO TRIGGER AREAS CROSS EACH OTHER! This will cause you problems! You can have an area inside and area, but no area that has crossing boundaries.

Now, add a player called Skirmish[Faction Name], make them the side of the faction and computer controlled.

You should have 3 names in your player list now:

(neutural)
PlyrCivilian="PlyrCivilian"
Skirmish[Faction Name]="Skirmish[Faction Name]"

Right, let's get going, whip up the script editor. You should see three folders named after their owning players. For now, we'll ignore the civilian player (we will later add music here), add a folder inside the folder named after your faction and call it "###_MASTER COUNTER" The hashes should be replaced with a prefix that is a shortening of your faction name. So, for The Cadians faction in Only War, I used CAD, thus making the folder: "CAD_MASTER COUNTER".

Continue to add folders inside the faction root until you have this:

###_MASTER COUNTER
###_Base Building
###_Cash Supply
###_Generals Powers
###_Setup
###_World Status
###_Upgrade
###_E_Build Conditions
###_N_Build Conditions
###_H_Build Conditions
###_Team Behaviour
###_Team Behaviour_Combat Zone
###_Team Behaviour_Priorities
###_Team Behaviour_Sequential Scripts
###_Team Behaviour_Teams Lost

You may have noticed my naming system by now. I use a system of listing the role of something in its name, seperating sections with underscores. So, ###_ means it belongs to the ### faction. Team Behaviour_ means the folder relates to team behaviour, and _Teams Lost means it relates to scripts on the loss of teams. I suggest you stick to a similar system.

Right, we'll start with some basic building blocks.

Into the "###_MASTER COUNTER" folder, add a [nsAD][ENH] script called "###_MASTER COUNTER_Set To 1 Initial" leave the condition as IF True and add the action "Set counter to a value" under Scripting/Counters with the value "Set '###_MASTER' to 1". You should have:

*** IF ***
	True.
*** THEN ***
  Set '###_MASTER' to  1 

Add three more scripts as follows: (The condition is in Scripting, at the top of the list in which true appears)

TYPE: [nsAnd][ENH]
NAME: "###_MASTER COUNTER_Set To 1"
*** IF ***
	Counter '###_MASTER' IS Equal To  3 
	*AND* Counter '###_TL_Overall' IS Equal To  10 
*** THEN ***
  Set '###_MASTER' to  1 
  Set '###_TL_Overall' to  0 

TYPE: [nsAnd][ENH]
NAME: "###_MASTER COUNTER_Set To 2"
*** IF ***
	Counter '###_MASTER' IS Equal To  1 
	*AND* Counter '###_TL_Overall' IS Greater Than or Equal To  3 
*** THEN ***
  Set '###_MASTER' to  2 

TYPE: [nsAnd][ENH]
NAME: "###_MASTER COUNTER_Set To 3"
*** IF ***
	Counter '###_MASTER' IS Equal To  2 
	*AND* Counter '###_TL_Overall' IS Greater Than or Equal To  6 
*** THEN ***
  Set '###_MASTER' to  3 

These scripts are what controls the behaviour of the AI, hence their name of master counter. Essentially, It starts at 1. Once three teams of units it has made have died, the timer goes to 2, and the AI will send different units, then when it has lost 6 teams, it goes to 3, and once it's lost 10 teams, it goes back to 1. Of course, depending on the complexity of AI you want to make, you can add more counter states to make the AI do more interesting things and try different tactics. Mine for Only War has 7 states for example, for now though, we'll stick with 3.

Right. Leave this folder and run off to "###_Setup" And add these scripts: (The actions are in Player/AI)

TYPE: [nsAD][E]
NAME: "###_E_Build Times"
*** IF ***
	True.
*** THEN ***
   Player '<This Player>' seconds between building teams. 20 

TYPE: [nsAD][N]
NAME: "###_N_Build Times"
*** IF ***
	True.
*** THEN ***
   Player '<This Player>' seconds between building teams. 15 

TYPE: [nsAD][H]
NAME: "###_H_Build Times"
*** IF ***
	True.
*** THEN ***
   Player '<This Player>' seconds between building teams. 10 

This simply tells the AI how often to build a team of units.

Now things get a bit tedious... You'll need to define what every unit you use in the game is, in order to make priorities. Start by making a [nsAD][ENH] script called "###_Define Object Types". Then add an action like: "'Tanks' : add 'TANK NAME'" which is under Scripting/ObjectTypeList. The list is Tanks, and the object is your tank. Continue to define types and add objects for each faction that are of that type. Part of mine looks like this:

*** IF ***
	True.
*** THEN ***
   'Tanks' : add 'CadianLemanRuss'
   'Tanks' : add 'CadianLemanRussVanquisher'
   'Tanks' : add 'TauHammerHead'
   'Tanks' : add 'TauCrisis'
   'Tanks' : add 'UltramarinePredator'
   'Tanks' : add 'UltramarineLandRaider'
   'Artillery' : add 'CadianBasilisk'
   'SuperUnit' : add 'ImperialNavyVulture'
   'Transport' : add 'CadianChimera'
   'Transport' : add 'CadianChimeraLoadK'
   'Transport' : add 'CadianChimeraLoadN'
   'Transport' : add 'TauDevilFish'
   'Transport' : add 'TauDevilFishLoadN'
   'Transport' : add 'TauDevilFishLoadP'
   'Transport' : add 'UltramarineRhino'
   'Heavy Weapons Infantry' : add 'CadianMissileLauncher'
   'Heavy Weapons Infantry' : add 'TauInfantryPathfinderRailGun'
   'Heavy Weapons Infantry' : add 'UltramarineMissileLauncher'
   'Heavy Weapons Infantry' : add 'UltramarineLasCannon'
   'Heavy Weapons Infantry' : add 'UltramarinePlasmaGun'
   'Standard Infantry' : add 'CadianDevastatorSargeant'
   'Standard Infantry' : add 'CadianLasgun'
   'Standard Infantry' : add 'CadianLasgun1'
   'Standard Infantry' : add 'CadianLasgun2'
   'Standard Infantry' : add 'CadianLasgun3'
   'Standard Infantry' : add 'CadianLasgun4'

Once you've finished this script you need to OK the scripts window to update the map. (If you don't, the script editor will not know your object types exist... Also, you should probably save.) Then re-open the scripts window and add a script like this:

TYPE: [nsAD][ENH]
NAME: "###_Define Priorities_Generic"
*** IF ***
	True.
*** THEN ***
  For Attack priority set 'CAD_Generic' set the priority of object type 'Artillery' to  100 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Base Buildings' to  90 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Base Defences' to  100 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Builder' to  90 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Bunkers' to  100 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Heavy Weapons Infantry' to  100 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Special Weapons Infantry' to  100 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Standard Infantry' to  100 
  For Attack priority set 'CAD_Generic' set the priority of object type 'SuperUnit' to  100 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Tanks' to  100 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Tech Buildings' to  0 
  For Attack priority set 'CAD_Generic' set the priority of object type 'Transport' to  100 

The action is in AttackPrioritySet/CreateSet (Modify a sets priority for a single unit type) For yours, name the set "###_Generic" and set your different types in order of their importance... i.e. what you want your AI to want to attack more. You can set things to the same value if you want. You could even set everything to the same value.

You can also define priority sets for different unit types, or specific units. It's cleaner to do this in a separate script. If you have rocket soldiers and machine gun soldiers for example, make one script called "###_Define Priorities_Machine Gun Soldier" and another called "###_Define Priorities_Rocket Soldier" with the sets named accordingly. Then, set tanks to have higher priorities than soldiers for the rocket guy, and vice versa for the Machine Gun guy... simple!

Now for the next script, a simple affair: (Action under Player/Build)

TYPE: [nsAD][ENH]
NAME: "###_Enable Building"
*** IF ***
	True.
*** THEN ***
   Player '<This Player>' is able to build units.
   Player '<This Player>' is able to build buildings.

Add another script like this: (Action under Scripting/Flags)

TYPE: [nsAD][ENH]
NAME: "###_Set Flags Initial"
*** IF ***
	True.
*** THEN ***
  Set Flag named '###_INFANTRY BUILDING' to FALSE
  Set Flag named '###_VEHICLE BUILDING' to FALSE
  Set Flag named '###_I Am Crippled' to FALSE
  Set Flag named '###_Enemy Crippled' to FALSE
  Set Flag named '###_Enemy In My Base' to FALSE

This script defines a bunch of really important flags that the AI uses to define what's going on. (Refer to my other tutorial). The infantry and vehicle building ones are called when a team starts building to tell the AI that its factory is busy. The crippled one is called when the AI cannot win the game. Enemy crippled is to tell the Ai to finish off an opponent. Enemy in my base is... well... obvious.. :p

Finally, add: (Action under Scripting/Counters)

TYPE: [nsAD][ENH]
NAME: "###_Set Counters Initial"
*** IF ***
	True.
*** THEN ***
  Set '###_TL_Overall' to  0 
  Set '###_TLB_Tanks' to  0 
  Set '###_TLB_Troops' to  0 
  Set '###_TLC_Tanks' to  0 
  Set '###_TLC_Troops' to  0 
  Set '###_TLF_Tanks' to  0 
  Set '###_TLF_Troops' to  0 

The names of these are short for "###_Teams Lost_Overall"... "###_Teams Lost_Centre_Tanks" etc... The main attack routes defined by waypoint paths are Centre (Spelt Center ingame due to silly American spelling :) ), Backdoor and Flank. These scripts set up counters that count how many teams that have been dispatched down these routes have died, and the overall number of teams lost. (Which is used back in our master counters! You'll see how everything links together in very complicated and subtle ways. This is what's so damn difficult about making an AI. There's no clear lines between parts, everything is linked together...)

Okay, we're done setting things up. Now we're gonna see what's going on in the world. Go to "###_World Status" and add this script: (Player/Destroyed for the condition and Scripting/Flags for the action)

TYPE: [nsAD][ENH]
NAME: "###_I Am Crippled"
*** IF ***
	All factories belonging to  Player '<This Player>' have been destroyed.
*** THEN ***
  Set Flag named '###_I Am Crippled' to TRUE

This as you might guess, checks to see if the player can build anything. If it can't, it's fucked.

Next, add these scripts: (condition in Player/Area and actions in Scripting/Flags and Scripting/Script)

TYPE: [nsAD][ENH]
NAME: "###_Base Invaded False"
*** IF ***
	 Player '<This Player's Enemy>' has Less Than or Equal  2000  within area  area '[Skirmish]MyOuterPerimeter'
*** THEN ***
  Set Flag named '###_Enemy In My Base' to FALSE
  Disable Script '###_Base Invaded False'.
  Enable Script '###_Base Invaded True'.

and

TYPE: [nsAD][ENH]
NAME: "###_Base Invaded True"
*** IF ***
	 Player '<This Player's Enemy>' has Greater Than or Equal To  4000  within area  area '[Skirmish]MyInnerPerimeter'
*** THEN ***
  Set Flag named '###_Enemy In My Base' to TRUE
  Disable Script '###_Base Invaded True'.
  Enable Script '###_Base Invaded False'.

As you might guess, these two scripts work together like a switch, checking to see if the AI is in danger or not. The reason that the switch is split into two separate scripts rather than using the false action is that if the enemy sends units into the AI's base and the script is set to say the base is invaded with any value of units, a single rifleman could walk in who costs say 200 and cause the AI to gather it's army at it's base for defence. Even if base guards and defence could easily dispatch a single soldier. So, it only calls a state of emergency if there's a lot of stuff inside the base. Later we'll see some other scripts that reset this switch once the threat is removed.

Oh, for the love of god, click "OK" on the scripts window and save your map from the file menu. Clicking cancel removes any changes to scripts, and if you just close your map it WILL NOT PROMPT YOU TO SAVE if you have only made scripting changes.

####Part 3####

Before you open worldbuilder, you'll need to make sure you've set up your AIData.INI file. This file lives in INI/Default and consists of three main chunks:

The first defines some basic constants used by the AI. Ignore these, you can fiddle if you want, but I don't advise it.

The Next is the skillset section where the generals powers used by each side are defined. The bog standard America one looks like this:

  SideInfo America
	ResourceGatherersEasy	 = 2
	ResourceGatherersNormal   = 2
	ResourceGatherersHard	 = 2   
	BaseDefenseStructure1	 = AmericaPatriotBattery  

	SkillSet1
	  Science = SCIENCE_PaladinTank
	  Science = SCIENCE_StealthFighter
	  Science = SCIENCE_A10ThunderboltMissileStrike1
	  Science = SCIENCE_A10ThunderboltMissileStrike2
	  Science = SCIENCE_A10ThunderboltMissileStrike3
	  Science = SCIENCE_SpectreGunshipSolo
	  Science = SCIENCE_DaisyCutter
	End

	SkillSet2
	  Science = SCIENCE_PaladinTank
	  Science = SCIENCE_StealthFighter
	  Science = SCIENCE_Pathfinder
	  Science = SCIENCE_Paradrop1
	  Science = SCIENCE_Paradrop2
	  Science = SCIENCE_Paradrop3
	  Science = SCIENCE_DaisyCutter
	End

  End

For each of your factions you need to make one of these, make sure you don't give the AI more sciences than it can possibly purchase during the game. The different sets are... different sets that the AI uses. You define which it uses in scripts.

The last chunk is the bit which tells the AI what buildings to build and where. The bog standard USA one is:

;Skirmish AI Build List
  SkirmishBuildList America
	Structure AmericaCommandCenter
	  Location = X:501.22 Y:546.25
	  Rebuilds = 0
	  Angle = -135.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = Yes
	END;Structure AmericaCommandCenter
	Structure AmericaPowerPlant
	  Location = X:770.43 Y:727.79
	  Rebuilds = 0
	  Angle = -45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaPowerPlant
	Structure AmericaPowerPlant
	  Location = X:759.90 Y:555.84
	  Rebuilds = 0
	  Angle = -45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaPowerPlant
	Structure AmericaPowerPlant
	  Location = X:374.82 Y:371.69
	  Rebuilds = 0
	  Angle = -45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaPowerPlant
	Structure AmericaPowerPlant
	  Location = X:477.11 Y:174.62
	  Rebuilds = 0
	  Angle = -45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaPowerPlant
	Structure AmericaPowerPlant
	  Location = X:189.19 Y:522.00
	  Rebuilds = 0
	  Angle = -45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaPowerPlant
	Structure AmericaPowerPlant
	  Location = X:418.35 Y:827.10
	  Rebuilds = 0
	  Angle = -45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaPowerPlant
	Structure AmericaAirfield
	  Location = X:767.04 Y:376.40
	  Rebuilds = 0
	  Angle = -135.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaAirfield
	Structure AmericaBarracks
	  Location = X:247.52 Y:734.59
	  Rebuilds = 0
	  Angle = 45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaBarracks
	Structure AmericaParticleCannonUplink
	  Location = X:594.59 Y:635.72
	  Rebuilds = 0
	  Angle = -45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaParticleCannonUplink
	Structure AmericaStrategyCenter
	  Location = X:384.36 Y:652.24
	  Rebuilds = 0
	  Angle = 45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaStrategyCenter
	Structure AmericaSupplyDropZone
	  Location = X:515.66 Y:347.88
	  Rebuilds = 0
	  Angle = 45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaSupplyDropZone
	Structure AmericaSupplyDropZone
	  Location = X:637.41 Y:490.20
	  Rebuilds = 0
	  Angle = 45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaSupplyDropZone
	Structure AmericaSupplyDropZone
	  Location = X:488.83 Y:760.27
	  Rebuilds = 0
	  Angle = 45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaSupplyDropZone
	Structure AmericaSupplyDropZone
	  Location = X:322.39 Y:502.03
	  Rebuilds = 0
	  Angle = 45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaSupplyDropZone
	Structure AmericaWarFactory
	  Location = X:650.93 Y:843.45
	  Rebuilds = 0
	  Angle = 45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaWarFactory
	Structure AmericaWarFactory
	  Location = X:259.56 Y:289.71
	  Rebuilds = 0
	  Angle = -135.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaWarFactory
	Structure AmericaPatriotBattery
	  Location = X:676.07 Y:670.51
	  Rebuilds = 0
	  Angle = 45.00
	  InitiallyBuilt = No
	  AutomaticallyBuild = No
	END;Structure AmericaPatriotBattery
  END;SkirmishBuildList FactionAmerica

Simply copy and pop in your buildings. You don't have to fill every "slot" and if you do, you can make more. Just be careful about locations. Also, don't worry about having things in the list which aren't part of that faction. Again, this file on its own will not do anything. You must specifically tell the AI to try to build something with scripts. Of course, if you try to tell it to build something that it can't, then it won't. In my Only War AI the factions only have a handful of buildings, but the AIData list has all sorts of GLA crap listed, just in case I wanted to add more buildings.

If you've got that ready, it's time to add some more basic bits. Let's start with the "###_Base Building" folder. (Which requires your AIData.INI fie :p)

Here's the first one: (Actions in Team/AI and Skirmish Only/Build)

TYPE: [nsAD][ENH]
NAME: "###_Build Initial"
*** IF ***
	True.
*** THEN ***
  [???]Start building team Team '###_Initial Builder 2'
  Build a building of type '[No-Prerequisite Building 1]'
  Build a building of type '[No-Prerequisite Building 2]'

Obviously in yours, you will choose a building instead of what I have in square brackets. Choose buildings that DO NOT HAVE PREREQUISITES (e.g. power plant and barracks) as the AI will run this script instantly, and if you happen to supply more than one builder at start, or have a builder than builds exceedingly fast, the AI will assign that builder to any buildings still waiting. The second construction unit is built using this script too, except you have not made the team that is referred to, hence question marks (you'll see these whenever a script has faults... [???]). Ignore them for now. Mine for Only War looks like this: (The last line is added to make the AI garrison the troops it starts with (Only War starts the player with a builder and a squad of basic Troops) as they're not much use otherwise.)

*** IF ***
	True.
*** THEN ***
  Start building team Team 'CAD_Initial Builder 2'
  Build a building of type 'CadianBarracks'
  Build a building of type 'CadianGeneratorium'
  Build a building of type 'CadianGeneratorium'
   Team 'teamSkirmishCadians' garrison a nearby building.

Now, depending on how complicated your prerequisite system is, you might have quite a few scripts in here. The next one should be the next BASIC building that the player can build once the initial buildings have been constructed.

Script two: (Conditions in Player/Owns and actions in Skirmish Only/Build)

TYPE: [nsAD][ENH]
NAME: "###_Build Initial Warfactory"
*** IF ***
	 Player '<This Player>' has Greater Than or Equal To  1  unit or structure of type '[No-Prerequisite Building 1]'
	*AND*  Player '<This Player>' has Greater Than or Equal To  1  unit or structure of type '[No-Prerequisite Building 2]'
*** THEN ***
  Build a building of type '[Prerequisite Building 1]'
  Build a building of type '[Prerequisite Building 2]'

Basically starts building once the AI has met the conditions, again, it doesn't matter what you set as conditions here, whether the AI can build it or not is determined by prerequisites in the buildings INI. By splitting them like this, the AI won't give up trying to build something it can't. You can keep adding these things until you have enough scripts for your AI to build a basic base. For example, if you need power to build a refinery, then a refinery to build a tank factory, that's 3 scripts you'll need.

Now, you want to start altering what the AI builds depending on the difficulty level. The reason I said specifically use the first few scripts for basic buildings is that the AI needs to build these structures to be able to play. Everything else is optional, such as defence, advanced buildings, extra barracks/factories.

Speaking of defence:

TYPE: [nsAD][E]
NAME: "###_E_Build Initial Defences"
*** IF ***
	 Player '<This Player>' has Greater Than or Equal To  1  unit or structure of type '[Prerequisite Building]'
*** THEN ***
  Build one additional '[Basic Defensive Building]', on the flank.
  Build one additional '[Basic Defensive Building]', on the front.

TYPE: [nsAD][N]
NAME: "###_N_Build Initial Defences"
*** IF ***
	 Player '<This Player>' has Greater Than or Equal To  1  unit or structure of type '[Prerequisite Building]'
*** THEN ***
  Build one additional '[Basic Defensive Building]', on the flank.
  Build one additional '[Basic Defensive Building]', on the front.
  Build one additional '[Basic Defensive Building]', on the flank.
  Build one additional '[Basic Defensive Building]', on the front.

TYPE: [nsAD][H]
NAME: "###_H_Build Initial Defences"
*** IF ***
	 Player '<This Player>' has Greater Than or Equal To  1  unit or structure of type '[Prerequisite Building]'
*** THEN ***
  Build one additional '[Basic Defensive Building]', on the flank.
  Build one additional '[Basic Defensive Building]', on the front.
  Build one additional '[Basic Defensive Building]', on the flank.
  Build one additional '[Basic Defensive Building]', on the front.
  Build one additional '[Basic Defensive Building]', on the flank.
  Build one additional '[Basic Defensive Building]', on the flank.

As you can tell, this makes the AI build better defence as the difficulty increases. Of course, you can add as many as you want, and add different types. Make sure that your AI isn't trying to build too many things simultaneously, it's a good idea to make sure that your conditions won't result in the AI trying to build, say, extra power, turrets and a war fac at the same time. You want to control the order of construction to make sure the AI doesn't accidentally run out of power too. If you think it will run out, add another power plant to the first building script. (That's why mine for Only War has the AI make 2 power plants). With the Hard one, you can make it build the power in the "###_H_Build Initial Defences" script as it'll probably be the one testing its energy grid.

Now we want our AI to build the rest of his base, or if you have very few buildings in your mod, you'll just want extras of what's already there.

You can do this with more prerequisites for example, perhaps set it to build the next few buildings once it has finished building 2 turrets. Or you can make the AI wait for a while. Personally I would not build anything else at this stage because your AI will be running out of money and will need to either start building units or try to find more cash.

What I have is this:

TYPE: [nsAD][N]
NAME: "###_N_Build Additional Structs"
*** IF ***
	Counter 'CAD_MASTER' IS Equal To  3 
*** THEN ***
  Build a building of type '[Extra Building 1]'
  Build a building of type '[Extra Building 1]'

Remember how our master counter increases? It uses a counter of how many teams of units have died. This means that the AI will wait until 6 attacks have failed before making any more buildings. This is handy because the AI might be able to catch the player unaware and score a quick victory. If not, it ups the ante. Simple but elegant!

Right, now we go the the folder called "###_Cash Supply". This is where the AI does it's dodgy dealings.

Add this script to start: (Action in Player/Set)

TYPE: [nsAD][ENH]
NAME: "###_Cash_Builder Bug Fix"
*** IF ***
	True.
*** THEN ***
   Player '<This Player>' gets $ 1200 

Just set the money to whatever your building vehicle costs, or the cost of what the AI starts with, as it has to pay for it.

Now, you can make your AI do some cheating. Don't worry about it though, the AI isn't as smart as us humans, it needs a bit of help. The standard AI in Generals and ZH cheats like a bastard :p Generally this comes in the form of some large one-off cash bonuses at a certain time, or if the AI accomplishes something. You can also give it a constant supply of money. Here's how:

TYPE: [nsAD][NH]
NAME: "###_Cash_Warfactory Boost"
*** IF ***
	 Player '<This Player>' has Greater Than or Equal To  1  unit or structure of type '[Warfactory]'
*** THEN ***
   Player '<This Player>' gets $ 5000 

This is a one-off, and will give the AI 5000 as soon as it owns a warfactory. (If on Normal or Hard difficulty) If you set up some more things like this that might be activated in late game (i.e. by buildings it takes a while to get) you should set the evaluation to every 1 second.

To give the AI an uninterruptible constant cash supply, change the type to [nsAnd][N] and set the evaluation to once per second and set an amount for normal, and duplicate for hard. Basically, every second the AI will get whatever amount you have set. Simple, huh? You can of course give them money on easy too, but easy is supposed to be easy... no? :p

Right, now run to the "###_Generals Powers" folder and add this script: (Actions in Scripting/Timer)

TYPE: [nsAD][ENH]
NAME: "###_Set Skillset Determine Timers"
*** IF ***
	True.
*** THEN ***
  Set timer '###_Choose Skillset 1' to expire between 8.00 and 14.00 seconds.
  Set timer '###_Choose Skillset 2' to expire between 8.00 and 14.00 seconds.

This script will set off two counters, and the one to expire first will choose the skillset that the AI uses with the next two scripts.

Now add these: (Condition in Scripting, Actions in Player/Set and Scripting/Script)

TYPE: [nsAD][ENH]
NAME: "###_Set Skillset 1"
*** IF ***
	Timer 'CAD_Choose Skillset 1' has expired.
*** THEN ***
   Player '<This Player>' uses skillset number  1  (1-5).
  Disable Script '###_Set Skillset 2'.

TYPE: [nsAD][ENH]
NAME: "###_Set Skillset 2"
*** IF ***
	Timer 'CAD_Choose Skillset 2' has expired.
*** THEN ***
   Player '<This Player>' uses skillset number  2  (1-5).
  Disable Script '###_Set Skillset 1'.

Of course, you're not limited to two, you can have up to 5 skillsets. These scripts in themselves will not fire Generals Powers though. You need some more to do that. For each generals power that is fired on a location, you'll have to add a pair of script like these: (Condition in Player/Special Power)

TYPE: [nsAnd][ENH]
NAME: ###_[Power] Ready"
*** IF ***
	 Player '<This Player>' is ready to fire Special power '[Power]'.
*** THEN ***
  Enable Script '###_[Power] Fire'.

This one just checks to see if the power is ready for use. The second: (Action in Player/Special Power)

TYPE: [nsnand][ENH]
NAME: ###_[Power] Fire"
*** IF ***
	 Player '<This Player>' is ready to fire Special power '[Power]'.
*** THEN ***
   Player '<This Player>' fire Special power '[Power]'
  Enable Script '###_Artillery Ready'.
  Disable Script '###_Artillery Fire'.

Simply add pairs of these for all your offensive firing powers. Things like artillery, carpet bombs, stuff like that.

Now, just one more folder to do before we get onto the terrors of teams and build conditions... Time to think about upgrades, so go to your "###_Upgrade" folder. Upgrades are a bit tricky, because the decision to upgrade or not will depend on a lot of things. I'll show you some simple ones, and from there on it's more or less up to you to decide how you want to make your AI decide to upgrade.

The most simple type of upgrade is one where the AI upgrades when it has enough money, and the building it needs to build the upgrade: (Conditions in Player/Owns and Player/Money, action in Player/AI)

TYPE: [nsAD][ENH]
NAME: ###_Upgrade [Upgrade]"
*** IF ***
	 Player '<This Player>' has Greater Than or Equal To  1  unit or structure of type '[Building You Need]'
	*AND*   5000  is Less Than or Equal the number of credits possessed by Player '<This Player>'
*** THEN ***
  Have AI Player '<This Player>' build this upgrade: Upgrade '[Upgrade]'

Of course, you can set the price cut-off to whatever you want, but it's a good idea to set it well above the cost of the upgrade to give the AI spare cash. You don't want them bankrupting themselves then finding they need to replace a building.

You can set upgrades to be bought with more specific conditions too. For example, if you have something that improves the armour of a unit, you could make the AI check to see if it has more than, say, 5 of the unit... and if it does (and has enough cash, and the building required) it'll upgrade.

Save your bloody map!

####Part 4####

Now we get onto setting up things that help out our teams of units. Lets go to the "###_Team Behaviour" folder to start. This folder contains lots of simple generic behaviour that is very important to the AI. You'll also see how some earlier parts of the AI find their use here.

First add this script: (Action in Team/Guard)

TYPE: [SAD][ENH]
NAME: "###_B_Guard Base"
*** IF ***
	True.
*** THEN ***
   Team '<This Team>' begins guarding  area '[Skirmish]MyOuterPerimeter'

This is just a simple script that is used to tell the AI to guard whilst it's building teams.

Now this: (Condition in Team/Attacked and actions in Team/Transport and Team/Hunt)

TYPE: [SAD][ENH]
NAME: "###_B_Hunt"
*** IF ***
	 Team '<This Team>' has been attacked by Player '<This Player's Enemy>'
*** THEN ***
   Team '<This Team>' unload.
   Team '<This Team>' begins hunting.

This is quite self explanatory. The unload is added in to make the AI empty any transports in the team.

Here's another simple one: (Actions in Team/Stop and Team/Guard)

TYPE: [SAD][ENH]
NAME: "###_B_Emergency Defend"
*** IF ***
	 Flag named '###_Enemy In My Base' IS TRUE
*** THEN ***
   Team '<This Team>' stops.
   Team '<This Team>' begins guarding  area '[Skirmish]MyInnerPerimeter'

The stop command is used to make the AI team stop whatever it is doing.. be that moving, attacking, whatever, and the guard makes the team run back home to fight off the invaders.

Now this:

TYPE: [SAD][ENH]
NAME: "###_B_Game Lost Return Home"
*** IF ***
	 Flag named '###_I Am Crippled' IS TRUE
*** THEN ***
   Team '<This Team>' begins guarding  area '[Skirmish]MyInnerPerimeter'

This script is kinda something I did a bit different from ZH. The standard AI sells all it's buildings when it's screwed, I thought it might be better to have it put up a last stand.

Now for the opposite of this script:

TYPE: [SAnd][ENH]
NAME: "###_B_Game Won Finish Off"
*** IF ***
	 Flag named '###_Enemy Crippled' IS TRUE
*** THEN ***
   Team '<This Team>' begins hunting.

I think it explains itself quite well... The AI will simply hunt down any remaining stragglers that the enemy still has.

Next: (Action in Team/Garrison)

TYPE: [SAnd][ENH]
NAME: "###_B_Garrison"
*** IF ***
	True.
*** THEN ***
  Set Flag named '###_INFANTRY BUILDING' to FALSE
   Team '<This Team>' garrison a nearby building.

This is an interesting one. The script is called by a team as soon as it has finished building, telling it to run off and garrison something nearby. Hence the action that sets the team building flag to false.

This next one is a tad more complex, but is very important: (Condition in Player/Special Power)

TYPE: [SAnd][ENH]
NAME: "###_B_Scatter"
*** IF ***
	Player Player '<This Player's Enemy>' starts using Special power '[Area Damage Power 1]'.
  *** OR ***
	Player Player '<This Player's Enemy>' starts using Special power '[Area Damage Power 2]'.
  *** OR ***
	Player Player '<This Player's Enemy>' starts using Special power '[Area Damage Power 3]'.
*** THEN ***
   Team '<This Team>' begins hunting.

Now, the powers you should list here are the ones that an enemy would throw at a large cluster of units, things like MOAB's, Carpet Bombs and so on. You should also list superweapons, not just generals powers. Naturally, this list will get very long if you have a lot of powers and abilities, so it might be best to remove some that are less threatening if you have lots... Otherwise the AI will end up disrupting its plans constantly.

Now the last one: (Condition in Team/Area... "Team has units in area" and Action in Team/AI)

TYPE: [SAnd][ENH]
NAME: "###_B_Team Succeed"
*** IF ***
	 Team '<This Team>' has one or more units in  area '[Skirmish]EnemyInnerPerimeter' (Surfaces Allowed: Ground or Air).
*** THEN ***
  Increase the AI priority forTeam '<This Team>'  by its Success Priority Increase amount.

This script is another very important one. It tells the AI what it should consider a successful team. This is a key part in how the AI decides which units to send where, basically if a team succeeds, the AI will be more inclined to send more units that way, and possibly a duplicate team. The success condition should not be something overly complex, and as such a team being able to make it into the inside of an enemy base is good enough.

Now we're going to the folder "###_Team Behaviour_Combat Zone"

This folder controls some special teams that are used to guard the middle of the map, intercept enemy groups and join up with the AI's attack forces should reinforcements be needed. These teams behave like a pool of units that the AI can draw from in times of need, and when not in need they will take care of themselves. These teams are especially useful in the early and later stages of a game, early on to defend, and later on to add to the finishing blow as the AI flows over your defences like a terrible tide of slaughter.

First add this: (I won't tell you where the scripts are, you should have a good enough idea by now :p)

TYPE: [SAnd][ENH]
NAME: "###_BCZ_Guard"
*** IF ***
	True.
*** THEN ***
  Set Flag named '###_VEHICLE BUILDING' to FALSE
  Set Flag named '###_INFANTRY BUILDING' to FALSE
   Team '<This Team>' begins guarding  area 'CombatZone'

Now, my Combat Zone teams are a combination of troops and vehicles, hence why I have the infantry and vehicle set to false actions. If you wanted, you could split this into two, one for infantry and another for vehicles.

Next: (Condition in Player/Area and action in Team/Hunt)

TYPE: [SAnd][ENH]
NAME: "###_BCZ_Join Attack"
*** IF ***
	 Player '<This Player>' has Greater Than  5000  within area  area '[Skirmish]EnemyInnerPerimeter'
*** THEN ***
   Team '<This Team>' begins hunting.

Basically, if the player has lots of stuff inside the enemy base, these guys will join in with the fun and lend their firepower to the fray.

TYPE: [SAnd][ENH]
NAME: "###_BCZ_Flee"
*** IF ***
	 Player '<This Player's Enemy>' has Greater Than  15000  within area  area 'CombatZone'
*** THEN ***
   Team '<This Team>' begins guarding  area '[Skirmish]MyOuterPerimeter'

Uh oh... run!

We also need to make our team go back to duty once the threat is removed:

TYPE: [SAnd][ENH]
NAME: "###_BCZ_Return To Guard"
*** IF ***
	 Player '<This Player's Enemy>' has Less Than  2000  within area  area 'CombatZone'
*** THEN ***
   Team '<This Team>' begins guarding  area 'CombatZone'

Well, that should do it for the Combat Zone. We're going to the next folder down now, "###_Team Behaviour_Priorities"

This folder has the behaviour scripts that assign the priority lists we set up all the way back in part 1. These scripts are exceedingly simple, and are all pretty much the same.

(Action in AttackPrioritySet/ApplySet)

TYPE: [SAD][ENH]
NAME: "###_BP_Generic"
*** IF ***
	True.
*** THEN ***
  Have Team '<This Team>' use Attack priority set '###_Generic'.

You need one of these for each set you have defined. If you made 7 sets, you need 7 of these scripts in this folder, one for each.

Now we're off to the last folder, "###_Team Behaviour_Teams Lost"

The purpose of these scripts is to alter counters when teams fail and die. Like the last folder, these scripts follow a very predictable pattern.

Number 1: (Again, you should know where these are... if not, it's a little test :p)

TYPE: [SAnd][ENH]
NAME: "###_B_TLB_Tanks"
*** IF ***
	True.
*** THEN ***
  Add  1  to counter '###_TLB_Tanks'
  Add  1  to counter '###_TL_Overall'
  Reduce the AI priority forTeam '<This Team>'  by its Failure Priority Decrease amount.

I'm hoping that by now you're getting a hang of scripting and can tell what a script does quite quickly. This script adds to loss counters and makes the team less important to the AI. This is part of the system that makes the AI choose where to attack and with what to use.

Of course, you can't just have the TLB (which stands for Team Lost Back), you need Centre and Flank too... so, duplicate this twice and alter the one single letter you need to :p

...
  Add  1  to counter '###_TL#_Tanks'
...

Okay, now we're done with our Tanks, but what about Troops? It's time to hit that copy button again! Three more duplicates, and this time replace the "Tanks" with "Troops".

Once you have your six scripts, it's time to go up a folder to "###_Team Behaviour_Sequential Scripts"

Here we have the orders that teams will execute when they're built. The scripts consist of a pair. One, the _BO_ (Build Orders) one is what the team is told to do once built, and it tells to team to execute another script sequentially (_BS_ Build Sequential)... So: (Actions in Scripting/Flags and Team/SequentialScript)

TYPE: [SAnd][ENH]
NAME: "###_BO_Generic_Attack Back"
*** IF ***
	True.
*** THEN ***
  Set Flag named '###_INFANTRY BUILDING' to FALSE
  Set Flag named '###_VEHICLE BUILDING' to FALSE
   Team '<This Team>' executes Script '###_BS_Generic_Attack Back' sequentially.

Paired with: (Actions in Skirmish Only/Move and Team/Attack)

TYPE: [SAnd][ENH]
NAME: "###_BS_Generic_Attack Back"
*** IF ***
	True.
*** THEN ***
  Have Team '<This Team>' move to the start of enemy path 'Backdoor'.
  Have Team '<This Team>' approach the enemy using path 'Backdoor', as a team is TRUE
   Team '<This Team>' attack anything in  area '[Skirmish]EnemyInnerPerimeter'

Okay, the first script tells the AI that it's free to start building other things, and that the team should execute the second script sequentially. Sequentially means it carries out the first action, then the second, then the third, and so on... So, the team will move to the start of the Backdoor path, then move along it, then attack anything inside the enemy base.

Now, this is only the scripts required for a generic team to attack the back, so you need duplicates of both of these for Centre and Flank too! Get copying!

Time for some more copying too... you'll need to make BO scripts for both tanks and troops, for Back, Centre and Flank too... so that's another 6 scripts... However, all you need to change is which flag is set to false, the teams can still use the Generic BS script... So here's two examples:

TYPE: [SAnd][ENH]
NAME: "###_BO_Tanks_Attack Back"
*** IF ***
	True.
*** THEN ***
  Set Flag named '###_VEHICLE BUILDING' to FALSE
   Team '<This Team>' executes Script '###_BS_Generic_Attack Back' sequentially.

TYPE: [SAnd][ENH]
NAME: "###_BO_Troops_Attack Flank"
*** IF ***
	True.
*** THEN ***
  Set Flag named '###_INFANTRY BUILDING' to FALSE
   Team '<This Team>' executes Script '###_BS_Generic_Attack Flank' sequentially.

Right, now we're getting into the really really tedious parts of making the AI, and as such I'm going to leave it here for this part. There should be 3 folders left that contain no scripts, and these 3 will contain by far the most. The build conditions are the mountain of scripts that tell your AI when to build squads of units. My Only War Cadian Hard AI has 65 of these buggers...

Save!
Posted Image

#3 Blbpaws

Blbpaws
  • New Members
  • 1 posts

Posted 21 April 2007 - 02:43 PM

Have you gone any further with this and could you show that to us? :p

Thanks.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users