Actions

Tutorial:AI Scripting

From Populous Wiki

What is a script?

A script is a set of instructions that Populous: the beginning reads and uses to command the computer controlled players.

Each level can have up to 3 scripts used at once, for each tribe respectively.

A script must also have an accompanying attribute file, which stores the default values for all internal_attribute_variables and the restrictions on buildings/spells similar to a header file used to set default restrictions for the player; I find it easier to simply set them all ON and simply not include them in the script if you do not wish the AI to use them. There is a built-in tool to do this on this scripter.

A script can be as simple as the following:

{

IF ( INT_MY_NUM_PEOPLE < 10 )
{
	DO GIVE_UP_AND_SULK ON
}
ELSE
{
	SET INT_ATTR_EXPANSION 50
}
ENDIF

}
SCRIPT_END

Populous cannot read that and utilise it to control the AI so you need a compiler to compile it to the correct format, and stored in files such as cpscr010.dat (located in the …Bullfrog\Populous\levels directory)

The compiler we will be using for this tutorial is World Editor’s compiler, and can be found. This also includes an attribute file editor, but as I have already stated – this is not really necessary. You will also need a plain text editor (such as notepad) to edit the scripts in their decompiled form.

A basic script layout

 
{
IF ( INT_GAME_TURN == 0 )
{
	// set starting attributes

	SET INT_ATTR_EXPANSION 50
	SET INT_ATTR_MAX_BUILDINGS_ON_GO 5
	SET INT_ATTR_HOUSE_PERCENTAGE 20

}
This part is triggered as soon as the level starts (game turn 0)

And is usually used to set up initial attributes and anything else that has to be done immediately on level start.

 
ELSE
{
	// rest of script

	DO CONVERT_AT_MARKER 10
	DO SPELL_ATTACK INT_ANGEL_OF_DEATH 10 -1

}
ENDIF

}
SCRIPT_END
Everything else that you need to do is put in this area.

Variables

INT_GAME_TURN
is an internal game variable and cannot be changed by the script – it is however changed by populous as the game progresses and can still be referenced to in order to trigger things, which is what happens initially at the start of the level with most scripts.
INT_ATTR_EXPANSION
is an internal attribute variable and can be changed by the script and controls the ai’s behaviour throughout the level. You can tell it is an attribute variable as it starts with INT_ATTR.

To change an attribute you use the SET command like this:

SET Internal_attribute_Variable constant/user_variable

Exemplar:

SET 	INT_ATTR_EXPANSION 	10
SET	INT_ATTR_EXPANSION	$var1

User Variables

There is also another type of variable which you can create yourself, a limit of 64 possible. They start with the dollar sign prefix ($) and may contain indentifiers, constants, internal attribute variables or internal game variables.

Exemplar:

SET 	$var1 		INT_BLAST
SET 	$var2 		INT_MY_NUM_PEOPLE
SET     $var3           10
SET     $var4           INT_M_SPELL_BLAST_COST

This has stored the identifier of the spell Blast in $var1 and could be used in an attack command if necessary.

It also stored the total number of people in $var2 that the tribe (using this script) has currently. A good waste of a variable, as the internal game variable can be referred to directly.

The constant "10" has been stored in $var3, useless really but it is still possible.

The mana cost of one shot of blast is stored in $var4. Another waste of a user variable, unless used cleverly with conditions, that will change the spell cost it is looking for.


Variables must have a name (like above) and start with a $ prefix, they cannot contain spaces and the data they store cannot contain spaces either.

Operators

Operators are used to test whether things are true.

Exemplar:

IF ( 4 > 3 )
{
	// do something
}
ENDIF

In this case, it will always be true, 4 and 3 are constants and unless the laws of the universe change : 4 will always be greater than 3 and thus the code will be executed every game turn.

However, you can use these operators to test variables against variables, or variables against constants/internal game varables.

header 1 header 2
IF ( $var1 > 10 )
{
	// do something
}
ENDIF
Code only executed if $var1 is greater than 10
IF ( $var2 < INT_MY_NUM_PEOPLE )
{
	// do something
}
ENDIF
Code only executed if $var2 is less than the number of people in the computer’s tribe

List of operators and their meanings:

  • > greater than
  • < less than
  • >= greater than or equal to
  • <= less than or equal to
  • == equal to (2x =)
  • != not equal to


The IF Statement

Basic Conditional Statement

IF ( 4 > 3 )
{
// do something
}
ENDIF

IF ( $var1 <= 4 )
{
// do something
}
ENDIF

The if statement tests whether certain values are true.

In the first one, if the constant “4” is greater than the constant “3” it will execute whatever is inside the braces { }

The second statement tests whether a variable ($var1) is less than or equal to the constant “4” If it is true it will execute whatever is inside the braces.

All if statements must be contained in brackets Then proceed with an opening brace Then some code e.g.

SET $var1 2
SET INT_ATTR_EXPANSION 100
DO BUILD_AT 12 13 INT_SMALL_HUT

You can also provide alternatives, in case the initial statement is false e.g.

IF ( INT_BLUE_PEOPLE < INT_RED_PEOPLE)
{
DO TRIGGER_THING 1
}
ELSE
{
// do something else
}
ENDIF

These are done with ELSE commands and must have a closing brace before them } and a new opening brace { , at the very end it must have a closing brace } followed by ENDIF

You can also put more conditional statements inside the ELSE braces, but be sure to close them properly before you close the initial IF statement.

EVERY Statement

This is comparable to a loop in programming, if it was stripped to its basic definition of a neverending loop.

It relies on game turns in populous, 12 game turns equal 1 second. (This can be changed in the debug menu, but that would probably mess things up, so don't change it!).

So if you want something triggering once every minute it is a simple matter of computing the game turns from seconds e.g.

One minute = 60 seconds 60 x 12 = 720

But: EVERY commands must always be a power of 2 number

i.e.

2^1 2^2 2^3 2^4 2^5
2 4 8 16 32
EVERY 1024 
{
// do something
}
Example EVERY command, it only needs braces for the actual EVERY statement.

If you did want something executing every 720 game turns, you can use an offset to alter the loop to 720.

EVERY 1024 304
{
// do something
}
This subtracts 304 from 1024 to get your final number you desire.

How to combine IF and EVERY statements

The following is an example loop, and some of the contents will trigger only if the conditions are met:

EVERY 512
{
DO COUNT_PEOPLE_IN_MARKER COUNT_WILD 7 1 $wild
	IF ( $WILD > 0 )
	{
		DO CONVERT_AT_MARKER 1
	}
	ENDIF
}

This will be triggered every 42.7 seconds (to 1d.p.) (512/12 = 42.7).

DO COUNT_PEOPLE_IN_MARKER COUNT_WILD 	10 	5 	$wild

DO COUNT_PEOPLE_IN_MARKER tribe marker radius variable

COUNT_WILD allows it to count the number of neutral people.

It is best to set the radius to around 5 or 7, any larger and there is a greater chance the wildmen will wander outside of the convert(command's) range.

More about converting:

The state command DO STATE_SHAMAN_GET_WILDS ON is useful as until this is turned off, they will continue to check within their INT_ATTR_EXPANSION for wild men, and will convert on their own.


Other resources

You can find more things to do with scripting on my site here or for a list of all the commands and their meaning go here on megafont's wiki

Writers

This tutorial was written by Nexus, for more information with scripting please email me here.

Tutorials
Matchmaker: MatchmakerEnabling BetaCoop vs AISubmitting MapsUltimate Populous Guide
Map Making: Creating a SP LevelCreating a MP LevelMaking a Map
AI Scripting: AI ScriptingCreating a SP Level
Mod Making: Sprite EditingTexture EditorTox Sprite Editor
Misc: Installing a CampaignSupport