The 3rd Age

BFME II: Deluxe Edition

BFME II: Deluxe Edition

Fixing problems with BFME 2 and giving it more depth.

Button for The 3rd AgeButton for The Dwarf HoldsButton for The Elven AllianceButton for Helm's Deep Last HopeButton for GothmogtheOrcButton for BFME+Button for The Four AgesButton for HDR HeadquartersButton for Middle Earth CenterButton for Project Perfect Mod

Become an affiliate!

   

Quick Lists

Top Rated Popular New Updated Last Comments Users

Register and log in to move these advertisements down

In-depth AI Coding

Avatar of Sulherokhh

Sulherokhh

Category: Code
Level: Expert
Created: Wednesday October 3, 2007 - 8:28
Updated: Friday August 27, 2010 - 21:26
Views: 21001
Summary: The beginning of a comprehensive guide to AI-modding for BfME2

Rating

Staff says

4.8

Members say

4.9

Average

4.9/5.0

20 votes

Page 1 2 3 4 5 6 7 8 9 10 11
B3. Complicating the issue.

Let's say you want Faramir to dismount when there are a lot of Pikemen around. He is pretty vulnerable to Pikemen when mounted, so this would make sense.
This is 'one' way of dealing with the issue. A similiar way can be implemented to make him mount again, replacing the above described rather simple procedure of letting him mount.
Bear with me.

Let's have a look at one of the most interesting modules. It was used originally just to let Sting glow when orcs where near:

              
Code
Behavior = SpecialEnemySenseUpdate ModuleTag_StingSeesOrcs
    SpecialEnemyFilter = ANY +ORC +URUK +MordorShelob
    ScanRange = 200
    ScanInterval = 2000
End


This Module lets Frodo sense specific enemies when they are at a range of 200 or less. The module checks for enemies ever 2 seconds.

Faramir needs this to be a bit modifier. In the end, it should look like this:

              
Code
Behavior = SpecialEnemySenseUpdate ModuleTag_FaramirSeesPikemen
    SpecialEnemyFilter = ANY +PIKEMAN ENEMIES
    ScanRange = 100
    ScanInterval = 2000
End


He will now sense Enemy Pikemen at a range of 100 'feet' (roughly the diameter of standard horde).
This module does one thing, and one thing only. It sets the ModelCondition 'SPECIAL_ENEMY_NEAR' when it's conditions are met.
Now we need this ModelCondition to actually DO something.
With Frodo, it made Sting glow. This was done with and additional W3DDraw-module.
But to make it trigger a SpecialPower, we will need an upgrade. And we also want it removed when the conditions are gone, so it can be reacquired and once again trigger the dismount when the conditions are met again at a later date.
AND we want Faramir to mount again when the Pikemen are gone.
This is the way to do it:

First we need two OBJECT-upgrades. There are some suitable unused ones already defined, but for clarity's sake, i will make up new ones.

              
Code
Upgrade Upgrade_DismountNOW
    Type = OBJECT
End

Upgrade Upgrade_MountNOW
    Type = OBJECT
End


Next, we need to adjust Faramir's LUAFunctions, or rather add two more to his set.

To explain briefly, the LUAFunctions of an object continuously check during the game if certain conditions are met. If they are, they will trigger a scripted response.
There are quite some useful triggers that can be used for the function, specifically a ModelCondition (or the sudden absence of a ModelCondition) can be used as a trigger!

Which 'EventLists' is used is defined in the object's module called 'AIUpdateInterface'

This is what we have for Faramir:
              
Code
Behavior = AIUpdateInterface ModuleTag_03
    AutoAcquireEnemiesWhenIdle = Yes ATTACK_BUILDINGS STEALTHED
    MoodAttackCheckRate = 500
    HoldGroundCloseRangeDistance = 41
End


Many other objects have an additional line which defines the LUAFunction-list used. Like Frodo:
              
Code
Behavior = AIUpdateInterface ModuleTag_03
    AutoAcquireEnemiesWhenIdle = Yes ATTACK_BUILDINGS
    MoodAttackCheckRate = 500
    AILuaEventsList = FrodoFunctions
    HoldGroundCloseRangeDistance = 40
    CanAttackWhileContained = Yes
End


So, i'll add a line with a new (yet undefined) list:
              
Code
AILuaEventsList = FaramirFunctions


My new module for Faramir will now look like this:
              
Code
Behavior = AIUpdateInterface ModuleTag_03
    AutoAcquireEnemiesWhenIdle = Yes ATTACK_BUILDINGS STEALTHED
    MoodAttackCheckRate = 500
    AILuaEventsList = FaramirFunctions
    HoldGroundCloseRangeDistance = 41
End



Now it's time to define Faramir's LUAFunction-set (in 'data\scripts\scriptevents.xml')

At the top of the file, you'll find a list of common triggers. Some are system triggers (so called 'InternalEvents' like 'OnCreated') some are ModelConditionTriggers ('ModelConditionEvent' like 'Moving').
The last ones have a set of condition definitions, listing ModelConditions with a '+' (to signify this ModelCondition must be present) and '-' (to signify it is not allowed to be present).

I'll make two new ones:
              
Code
<ModelConditionEvent Name="OnEnemyNear">
    <Conditions>+SPECIAL_ENEMY_NEAR</Conditions>
</ModelConditionEvent>

<ModelConditionEvent Name="OnEnemyNOTNear">
    <Conditions>-SPECIAL_ENEMY_NEAR</Conditions>
</ModelConditionEvent>



Scroll down. You'll find a lot of EventLists. I'll make a new one for Faramir by copying one that inherits the BaseScriptFunctions (that contain nothing), for exaxmple:
              
Code
<EventList Name="InfantryFunctions" Inherit="BaseScriptFunctions">
    <EventHandler EventName="BeAfraidOfBalrog" ScriptFunctionName="BecomeAfraidOfBalrog" DebugSingleStep="false"/>
    <EventHandler EventName="BeAfraidOfRampage" ScriptFunctionName="BecomeAfraidOfRampage" DebugSingleStep="false"/>
    <EventHandler EventName="BeTerrified" ScriptFunctionName="BecomeTerrified" DebugSingleStep="false"/>
    <EventHandler EventName="BeUncontrollablyAfraid" ScriptFunctionName="BecomeUncontrollablyAfraid" DebugSingleStep="false"/>
</EventList>


Now i have this:
              
Code
<EventList Name="FaramirFunctions" Inherit="BaseScriptFunctions">
    <EventHandler EventName="OnEnemyNear" ScriptFunctionName="BecomeDismounted" DebugSingleStep="false"/>
    <EventHandler EventName="OnEnemyNOTNear" ScriptFunctionName="BecomeMounted" DebugSingleStep="false"/>
</EventList>


As you can see, i already inserted two new ScriptFunctions, 'BecomeDismounted' and 'BecomeMounted'.
Now i will need to define them.

Open up 'scripts.lua' (in 'data\scripts\')

You'll see a list of functions that can be triggered by any condition in 'scriptevents.xml'
Now i insert my new functions, which would look like this:

              
Code
function BecomeDismounted(self)
    ObjectRemoveUpgrade( self, "Upgrade_MountNOW" )
    ObjectGrantUpgrade( self, "Upgrade_DismountNOW" )
end

function BecomeMounted(self)
    ObjectRemoveUpgrade( self, "Upgrade_DismountNOW" )
    ObjectGrantUpgrade( self, "Upgrade_MountNOW" )
end


Pretty self-explanatory, right?

All we need to do now is redo our DoCommand modules:

We already have this:
              
Code
// Switch CommandSet when MOUNTED
Behavior = MonitorConditionUpdate ModuleTag_CommandSetSwapper
    WeaponSetFlags = MOUNTED
    WeaponToggleCommandSet = GondorFaramirCommandSetMounted
End


Now we add two modified modules to the object code:
              
Code
Behavior = DoCommandUpgrade Module_DoCommandMount    
    TriggeredBy = Upgrade_ObjectLevel3 Upgrade_ObjectUnderAIControl Upgrade_MountNOW
    RequiresAllTriggers = Yes
    GetUpgradeCommandButtonName = Command_ToggleMount
End

Behavior = DoCommandUpgrade Module_DoCommandDismount
    TriggeredBy = Upgrade_ObjectUnderAIControl Upgrade_DismountNOW
    RequiresAllTriggers = Yes
    GetUpgradeCommandButtonName = Command_ToggleDismount
End


As mentioned earlier, you may need two differently named CommandButtons and two different CommandSets for it to work flawlessly.


RESULT INGAME:

When created, the LUAscripts immediately recognize the missing 'SPECIAL_ENEMY_NEAR' ModelCondition and give the yet unmounted Faramir the upgrade 'Upgrade_MountNOW' which will do nothing (yet) until he is level 3.
Then he will immediately mount his horse. If Pikemen are near, the flag 'SPECIAL_ENEMY_NEAR' is set, 'Upgrade_MountNOW' is removed, 'Upgrade_DismountNOW' is granted, this in turn triggers the dismount. The reverse happens as soon as the PIKEMEN are gone for at least 2 seconds.
And all of this will only work in the presence of the upgrade 'Upgrade_ObjectUnderAIControl' and so is only set into motion for AI-PLAYERS ONLY!


Next page: Making Faramir use 'Wounding Arrow' ONLY when appropriate (good for many other abilities as well)


-------------------------------------------------------------------------------------------------------------------------------------------------------------------

Comments

Display order: Newest first | Page: 1, 2

Prolong - Thursday July 29, 2010 - 11:21

Very nice guide, even though it never got finished there is still a ton of great information here.

Elrond99 - Sunday February 15, 2009 - 8:52

Thanks for updating the Tutorial
It´s great to hear that it´s possible to make a new AI from scratch

Can´t wait for section F, custom AI scripts, that sounds really interesting
Especially since I know D and E already ;)

Sulherokhh (Team Chamber Member) - Saturday February 14, 2009 - 22:19

After 'D. The Faction Base and Fortress', i guess.
If you are asking for a date, you are asking the wrong guy. I don't have one. I work on modding related stuff mostly when i find the time and the mood strikes me. It's supposed to be fun, right? :)

Edit: But i might do it before the fortress and base setup. Depends on, well, which kind of work is going to be most interesting to me at the time. Scripts should be easier to do then the Basebuilding stuff, but a bit more to write as well, at least if i am going to do it right. I am starting to ramble... :O

jakonic - Saturday February 14, 2009 - 13:05

when will be finished Spell Purchase Scripts???please answer

Sulherokhh (Team Chamber Member) - Thursday November 29, 2007 - 18:26

I sure will...

Edit: Do you have any particular requests? If i have done it already, it shouldn't be hard to post a solution here. If it's not, chances are that i was going to look into it anyway. Except for the general skirmishsetup and bases, since those will require extesive explanations which i was planning to do anyway when i find the time.

So shoot! :)

Rob38 (Team Chamber Member) - Thursday November 29, 2007 - 12:00

This is by far one of my favorite tutorials on T3A! Please continue to add more :)

Sulherokhh (Team Chamber Member) - Thursday October 4, 2007 - 1:07

I am glad you can put it to use, Rob! Your feedback means a lot to me. :D

Rob38 (Team Chamber Member) - Thursday October 4, 2007 - 0:27

Amazing! I also found a way to recognize if a player is controlled by the AI, but this looks to be a much easier method to use. Thank you for all your wonderful knowledge as there is some really cool stuff in here.

Sulherokhh (Team Chamber Member) - Wednesday October 3, 2007 - 21:18

I'll split it up. Let's see what would be a good way. - Edit: Done. I hope you like it.

Crashdoc - Wednesday October 3, 2007 - 18:48

Nice findings and interesting ways to use them. Thanks for sharing the knowledge!

Go to top

 

"One site to rule them all, one site to find them,
one site to host them all, and on the network bind them."

 
21:41:42