Last updated: 08/05/2024, 00:52

Current Project: Rejuvenation of Hellkeeper.net

Tutorials index

Advanced AI scripting techniques

By popular demand here's some more .u2s scripting techniques. No tutorial can replace the AI documentation, however, so I suggest that you check it out. And you should look at the game's scripts. If you don't know how to do something try to think of a similar situation that happened in the game and at the script.

Here's a list of things you'll learn in this tutorial:

Random decisions

You can easily create randomness in your scripts using the "testrandom" command. Here's how you use it: testrandom creates a random number between 0 and 1 and allows you to jump to a new label based on the outcome. Here's an easy example:

testrandom 0.5 gotolabel Outcome1
gotolabel Outcome2

:Outcome1
[...]

:Outcome2
[...]

If the randomly created number is bigger or equal to 0.5 the script will jump to the Outcome1 label, otherwise script execution will continue (and we jump to the Outcome2 label). Note that you don't need to do a gotolabel after the testrandom statement, because script execution will just continue after that line. But in many cases it makes the script easier to read because it creates a clean break.

Of course you can also create more than 2 possible outcomes:

testrandom 0.66 gotolabel Outcome1
testrandom 0.66 gotolabel Outcome2
gotolabel Outcome3

This creates an equal 33% chance that we jump to any given label. Please note that you're actually creating a new random number in each testrandom line, but in the end this doesn't matter. A 1/3 chance of something happening plus another 1/3 chance of something happening automatically make the last choice a 1/3 probability as well.

Also note that you have to use the number 0.66 to create a 33% chance! The testrandom check is true if the result is BIGGER or equal to your value, and in this case the result will have to be in the 0.66-1.0 range to happen (which is roughly a 1/3 probability).You can also call labels with testrandom:

testrandom 0.5 gotolabel Outcome1
gotoactor PathNode3
[...]

:Outcome1
gotoactor PathNode1
return

In this example there's a 50% chance that AI will jump to the Outcome1 label (and walk to PathNode1), then will return (and walk to PathNode3). In the other 50% he will directly go to PathNode3, skipping the Outcome1 label.

Random encounters using "setlocation"

^

Setlocation allows you to teleport a pawn to any given point in the map. You simply use "setlocation PathNode11" and the pawn will pop there. You can use this command for a whole bunch of things, but one neat application is randomly placed enemies. Let's say we have a linear playerpath that leads throught three rooms, and we want the player to encounter a Skaarj somewhere along the way. You can add replay value by randomizing where the Skaarj will actually appear.

testrandom 0.66 gotolabel TeleportToRoom1
testrandom 0.66 gotolabel TeleportToRoom2
setlocation PathNode3 //pathnode located in room #3
sleep

:TeleportToRoom1
setlocation PathNode1 //pathnode located in room #1
sleep

:TeleportToRoom2
setlocation PathNode2 //pathnode located in room #2
sleep

The engine does not check for visibility when it teleports the pawn, you'll want to make sure that all three pathnodes are out of sight when you perform the script action or the Skaarj might appear out of thin air.

If you DO need visibility checking in these you should use a PawnFactory and SpawnPoints. The testrandom implementation works best for NPCs that are already in the level and need a complex script (note that you CAN assign .u2s scripts to PawnFactory AIs, but that's another story). I like it beause it's easier to set up than SpawnPoints (at least I think so :))

Finding actors dynamically and sending the pawn there

FindActor is a neat little command that you can use to great effect. Using it you can do a radius for any given actor in the level while the game is running, and you can send the pawn to that location with the gotoactor found command.

FindActor is pretty complex with a lot of parameters, so let's break it down step by step: Here's how the AI doc describes the command:
findactor [TargetName [Min [Max [DistanceType [VisibilityType [Num [gotolabel/call targetlabel]]]]]]]
The [] brackets mean that a parameter is optional, so we don't need to use all of them most of the time. A simple application of FindActor looks like this:
findactor engine.pathnode 32 1024 1
gotoactor found

This makes the pawn perform a radius check for any pathnode, starting at 32 world units and stopping at 1024 units. The last parameter (1) means that the closest pathnode found is picked and cached in the "found" variable. The second line is a special use of the gotoactor command, it is used to send the pawn to the cached actor.

The above example should give you a good idea of the things you can do with this command. You could, for example, create a scared scientist that follows the player, but runs away at any sign of danger and cowers at the closest hiding spot (special actors that you placed in the level, in this case ShadowNodes). Here's how you would implement this (the // characters mark C++-style comments):

:FollowPlayer
onevent SeeEnemy gotolabel CheckForHiding
gotoactor Player 128 // follow the player with a radius of 128 units
sleep 3
gotolabel FollowPlayer //in case we ever reach the player wait 3 seconds and start over

:CheckForHiding
findactor U2AI.ShadowNode 0 2048 1 gotolabel Hide // jump to Hide label if shadownode is found
message "No hiding spot found, continue script" // no suitable actor found in a 2048 radius
sleep 1
gotolabel FollowPlayer
:Hide
gotoactor found 64 // get within 64 units of found shadownode
findactor // clear found spot
setstance crouching
onevent SeePlayer gotolabel FollowPlayer

Add two more lines to the beginning and end of your script and delete the final sleep statement, so that it looks like this:

findactor u2.alarmtrigger 0.0 4096.0 0 0 0 gotolabel useit // match first within 4K units, no vis test
findactor u2.alarmtrigger 0.0 4096.0 1 0 0 gotolabel useit // match closest within 4K units, no vis test
findactor u2.alarmtrigger 0.0 4096.0 2 0 0 gotolabel useit // match furthest within 4K units, no vis test
findactor u2.alarmtrigger 0.0 4096.0 3 0 2 gotolabel useit // match all within 4K units, no vis test, max 2
findactor u2.alarmtrigger 0.0 0.0 2 0 0 gotolabel useit // match furthest, no vis test
findactor u2.alarmtrigger 0.0 0.0 2 1 0 gotolabel useit // match furthest, must be visible

Download this archive to find more information about AI Scripting.

Tutorial by Matthias Worch

© 2005-2024, by Hellkeeper.

Valid XHTML 1.1 & CSS 3