Page 1 of 11 12345678910 ... LastLast
Results 1 to 20 of 204

Thread: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

  1. #1
    z3n's Avatar State of Mind
    Moderator Emeritus

    Join Date
    Aug 2011
    Posts
    4,640

    Default Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    This tutorial is composed of eight main elements.

    1) Campaign AI & Battle AI Pathfinding
    2) Understanding XML
    3) The Battle AI
    4) The Campaign AI
    5) The Battle Config
    6) Custom CAI Labels (2nd post)
    7) Custom BAI Tactics (2nd post)
    8) BAI Pathfinding Scheduler/Node Info (2nd post)

    Foreword

    In 9 years of MTW2 modding there has been next to nothing said about any of these topics, in fact we don't have a single tutorial here explaining any of these parts that I could find. Therefore once I had learnt, researched/read and experimented enough about this over the course of upwards of 40+ (and counting) Europa Barbaroum II patches I released it here in hopes others could one day contribute towards this little explored part of modding.

    Campaign AI & Battle AI Pathfinding

    MTW2's campaign AI utilizes a version of pathfinding that has been around for awhile called a waypoint graph.
    A waypoint graph is exactly what it sounds like, a series of points which act as a way. The shortest route between point a) and point b) is used when the AI wants to get somewhere, i.e. assault or garrison a city.
    Spoiler Alert, click show to read: 



    Descr_Regions is the file which controls the location of each 'waypoint', which in this case would be a city.
    This is simplifying how it works to some extent, but that is the general idea behind it. There are algorithms within the exe which control the actual calculation used by the AI when it decides to a) get into a position to ambush b) defend a bridge c) decide whether to continue through a possible ambush site or not d) the troops within a region. For example, the AI when targeting a region calculates the strength of the troops within only that region. If there are troops one region over, it won't take those into account when doing the pathfinding calculations.

    Even if you increase the amount of pathfinding calculations the AI can use to an absurd amount within the descr_character file, the AI will not take other regions into account. It will however increase the amount of troops it merges into its stacks across its entire faction. This also increases the amount of cities/regions the AI checks when deciding where to invade.

    Another note here- if you decrease it to 0 after the first couple of moves the AI will not move at all. Again, suggesting that the note here is completely relevant and correct. This is the pathfinding value, or amount of calculations allocated to the AI. The only things that move on the map at T2/T3+ are all agents or ships which are hardcoded to a degree.

    While I'm talking about that file, another possible tweak though has only a small effect, is changing the AI general and captain movement/wages. If one makes the captain wages more than the AI may choose its generals to lead its stacks more often rather than captains especially when general movement is more than the captains. In reality most of the time the AI prioritizes public order above millitary campaigns and keeps its generals within cities rather than leading an offensive.


    Additionally the AI makes use of roads and the types of ground to assist in its pathfinding and its worth noting that roads can be 'directed' by using appropriate ground types.
    Explanation of how to do so by Anarchon
    Essentially you need to block where you do not want the roads to go.By using impassable terrain. mountains and deep forest.

    A support/secondary tactic is using 'easier' terrain where you do want
    the road to run.
    eg. fertile, medium fertile, low fertile wasteland are easier than
    hills, forest and marsh.

    But blocking with impassable terrain the main tactic.

    The key squares are 'top-lefts', those with both numbers odd. eg 45-175.
    The other 3 squares which together form a map-regions square matter
    less if at all.

    Roads will not run through diagonally offset impassables.
    So eg 45-175 and 47-173 being impassable will block roads.
    However, normal movement is possible between these diagonals.
    This of course can be very useful to keep an area accessible but not
    having a road go there.
    AI pathfinding can be aided by the use of low fertility as a ground type to emulate a road, in fact you can create an 'obvious' road with only that ground type going from one town to another in a place which lacks a road, and the AI will follow that path. This I suppose is a direct result of content costs/groupings within the exe we do not have access to on the campaign side of pathfinding, however we do have access to the battle pathfinding. As such, we can assume similar architecture is used for the campaign pathfinder.

    Presumably the AI has a lesser cost of finding a path across fertility tiles than on other tiles, which would mean that the 50 tile limit which a CA dev once posted this about mapping for RTW would not be as much of a problem per se.

    A few other caveats about regions:
    - they should be 'convex' (one landmass, no inaccessible areas)*
    - they should have only one settlement and only one port
    - all land tiles should be part of a known region
    - each non-sea region should contain at least some fertile tiles
    - continuous sea surfaces should form one region
    - the maximum number of regions supported is 200
    - the distance between the centres of any two adjacent regions should not exceed 50 tiles*
    Anyway the main thing that we can assume from this is that the pathfinder has a difficult time finding a path beyond 50 tiles.


    Using the console command toggle_terrain frontier_defend and frontier Let's take a look at the defensive terrain the AI reads. The AI seems to prefer the green squares, the reason being it is seemingly the optimal place (for the most part) from city 1 (which they are defending) to city 2.
    Looking at the distance from point a to b in this one seemingly confirms this , as does the next screenshot.
    Spoiler Alert, click show to read: 




    Here we can clearly see, this is definitely some sort of defensive point on the frontline meant to be the optimal position. Sometimes the AI will use the blue squares however, while green seems to be prioritized above blue it is not always used above blue.
    Spoiler Alert, click show to read: 




    For example in this screenshot you can clearly see that the AI is positioned on a blue square, next to the fort. Now from what I gather, light blue means an ambush site, whereas blue can mean either a defensive point like a bridge, an elevated area to the AI's advantage or an ambush with an elevated area. I assume green is prioritized for normal defense, light blue for ambushes and dark blue for something in between.

    Oddly, the blue squares in particular seem to come in groups of 4 except in a few rare instances. Why this might be I am not entirely sure, my first thought would be that blue may also act as a 'staging' area for troops to merge into each other while being in a relatively defensive position. This is reinforced by the fact that I've observed said areas as staging areas before. Although I could very well be wrong.
    Spoiler Alert, click show to read: 


    Something odd but perhaps explained by some other code within the exe pertaining to this is the fact that not every river crossing is defined as a green, blue or even light blue area.
    Spoiler Alert, click show to read: 




    Neither is every bridge. Again not entirely sure why this is the case but it could be because of elevation or some other code specifically to do with bridges/crossings within the exe.
    Spoiler Alert, click show to read: 


    This is the 'frontier' or frontline as the AI defines it (in fact you can view the defensive decision defend_frontline within the descr_campaign_ai_db). Supposedly it must be to do with the way the region is drawn in the descr regions and how it borders with the other ones.
    Spoiler Alert, click show to read: 





    Moving on to the pathfinding within battles. This is rather complicated since the file involved is one of those 'dual purpose' bits of code that are a rare sight unless it involves the exe.

    The type of pathfinding the battle AI is likely either A* itself or a variant of the Hierarchical Annotated A* algorithm which was inspired/mostly created by Edsger W. Dijkstra. He pioneered an algorithm we still use as the basis for HAA*/D*/A* etc today. The associated costs for each type of terrain are located within this file, alongside the content groupings.

    Each cost is filtered into the algorithm and takes into account both the individual units movement pattern and much more importantly, the BAI's decision on which terrain to use and other calculations relating to where to defend.

    It's very important to note that the pathfinding algorithms themselves have something fundamentally wrong with them- the fact that they are pathing based, rather than movement. Movement would mean that the unit checks from time to time for enemies in front or around them. With pathing the algorithm simply calculates the cost to move from point a to point b and then the BAI orders the unit to do it, ignoring all other events around it. This leads into something I will discuss later, which is how we can solve this, as the BAI does have functions that allow for us to emulate a movement based algorithm.

    To start with I suggest checking out this tutorial, which gives some basic information about the underlying mechanism of the pathfinder (how the graph/algorithm/search technique applies to battles themselves).


    Now let's start dissecting the pathfinding file, we'll start at the very top.

    Code:
    version_info
    {
        63
    }
    Changing the number on the version info is what regenerates the pathfinding db. Although the pathfinding db is in my experience unnecessary, the txt file seems to take precedence for some reason and functions on its own.


    Code:
    multires_pathfinder
    This is an important part of the pathfinder, it plots a path on varying resolutions. First a low resolution/load, read here for more info

    Code:
            
                   ; low load config        load low
            {
                max_paths        320        ; maximum paths till shifting to the next loading
                total_nodes        650000        ; total nodes allocated across all paths
            }
    Then mid, then high.

    'max_paths'/'total_nodes' speak for themselves. Each path is plotted at a low resolution, then as it gets closer to the point it gets driven into a higher resolution and it subsequently becomes more accurate due to the increased res.

    Nodes, these are what the pathfinder searches. They are an integral part of the algorithm check, when the algorithm activates it ends up looking something like this. Those are the nodes getting activated.
    Spoiler Alert, click show to read: 


    Increasing these can help the AI keep/hold a formation or plot better movement during a battle as long as you don't go overboard.

    This code is mainly for the units, rather than the pathfinder per se. This code determines how individual units react and behave.
    For example the budget of movement causes a difference in the movement behaviour across the nodes when a unit enters the low load config stage, when entering the med it changes, as does the high config.
    This mainly determines the 'randomness' of soldiers starting to move, and their individual unit based pathfinding from point a to point b but also may have some interaction with the BAI.
    Code:
            
                   ; low priority config        priority low
            {
                budget            0.98        ; percentage of budget for these paths
                max_nodes        500000        ; maximum nodes per path
                max_zone_nodes    100000        ; maximum zone nodes per path
            }
    These are the various configurations that have different effects on the pathfinding for units and the BAI.

    zone_configuration is a reference to the highest slope the pathfinder can use when checking for a path. I also have a suspicion that this plays a role during deployment in choosing how high of a slope the unit can deploy on, although that may either be connected simultaneously or solely to the unit type conifguration it is doubtful (unless it uses 'default') since the deployment would need a single value to deploy the army en-masse rather than in isolated pockets of units.
    heuristic_configuration is there to help solve pathfinding problems faster, increasing 'computational performance' in doing so. Basically its a quick way of inputting a large cost into the algorithm and allowing it to stop calculating a bad route faster.
    movement_configuration refers to how the units move, during a charge.
    silhouette_configuration is a tricky one, presumably it has something to do with the interaction of a unit with the nodes or it may simply be a removed feature. Originally it was disabled by the developers, however in various tests indications seem to be an increased emphasis on cohesion/flanking. Again it is probably to do with how the unit interacts/stays within a 'node'.
    Code:
        ; Zone configuration
        zone_configuration
        {
            maximum_slope                60        ; limit is 60 degrees
        }
        
        ; Heuristic configuration
        heuristic_configuration
        {
            invalid_zone_cost        10.0        ; invalid areas have ten times the cost - offset for the slope 'penalty'
        }
        
        ; Movement configuration
        movement_configuration
        {
            formation_hold_distance 25.0    ; formations update after the last point
        }
        
        silhouette_configuration
        {
            silhouette_ratio        1.0     ; Silhouette points are now 100% of unit width
            enabled                    yes
        }
    These three groups of code are interconnected
    content_groupings is neither recommended or necessary to mod but to explain what it does, the list starting with free and ending with ford (from top to bottom) is the 'content', the group (e.g. types of 'free' content) that comes after free are what is read by the pathfinder as a 'free' area.

    Once you understand that the next two are self explanatory.
    Code:
        
        ; Defines what content types may be grouped together
        content_groupings
        {
            free                    free    forest    scrub    tall_vegetation average_vegetation    short_vegetation    large_rocks    small_rocks        bridge_platform
            obstructed                obstructed    steep_terrain
            tall_vegetation                free    forest    scrub    tall_vegetation average_vegetation    short_vegetation    large_rocks    small_rocks        bridge_platform
            average_vegetation            free    forest    scrub    tall_vegetation average_vegetation    short_vegetation    large_rocks    small_rocks        bridge_platform
            short_vegetation            free    forest    scrub    tall_vegetation average_vegetation    short_vegetation    large_rocks    small_rocks        bridge_platform
            large_rocks                free    forest    scrub    tall_vegetation average_vegetation    short_vegetation    large_rocks    small_rocks        bridge_platform
            small_rocks                free    forest    scrub    tall_vegetation average_vegetation    short_vegetation    large_rocks    small_rocks        bridge_platform
            water                    water
            swamp                    swamp
            platform                platform
            bridge_platform                free    forest    scrub    tall_vegetation average_vegetation    short_vegetation    large_rocks    small_rocks        bridge_platform
            steep_terrain                obstructed    steep_terrain
            ford                           free    forest    scrub    tall_vegetation average_vegetation    short_vegetation    large_rocks    small_rocks        bridge_platform
        }
    
    
        ; defines the priorities (ie. which content is more important than the other)
        precedence_mask
        {
            ; content type        lower priority types
            free                all
            obstructed            free    water        swamp    tall_vegetation    average_vegetation    short_vegetation    large_rocks    small_rocks    platform
            water                free    obstructed    swamp    tall_vegetation    average_vegetation    short_vegetation    large_rocks    small_rocks    platform
            swamp                free    obstructed
            tall_vegetation        free    swamp        average_vegetation    short_vegetation    small_rocks    water
            average_vegetation    free    swamp        short_vegetation    small_rocks    water
            short_vegetation    free    swamp
            large_rocks            free    swamp        short_vegetation    small_rocks    water
            small_rocks            free    swamp
            platform            free
            bridge_platform        all
            steep_terrain        free    average_vegetation    short_vegetation    large_rocks    small_rocks
            ford                   free
        }
    
    
        ; Determines what parameters are checked for this content when filling in the zones
        checking_level
        { ; slope, content or all
            free                all
            obstructed            all
            water                content
            swamp                all
            tall_vegetation        all
            average_vegetation    all
            short_vegetation    all
            large_rocks            all
            small_rocks            all
            platform            content
            bridge_platform        content
            steep_terrain        content
            ford                   content
        }
    Now we get to the good part, there are several important functions that give us access to the algorithm itself.

    1) The min_slope, this is what defines the min_slope before a 'penalty' (or more simply put, a cost) applies

    2) The max_slope is what defines the maximum slope the unit will try to calculate.

    3) This is the penalty/cost for each degree, simple enough but it can be manipulated like I did to be less for each degree, rather than the default which is more.
    When using it like that, the AI according to reports and my own finding is more prone to use higher ground when it can.

    4) min_width/max_width these are odd values, I honestly am not sure what exactly the 'width' refers to, other than again something to do with the node and perhaps cohesion. (also note that max wasn't necessarily there to begin with so it likely is not even parsed/read, it was there simply to contribute towards the tests I did). One idea is that it could possibly be there as part of the pathing check for obstacles, anotherwords the unit would act like an obstacle to that measurement within the pathfinder. It could also be tied to the formations ai min_unit_width line in conjunction with the pathing check. But again this is just a 'guess' as I haven't been able to verify the exact function even after several tests. It could in fact be disabled for certain types of units, as it is only enabled in vanilla for the siege equipment. Which may mean it was a value only ever meant to apply towards walls and act as a measurement for the amount of space the siege equipment needs during the pathing process/search (by the algorithm).

    5) content_costs, these are what we can manipulate to change the units decision making on the battle map when finding a path from point a to point b. Higher = less chance the unit will choose that path, lower = more chance. inf is infinite cost. These values are further modified by the slope penalty/cost. Also keep in mind it's not a brilliant idea to change them overly much, less than 1.5 and not under .75 (even .99 is pushing it). Otherwise you will observe strange unit pathfinding unless you know your math very well, along with a real guess as to what the exact variant of the algorithm is.

    Code:
        
    ; Configures the per unit behaviour of the pathfinding    unit_type_configuration
        {
            default
            {
                min_slope    10        ; minimum slope before penalty applies
                max_slope    60        ; maximum slope the unit can handle
                penalty        0.999    ; penalty per degree
                min_width    1.0 ; minimum width = 1m
                max_width    1.5        ; maximum width = 1.5m
    
    
    
                content_costs
                {
                    free                    1.0
                    obstructed                inf
                    water                    1.0
                    swamp                    1.0
                    tall_vegetation                1.2
                    average_vegetation            1.0
                    short_vegetation            1.0
                    large_rocks                1.2
                    small_rocks                1.0
                    platform                1.0
                    bridge_platform                1.0
                    steep_terrain                inf
                    ford                    1.0
                }
            }




    Understanding XML
    Presumably the exe has an XML parser (or processor) within it.
    However, the exe itself processes the information located within the XML configuration files. Naming it a 'configuration' file for the battle AI was no mistake, the battle AI itself is located within the exe as are its basic functions and algorithms which determine basic behaviours.
    The configuration however as it is written in XML gets passed back to the exe and is what we can 'configure' to our own liking.

    To create an XML markup construct (complicated phrase I know, but simple in execution) one must start with this character < and end with this character >

    <start></end>

    There must be a / to designate the 'ending' tag. Generally one must input a value between the two tags, which gets passed back to the exe which then interprets this value + tag within the algorithms and reacts accordingly.

    It is also worth noting that each file starts with a root element (in the BAI's case it is named config).
    Anotherwords the file will go like this

    Code:
    <root>
    
    <element1>
     <blah1>
      0
     </blah1>
     <blah2>
      <lotsofblahs>1</lotsofblahs>
     </blah2>
    </element1>
    
    </root>
    The 'element' is basically the combination of a tag+value.

    Here we have CA's composition of elements. They start off with the cavalry, then specify what they're controlling for the cavalry, then go on to define the difficulty levels at for the engage distance and the value which applies. Look at how and when they close the final part of the element. The elements are 'contained' within each other.
    Code:
               
    
                <cavalry>                
                    <max-engage-dist>
                        <easy>75</easy>
                        <medium>75</medium>
                        <hard>75</hard>
                        <very_hard>75</very_hard>
                    </max-engage-dist>
                </cavalry>
    Now something you may immediately notice is the difference of my white space formatting, while not exactly my preferred style of format (I use a tab in for each element usually exactly like CA did theirs) it's usually not required by the applications that use XML to follow a certain format. In our case, I can say with 99.9% certainty (I can't say 100% since I like to follow CA's format simply because it's 'clearer' to read for me) white space is discarded and not passed on to the MTW2/Kingdoms exe.

    However, I feel that its necessary to point out while some modders technically tried to do something like this

    Code:
    <attack-the-lake>
     <run-threshold>1200000</run-threshold>
     <priority>100000</priority>
    </attack-the-lake>
    The run threshold by the way is just an order to make the AI run if it is within that distance of the lake. The priority is how important the AI should view the order.

    The exe won't (!) understand what the 'attack-the-lake' is. Using CA's tags/values is necessary (you can't make up a random new XML tag, parser will only understand pre-existing ones) although caution is advised as using them improperly may end up having unintended side effects.

    For example if you abuse the unit priority or change the values to something incorrect while placing it in melee manager it may give you unintended results during a battle, like the units always prioritizing certain ones over others.

    Code:
                <unit-priority>
                            <cavalry-vs-phalanx>100</cavalry-vs-phalanx>
                            <cavalry-vs-melee>100</cavalry-vs-melee>
                             <vs-cavalry>10</vs-cavalry>
                            <vs-routers>0.25</vs-routers>
                    <vs-melee>5</vs-melee>
                    <vs-missile>10</vs-missile>
                    <vs-spearmen>25</vs-spearmen>
                    <vs-artillery>15</vs-artillery>
                    <behind-stakes>30.0</behind-stakes>
                </unit-priority>

    Also worth noting is that using CA's tag + priority values incorrectly will result in a gate crash if you do not define the priority within a specific unit set and or area.

    Code:
                            
                        
            <outflanking>
            <run-threshold>120000</run-threshold>
                <strength-ratio>0.25</strength-ratio>
                <tracking-tolerance>125.0</tracking-tolerance>
            <stall-limit>10</stall-limit>
            <open>
                <cavalry>
                    <priority>75000</priority>
                </cavalry>
            </open>
                    <double-envelopment>
                <strength-ratio>0.1</strength-ratio>
                <stall-limit>10</stall-limit>
                           <tracking-tolerance>125.0</tracking-tolerance>
                <open>
                  <cavalry>
                    <priority>100000</priority>
                  </cavalry>
                </open>
                            <!-- minimum number of cavalry units required for double envelopment -->
                           <min-units>1</min-units>
                    </double-envelopment>
            </outflanking>
    Open defines the 'open', instead of siege battles. While I even narrowed it down to cavalry prioritizing the outflanking, rather than any unit.

    One should note that the campaign_db is a different story, even by its name 'db' it implies the existence of a database. This file really is just a database, and what drives the high level AI according to CA.
    The ai_labels specify the structure of the long term goal director (LTGD) which drives the high level campaign AI.
    Anotherwords all these decision entries and faction attitudes have actual functions written in for them in the high level CAI located within the exe. But as its just a database for the AI itself simply adding a new tag i.e. want_albuquerque=true probably won't work like you want it too.
    Not that I want to discourage experiments, its simply an observation that since the exe doesn't necessarily know that it is supposed to look in various files for 'albuquerque' and then figure out why it would 'want' it then it probably won't do that. A simpler method would be adding the name of the town into win_conditions, which is prioritized sequentially. From town a) to town b) to town c), starting with town a though.

    So to close the main points to remember are
    1) As an extensible language, custom tags can be used but beware they are interpreted by the exe. Therefore following developer format/words in appropriate elements (places of the file, i.e. melee manager) is highly recommended. Especially as the developers had the option to tie internal architecture to the XML file during the compiling process. Alongside certain key functions of the BAI into those elements.
    2) The AI uses multiple files in conjunction and you must remember to mod the other files accordingly





    The Battle AI
    There are 3 main parts.
    1) The various configuration files
    2) The pathfinding

    To start lets go over the configuration files and how they work
    1) battle config located within the data folder - Its purpose, controls unit behaviour and calculations based on that

    2) config ai battle located within the data folder. Its purpose, a) making the pathing based pathfinder to act as a movement based pathfinder via tags like tracking tolerance/defend radius etc and more importantly b) how the battle AI works, mostly ways to control how calculations work/start/finish and what it prioritizes.

    3) pathfinding located within the data. Its purpose, already explained in the pathfinding tutorial above.

    4) descr formations ai is located within the data folder. Its purpose is to determine the initial formation of ai and player armies. It makes its calculations based on relative unit position from player. This file can be manipulated in various ways, i.e. forcing shield_wall as a formation for certain types of units. In reality, this file is mainly used only as a basis to start off, the AI controls how it keeps and moves its formations in a different way located within the exe itself. In fact, the config battle ai XML tags for the formations are linked to that hardcoded function, so if you change them you will end up getting a CTD upon pressing start battle due to a memory fault in the exe.

    5) background script usually located within data/scripts/show_me or the campaign script located within the world/maps/campaign/imperial_campaign folder. Its purpose can be converted to emulate MTW2's historical scripted battles with various conditions + effects. It is worth noting that MTW2 devs did not intend for this to take the place of the battle AI, instead simply allow for a cinematic experience solely for the purpose of the pre historic battle video and or allow for 'historic' moves to be done in the battle.

    6) export_descr_unit is located within the data folder. Its purpose is to do with the unit calculations the internal algorithms make when assigning units via the resourcing or threat management. The various other functions e.g. cycle charge/flanking/unit matching are also dependant on the values here. Therefore this file is the 'base' from which one should create a BAI, focusing their efforts on contriving a) a balanced unit formula and b) if need be, tiers of units based on the formula.

    Now lets revisit the config_battle_ai file.
    To start, I don't necessarily claim that this information is 100% accurate as I do not have direct access to the exe to confirm these things however they are observations from testing.

    Code:
        <deployment>
            <!-- <placement-offset>60</placement-offset> -->
            <settlements>
                <inside-gate-dist>5</inside-gate-dist>
                
                <units-per-wall>2</units-per-wall>
                
                <surprise-attack>
                    <!-- percentage of units to try and place on the walls of the settlement -->
                    <percent-on-walls>0.75</percent-on-walls>    
                    
                    <!-- percentage of units that are randomly scattered -->
                    <percent-scattered>0.0</percent-scattered>
                    
                    <!-- bias towards defenders -->
                    <defender-bias>0.05</defender-bias>
                    
                    <!-- controls the spread of the units along the wall as a percentage of unit width -->
                    <unit-spread>1.0</unit-spread>
                </surprise-attack>
                
                <expected-attack>
                    <!-- percentage of units to try and place on the walls of the settlement -->
                    <percent-on-walls>0.75</percent-on-walls>    
                    
                    <!-- percentage of units that are randomly scattered -->
                    <percent-scattered>0.0</percent-scattered>
                    
                    <!-- bias towards defenders -->
                    <defender-bias>0.05</defender-bias>
                    
                    <!-- controls the spread of the units along the wall as a percentage of unit width -->
                    <unit-spread>1.0</unit-spread>
                </expected-attack>
            </settlements>
        </deployment>

    This is obviously the deployment code that the AI applies to its decision making, most of these are in fact self explanatory.

    In regards to defender-bias I believe this acts as a morale multiplier for the defending units on a wall. (note the word bias which means that it'd added to the base morale)
    So if they have 5 morale it would be multiplied by 0.75 if the bias was 0.75. Giving them about 9 morale in total.

    Now lets take a look at the melee manager.
    Within this element we can define several different things.

    1) Changing certain 'plans' for the BAI, I don't want it to consider missile units like horse archers for melee, as such I told the AI 'no' don't consider them for melee.
    2) Resourcing code with two levels (often I go overboard in 'backup' checks in hopes it will reinforce the AI's decision) to make the AI focus more on using its melee units to engage rather than other less effective ones
    3) Threat control, this is a direct reference to the EDU calculations the BAI uses and checks when assigning units to each other. If a unit doesn't contribute enough 'threat' to another unit it will choose another unit to assign.

    attack-dist-multiplier. The attack-dist is a fancy way of saying charge distance, which is then multiplied by the value you place there for example 3 or 10.

    This could also be construed as the multiplier or amount of distance that a unit will chase another unit before disengaging and re-evaluating the unit it wants to engage. Additionally it is the distance a unit disengages to if it's cavalry for example, then starts it's attack run back.

    I believe that the max engage distance could have something to do with the distance from the defensive line from which a unit can engage another unit, except under special circumstances. Although I could very well be wrong, that's the only purpose I've been able to determine. As setting to lower values or higher has little observable effect.

    Code:
        <melee-manager>
            <attack-dist-multiplier>10.0</attack-dist-multiplier>
                <plan>
                    <!-- amount of benefit the strength ratio yields -->
                    <strength-contribution>10.0</strength-contribution>
                    <consider-missile>0</consider-missile>
                    <consider-cavalry>0</consider-cavalry>
                    <missile-cavalry>0</missile-cavalry>
                </plan>
                <resourcing>
                    <!-- backup priority -->
                    <melee-infantry>10</melee-infantry>
                    <cavalry>0</cavalry>
                        <unit-suitability>
                            <!-- all missile units -->
                            <missile>0</missile>
                            
                            <!-- all artillery units -->
                            <artillery>0</artillery>
                            
                            <!-- all melee units -->
                            <melee>1</melee>
                            
                            <melee-infantry>10</melee-infantry>
                            
                            <!-- all cavalry units (elephants, horses etc) -->
                            <cavalry>0</cavalry>
                            
                            <elephant>10</elephant>
                        </unit-suitability>
                        <!-- amount of threat a single unit can counter (controls how many units will get assigned) -->
                        <threat-per-unit>25</threat-per-unit>
                </resourcing>
    Next we have specific unit decisions like this

    Code:
            <cavalry>
              <prioritise-outflanking>1</prioritise-outflanking>
            </cavalry>
            <missile>
              <prioritise-out-of-combat>1</prioritise-out-of-combat>
             </missile>
    What this does is 'prioritize' the preferred unit decision while units engage.
    No doubt other choices could be added but I prefer to leave most of the decisions to the AI, while focusing on the main ones. As one cannot account for all the situations.


    This next part of the code is called the retreat analyser. What it does is decide when the unit should disengage.
    I added a strength ratio to further refine when the unit disengages and also added a seperate one for cavalry.
    It's important to note that the strength ratio of '1.1' means that the cavalry prefer having a strength ratio of 1.1, so they will disengage once it drops below that ratio and retreat, then charge back in.

    The retreat counter is code that controls how difficult it is to rout units. Higher = more difficult, while lower = easier.

    The retreat point seems to be the area at which a routed unit can recover and rally.
    The multiplier seems to control the how often quickly a unit disengages. If I remember correctly setting it to 0 or below can result in cavalry for example refusing to disenge.
    Code:
            <retreat-analyser>
            <strength-ratio>0.25</strength-ratio>
            <cavalry>
            <strength-ratio>1.1</strength-ratio>
            </cavalry>
            <retreat-counter>
                <easy>140</easy>
                <medium>160</medium>
                <hard>180</hard>
                <very_hard>200</very_hard>
            </retreat-counter>
            
            <retreat-point>
                <default>
                    <distance>100</distance>
                </default>
                <melee-infantry>
                    <distance>0</distance>
                </melee-infantry>
                <phalanx>
                    <distance>0</distance>
                </phalanx>
                
                <melee-infantry>
                    <multiplier>0.0</multiplier>
                </melee-infantry>
                <cavalry>
                    <multiplier>5.0</multiplier>
                </cavalry>
            
                <missile-infantry>
                    <multiplier>1.25</multiplier>
                </missile-infantry>
            </retreat-point>
            </retreat-analyser>
    Next we have the outflank analyser. The unit priority probably scales from 0-100 or -100 to +100.

    Within the EDU the campaign AI recruit priority seems to scale from -100 to +100 which means this priority could also scale the same way, note that base priorities of recruit priority likely change depending on the algorithms calculations depending on the financial, recruit pool and war situation alongside the descr_strat personalities which add base priorities to certain types of units.

    What the unit priority seems to do is increase the likelyhood of outflanking being analyzed as a priority. Although incorrectly prioritized the AI may end up simply choosing to do a frontal assault if the priorities to outflank end up cancelling each other out. So beware of 'cancelling out' priorities.

    Code:
            <outflank-analyser>
                <unit-priority>
                    <cavalry-vs-phalanx>100</cavalry-vs-phalanx>
                    <cavalry-vs-melee>100</cavalry-vs-melee>
                    <vs-cavalry>10</vs-cavalry>
                    <vs-routers>0.25</vs-routers>
                    <vs-melee>5</vs-melee>
                    <vs-missile>10</vs-missile>
                    <vs-spearmen>25</vs-spearmen>
                    <vs-artillery>15</vs-artillery>
                    <behind-stakes>30.0</behind-stakes>
                </unit-priority>
            </outflank-analyser>

    Now we have a completely custom tag created mostly to prevent phalanx units from trying to outflank other units.
    I suggest reading through it, its rather readable and doesn't need an explanation.
    Also included are various backups of my resourcing code, to aid in the analytic process not just management phase.

    Code:
            <melee-analyser>
                    <prioritise-attack-direction>1</prioritise-attack-direction>
                    <attack-direction>
                        <!-- amount of benefit the strength ratio yields -->
                        <strength-contribution>10.0</strength-contribution>
                        <front>
                            <cavalry>0</cavalry>
                            <melee>1</melee>
                            <spearmen>1</spearmen>
                            <phalanx>1</phalanx>
                            <missile>1</missile>
                        </front>
                        <right>
                            <cavalry>0</cavalry>
                            <melee>1</melee>
                            <spearmen>1</spearmen>
                            <phalanx>0</phalanx>
                            <missile>1</missile>
                        </right>
                        <left>
                            <cavalry>0</cavalry>
                            <melee>1</melee>
                            <spearmen>1</spearmen>
                            <phalanx>0</phalanx>
                            <missile>1</missile>
                        </left>
                        <rear>
                            <cavalry>1</cavalry>
                            <melee>1</melee>
                            <spearmen>1</spearmen>
                            <phalanx>0</phalanx>
                            <missile>1</missile>
                        </rear>
                    </attack-direction>
                <plan>
                    <consider-missile>0</consider-missile>
                    <consider-cavalry>0</consider-cavalry>
                    <missile-cavalry>0</missile-cavalry>
                </plan>
                <resourcing>
                    <!-- backup priority -->
                    <melee-infantry>20</melee-infantry>
                    <cavalry>0</cavalry>
                        <unit-suitability>
                            <!-- all missile units -->
                            <missile>0</missile>
                            
                            <!-- all artillery units -->
                            <artillery>0</artillery>
                            
                            <!-- all melee units -->
                            <melee>1</melee>
                            
                            <melee-infantry>10</melee-infantry>
                            
                            <!-- all cavalry units (elephants, horses etc) -->
                            <cavalry>0</cavalry>
                            
                            <elephant>10</elephant>
                        </unit-suitability>
                        <!-- amount of threat a single unit can counter (controls how many units will get assigned) -->
                        <threat-per-unit>25</threat-per-unit>
                </resourcing>
                <cavalry>
                    <prioritise-outflanking>1</prioritise-outflanking>
                </cavalry>
                <missile>
                    <prioritise-out-of-combat>1</prioritise-out-of-combat>
                </missile>
        </melee-analyser>
    Something very important to keep in mind your custom tags will have unintended effects unless you add a backup code.
    It is in fact a necessity, as I once added some custom detachment code within the melee manager section which ended up causing too many units to detatch from the main army and attack the general unit.


    This section is the GTA, it is what controls the general troop assignment (my interpretation of what it means) since this entire section continues with how troops are in general, assigned.

    The assault/contact/close approach etc all seem to control reserves and how reserves are placed. The rest is probably related to the pathfinding.
    Unit group speed is to do with the tolerence for slower or faster troops (from the EDU setting). This was probably done for the horses/infantry.

    The unit group merge code is to do with objectives and how the AI assigns troops. I use ridiculously high settings in hope to disable that part, because I don't really want 'limits' after seeing how lower/normal settings tend to create unwanted behaviour with troop assignment/unnecessary disengagement at bad times which is already made up for with my own code.
    Code:
        <gta>
            <assault-dist>30.0</assault-dist>
            <contact-dist>60.0</contact-dist>
            <close-approach-dist>120.0</close-approach-dist>
            <distant-approach-dist>240.0</distant-approach-dist>
            <enemy-far-dist>450.0</enemy-far-dist>
            <enemy-near-dist>90.0</enemy-near-dist>
    
            <unit-group-merge-dist>999999.0</unit-group-merge-dist>
            <unit-group-speed-tolerance>0.25</unit-group-speed-tolerance>
            <unit-group-strength-tolerance>999999</unit-group-strength-tolerance>
    
    
            <!-- merge own units with a greater tolerance -->
            <local-group-merge-dist>999999.0</local-group-merge-dist>


    Next up is the common tactics section.
    These tactics cross into both settlement and open battles.

    They are also what can help give a pseudo movement effect to the pathfinder rather than leaving it at pathing. I say this because we're making the BAI check to make sure it isn't engaged with an enemy via the common tactics code. Granted I didn't bother prioritizing it in because again I can't account for every situation, and I also want the BAI to be able to disengage if it thinks retreating with its general is a good idea, or cavalry. It's best to keep things simple and efficient as possible so as to contradict the least amount of things possible.

    Code:
            <common-tactics>
                    <!-- engage state - will hold position and fight the enemy -->
                    <engage>
                        <!-- used to prevent infantry disengaging without a clear objective -->
                        <priority>50000</priority>
                        <state-criteria>
                            <infantry>
                                <!-- engage if the percentage of soldiers in the unit in combat is above this -->
                                <combat-percentage>0.01</combat-percentage>
                                    
                                <!-- engage if the enemy is within this distance - in metres squared -->
                                <nearest-enemy>500.0</nearest-enemy>
                            </infantry>
                        </state-criteria>
                    </engage>
                    <outflanking>
                        <cavalry>
                            <!-- outflank if the enemy is within this distance - in metres squared -->
                            <nearest-enemy>1000.0</nearest-enemy>
                            <!-- run if closer than this distance (in metres squared) -->
                            <run-threshold>1200000</run-threshold>
                        </cavalry>
                    </outflanking>
                <move-to-point>
                    <!-- switch to idle hint if within this distance of the target -->
                    <open>
                        <proximity>
                        
                        <default>500.0</default>
                        
                        <infantry>450.0</infantry>
                        
                        <cavalry>650.0</cavalry>
                        
                        </proximity>
                        
                    </open>
                    
                    <settlement>
                        <!-- switch to idle hint if within this distance of the target -->
                        
                        <proximity>900.0</proximity>
                        
                    </settlement>
                    
                </move-to-point>
            </common-tactics>
    Next we have the ambush section which is rather self explanatory again but the main thing to note is the use of 'consider-max-force' this will allow for ambushes no matter what type of battle it is, normal or default.

    Code:
            <ambush>
                <plan>
                    <!-- consider or not missile units for hiding (1/0) -->
                    <consider-missile>0</consider-missile>
                    <!-- consider up to the specified percentage of the force for hiding -->
                    <consider-max-force>90</consider-max-force>
                    <!-- time it should take hidden units to approach the fight (seconds) -->
                    <approach-time>60</approach-time>
                    <!-- deployment search increment (metres) -->
                    <deployment-search-inc>6</deployment-search-inc>
                </plan>
            </ambush>

    Now we get to something rather interesting. It's possible to make the AI attack via two streets or more.

    1) The base threat of a street for attacking units means the AI will assign units on more than one street.
    2) The some tweaks to how units attack walls (the number)
    3) Threat per unit changes the unit distribution across streets, which should mean 1 unit should assault the other street during an attacking AI battle of 3 units unless the AI needs all of them in one street according to its threat management.


    Code:
            <attack-settlement>
                <detachment>
                    <engine-collection>
                        <!-- when stealing an engine, prioritise being out of combat over distance -->
                        <prioritise-out-of-combat>1</prioritise-out-of-combat>
                    </engine-collection>
                    <commit>
                        <!-- commit sufficient forces to outnumber the enemy by this ratio -->
                        <strength-ratio>1.6</strength-ratio>
                    </commit>
                    <perimeter-attack>
                        <termination-criteria>
                            <enemy-in-perimeter>0.25</enemy-in-perimeter>
                        </termination-criteria>
                    </perimeter-attack>
                    <max-plaza-assault-groups>5</max-plaza-assault-groups>
                    <units-per-plaza-assault-group>4</units-per-plaza-assault-group>
                    <street-position>
                        <!-- base threat level possessed by street positions - used to enforce an attacking spread of units -->
                        <base-threat>100.0</base-threat>
                    </street-position>
                    <artillery-times>
                        <!-- successful assaults performed for at least 1 minute -->
                        <minimum>1.0</minimum>
                        
                        <!-- don't prolong the bombardment longer than 6 minutes if we have no targets left -->
                        <maximum>6.0</maximum>
                        
                        <!-- stall test is a moving average that checks if any artillery is active (moving/firing/reloading) each tick -->
                        <stall-test>
                            <!-- minimum number of ticks to collect before detecting a stall -->
                            <minimum-samples>900</minimum-samples>
                            
                            <!-- track at most this number of samples -->
                            <maximum-samples>1200</maximum-samples>
                            
                            <!-- if the artillery has be inactive for at least 75% of its time -->
                            <limit>0.5</limit>
                        </stall-test>
                    </artillery-times>
                </detachment>
                <tactics>
                    <assault-gate>
                        <inside-position-dist>25</inside-position-dist>
                        <formation>ai_settlement_assault_gate</formation>
                    </assault-gate>
                    <reform>
                        <offset>30</offset>
                        <formation>ai_settlement_attack_reform</formation>
                        <percentage-formed>10</percentage-formed>
                        <advance-timer>1.0</advance-timer>
                        <siege-equipment-advance-timer>6.0</siege-equipment-advance-timer>
                    </reform>
                    <capture-plaza>
                        <reform-dist>120</reform-dist>
                    </capture-plaza>
                    <attack-walls>
                        <!-- configure resourcing strategy for this tactic -->
                        <resourcing>
                            <!-- maximum number of units that can be assigned -->
                            <max-units>12</max-units>
                            
                            <!-- amount of threat a single unit can counter (controls how many units will get assigned) -->
                            <threat-per-unit>30</threat-per-unit>
                        </resourcing>
                    </attack-walls>
                           
                    <!-- plaza attack -->
                    <attack-plaza>
                        <!-- only issue move orders if we are more than this distance away from the destination -->
                        <destination-proximity>2700.0</destination-proximity>
                    </attack-plaza>
                    
                    <!-- street attack to work in conjunction with street threat  -->
                    <attack-street>
                        <!-- configure resourcing strategy for this tactic -->
                        <resourcing>
                            <!-- priority to encourage attacks from another street -->
                            <prioritise-outflanking>1</prioritise-outflanking>
                            <!-- amount of threat a single unit can counter (controls how many units will get assigned) -->
                            <threat-per-unit>35</threat-per-unit>
                        </resourcing>
                        
                    </attack-street>
                </tactics>
            </attack-settlement>
    Tracking tolerence is another pathfinding based command and it determines the distance the AI can 'track' another unit as an obstacle/threat. It also seems to help the AI distrubute its units during a battle. Hence it appearing next to everywhere possible in my code.

    Formed percentage and formed percentage finished are two different pieces of code. Formed percentage can be dangerous to change to a higher number as it can result in something unlooked for- the AI not changing its position during a defensive battle if added to defend-line section for example. Formed percentage finished is more lenient as it refers to the max number that can be moved at any given time by the AI throughout its formation to counter threats.

    *Note, more on this section in a later tutorial.

    Code:
            <attack-battlegroup>
                <tracking-tolerance>125.0</tracking-tolerance>
                <formed-percentage>20</formed-percentage>
                <formed-percentage-finished>66</formed-percentage-finished>
                <shootout-distance-tolerance>75</shootout-distance-tolerance>
            <prioritise-merge-friendly-armies>1</prioritise-merge-friendly-armies>
    I like units defending walls so I made the max units much greater, although the threat too is greater to prevent too large of a focus for defensive units in one place.
    There will be more on defensive part of the AI in a later tutorial, due to it being slightly more advanced.

    Code:
                    <defend-walls>
                        <!-- configure resourcing strategy for this tactic -->
                        <resourcing>
                            <!-- maximum number of units that can be assigned -->
                            <max-units>20</max-units>
                            
                            <!-- amount of threat a single unit can counter (controls how many units will get assigned) -->
                            <threat-per-unit>50</threat-per-unit>
                        </resourcing>
                    </defend-walls>

    Some common myths about the battle analyzer is that it crosses over to the campaign AI as well, this is not the case. XML files were designed for specific parts of each AI and for all intents and purposes are in fact processed by different parts of the exe.
    This can be observed by a simple test. If you mess up in your syntax or format, and forget to close a tag in your config_ai_battle, the result is no crash although presumably it gets reported by the validator but doesn't lead to a fatal crash.

    Whereas the validator within the campaign_ai_db will cause a crash if you forget to close a tag.

    Also note that this file has an XML declaration for the validator/parser
    Code:
    <?xml version="1.0"?>
    This declaration isn't necessary per se from my various tests without it implying that the parser can interpret/guess what the file is encoded and written in without the declaration telling it those things but it is recommended that you include the declaration because CA did. Even if they did not in their BAI files or many of their other XML docs.

    Additionally the enemy position buffer distance seems to have an effect on morale rather than any AI calculation, it seems to be one of the very few field battle specific morale changes we have access. Although it could be a dual-purpose piece of code relating to more than just that.
    Code:
            <battle-analyser>
                <!-- ratio of friendly to enemy strength to be considered more powerful -->
                <friendly-to-enemy-strength-ratio>1.0</friendly-to-enemy-strength-ratio>
                <!-- ratio of friendly to enemy melee strength to be considered overwhelmingly powerful -->
                <enemy-melee-strength-multiplier>3.0</enemy-melee-strength-multiplier>
                <!-- ratio of enemy to friendly ranged strength to be considered overwhelmingly powerful -->
                <friendly-ranged-strength-multiplier>1.0</friendly-ranged-strength-multiplier>
                <!-- ratio of friendly strength to enemy ranged strength to force attack -->
                <friendly-ranged-strength-divisor>1.25</friendly-ranged-strength-divisor>
                <!-- distance behind the defensive line the enemy must reach to be considered to have broken through -->
                <enemy-position-buffer-distance>100</enemy-position-buffer-distance>
            </battle-analyser>





    The Campaign AI

    In my own opinion CA does a rather good job at explaining how the file works which is worth a read through if you've never unpacked MTW2 and read through their explanations.

    Spoiler Alert, click show to read: 
    <!--

    <trusted_ally_fs_threshold float="0.5"/> // min threshold for how much we like the target faction to consider them a trusted ally
    <trusted_ally_target_fs_threshold float="0.5"/> // min threshold for how much the target faction likes us to consider them a trusted ally
    <trusted_ally_target_human_fs_threshold float="0.0"/> // min threshold for how much the target (human) faction likes us to consider them a trusted ally
    <trusted_ally_gs_threshold float="-1.0"/> // min threshold for how trustworthy we are to consider the target faction a trusted ally
    <trusted_ally_target_gs_threshold float="-0.1"/> // min threshold for how trustworthy is the target faction to consider them a trusted ally
    <trusted_ally_enemy_auto_war bool="false"/> // flag to indicate if a faction automatically goes to war with a trusted allies enemy


    <use_cheat_overrides bool="true"/> // determines if cheat overrides (force peace with ai, force attack with humans) are applied
    <invade_priority_fs_modifier float="-400.0"/> // modifies the final invade priority for new faction targets by += (faction_standing * modifier) {makes factions more likely to start war with disliked targets}
    <invade_priority_gs_modifier float="0.0f"/> // modifies the final invade priority for new faction targets by += (global_standing * modifier) {makes factions more likely to start war with untrustworthy targets}
    <invade_priority_assistance_offset int="200"/> // modifies the final invade priority for new faction targets where military assistance has been asked by += (offset) {makes factions more likely to start war with military assistance targets}
    <invade_priority_min int="50"/> // min clamp for final invade priorities calculated
    <invade_priority_max int="1000"/> // max clamp for final invade priorities calculated


    <faction_ai_label name="default"> :: The label for a following set of campaign ai faction parameters



    // The ai_labels specify the structure of the long term goal director (LTGD) which drives the high level campaign AI.
    // At the start of every factions turn (or when diplomacy changes), the LTGD is re-evaluated as follows:
    // for every target faction (all other factions), evaluate the defend decisions
    // for every target faction, evaluate the invasion decisions
    // any invasion priorities are modified by the faction standing (relationship) towards the target
    // depending on current game state, a new target with a high invasion priority may be selected to invade
    // the LTGD can be debugged with the preferences '[log] level = ai.ltgd trace' and '[ai] ltgd_logging = true.



    // The 'defend_decisions' entry specifies a list of decision entries related to ai defence strategies. To evaluate a defence
    // strategy, a set of parameters is built and the list of decision entries is iterated until an entry satisfies the min and max
    // conditions for the set of parameters. As soon as an entry is satisfied (min <= current <= max), the iteration ends. The decision
    // is taken from the default 'faction_attitude' parameters with certain parameters overridden. The 'min_entry' specifies the minimum
    // thresholds specified for evaluation and the 'max_entry' specifies the maximum. Note that care should be taken when entering new
    // entries since as soon as the thresholds for an entry are successfully met, the iteration ends. An exception to this is the use of
    // the 'continue' faction_attitude parameter, which allows the decision iteration to continue (This is useful for changing certain
    // parameters of the faction_attitude but allowing the process to continue so other entries can apply additional modifications).


    <defend_decisions> :: List of ai defend decisions. when choosing a decision, code will iterate from first to last until a set of thresholds succeeds

    <min_entry :: The minimum threshold for decision comparison
    frontline_balance="0.0" :: ratio of factions frontline military strength vs the target
    military_balance="0.0" :: ratio of factions overall military strength vs the target
    production_balance="0.0" :: ratio of factions overall production strength vs the target
    target_num_enemies="0" :: the number of enemies the target has
    num_enemies="0" :: the number of enemies the faction has
    has_alliance_against="false" :: is the faction part of an alliance against target
    military_balance_plus_enemies="0.0" :: ratio of factions overall military strength vs the target (plus all of its enemies)
    alliance_military_balance="0.0" :: ratio of factions (plus its allies) overall military strength vs the target
    strongest_neighbour="false" :: is the target the factions strongest neighbour
    most_desirable="false" :: is the target the factions most desirable target
    faction_standing="-1.0" :: how much does the faction like the target
    target_global_standing="-1.0" :: how trustworthy is the target to the rest of the world
    target_faction_standing="-1.0" :: how much does the target faction like this faction
    global_standing="-1.0" :: how trustworthy is this faction to the rest of the world
    target_religion="catholic" :: the religion of the target (see descr_religions.txt)
    enemy_excommunicated="false" :: is the target excommunicated
    excommunicated="false" :: is this faction excommunicated
    num_turns_allied="0" :: the number of turns since the faction agreed to an alliance with the target
    num_turns_ceasfire="0" :: the number of turns since the faction has agreed to a ceasefire with the target (-1 for no agreement)
    stance="Allied" :: diplomatic stance with the target (Allied, Neutral, AtWar)
    target_faction="england" :: target faction label (see descr_sm_factions.txt)
    target_human="false" :: is the target a human player
    target_is_shadow="false" :: is the target this factions shadow faction
    turn_number="0" :: the game turn number (starting at 0)
    is_protectorate="false" :: is the target our protectorate
    is_protectorate_of_catholic="false" :: is the target a protectorate of a non-excommunicated catholic faction
    free_strength_balance="0.0" :: ratio of factions free military strength vs the target
    borders_all_our_regions="false" :: does the target border on all the factions region groups
    target_weakest_neighbour="false" :: is the faction the targets weakest neighbour
    has_ceasehostilities="false" :: does the faction have a cease hostilities mission against the target from the papal faction
    is_neighbour="false" :: does the target neighbour on any of the factions regions
    trusted_ally="false" :: is the target a trusted ally (they like us more than fs_thresh, and their global standing > gs_thresh, and they are allied)
    trusted_ally_enemy="false" :: is the target an enemy of a trusted ally
    trusted_ally_protectorate="false" :: is the target a protectorate of a trusted ally
    num_settlements="0" :: how many settlements does the faction own
    rand="0.0" :: a random value
    difficulty="easy"/> :: the chosen difficulty for the current local player (easy, medium, hard, very_hard)

    <max_entry :: The maximum threshold for decision comparison
    frontline_balance="999.0"
    military_balance="999.0"
    production_balance="999.0"
    target_num_enemies="999"
    num_enemies="999"
    has_alliance_against="true"
    military_balance_plus_enemies="999.0"
    alliance_military_balance="999.0"
    strongest_neighbour="true"
    most_desirable="true"
    faction_standing="1.0"
    target_global_standing="1.0"
    target_faction_standing="1.0"
    global_standing="1.0"
    target_religion="heretic"
    enemy_excommunicated="true"
    excommunicated="true"
    num_turns_allied="999"
    num_turns_ceasfire="999"
    stance="AtWar"
    target_faction="slave"
    target_human="true"
    target_is_shadow="true"
    is_protectorate="true"
    is_protectorate_of_catholic="true"
    free_strength_balance="999.0"
    borders_all_our_regions="true"
    target_weakest_neighbour="true"
    has_ceasehostilities="true"
    is_neighbour="true"
    trusted_ally="true"
    trusted_ally_enemy="true"
    trusted_ally_protectorate="true"
    num_settlements="999"
    rand="1.0"
    difficulty="very_hard"/>

    <faction_attitude :: A list of modifiers to apply if min and max entries above are successful
    defense="defend_normal" :: The long term defense type (defend_minimal, defend_normal, defend_raid, defend_frontline, defend_fortified, defend_deep)
    defend_priority="0" :: The defensive priority of achieving stance against this faction (NOT USED AT PRESENT) (additive with previous decisions this turn)
    invade="invade_none" :: The long term invade type (invade_buildup, invade_immediate, invade_raids, invade_opportunistic, invade_start, invade_none)
    invade_priority="0" :: priority of achieving invasion against this faction (additive with previous decisions this turn). Compared with priority for decisions against all other factions to choose highest. Value modified internally by faction standing.
    at_war="false" :: are we at war with this enemy
    want_peace="false" :: do we want to be at peace with this faction
    want_ally="false" :: do we want to ally with this faction
    want_be_protect="false" :: do we want to be a protectorate of this faction
    want_offer_protect="false" :: do we want to offer protectorate status to this nation
    force_invade="false" :: must we invade now
    alliance_against="0" :: how much do we want to have an alliance against this nation (additive with previous decisions this turn)
    pts_desire="0" :: points total for measuring our desire for this faction's territory (additive with previous decisions this turn)
    pts_alliance="0" :: points total for measuring how much we want to be allies with these people (additive with previous decisions this turn)
    can_force_invade="true" :: can naval or forced invasion settings overwrite invade parameters
    continue="false"/> :: do we stop evaluating decision entries (false) or continue


    It's rather important to note that again, this file only drives the LTGD (the high level CAI code named the Long Term Goal Director). These are the decision entries the LTGD checks.

    The files that affect the campaign AI are listed here.

    1) descr campaign ai db located within the data folder- effect - faction based cai are controlled through this method, rules for the factions can be put into place.

    2) descr campaign db located within the data folder - effect - game wide cai changes and campaign changes

    3) export descr unit located within the data folder - effect - recruitment cost/upkeep controls ai recruitment of units

    4) descr win conditions located within data/world/maps/campaign/imperial campaign - effect - controls ai settlement priorities

    5) descr character located within the data folder - effect - campaign pathfinding (also groundtypes affects this too in data/world/maps/base)

    6) descr missions located within the data folder - effect - affects settlement attack priority (disabled)

    7) descr diplomacy located within the data folder - effect - affects player/ai diplomacy and partly influences cai invasions

    8) descr strat located within data/world/maps/campaign/imperial campaign - effect - controls ai invasion decisions based on unit strength in rebel settlements/starting settlements, if you created a faction based CAI within your campaign ai db, you must also add an ai_label in this file for the faction with the right AI label

    9) campaign script located within data/world/maps/campaign/imperial campaign - effect - controls economics for ai, debt causes much less aggressive ai (also it cannot handle it)

    10) export descr building located within the data folder - effect - controls ai building (based on calculations per region and cost benefit/effects of a building)

    11) export descr character traits located within the data folder - effect - can add several ai only buffs, e.g. movement point increase, line of sight increase. (either of these have an effect on ai decisions)

    12) descr regions located within the data/world/maps/base - effect - can set horde targets here

    13) descr sm factions located within the data folder - effect - naval invasions can be set as 'preferred' here if you wish to encourage said invasions

    Lets take a brief look at the descr_campaign_db.

    Within this file there are several important functions.
    1) autoresolve, this is rather important. It is a part of the internal algorithm for how the AI attacks and what it expects.
    A line I'd like to point out is a new part of the kingdoms algorithm for assaulting settlements which can be activated or deactivated by switching it to true or false. In my case I switched it to false and have better results.
    Code:
       
    <autoresolve>     
         <min_capture_percent float = "5.0"/>
         <max_capture_percent float = "35.0"/>
         <lopsided_thresh float = "1.5"/>
         <lopsided_hn_mod float = "1.1"/>
         <separation_missile_add uint = "1"/>
         <naval_sink_modifier float = "5.0"/>
         <naval_sink_offset float = "2.5"/>
         <naval_sink_max float = "80.0"/>
         <sally_att_def_draw_divisor float = "1.25"/><!--sally battles are a draw if attacker lost and (number of attacker troops) > (number of defender troops)/divisor -->
         <use_new_sett_autoresolve bool = "false"/><!--do we use the new settlement autoresolve -->
         <switchable_defence_exposed bool = "false"/><!--can unit switch exposed state during autoresolve -->
         <gate_defence_num_oil_attacks int = "0"/><!--number of gate oil attacks -->
         <gate_defence_strength_oil_base float = "0.0"/><!--gate oil attack strength -->
         <gate_defence_strength_oil_level_modifier float = "0.0"/><!--gate oil attack strength += modifier * oil_level -->
         <gate_defence_num_arrow_attacks int = "0"/><!--number of gate arrow attacks -->
         <gate_defence_strength_arrow_base float = "0.0"/><!--gate arrow attack strength -->
         <gate_defence_strength_arrow_level_modifier float = "0.0"/><!--gate arrow attack strength += modifier * arrow_level -->
         <gate_defence_num_default_attacks int = "0"/><!--number of gate generic advantage attacks -->
         <gate_defence_strength_default_base float = "0.0"/><!--gate generic advantage attack strength -->
         <gate_defence_strength_default_level_modifier float = "0.0"/><!--gate advantage attack strength += modifier * fortification_level -->
         <sett_defence_num_arrow_attacks int = "0"/><!--number of settlement arrow attacks -->
         <sett_defence_strength_arrow_base float = "0.0"/><!--settlement arrow attack strength -->
         <sett_defence_strength_arrow_level_modifier float = "0.0"/><!--sett arrow attack strength += modifier * arrow_level -->
         <sett_defence_num_default_attacks int = "0"/><!--number of sett generic advantage attacks -->
         <sett_defence_strength_default_base float = "0.0"/><!--sett generic advantage attack strength -->
         <sett_defence_strength_default_level_modifier float = "0.0"/><!--sett advantage attack strength += modifier * fortification_level -->
         <display_strength_oil float = "0"/><!--additional effective army strength for defending residence with oil -->
         <display_strength_arrow float = "1"/><!--additional effective army strength for defending residence with arrows -->
         <display_strength_default float = "1"/><!--additional effective army strength for defending residence with fortification levels -->   
        </autoresolve>
    Additionally you can make the rebels move around more by changing this line and assigning their own ai_label/designing a proper AI profile for them.

    Code:
            <min_turn_keep_rebel_garrison int = "1"/>
        <max_turn_keep_rebel_garrison int = "2"/>
    The revolt part of the code is something that seems inherently broken unless you mod it, the AI constantly focuses on managing public order with its AI generals rather than have them lead armies. This presumably is because RTW was the same and MTW2 introduced several complicated mechanics like revolt for example which the AI captain led armies cannot handle and usually just revolt within a few turns.

    Code:
        <revolt>
            <end_turn_modifier float = "2"/>
            <excommunicated_modifier float = "15"/>
            <new_leader_modifier float = "5"/>
            <max_effective_loyalty float = "10.0"/>
            <rebel_region_modifier float = "2.0"/>
            <shadow_region_modifier float = "0.0"/>
            <rebel_border_modifier float = "1.1"/>
            <shadow_border_modifier float = "1.0"/>
            <num_units_modifier float = "1.0"/>
            <captain_modifier float = "-0.1"/>
            <min_revolt_chance float = "0.0"/>
            <max_revolt_chance float = "50.0"/>
            <ai_revolt_modifier float = "0.25"/>
            <revolt_additional_armies bool = "false"/>
            <revolt_crusading_armies bool = "false"/>
        </revolt>
    Hordes also have their own code here, the references are to horde targets in descr_regions.

    Code:
       <hordes>
          <end_target_faction_bonus int="-30000"/>
          <start_target_faction_bonus int="-25000"/>
          <farming_level_bonus int="7500"/>
          <shared_target_bonus int="-10000"/>
          <disbanding_horde_bonus int="27500"/>
          <starting_region_bonus int="25000"/>
          <horde_target_resource_bonus int="30000"/>
       </hordes>
    This part of the code controls the various campaign AI related functions. Please keep in mind those values are meant for EBII rather than getting placed into any campaign_db. Generally you should simply keep CA's defaults unless you intend to change specific parts of the AI actions/interactions.
    They work hand in hand with an internal algorithm/the LTGD and the descr_campaign_ai_db file.

    Att_str seems to apply to field battles and acts as a general baseline for the rest of the algorithms.
    An explanation of what I found, the siege_att_str seems to have an effect on the troop buildup process of the AI.
    The rest don't really require explanation except for str_limit, this is by far the most complicated part of the algorithm. It modifies the rest of the algorithms and decisions.
    Personally I once decided to place a 999 there in str limit strong to (as I understand it) disable the function. According to some old CA note I found 999 is a value which disables the function and stops it from working (like how the old rebel garrison was).

    At present

    str weak has a .99 (tests indicate this would be the minimum strength required for any attack, as a ratio of 1:1)
    str strong has a 1.01 (this value may not be used, as setting it to .01 has no effect for example, neither does 99 or 999, in fact it may be disabled by default... perhaps the 99 was not a properly typed 999)

    This as I understand it means if the str ratio if less than the ideal (which I believe is 1.0) multiplied by this value then the army will not attack.

    Additionally if the enemy has a str ratio greater than 1.01 of their own force the AI will not attack.

    Think of it this way

    Attack calculation sees a value of 2:3 in favour of the enemy.
    Current value/equation of str weak is .99(1) which means the value below which it considers itself to weak to attack is .99

    Therefore if it has less than a 1:1 ratio it should not attack.
    Whereas it will never consider itself too strong to attack due to the limit being disabled/insanely high. Keep in mind this is only my interpretation after seeing CA's original value of 99.0.

    Code:
       <ai>
          <!--CAMPAIGN ATTACK -->
          <att_str_modifier float = "1.0"/>            <!-- modifies the effective attackers strength when determining the priority of making attack decision (i.e. att_def_strength_ratio = ((att_str*att_str_modifier)/def_str) -->
          <siege_att_str_modifier float="0.9"/>      <!-- modifies the effective sieging attackers strength when determining the priority of making attack decision -->
          <crusade_att_str_modifier float = "1.0"/><!--modifies the effective crusading sieging attackers strength when determining the priority of making attack decision -->
          <sally_att_str_modifier float = "1.0"/><!--modifies the effective sallying attackers strength when determining the priority of making attack decision -->
          <ambush_att_str_modifier float = "1.2"/><!--modifies the effective ambushing attackers strength when determining the priority of making attack decision -->
          <str_limit_weak float = "0.99"/>              <!-- min ideal strength ratio modifier for determining when an army is far too weak for an attack ( att_def_strength_ratio < (ideal_str_ratio*str_limit_weak) ) -->
          <str_limit_strong float = "1.01"/>            <!-- max ideal strength ratio modifier for determining when an army is far too strong for a fair attack ( att_def_strength_ratio > (ideal_str_ratio*str_limit_strong) ) -->
          <merchant_min_survival_acquire int = "75"/><!--the minimum survival chance for a merchant to consider attempting an acquisition -->
       </ai>
    In regards to the descr campaign ai db, every decision entry is checked in order from first to the last, prioritizing the first decision over the successive ones.

    When you start creating a decision entry you want to think of several things
    1) What your goal is
    2) Of what requirment you want for the min_entry (and whether you want a max_entry)
    3) What the faction attitude will be and target faction or function
    4) You must close your tag

    The campaign AI is in my opinion somewhat more complex than the battle AI.

    There are several underlying pieces in this puzzle.
    1) The campaign AI internally modifies the invasion priorities according to internal decisions for behaviour versus factions it likes or dislikes but this can be offset by this part of the code (keep in mind successive entries for each difficulty level are reccomended)
    2) The CAI will also use the values from the descr_campaign_db for its algorithms in the exe (also those specific algorithms themselves are explained in the db)
    3) Each of the files listed above come to into play
    4) The financial state of the CAI is a factor
    5) Whether code regarding slaves exists, will determine whether the AI expands into slave held territory first or other factions
    6) The faction specific invasion priorities, if you make the faction attitude for one faction vs another high (e.g. 25000 invasion priority + invade_start invasion order force_invade = "true" etc, it will them a bigger target for the faction you are creating the ai_label for, also remember to always have a default label)
    7) The troop composition within a town will determine when the AI expands into it and also the priority of taking that town based on its existance in the descr win conditions.

    The trusted ally FS stands for faction standing threshold. Checking your descr_faction standings for the matching parameter generally being min_faction_standing -1.0 and max_faction_standing 1.0.
    0 is the neutral FS, whereas 0.3 would be 30%+ good relations meaning they're a trusted ally.

    FS stands for faction standing.
    GS stands for global standing.
    These two settings are what cause the AI to act differently towards factions it likes or dislikes and trusts or distrusts. (world reputation)
    Whereas priority assistance refers to the offset (addition to the base amount of invade priority) that a faction gets when an invade assistance request was successful during diplomacy.

    The min and max priorities determine the amount of resources that the AI puts into the war effort.
    Merge fort priority determines how the AI uses its forts. I don't want it to bug out and merge its units into forts, hence the focus on individual generals outside of towns instead of in forts. Although its worth noting that this is only a stop gap measure, not a full out fix. It does significantly improve the AI's use of force by preventing the merge/priority of forts and as far as I have seen the AI will not stay in its forts forever except in very circumstances i.e. army heavily defeated within a previously (until a turn later) held region and retreats into the fort.
    Forts are inherantly bugged in some ways, with the AI simply sitting in them while its towns are attacked unless the defend decisions lack any other entries than defend normal or in rare circumstances where it applies.

    Note that the modifier means it is multiplied into the faction standing.
    So a negative faction standing times a positive multiplier will end up modifying the invasion priority by a negative amount.

    Therefore I've made it a negative modifier, as I want positive invasion priority when relations are bad (negative faction standing times negative modifier = positive invasion priority)
    Code:
    <root>
            <trusted_ally_fs_threshold float = "0.5"/>
            <trusted_ally_target_fs_threshold float = "0.5"/>
            <trusted_ally_target_human_fs_threshold float = "0.5"/>
        <trusted_ally_gs_threshold float="-0.1"/>
        <trusted_ally_target_gs_threshold float="-0.1"/>
            <invade_priority_fs_modifier float = "-30000.0"/>
            <invade_priority_gs_modifier float = "-30000.0"/>
            <invade_priority_assistance_offset int = "25000"/>
            <invade_priority_min int = "10000"/>
            <invade_priority_max int = "30000"/>
            <merge_fort_priority_offset int = "-30000"/>
        </easy>
    I would like to re-emphasize the fact that reading CA's notes in the descr_campaign_ai_db is definitely a very good idea, for ideas on how the AI works and what each line means. For example, figuring out how the defend_frontline works is intuitive after reading this.

    Code:
                <decision_entry>
                    <!--
                        if we're not at war && we are his weakest neighbour && he is not at war elsewhere && we are not at war elsewhere,
                        then >>> frontline defense
                    -->
                    <min_entry    target_weakest_neighbour="true"/>
                    <max_entry    target_num_enemies="0" num_enemies="0"/>
                    <faction_attitude    defense="defend_frontline"/>
                </decision_entry>

    The Battle Config

    In regards to the battle config, this file seems to be primarily used for the configuration of unit behaviour. For example of units within a special ability like 'skirmish mode' or 'phalanx'.

    However there are important values related to both cohesion/hit rates here. The melee hit rate for example contributes towards the cohesion of a unit, presumably because it has a connection towards the hitbox of a unit.

    Code:
      <combat-balancing>
        <missile-target-accuracy>
          <infantry>0.4</infantry>
          <cavalry>0.4</cavalry>
          <elephants>0.4</elephants>
        </missile-target-accuracy>
        <melee-hit-rate>
            0.35
    What an unformed charge is exactly is difficult to figure out, after testing it would appear that this is relevant to controlling when a unit exits the charging state.
    1.0 would be 100%, on a very low value like 0.01 or 0.1 cohesion of a charge seems to be worse.
    Therefore 1.0 seems to 'force' the unit task to be proportionate to 100%, or 100% of the unit entering charging state even if they are unformed. An unformed state being not running in unison before entering the charging state. This doesn't completely solve the infrequent problem of only the frontline charging but it does seem to help. I believe the root of that problem (based on tests specifically regarding this) is primarily to do with the speed of the unit and the speed of the charging animations interfering with the actual charge itself.

    Code:
        <unit-tasks>
            <!-- wall reform - used for split up units -->
            <wall-reform>
                <!-- once more than this number are queued up, split across the other ladders -->
                <queue-length-before-split>10</queue-length-before-split>
            </wall-reform>
    
    
            <!-- unformed charge -->
            <unformed-charge>
                <!-- proportion of unit that will charge before unit task will finish -->
                <finish-proportion-infantry>1.0</finish-proportion-infantry>
                <finish-proportion-cavalry>1.0</finish-proportion-cavalry>
                <finish-proportion-phalanx>0.00001</finish-proportion-phalanx>
            </unformed-charge>
        </unit-tasks>
    If you add a gunpowder attribute to a unit within the EDU it will use the gunpowder section for the corresponding unit type (infantry or cavalry) of the skirmish element.

    This means that you can adjust say, the javelin cavalry to have a skirmish range of 40 whereas horse archers (with and because of the gunpowder attribute added) will skirmish at a greater range, like 75.

    The reason they have a longer skirmishing range is to help encourage them to keep away from melee combat with opposing horsemen.
    Code:
        <unit>
            <!-- phalanx configuration ++disabled++ -->
            <!-- phalanx -->
                <!-- intercept-range>15</intercept-range -->
            <!-- /phalanx -->
            <!-- skirmish configuration -->
            <skirmish>
                <infantry>
                    <default>
                        <!-- ignore targets at a distance greater than the maximum range times this scale factor -->
                        <max-range-scale>4.0</max-range-scale>
                        
                        <!-- must skirmish if within this range -->
                        <min-range>30</min-range>
                        
                        <!-- stop at this distance if the enemy is blocking the path -->
                        <min-stopping-range>45</min-stopping-range>
                        
                        <!-- time to react to being intercepted -->
                        <collision-reaction-time>1</collision-reaction-time>
                        
                        <!-- retreat buffer time -->
                        <retreat-time>1</retreat-time>
                        
                        <!-- don't skirmish until attackers are within this fraction of the missile range -->
                        <range-factor>
                            <moving>1.0</moving>
                            
                            <shooting>0.75</shooting>
                        </range-factor>
                    </default>
                    
                    <gunpowder>
                        <!-- ignore targets at a distance greater than the maximum range times this scale factor -->
                        <max-range-scale>1.5</max-range-scale>
                        
                        <!-- must skirmish if within this range -->
                        <min-range>40</min-range>
                        
                        <!-- stop at this distance if the enemy is blocking the path -->
                        <min-stopping-range>50</min-stopping-range>
                        
                        <!-- time to react to being intercepted -->
                        <collision-reaction-time>1</collision-reaction-time>
                        
                        <!-- retreat buffer time -->
                        <retreat-time>14</retreat-time>
                        
                        <!-- don't skirmish until attackers are within this fraction of the missile range -->
                        <range-factor>
                            <moving>1.15</moving>
                            
                            <shooting>0.80</shooting>
                        </range-factor>
                    </gunpowder>
                </infantry>
                
                <cavalry>
                    <default>
                        <default-run>1</default-run>
                        <!-- ignore targets at a distance greater than the maximum range times this scale factor -->
                        <max-range-scale>5.0</max-range-scale>
                        <max-missile-range-extension>60</max-missile-range-extension>
                        <!-- must skirmish if within this range -->
                        <min-range>25</min-range>
                        
                        <!-- stop at this distance if the enemy is blocking the path -->
                        <min-stopping-range>150</min-stopping-range>
                        
                        <!-- time to react to being intercepted -->
                        <collision-reaction-time>1</collision-reaction-time>
                        
                        <!-- retreat buffer time -->
                        <retreat-time>1</retreat-time>
                        
                        <!-- don't skirmish until attackers are within this fraction of the missile range -->
                        <range-factor>
                            <moving>1.0</moving>
                            
                            <shooting>1.0</shooting>
                        </range-factor>
                        
                        <!-- reaction time for cantabrian circle -->                    
                        <cantabrian-reaction-time>1</cantabrian-reaction-time>
                    </default>
                    
                    <gunpowder>
                        <!-- ignore targets at a distance greater than the maximum range times this scale factor -->
                        <max-range-scale>1.5</max-range-scale>
                        
                        <!-- must skirmish if within this range -->
                        <min-range>75</min-range>
                        
                        <!-- stop at this distance if the enemy is blocking the path -->
                        <min-stopping-range>50</min-stopping-range>
                        
                        <!-- time to react to being intercepted -->
                        <collision-reaction-time>8</collision-reaction-time>
                        
                        <!-- retreat buffer time -->
                        <retreat-time>12</retreat-time>
                        
                        <!-- don't skirmish until attackers are within this fraction of the missile range -->
                        <range-factor>
                            <moving>1.0</moving>
                            
                            <shooting>1.0</shooting>
                        </range-factor>
                        
                        <!-- reaction time for cantabrian circle -->                    
                        <cantabrian-reaction-time>20</cantabrian-reaction-time>
                    </gunpowder>
                </cavalry>            
                
            </skirmish>
            
        </unit>
    This code controls (although I haven't completely verified this as I generally left CA's rules unchanged) the plaza count down timer and the ratio of attackers to defenders necessary to hold the plaza.
    If the defenders only have 20% of the attackers numbers within the plaza the countdown begins.

    I believe each 1.0 counts for 60 seconds within the time limit area.
    Code:
        <settlements>
            <rules>
                <!-- rules relating to plaza capture -->
                <plaza-capture>
                    <!-- time the plaza needs to be held for to capture -->
                    <time-limit>3.0</time-limit>
                    
                    <!-- ratio of attackers to defenders to hold a plaza -->
                    <soldier-ratio>0.8</soldier-ratio>
                </plaza-capture>
            </rules>
        </settlements>
    The following section is related to how units climb and use siege equipment.
    I've currently made it so that the units can literally swarm up the ladders, just to make things more difficult for the defender.

    I aim to eventually make it so that units go one by one, but for the moment I have had more pressing and 'top priority' matters in EBII to deal with unfortunately.
    Such a thing however is quite likely possible and just a matter of timing/amounts of soldiers and distances.
    Code:
                <!-- end distance for the blockout region -->
                <entry-blockout-outer>225.0</entry-blockout-outer>
    
    
                <!-- limit on number of soldiers in the blockout region -->
                <number-in-blockout>2</number-in-blockout>
    
    
                <!-- distance soldiers should stop at if the entrance is blocked (in metres squared) -->
                <stand-off-distance>324.0</stand-off-distance>
    
    
                <!-- distance within which soldiers will step onto the line (in metres squared) -->
                <step-on-distance>400.0</step-on-distance>
    I hit the character limit so I removed some parts of my text as I wanted to put this as a closing note, I would highly recommend that you install and use Notepad++, its colour coding is invaluable.
    Campaign AI/Battle AI part may be updated in the future in the next post.
    Also feel free to check out Europa Barbaroum II's various files for more in depth code.
    Last edited by z3n; April 29, 2022 at 02:11 PM. Reason: minor update
    The AI Workshop Creator
    Europa Barbaroum II AI/Game Mechanics Developer
    The Northern Crusades Lead Developer
    Classical Age Total War Retired Lead Developer
    Rome: Total Realism Animation Developer
    RTW Workshop Assistance MTW2 AI Tutorial & Assistance
    Broken Crescent Submod (M2TW)/IB VGR Submod (BI)/Animation (RTW/BI/ALX)/TATW PCP Submod (M2TW)/TATW DaC Submod (M2TW)/DeI Submod (TWR2)/SS6.4 Northern European UI Mod (M2TW)

  2. #2
    z3n's Avatar State of Mind
    Moderator Emeritus

    Join Date
    Aug 2011
    Posts
    4,640

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    6) Custom CAI Labels
    7) Custom BAI Tactics
    8) BAI Pathfinding Scheduler/Node Info


    Custom CAI Labels


    To do this you have to
    1) Create a new AI label after your default label in the descr_campaign_ai_db (you can simply copy and paste the default ai label + code and then rename it and work off that)
    2) Assign the label to the corresponding faction in the descr_strat.

    Note that this code is not intended for any use other than a confirmation as to what I mean by default label, its missing the defend_decisions and invasion_decisions along with the corresponding closure of the element i.e. </faction_ai_label>
    I also recommend that you do a 'real' AI label for the default one, just without any adjustments to diplomacy because this is the fall back label if the AI cannot find any other labels to use and there are probably other uses I do not know about or else CA would not have said that you have to have it.
    Code:
        <faction_ai_label name = "default">
            <defend_decisions>
                <decision_entry>
                    <faction_attitude defense = "defend_normal" continue = "true"/>
                </decision_entry>

    Now lets look at parts of romes ai label as to why custom faction AI's can be useful and ideas as to what you can do with them
    If you've already read CA's notes you'll know that every ai label starts with the defend decisions, and that the very first decision is the 'main' decision or personality of the label, as it's the very first taken into account. The continue = "true" ensures that this decision is taken into account no matter what and since decisions are processed sequentially this is the main reason I call it the 'personality' of the label.

    Also you 'must' have this fall back decision or else the AI will not work properly. As it lacks any min/max entries it is the 'fall back' decision.

    Please note that the continue = "true" part plays a large role in this. Alongside any other 'continue = "true" command. If marked as such, the faction attitude will literally continue if the entry requirements are met. Or "iterate" as CA devs described it if the requirements are met.

    This is in fact a very small detail but one I had only just had time to look at in depth, it's a very 'powerful' way to control how a faction behaves. It also ends up having the AI (if you so desire) consider every single type of other invasion 'plan' for every invasion. For example, invade_immediate alongside invade_opportunistic and so on. This seems to work quite well and ends up having better naval invasions. (at least from the reports) Alongside other types of invasions.


    Code:
                   Note that care should be taken when entering new 
            // entries since as soon as the thresholds for an entry are successfully met, the iteration ends.  An exception to this is the use of
            // the 'continue' faction_attitude parameter, which allows the decision iteration to continue (This is useful for changing certain
            // parameters of the faction_attitude but allowing the process to continue so other entries can apply additional modifications).

    I'm still experimenting with how to implement the continue command exactly, as there are some very interesting things you can do with it or without it. Here we can see the effect of not using the continue command at all, and simply forming certain decision defensive and invasion entries within the file.

    What ends up happening is this, epeiros is able to save the day sailing over to the peninsula but not able to hold onto their former conquests. The turn after however they soon marched back to take the towns. However, a lack of a continue command at times (depending on how strict your decision entries are and the current state of your force_invade/modifications to the str weak/strong algorithm) means without specific orders and priorities towards a target not much will happen.

    http://imgur.com/a/NGvCp



    Code:
        <faction_ai_label name = "f_rome">
            <defend_decisions>
                <decision_entry>
                    <faction_attitude defense = "defend_normal" continue = "true"/>
                </decision_entry>
    Now because we hate humans we have this put into place, to make it more difficult for them to make an alliance
    Code:
                <!--Human Alliance-->
                <decision_entry>
                    <!--Adjust this value for the whole Alliance system for the mod (human)-->
                    <min_entry target_human = "true"/>
                    <faction_attitude pts_alliance = "-130" continue = "true"/>
                </decision_entry>
    We also have this, which is the AI alliance scale. Although modding the diplomacy xml file is just as efficient.

    Code:
                <!--AI Alliance Scale-->
                <decision_entry>
                    <!--Adjust this value for the whole Alliance system for the mod (AI)-->
                    <min_entry target_human = "false"/>
                    <max_entry target_human = "false"/>
                    <faction_attitude pts_alliance = "-1" continue = "true"/>
                </decision_entry>
    In the diplomacy xml you will want to remember the decisions that the war AI are prioritized above these values, unless you make them very low.
    Values such as these will make the alliances a) much more rare and b) much more dependent on your own 'alliance_points' within the campaign ai db, as such you can basically narrow down which factions like which and also which factions don't like other factions. You can even add things like want_peace = true or false or narrow decisions like that down to faction relations. To make things even more 'realistic' (although exponentially more complicated/intertwined with each other)

    Code:
            <item name="offer_alliance">
                <cost                modifier="0.001"/>
                <faction_standing   modifier="0.099"/>
                <global_standing    modifier="0.0"/>
            </item>
    Here's what I talk about when I say alliance points.

    Code:
                <!--Alliance chance We will use this section to balance Diplomacy for the AI -->
                <decision_entry>
                    <min_entry target_faction = "f_carthage"/>
                    <max_entry target_faction = "f_carthage"/>
                    <faction_attitude pts_alliance = "-10" want_peace = "false" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_epeiros"/>
                    <max_entry target_faction = "f_epeiros"/>
                    <faction_attitude pts_alliance = "-10" want_peace = "false" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_aedui"/>
                    <max_entry target_faction = "f_aedui"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_seleukid"/>
                    <max_entry target_faction = "f_seleukid"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_saba"/>
                    <max_entry target_faction = "f_saba"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_sauromatae"/>
                    <max_entry target_faction = "f_sauromatae"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_saka"/>
                    <max_entry target_faction = "f_saka"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                    <decision_entry>
                    <min_entry target_faction = "f_baktria"/>
                    <max_entry target_faction = "f_baktria"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_hayasdan"/>
                    <max_entry target_faction = "f_hayasdan"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_pontos"/>
                    <max_entry target_faction = "f_pontos"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_parthia"/>
                    <max_entry target_faction = "f_parthia"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_arverni"/>
                    <max_entry target_faction = "f_arverni"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_casse"/>
                    <max_entry target_faction = "f_casse"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_lusotannan"/>
                    <max_entry target_faction = "f_lusotannan"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_sweboz"/>
                    <max_entry target_faction = "f_sweboz"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_getai"/>
                    <max_entry target_faction = "f_getai"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_gandhara"/>
                    <max_entry target_faction = "f_gandhara"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_lugia"/>
                    <max_entry target_faction = "f_lugia"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_bosporan"/>
                    <max_entry target_faction = "f_bosporan"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_arevaci"/>
                    <max_entry target_faction = "f_arevaci"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_boii"/>
                    <max_entry target_faction = "f_boii"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_nabatu"/>
                    <max_entry target_faction = "f_nabatu"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_kh"/>
                    <max_entry target_faction = "f_kh"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_numidia"/>
                    <max_entry target_faction = "f_numidia"/>
                    <faction_attitude pts_alliance = "2" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_makedonia"/>
                    <max_entry target_faction = "f_makedonia"/>
                    <faction_attitude pts_alliance = "10" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_pergamon"/>
                    <max_entry target_faction = "f_pergamon"/>
                    <faction_attitude pts_alliance = "10" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_faction = "f_ptolemaioi"/>
                    <max_entry target_faction = "f_ptolemaioi"/>
                    <faction_attitude pts_alliance = "10" continue = "true"/>
                </decision_entry>
    Now to add some 'realism' based on faction relations rather than our default alliance points

    Code:
                <!--Random Alliance generator w/ wanting peace both Target FS and GS -->
                <decision_entry>
                    <max_entry rand = ".20" target_faction_standing = ".2"/>
                    <faction_attitude pts_alliance = "1" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".20" target_global_standing = ".2"/>
                    <faction_attitude pts_alliance = "1" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".30" target_faction_standing = ".4"/>
                    <faction_attitude pts_alliance = "2" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".30" target_global_standing = ".4"/>
                    <faction_attitude pts_alliance = "1" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".40" target_faction_standing = ".6"/>
                    <faction_attitude pts_alliance = "3" want_peace = "true" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".40" target_global_standing = ".6"/>
                    <faction_attitude pts_alliance = "2" want_peace = "true" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".20" target_faction_standing = "-.2"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".20" target_global_standing = "-.2"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".30" target_faction_standing = "-.4"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".30" target_global_standing = "-.4"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".40" target_faction_standing = "-.6"/>
                    <faction_attitude pts_alliance = "-9" want_peace = "false" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry rand = ".40" target_global_standing = "-.6"/>
                    <faction_attitude pts_alliance = "-9" want_peace = "false" continue = "true"/>
                </decision_entry>
    You can also create some other effects like this

    Code:
                <decision_entry>
                    <min_entry is_neighbour = "false"/>
                    <max_entry is_neighbour = "false"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry borders_all_our_regions = "true"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry strongest_neighbour = "true"/>
                    <faction_attitude pts_alliance = "-9" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry trusted_ally_protectorate = "true"/>
                    <faction_attitude pts_alliance = "1"/>
                </decision_entry>
                <decision_entry>
                    <min_entry trusted_ally = "true"/>
                    <faction_attitude pts_alliance = "1"/>
                </decision_entry>

    Now we get to the 'actual' defend decisions. Note that diplomacy was processed first, this is done to aid the AI in choosing whether to attack or not and let the diplomacy AI/campaign AI work together rather than against each other.

    You may think I am very minimalist when it comes to these decisions.
    It's for good reason in my opinion, defend_normal works perfectly fine, whereas decisions like defend_fortified have a suspicious looking 'fort' in their code and are used by CA solely to encourage the 'defense' of frontlines via forts from the way I interpreted their code.
    Code:
                <decision_entry>
                    <min_entry is_protectorate = "true"/>
                    <faction_attitude defense = "defend_minimal"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "Allied" target_faction_standing = "0.01"/>
                    <max_entry stance = "Allied"/>
                    <faction_attitude defense = "defend_minimal"/>
                </decision_entry>
                <decision_entry>
                    <min_entry trusted_ally_enemy = "true" is_neighbour = "true"/>
                    <max_entry num_enemies = "2"/>
                    <faction_attitude defense = "defend_frontline"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" is_neighbour = "true"/>
                    <max_entry num_enemies = "2"/>
                    <faction_attitude defense = "defend_frontline"/>
                </decision_entry>
                <decision_entry>
                    <min_entry is_neighbour = "false" num_settlements = "1"/>
                    <max_entry is_neighbour = "false"/>
                    <faction_attitude defense = "defend_minimal"/>
                </decision_entry>
    CA's code here just as another reminder to read through their entire file is here as an example of what I mean.
    From this we can deduce that a) the AI is looking for units on the 'frontline' and reading their free_strength (or what they can mobilize) as a maximum of equal to the other faction.
    Their attitude is to defend their frontline via forts or sit in their town.

    Also note that defend fortified is a blanket decision which will end up meaning that they very probably won't try to lift a siege if it happens. I suppose how you write your own AI and for what purpose is up to you.
    Code:
                <decision_entry>
                    <!--
                        if we're not at war && his frontline strength is more than twice ours && our free strength is less than his,
                        then >>> fortified defense
                    -->
                    <max_entry    frontline_balance="0.5" free_strength_balance="1.0"/>
                    <faction_attitude    defense="defend_fortified"/>
                </decision_entry>
    With this code its intended to ally a faction against another if the stance applies.
    CA was much more generous with their points of alliance_against but I prefer not to make it overt. In fact it would seem that alliance points are determined in conjunction with this command, rather than seperately. It seems to add a bonus point to the cumulative amount of current alliance points between factions.
    Nevertheless there is 'reset' code later on in the file that contains a series of -1s setting the alliance_against back to 0 as an 'in case' and since the decisions are processed sequentially the diplomats will already have made their way towards another faction before the reset happens.

    Code:
                <decision_entry>
                    <min_entry stance = "AtWar" num_enemies = "2"/>
                    <max_entry stance = "AtWar"/>
                    <faction_attitude alliance_against = "1" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" num_enemies = "3"/>
                    <max_entry stance = "AtWar"/>
                    <faction_attitude alliance_against = "1" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "0.71" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" military_balance = "0.90"/>
                    <faction_attitude alliance_against = "1" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" military_balance = ".70"/>
                    <faction_attitude alliance_against = "1" continue = "true"/>
                </decision_entry>
    This code determines whether a faction will request to become a vassal and when.

    Code:
                <decision_entry>
                    <min_entry stance = "AtWar" num_enemies = "2" is_neighbour="true" target_weakest_neighbour="false"/>
                    <max_entry num_settlements = "2" production_balance = "0.75" military_balance = "0.6" frontline_balance = "0.5"/>
                    <faction_attitude want_be_protect = "true" pts_alliance = "3" continue = "true"/>
                </decision_entry>
                <!--@WAR SEEK Protect RULES,  Defense -->
                <decision_entry>
                    <!--@WAR Seek Protect Cond 1-->
                    <min_entry stance = "AtWar" num_settlements = "5"/>
                    <max_entry stance = "AtWar" num_settlements = "6" military_balance = ".10" production_balance = "0.25" rand = ".50"/>
                    <faction_attitude want_be_protect = "true" pts_alliance = "3" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <!--@WAR Seek Protect Cond 2 (Not Looking good)-->
                    <min_entry stance = "AtWar" num_settlements = "3"/>
                    <max_entry stance = "AtWar" num_settlements = "4" military_balance = ".30" production_balance = "0.30" rand = ".65"/>
                    <faction_attitude want_be_protect = "true" pts_alliance = "4" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <!--@WAR Seek Protect Cond 3 (Desparation)-->
                    <min_entry stance = "AtWar" num_settlements = "1"/>
                    <max_entry stance = "AtWar" num_settlements = "2" military_balance = ".40" production_balance = "0.40" rand = ".75"/>
                    <faction_attitude want_be_protect = "true" want_peace = "true" pts_alliance = "5" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <!--@WAR Seek Protect Cond 4 (neighbor)-->
                    <min_entry stance = "AtWar" is_neighbour = "false" num_settlements = "1"/>
                    <max_entry stance = "AtWar" is_neighbour = "false" num_settlements = "6" rand = ".50"/>
                    <faction_attitude want_be_protect = "false" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <!--@WAR Seek Protect Cond 4 (human)-->
                    <min_entry stance = "AtWar" target_human = "true" num_settlements = "1"/>
                    <max_entry stance = "AtWar" target_human = "true" num_settlements = "6" rand = ".25"/>
                    <faction_attitude want_be_protect = "false" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <!--Never seek From Slaves -->
                    <min_entry stance = "AtWar" target_faction = "slave"/>
                    <max_entry stance = "AtWar" target_faction = "slave"/>
                    <faction_attitude want_be_protect = "false" continue = "true"/>
                </decision_entry>
    This is the final part of the defensive code mostly to allow for opportunistic attacks if a target has 3 or more enemies but with a max entry just before it to ensure that it doesn't overextend an opportunistic faction on more than one front.

    Code:
                
                <decision_entry>
                    <min_entry stance = "AtWar" target_num_enemies="3" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" num_enemies="2"/>
                    <faction_attitude defense = "defend_frontline"/>
                </decision_entry>
            </defend_decisions>
    Now we come to the invasion decisions.

    My assumption is that force_invade relies upon what we discussed earlier in the campaign db to an extent, its what forces a faction to consider an invasion either (importantly) naval or land from what CA said, so I designed mine around the idea that naval invasions should exist as a top priority and be tied into all the other decisions as a baseline consideration.
    can_force_invade="true" :: can naval or forced invasion settings overwrite invade parameters
    Code:
            <invasion_decisions>
                <!--diplomatic, @war-neutral, always start with the default offense-->
                <decision_entry>
                    <faction_attitude force_invade = "true" invade = "invade_immediate" continue = "true"/>
                </decision_entry>
                <!--encourage invasion plans-->
                <decision_entry>
                <min_entry stance = "AtWar"/>
                    <faction_attitude force_invade = "true" invade = "invade_start" continue = "true"/>
                </decision_entry>
    Most parts of the code after this are self explanatory and part of the 'planning' phase of how the AI strategies and contributions towards each strategy.
    Although my thoughts on what each part means
    a) invade_start - I believe this is an order directly to the LTGD, what the LTGD probably does is check (or send spies) to various regions within the target faction before it locates a suitable target (either a) a win condition settlement *which again is a sequentially based series of towns within the win conditions file, or a sufficiently under-garrisoned garrisoned town on the border). Anyway, from what CA said within their notes this is definitely a 'planning' order.
    b) invade_opportunistic - from observation this increases the amount of naval blockades, assassins, spies, and attacks on weakpoints
    c) most_desirable - after reading through the AI LTGD logs while doing tests, I believe this has to do with the highest invasion priority and number of victory conditions tied to the faction, which leads to it being the 'most_desirable' of them all

    Code:
                <decision_entry>
                <min_entry stance = "Neutral"/>
                    <faction_attitude force_invade = "true" invade = "invade_start" continue = "true"/>
                </decision_entry>
                <!--no more buildup-->
                <decision_entry>
                    <!--
                        if we're not at war && we're not at war with anyone else && we outproduce him && he is our strongest
                        neighbour, then >>> plan future invasion
                    -->
                    <min_entry    production_balance="1.0" strongest_neighbour="true"/>
                    <max_entry    num_enemies="0"/>
                    <faction_attitude    invade="invade_start" invade_priority="15000" alliance_against="1"/>
                </decision_entry>
                <decision_entry>
                    <!--
                        if we're not at war && we're not at war with anyone else && we outproduce him && he is most desireable, 
                        then >>> plan future invasion
                    -->
                    <min_entry    production_balance="1.0" most_desirable="true"/>
                    <max_entry    num_enemies="0"/>
                    <faction_attitude    invade="invade_start" invade_priority="15000" alliance_against="1"/>
                </decision_entry>
                <!--Human Difficulty -->
                <decision_entry>
                    <min_entry target_human = "true" stance = "AtWar"/>
                    <max_entry stance = "AtWar"/>
                    <faction_attitude can_force_invade = "true" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_human = "true" stance = "AtWar"/>
                    <faction_attitude force_invade = "true" invade_priority = "30000" pts_desire = "998" invade = "invade_immediate" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_human = "true" stance = "AtWar"/>
                    <max_entry stance = "AtWar"/>
                    <faction_attitude at_war = "true" force_invade = "true" invade = "invade_opportunistic" invade_priority = "30000"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_human = "true" stance = "Neutral" is_neighbour = "true"/>
                    <faction_attitude force_invade = "true" invade_priority = "20000" pts_desire = "998" invade = "invade_start" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <!--turn off forced invasion -->
                    <faction_attitude can_force_invade = "false" continue = "true"/>
                </decision_entry>
    Attacking factions offer vassalage.
    Code:
                <decision_entry>
                    <!--
                        if we're at war && we've more than five times his frontline strength && we're superior overall &&
                        we outproduce him, && IS NOT OUR SHADOW FACTION >>> propose he become vassal, invade immediate.  If not our shadow, also want to offer protectorate
                    -->
                    <min_entry    stance="AtWar" frontline_balance="1.5" military_balance="1.0" production_balance="1.0"/>
                    <faction_attitude    invade="invade_immediate" invade_priority="25000" want_offer_protect="true"/>
                </decision_entry>
    Assigning/distributing invasion priorities.
    Code:
                <decision_entry>
                    <max_entry military_balance_plus_enemies = ".49"/>
                    <faction_attitude invade_priority = "-15000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry military_balance_plus_enemies = ".50"/>
                    <max_entry military_balance_plus_enemies = ".80"/>
                    <faction_attitude invade_priority = "-10000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry military_balance_plus_enemies = ".81"/>
                    <max_entry military_balance_plus_enemies = "1.5"/>
                    <faction_attitude invade_priority = "-7500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry military_balance_plus_enemies = "1.51"/>
                    <max_entry military_balance_plus_enemies = "1.99"/>
                    <faction_attitude invade_priority = "7500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry military_balance_plus_enemies = "2.0"/>
                    <max_entry military_balance_plus_enemies = "2.99"/>
                    <faction_attitude invade_priority = "10000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry military_balance_plus_enemies = "3.0"/>
                    <faction_attitude invade_priority = "15000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry alliance_military_balance = ".79"/>
                    <faction_attitude invade_priority = "-15000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry alliance_military_balance = ".80"/>
                    <max_entry alliance_military_balance = "1.24"/>
                    <faction_attitude invade_priority = "7500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry alliance_military_balance = "1.25"/>
                    <max_entry alliance_military_balance = "2.0"/>
                    <faction_attitude invade_priority = "10000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry alliance_military_balance = "2.01"/>
                    <max_entry alliance_military_balance = "3.0"/>
                    <faction_attitude invade_priority = "15000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry alliance_military_balance = "3.01"/>
                    <faction_attitude invade_priority = "20000" continue = "true"/>
                </decision_entry>
                <!--Targets-->
                <decision_entry>
                    <min_entry most_desirable = "true"/>
                    <faction_attitude invade="invade_immediate" invade_priority = "25000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry strongest_neighbour = "true"/>
                    <faction_attitude invade="invade_start" invade_priority = "20000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry weakest_neighbour = "true"/>
                    <faction_attitude invade_priority = "7500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry borders_all_our_regions = "true"/>
                    <faction_attitude invade_priority = "30000" continue = "true"/>
                </decision_entry>
                <!--Enemies Of Enemy-->
                <decision_entry>
                    <max_entry target_num_enemies = "1" num_enemies = "3"/>
                    <faction_attitude invade_priority = "25000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry target_num_enemies = "2"/>
                    <max_entry target_num_enemies = "3"/>
                    <faction_attitude invade_priority = "20000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry most_desirable = "false"/>
                    <max_entry target_num_enemies = "4" strongest_neighbour = "false"/>
                    <faction_attitude invade_priority = "17500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry most_desirable = "false" strongest_neighbour = "false"/>
                    <max_entry target_num_enemies = "5"/>
                    <faction_attitude invade_priority = "7500" continue = "true"/>
                </decision_entry>
                <!--Own Enemies-->
                <decision_entry>
                    <max_entry num_enemies = "1"/>
                    <faction_attitude invade_priority = "30000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry num_enemies = "2"/>
                    <faction_attitude invade_priority = "25000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry num_enemies = "3"/>
                    <faction_attitude invade_priority = "-7500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <max_entry num_enemies = "4"/>
                    <faction_attitude invade_priority = "-10000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry num_enemies = "5"/>
                    <faction_attitude invade_priority = "-15000" continue = "true"/>
                </decision_entry>
    This code is for the specific 'enemies' that you want a faction to focus on. (you can also design neutral or allied stance decisions, but this is for during a war)
    Code:
                <!--@War Religous and Specials -->
                <decision_entry>
                    <min_entry stance = "AtWar" target_faction = "f_carthage"/>
                    <max_entry stance = "AtWar" target_faction = "f_carthage"/>
                    <faction_attitude force_invade = "true" invade = "invade_immediate" invade_priority = "30000" pts_alliance = "-30" pts_desire="60" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" target_faction = "f_epeiros"/>
                    <max_entry stance = "AtWar" target_faction = "f_epeiros"/>
                    <faction_attitude  force_invade = "true" invade = "invade_start" invade_priority = "30000" pts_alliance = "-30" pts_desire="30" continue = "true"/>
                </decision_entry>
    Some invasion decision rules, they're rather necessary to ensure the correct strength is applied during an invasion.

    Code:
                <!--@War Frontline Balance (neighbor)-->
                <decision_entry>
                    <min_entry stance = "AtWar" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" frontline_balance = "0.99" free_strength_balance = ".50"/>
                    <faction_attitude invade = "invade_buildup" invade_priority = "-10000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" frontline_balance = "1.01" free_strength_balance = "0.51" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" frontline_balance = "1.5"/>
                    <faction_attitude invade = "invade_buildup" invade_priority = "7500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" frontline_balance = "1.5" free_strength_balance = ".80" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" frontline_balance = "1.99"/>
                    <faction_attitude invade = "invade_start" invade_priority = "7750" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" frontline_balance = "2.0" free_strength_balance = ".90" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" frontline_balance = "2.99"/>
                    <faction_attitude invade = "invade_start" invade_priority = "8000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" frontline_balance = "3.0" free_strength_balance = "1.0" is_neighbour = "true"/>
                    <faction_attitude invade = "invade_immediate" invade_priority = "9000" continue = "true"/>
                </decision_entry>
                <!--@War Final Invasion Land  -->
                <decision_entry>
                    <min_entry stance = "AtWar"/>
                    <max_entry stance = "AtWar" military_balance = "0.32" free_strength_balance = ".32" is_neighbour = "true"/>
                    <faction_attitude invade = "invade_buildup" invade_priority = "7500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "0.33"/>
                    <max_entry stance = "AtWar" military_balance = "0.50" free_strength_balance = ".50" is_neighbour = "true"/>
                    <faction_attitude invade = "invade_opportunistic" invade_priority = "7750" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "0.51" free_strength_balance = ".65" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" military_balance = "0.99"/>
                    <faction_attitude invade = "invade_start" invade_priority = "8500" continue = "true" is_neighbour = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "1.5" free_strength_balance = "1.00" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" military_balance = "1.99"/>
                    <faction_attitude invade = "invade_immideate" invade_priority = "20000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar"/>
                    <max_entry stance = "AtWar"/>
                    <faction_attitude can_force_invade = "true" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "2.00" free_strength_balance = "1.25" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" military_balance = "2.99"/>
                    <faction_attitude invade = "invade_start" force_invade = "false" invade_priority = "17500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "3.0" free_strength_balance = "1.60" is_neighbour = "true"/>
                    <max_entry stance = "AtWar" military_balance = "3.99"/>
                    <faction_attitude invade = "invade_immediate" force_invade = "true" invade_priority = "18000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "4.0" free_strength_balance = "2.0" is_neighbour = "true"/>
                    <faction_attitude invade = "invade_immediate" force_invade = "true" invade_priority = "25000" continue = "true"/>
                </decision_entry>
                <!--@War Final Sea Invasion -->
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "1.5" free_strength_balance = "1.25" is_neighbour = "false"/>
                    <max_entry stance = "AtWar" military_balance = "1.99" is_neighbour = "false"/>
                    <faction_attitude invade = "invade_buildup" force_invade = "false" invade_priority = "16500" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "2.00" free_strength_balance = "1.5" is_neighbour = "false"/>
                    <max_entry stance = "AtWar" military_balance = "2.99" is_neighbour = "false"/>
                    <faction_attitude invade = "invade_opportunistic" force_invade = "true" invade_priority = "17000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "3.0" free_strength_balance = "2.0" is_neighbour = "false"/>
                    <max_entry stance = "AtWar" military_balance = "3.99" is_neighbour = "false"/>
                    <faction_attitude invade = "invade_start" force_invade = "true" invade_priority = "18000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" military_balance = "4.0" free_strength_balance = "3.0" is_neighbour = "false"/>
                    <max_entry stance = "AtWar" is_neighbour = "false"/>
                    <faction_attitude invade = "invade_immediate" force_invade = "true" invade_priority = "19000" continue = "true"/>
                </decision_entry>
                <decision_entry>
                    <min_entry stance = "AtWar" is_neighbour = "true"/>
                    <faction_attitude invade_priority = "7500"/>
                </decision_entry>
                <decision_entry>
                    <!--turn off forced invasion -->
                    <faction_attitude can_force_invade = "false" continue = "true"/>
                </decision_entry>
    There's more code but I'll stop there, as this is simply meant to give ideas for what you can do with faction specific decisions.
    As a closing note, CA pointed out in their file that you can switch AI labels mid game via script commands or console
    Additionally, the ai_labels can be tested through event conditions and set through
    // a script command, potentially allowing ai behaviour to be changed dynamically in game depending on current game state.
    Which if applied correctly could allow for the recreation of historical wars during specific dates for example or other more ambitious plans. So again I highly reccomend reading through CA's descr campaign ai db, they explain everything quite well and their examples are something worth checking out.

    We could probably create faction specific BAI's but I dropped that idea because while technically feasible there wouldn't be a point but maybe one day someone else will be able to use that idea. (also note CA's 'variations' code that exists in their config ai battle, probably specifically for that purpose)


    Custom BAI Tactics

    Within the config battle ai we can write new tactics like this for example. Keep in mind it's relatively new code, so it's unpolished and simply designed by myself to confirm and show as an example that this is possible.

    What this code does is force the AI when using ladders to run with them towards the walls if they feel their missile ratio is less than 40% greater than their enemies.
    This tactic is also meant to make all the troops attack the walls and take them over. Instead of running to the plaza as normal

    The video contains footage of these tactics in action, and the fact that it's somewhat unpolished. Additionally what doesn't help is that this was tested on settlements that don't have fully functional pathfinding yet.
    https://vid.me/a12n


    Code:
                    <assault-walls>
                    <!-- target ratio of our forces to enemy forces. > 1 means outnumber the enemy, < 1 means enemy outnumbers us -->
                    <strength-ratio>1.3</strength-ratio>
                    <!-- attack the top x defense points. sorted by descending threat -->
                    <assignment>
                        <!-- attack the top x surfaces. sorted by descending assault threat -->
                        <surfaces>4</surfaces>
                    </assignment>
    
    
                    
                    <street-position>
                        <!-- base threat level possessed by street positions - used to enforce an initial spread of units -->
                        <base-threat>125.0</base-threat>
                    </street-position>
    
    
                    
                        <ladder>
                        
                        <resourcing>
                            <!-- maximum number of units that can be assigned -->
                            <max-units>8</max-units>
                            
                            <!-- amount of threat a single unit can counter (controls how many units will get assigned) -->
                            <threat-per-unit>25</threat-per-unit>
                        </resourcing>
                        
                        <phases>
                            <!-- phase 1 - running with the ladder to the walls -->
                            <phase1>
                            
                                <!-- configuring the conditions for entering this phase -->                            
                                <entry-conditions>
                                    <!-- ratio of current to initial melee strength -->
                                    <melee-remaining>1.0</melee-remaining>
    
    
                                    <!-- outshoot ratio between enemy and us. Test: [enemy strength] > [ally strength] * [ratio] -->
                                    <outshoot-ratio>1.4</outshoot-ratio>
                                </entry-conditions>
                            
                                <!-- run if closer than this distance (in metres squared) -->
                                <run-threshold>400000</run-threshold>
                            </phase1>
                            
                            <!-- phase 2 - in case of artillery or heavy fire -->
                            <phase2>
                            
                                <!-- configuring the conditions for entering this phase -->                            
                                <entry-conditions>
                                    <!-- ratio of current to initial melee strength -->
                                    <melee-remaining>0.75</melee-remaining>
    
    
                                    <!-- outshoot ratio between enemy and us. Test: [enemy strength] > [ally strength] * [ratio] -->
                                    <outshoot-ratio>1.1</outshoot-ratio>
                                </entry-conditions>
                            
                                <!-- run if closer than this distance (in metres squared) -->
                                <run-threshold>400000</run-threshold>
                            </phase2>
                        </phases>
                        </ladder>
                        
                        <tower>
    
    
                        <resourcing>
                            <!-- maximum number of units that can be assigned -->
                            <max-units>8</max-units>
                            
                            <!-- amount of threat a single unit can counter (controls how many units will get assigned) -->
                            <threat-per-unit>25</threat-per-unit>
                        </resourcing>
                        
                        </tower>
                        
                    </assault-walls>
    I've also applied a similar type of tactic within the attack-battlegroup element as a sub element containing the tactic.
    https://vid.me/Oef0

    The code is meant to enhance the AI's ability to assault the hill by getting the troops to run if they have less than 90% of their starting melee strength. The entry conditions being, if their missile strength is not greater than their enemies by 40%.

    Code:
            <attack-hill>
                <tactics>
                    <assault>
                        <!-- minimum number of melee units for this task -->
                        <melee-ratio>1.2</melee-ratio>
                        
                        <!-- tick limit on being stalled -->
                        <stall-limit>100</stall-limit>
                        
                        <!-- per phase configuration -->
                        <phases>
                            <!-- phase 1 - running up the hill -->
                            <phase1>
                            
                                <!-- configuring the conditions for entering this phase -->                            
                                <entry-conditions>
                                    <!-- ratio of current to initial melee strength -->
                                    <melee-remaining>0.9</melee-remaining>
    
    
                                    <!-- outshoot ratio between enemy and us. Test: [enemy strength] > [ally strength] * [ratio] -->
                                    <outshoot-ratio>1.4</outshoot-ratio>
                                </entry-conditions>
                            
                                <!-- run if closer than this distance (in metres squared) -->
                                <run-threshold>400000</run-threshold>
                            </phase1>
    
    
                            <!-- phase 2 - in case of artillery -->
                            <phase2>
                            
                                <!-- configuring the conditions for entering this phase -->                            
                                <entry-conditions>
                                    <!-- ratio of current to initial melee strength -->
                                    <melee-remaining>0.75</melee-remaining>
    
    
                                    <!-- outshoot ratio between enemy and us. Test: [enemy strength] > [ally strength] * [ratio] -->
                                    <outshoot-ratio>1.1</outshoot-ratio>
                                </entry-conditions>
                            
                                <!-- run if closer than this distance (in metres squared) -->
                                <run-threshold>400000</run-threshold>
                            </phase2>
    
    
                            </phases>
                            </assault>
                </tactics>
                <plan>
                    <!-- time it should take units to approach the fight (seconds) -->
                    <approach-time>15</approach-time>
                    <!-- prioritize outflanking the enemy -->
                    <prioritise-outflanking>1</prioritise-outflanking>
                    <!-- deployment search increment (metres) -->
                    <deployment-search-inc>6</deployment-search-inc>
                </plan>
            </attack-hill>

    I've also written this to aid the AI and it's trouble with the reloading bug. However it doesn't fully fix the issue as no matter how many times the AI is ordered to attack, (or how many times you order your own unit to attack when stuck in a reloading stage) it won't change the fact that you're perpetually going to have a unit that can't get out of the reloading stage if some of their units are in melee. I believe this might only be fixed via the battle config but that file isn't as moddable as the config ai again likely due to different parsers/ways the exe works in regards to specific files. Nevertheless it is somewhat resolved.

    The code gets nested inside the <missile-analyser> element.

    What it's meant to do is detect when there is a stall between actions like reloading and firing and fix that. The 'ticks'/samples are processing cycles, so the test and solution happens pretty fast.

    Code:
            <stall-test>
                    <!-- minimum number of ticks to collect before detecting a stall -->
                    <minimum-samples>900</minimum-samples>
                    
                    <!-- track at most this number of samples -->
                    <maximum-samples>1200</maximum-samples>
                    
                    <!-- if the artillery has be inactive for at least 25% of its time -->
                    <limit>0.01</limit>
            </stall-test>
            
            <missiles_reloading>
                    <stall-limit>100</stall-limit>
                    <!-- minimum number of ticks to collect before detecting a stall -->
                    <minimum-samples>900</minimum-samples>
                    
                    <!-- track at most this number of samples -->
                    <maximum-samples>1200</maximum-samples>
            </missiles_reloading>

    According to the text files
    {BMT_AISA_AGGRESSIVE}Aggressive stance
    {BMT_AISA_AGGRESSIVE_SELECTED}Aggressive stance (active)
    {BMT_AISA_DEFENSIVE}Defensive stance
    {BMT_AISA_DEFENSIVE_SELECTED}Defensive stance (active)
    {BMT_AISA_SHOOTOUT}Shootout stance
    {BMT_AISA_SHOOTOUT_SELECTED}Shootout stance (active)

    The AI reinforcements are referred to as 'support army or armies'. Most likely armies due to there being the possibility for more than one. Therefore I wrote this code within the melee manager.

    This is meant to encourage the main army (managed both indirectly and directly by the melee manager) to work together with the support army.


    Code:
                <support-armies>
                    <approach-time>5</approach-time>
                </support-armies>
    This is an element on its own meant to encourage the AI to act on its own. I feel as if it's somewhat of a risk, after all there probably aren't any strings being read other than the root when you put it in your own element but then and again since the root ones are read with everything in between it will hopefully get interpreted.

    Anyway, from what tests I have done reinforcements/armies do seem to behave somewhat better. If you order them to defend a part of the terrain for example they'll often park themselves in place on a hill from the reports I've read and my own observations. So in entirely in its own element this does seem to work to an extent. I believe that the hardcode commands will override most of our own (from observation), which means certain induced behaviours such as the sally out code have hardcoded elements, no matter how hard I've tried sometimes I just can't change that. The algorithms take precedence and priority, perhaps one day I'll find an truly efficient way in all circumstances but for now this will have to be what I work from.

    Code:
            <support-armies>
                <plan>
                    <merge-friendly-armies>1</merge-friendly-armies>
                    <!-- consider up to the specified percentage of the force for reform -->
                    <consider-max-force>100</consider-max-force>
                    <!-- time it should take support units to approach the fight (seconds) -->
                    <approach-time>5</approach-time>
                </plan>
                <run-threshold>300000</run-threshold>
                            <reform-dist>0</reform-dist>
            </support-armies>
    Within the attack battlegroup element I've included this, in case it has an actual effect.

    Code:
            <attack-battlegroup>
                <tracking-tolerance>125.0</tracking-tolerance>
                <formed-percentage>20</formed-percentage>
                <formed-percentage-finished>66</formed-percentage-finished>
                <shootout-distance-tolerance>75</shootout-distance-tolerance>
            <prioritise-merge-support-armies>1</prioritise-merge-support-armies>

    Some definitions/notes about the way the battle AI works in regards to sieges and tactics in general.

    Perimeter is defined as a street/moveable area within the walls *(but not on the walls).

    Surfaces are defined as the actual walls themselves.

    Defense points can be placed on top of the walls as they are a moveable surface which the pathfinder can take into account, generally however these 'points' as the BAI interprets them also have a default position at the plaza.

    Each point/surface is sorted via a descending 'threat' , which is in fact a mechanism that interprets the numbers and strength of the troops in that area.
    It's worth noting that 'contribution' seems to be a sort of 'offensive threat' (although in a very primative form) which I very much want to implement as a type of offensive threat but that idea is still being processed. It's worth noting that both contribution and threat need a supporting definition for what type of threat/contribution the value is. For example 'strength-contribution' or 'distance-contribution'.


    Let's also take a quick look at this tactic switch limit.
    What this appears to be is a bit of math. The threat as we know it, seems to range on a scale of 0-100. It seems to act as a reference point as to what area of the battle contributes the most 'danger' to the defending units. Which is why I applied modified threat-assesment code to the defend-line element. We seem to be able to define the way threat itself is defined, via several different elements. We even have a way to resource the units via 'threat-per-unit', I like to think that this is solely a siege based mechanism rather than open battles. In which I would hope and presumed there is an internal unit 'matching' architecture/code of some sort.

    If the new tactics threat is less than the old (for example 1.0 divided by .5 = 2.0 or even 1.0 divided by .75 = 1.3333*repeated which might make more sense) means there is ,if we decide to think about it in percents, 100% threat vs 75%. Which means the BAI will choose the tactic which controls the most amount of threat.

    The way threat is countered was already mentioned, threat-per-unit. There's also a few other things that control it like the way threat fades, additionally points/surfaces are sorted via descending threat. So the greatest amount of threat gets the units first and then the lesser.

    Code:
                            <!-- allow stealing if (new tactic threat / old tactic threat) is this greater than this ratio -->
                            <tactic-switch-limit>1.3</tactic-switch-limit>
    The way I confirmed these things are via tests and even a console command I couldn't find record of anyone trying on the battlemap before. "show_cursorstat" actually gives you a lengthy amount of code.


    There are seemingly two zone ID's (presumably related to moveable areas for the pathfinder, one is the normal walking areas like land and roads. Zone ID 2 is the walls themselves. Zone ID 0 is a building for example which is invalid. Interestingly enough and again giving me this idea, is the fact that siege towers are also confirmed as Zone ID 2 as a corresponding confirmation Zone ID 2 refers to walls.

    Spoiler Alert, click show to read: 





    Here's a view of the perimeter being 'inside' the walls

    Spoiler Alert, click show to read: 




    We can also see several other important details related to climates.

    BAI Pathfinding Scheduler/Node Info

    To start off with, you should already know how the grid works from the above tutorial however if you haven't seen it yet I'll explain here.

    Spoiler Alert, click show to read: 




    The above picture indicates the existence of a grid, this grid according to the three numbers after position has a layout of x being the first number, height being the middle number and y being the last number.

    The middle point on this "graph" would be the plaza and center of the battlefield.

    Here is a picture of the 0,0 point on the battlefield. Although you cannot see my cursor note the position of the camera on the minimap (upper right) and imagine my cursor somewhere within the circle and the rectangle. I can also confirm that the plaza contains the 0,0 point.

    Spoiler Alert, click show to read: 




    Thanks to this graph, there is a need for a scheduler for the pathfinder. Each time a path is plotted, it requires resources. The scheduler apparently determines the amount of resources that go into the pathfinders efforts. Which in turn makes for different behaviour.

    Here is a good explanation of how the "grid" and resolutions work. And I assume this is what they're talking about in regards to 'low/medium/high', the resolution. There is also one on havard.edu which I used however it doesn't give as in depth or illustrative of an explanation as that book does.

    This sites information is also worth noting, seemingly confirming my initial theory about x/h/y and the graph technique used by the pathfinder.

    Additionally in case I have not made it clear, while I do frequently mention that pathfinding is used by the BAI it is also (obviously perhaps) used by the player as well. Whenever we click on any area on the map the pathfinder starts to work, trying to figure out a viable route via the information detailed in the file. So if you, decide to make the costs for forests much less in addition to height the pathfinder will choose that route as it 'costs' less. Anotherwords, it doesn't just affect the BAI, it also passively affects the player as well.

    Notes About CA's AI Methodology

    CA seems to follow the random number principle (randomness in AI behaviour and otherwise) to an extent.

    Spoiler Alert, click show to read: 

    Quote Originally Posted by Argantonio View Post
    The time of study, offers his results:
    I present to you the "random numbers generators" of MTWII.
    I have identified four, I will show in this post who works , one of them.
    First a bit of theory:
    All the events of the game they seem to be associate to variability.
    The composition of every soldier of a unit, The animation action of every individual soldier, the decisions in campaign-mode , the own AI of the game.

    These variabilities are produced by computation algorithms , and determine all the aspects of game.
    subroutine 01127EA0 , and controls between other many things, the assign the different mesh groups of a individual soldier , for to the soldiers seem to be different.
    True .... remenber
    Unit_Model file mesh
    mesh type example Head
    mesh groups ex. Head_12 Head_13 Head_14

    The mechanisms of generation of random numbers that are in use in the majority of the Informatic systems are actually pseudo-random processes
    http://en.wikipedia.org/wiki/Random_number_generation
    http://en.wikipedia.org/wiki/Pseudor...mber_generator
    http://en.wikipedia.org/wiki/Linear_...tial_generator
    http://en.wikipedia.org/wiki/Pseudorandom_numbers

    A pseudo-random number is a number generated in a process that seems to produce numbers at random, but it does not do it really. The sequences of pseudo-random numbers do not show any regularity from a statistical point of view, in spite of having being generated by a completely deterministic algorithm, in which the same initial conditions produce always the same result.


    I present to you a small precious.

    01127EA0 / 8B01 MOV EAX,DWORD PTR DS:[ECX] (guessed Arg1,Arg2)
    01127EA2 69C0 FD430300 IMUL EAX,EAX,343FD
    01127EA8 05 C39E2600 ADD EAX,269EC3
    01127EAD |. 8901 MOV DWORD PTR DS:[ECX],EAX
    01127EAF |. 8B4C24 08 MOV ECX,DWORD PTR SS:[ARG.2]
    01127EB3 |. C1F8 10 SAR EAX,10
    01127EB6 |. 56 PUSH ESI
    01127EB7 |. 8B7424 08 MOV ESI,DWORD PTR SS:[ARG.1]
    01127EBB |. 25 FF7F0000 AND EAX,00007FFF
    01127EC0 |. 2BCE SUB ECX,ESI
    01127EC2 |. 83C1 01 ADD ECX,1
    01127EC5 |. 0FAFC8 IMUL ECX,EAX
    01127EC8 |. 83E9 01 SUB ECX,1
    01127ECB |. B8 03000180 MOV EAX,80010003
    01127ED0 |. F7E9 IMUL ECX
    01127ED2 |. 03D1 ADD EDX,ECX
    01127ED4 |. C1FA 0E SAR EDX,0E
    01127ED7 |. 8BC2 MOV EAX,EDX
    01127ED9 |. C1E8 1F SHR EAX,1F
    01127EDC |. 03C2 ADD EAX,EDX
    01127EDE |. 03C6 ADD EAX,ESI
    01127EE0 |. 5E POP ESI
    01127EE1 \. C2 0800 RETN 8


    From a seed, it realizes a multiplication and a sum, and there assigns an entire positive number, that this in the range that we determine. ( 3 Heads 0,1,2 Arg1, 0 Arg2, 2)
    In the memory the mesh groups are placed arranged in mesh type position, since the drawing shows, the subroutine generates a number, and this will be the chosen mesh groups of a model.
    Attachment 103709
    By means of a Debuger ( Ollydebug ) I can see the assignation process step to step and assign the mesh groups that I wish for every individual soldier.

    I propose a simple exercise , we modify the algorithm so that it multiplies for 0 and adds 0, the result of generated number will be always 0.
    Attachment 103710Attachment 103711Attachment 103713
    The individual soldiers are equal composed on mesh groups that first appears in the file mesh of every mesh type
    The composition of trebizond_archers_ug1_lod0.mesh are
    Hands Hands01 Arms Arms 1 Body Body_04 Legs Legs_06 Head Head_08
    Helmet helmet_06Helmet Object01Hands Hands 2Head Head_12
    Head Head_13Head Head_14Arms Arms 02 Arms Arms 03Legs Legs_09
    Legs Legs_10Body Body_15 Body Body_16secondaryactive0 sword secondary_3 sword
    secondary_4 sword secondary_5secondaryactive1 sword secondary_9 sword secondary_10
    sword secondary_11shield0 small round plain_18 round plain_19 small round plain_20
    small round plain_21 small round plain_22 small round plain_23 small round plain_24
    small round plain_25 small round plain_26 small round plain_27primaryactive0
    composite bow_40 composite bow_41 composite bow_42equipment0 quiver_67
    quiver_68 quiver_69

    with random number 0 , all soldier are
    Hands Hands01 Arms Arms 1 Body Body_04
    Legs Legs_06 Head Head_08 Helmet helmet_06
    secondaryactive0 sword secondary_3 shield0 small round plain_18
    primaryactive0 composite bow_40 equipment0 quiver_67


    Attachment 103714

    But that's not all, also controls the Non-Combat animations (stand, taunt) all soldiers do the same.
    His control is subroutine 01127EA0
    Attachment 103717
    All soldiers animated by MTW2_Musket_taunt_1.cas

    I'm sure that the success or failure in combat, the effectiveness of an arrow impact are guided by these or similar mechanisms, do you find interesting?


    What this means is that not everything can be perfectly/accurately predicted as to the exact behaviour the AI will take during a battle. This is both good and bad, personally I wish to control unit based actions/decisions to a very great degree.


    I'd be willing to believe they also have certain thresholds/adopted behaviours with random factors for adoption, construction and recruitment based on the information about Rome II's AI. Even though it's important to note that the AI has a preference for units which have a larger recruitment priority than another. I'll state again that the recruit priority seems to scale on a range of negative 100 to positive 100. With other factors being the financial situation and recruitment pool options alongside its current orders/priorities within the CAI decision file. It could very well be that there are other inherent factors I am missing like a hidden preference towards siege and naval units.

    If AI adoptions happen in a similar way compared to the player, and they seem too as some factions die out via stagnation. The adoptions for the AI probably have an age threshold (for which the AI will not 'adopt' characters) and perhaps a preference for command stars.

    Notes about CA's CAI pathfinder Methodology


    Hopefully they don't mind me making this sort of hypothesis but here it is. In fact, after a certain amount of testing over the course of several months.

    The map generation process involves the map.rwm, alongside several other CAI aspects/game mechanics related to the CAI + map. This is basic information most every modder learns at one point or another.

    However, some lesser known information is referenced in the tutorial above about the CAI pathfinder. The fact that there is a value in the descr_character file which controls the amount of calculations the pathfinder is allowed when making invasion/defensive decisions, alongside merging its troops within its empire.

    This value can also be set to 0, which vastly speeds up the map.rwm generation process. This is because all of the map info/terrain is then read by the pathfinder which uses it as a future reference. If you've ever wondered why we can change the map at all and still have the pathfinder work, that's why. The map.rwm plays a much more 'significant' role than previously imagined in regards to the exe based functions.

    However, if set to 0, the pathfinder will stop working and in turn the CAI will cease functioning as well as the two are inherently one and the same, simply being a different facet of the other. In the end they're all calculations/functions which contribute towards how the "AI" behaves. Which means you should definitely set this value back to your original one once you're done testing (values higher/past 500 will cause a significantly longer generation process, even while aiding the CAI in the game itself).
    Last edited by z3n; December 21, 2015 at 11:54 PM.
    The AI Workshop Creator
    Europa Barbaroum II AI/Game Mechanics Developer
    The Northern Crusades Lead Developer
    Classical Age Total War Retired Lead Developer
    Rome: Total Realism Animation Developer
    RTW Workshop Assistance MTW2 AI Tutorial & Assistance
    Broken Crescent Submod (M2TW)/IB VGR Submod (BI)/Animation (RTW/BI/ALX)/TATW PCP Submod (M2TW)/TATW DaC Submod (M2TW)/DeI Submod (TWR2)/SS6.4 Northern European UI Mod (M2TW)

  3. #3
    Withwnar's Avatar Script To The Waist
    Join Date
    Oct 2008
    Location
    Earth
    Posts
    6,329

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Very thorough. Thank you.

  4. #4

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Thank you so much. AI and balancing are the last but very important bit in mod development. This will come handy soon enough.

    Sandy
    Last edited by G|I|Sandy; September 28, 2015 at 01:35 AM.

  5. #5
    Gigantus's Avatar I am not special - I am a limited edition.
    Patrician Moderator Emeritus Administrator Emeritus

    Join Date
    Aug 2006
    Location
    Goa - India
    Posts
    53,068
    Blog Entries
    35

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Marvelous work!










  6. #6
    Aneirin's Avatar of flowing verse
    Join Date
    Nov 2012
    Location
    Gododdin
    Posts
    2,734

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Just awesome!
    Proud son of Aikanár and brother of Iskar

  7. #7

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Great job!

  8. #8
    z3n's Avatar State of Mind
    Moderator Emeritus

    Join Date
    Aug 2011
    Posts
    4,640

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    A few updates, campaign pathfinding and AI decisions based on pathfinding further explained with some new pictures that may give insight to how things work in the exe.
    Explanation on how to create a custom AI label and ideas on what you can do with it. An explanation on how to create custom BAI tactics and videos showing how it works.

    edit: I forgot to mention I've also included an explanation to do with the battle config and how to use gunpowder labels and the battle config to distinguish skirmishing distances for horse archers versus javelin cavalry for example. Based on an educated guess, (as it stands to reason) you can alternatively do it with infantry as well to differentiate the distance archers skirmish versus javelins.
    Last edited by z3n; October 27, 2015 at 11:09 PM.
    The AI Workshop Creator
    Europa Barbaroum II AI/Game Mechanics Developer
    The Northern Crusades Lead Developer
    Classical Age Total War Retired Lead Developer
    Rome: Total Realism Animation Developer
    RTW Workshop Assistance MTW2 AI Tutorial & Assistance
    Broken Crescent Submod (M2TW)/IB VGR Submod (BI)/Animation (RTW/BI/ALX)/TATW PCP Submod (M2TW)/TATW DaC Submod (M2TW)/DeI Submod (TWR2)/SS6.4 Northern European UI Mod (M2TW)

  9. #9
    z3n's Avatar State of Mind
    Moderator Emeritus

    Join Date
    Aug 2011
    Posts
    4,640

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    2nd post now contains updated and more in depth information about the pathfinder.
    The AI Workshop Creator
    Europa Barbaroum II AI/Game Mechanics Developer
    The Northern Crusades Lead Developer
    Classical Age Total War Retired Lead Developer
    Rome: Total Realism Animation Developer
    RTW Workshop Assistance MTW2 AI Tutorial & Assistance
    Broken Crescent Submod (M2TW)/IB VGR Submod (BI)/Animation (RTW/BI/ALX)/TATW PCP Submod (M2TW)/TATW DaC Submod (M2TW)/DeI Submod (TWR2)/SS6.4 Northern European UI Mod (M2TW)

  10. #10
    Cesco's Avatar Decanus
    Join Date
    Apr 2013
    Location
    Italia
    Posts
    595

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Very interesting subject (even though i have some problem in completely understanding it)

    Do you think it would be possible to make units desengage from melee without substaining casualities (or with very few casualties)?...maybe via a script reducing unit radious to 0 during the actual disengagement.
    I think this feature could add more depth to battles, as you'd have more tactics and options (for example romans used to relief front units)
    Last edited by Cesco; November 26, 2015 at 05:46 PM.
    Huic ab adulescentia bella intestina, caedes, rapinae, discordia civilis grata fuerunt ibique iuventutem suam exercuit

  11. #11
    z3n's Avatar State of Mind
    Moderator Emeritus

    Join Date
    Aug 2011
    Posts
    4,640

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Sorry about the late reply, haven't had much time lately.
    I can't think of a way as of yet and I'll be writing a tutorial about battle scripting soon which might help in understanding it. Here's a work in progress script if you're interested, the "AI Unit Monitors & AI Reactions" section might be what you're looking for.

    As to a quick explanation of how it all works, basically there's four simple principles. 1) Unit Labels & AI Labels (located at the top) 2) Counters (which act as a 'yes' and a 'no' for conditions) 3) Conditions (which means exactly what that means) 4) Commands (again same thing).

    There are a few finer points as to putting it all together, and basis of formatting etc... I'm still experimenting with it myself but an immediate idea that 'may' work, is giving a unit increased experience if it's being attacked in the rear allowing for it to likely withdraw without routing although making double envelopments less useful. Maybe you will think of something else.

    I suggest you check the docudemon files for the full list of possible conditions/commands. If you look through them you will no doubt have ideas I didn't.
    http://www.twcenter.net/forums/showt...Docudemons-4-0

    I really hope you experiment with it as not many do and like all modding it might be trial and error. Worth it in the end though.


    *Minor warning, apparently this script doesn't work for everyone according to internal testing... I'll update it eventually with one that works for everyones processors.
    Code:
    ; *********************************************************************************************
    ;    EBII Battle Scripts
    ;    11/28/2015
    ;    z3n
    ; *********************************************************************************************
    
    
    script
    
    
            
    ;;;;*************************************************************************************************
    ;;;;    Initialize Battle
    ;;;;*************************************************************************************************
    
    
    prepare_for_battle
    
    
    while ! I_BattleStarted
    end_while
    
    
    log always EBII Battle Scripts 
    log always Activated
    
    
    ;;;;*************************************************************************************************
    ;;;;    Counters
    ;;;;*************************************************************************************************
    
    
    declare_counter AI_reinforcements
    declare_counter AI_GRP
    declare_counter AI_GDEP
    declare_counter AI_GFP
    declare_counter release_units
    declare_timer AI_reinforcements_timer
    declare_timer rout_timer
    suspend_unscripted_advice true
    
    
    ;;;;*************************************************************************************************
    ;;;;    Create Labels
    ;;;;*************************************************************************************************
    
    
    ; *********************************************************************************************
    ;    Allied Unit Labels
    ; *********************************************************************************************
    
    
        label_unit 0 0 0 MAu1
        ai_gta_add_unit_label 0 0 MAu1
        define_unit_group Allies MAu1
    
    
        label_unit 0 0 1 MAu2
        ai_gta_add_unit_label 0 0 MAu2
        define_unit_group Allies MAu2
    
    
        label_unit 0 0 2 MAu3
        ai_gta_add_unit_label 0 0 MAu3
        define_unit_group Allies MAu3
    
    
        label_unit 0 0 3 MAu4
        ai_gta_add_unit_label 0 0 MAu4
        define_unit_group Allies MAu4
    
    
        label_unit 0 0 4 MAu5
        ai_gta_add_unit_label 0 0 MAu5
        define_unit_group Allies MAu5
    
    
        label_unit 0 0 5 MAu6
        ai_gta_add_unit_label 0 0 MAu6
        define_unit_group Allies MAu6
    
    
        label_unit 0 0 6 MAu7
        ai_gta_add_unit_label 0 0 MAu7
        define_unit_group Allies MAu7
    
    
        label_unit 0 0 7 MAu8
        ai_gta_add_unit_label 0 0 MAu8
        define_unit_group Allies MAu8
    
    
        label_unit 0 0 8 MAu9
        ai_gta_add_unit_label 0 0 MAu9
        define_unit_group Allies MAu9
    
    
        label_unit 0 0 9 MAu10
        ai_gta_add_unit_label 0 0 MAu10
        define_unit_group Allies MAu10
    
    
        label_unit 0 0 10 MAu11
        ai_gta_add_unit_label 0 0 MAu11
        define_unit_group Allies MAu11
    
    
        label_unit 0 0 11 MAu12
        ai_gta_add_unit_label 0 0 MAu12
        define_unit_group Allies MAu12
    
    
        label_unit 0 0 12 MAu13
        ai_gta_add_unit_label 0 0 MAu13
        define_unit_group Allies MAu13
    
    
        label_unit 0 0 13 MAu14
        ai_gta_add_unit_label 0 0 MAu14
        define_unit_group Allies MAu14
    
    
        label_unit 0 0 14 MAu15
        ai_gta_add_unit_label 0 0 MAu15
        define_unit_group Allies MAu15
    
    
        label_unit 0 0 15 MAu16
        ai_gta_add_unit_label 0 0 MAu16
        define_unit_group Allies MAu16
    
    
        label_unit 0 0 16 MAu17
        ai_gta_add_unit_label 0 0 MAu17
        define_unit_group Allies MAu17
    
    
        label_unit 0 0 17 MAu18
        ai_gta_add_unit_label 0 0 MAu18
        define_unit_group Allies MAu17 MAu18
    
    
        label_unit 0 0 18 MAu19
        ai_gta_add_unit_label 0 0 MAu19
        define_unit_group Allies MAu19
    
    
        label_unit 0 0 19 MAu20
        ai_gta_add_unit_label 0 0 MAu20
        define_unit_group Allies MAu20
    
    
        label_unit 0 0 20 MAu21
        ai_gta_add_unit_label 0 0 MAu21
        define_unit_group Allies MAu21
    
    
        label_unit 0 0 21 MAu22
        ai_gta_add_unit_label 0 0 MAu22
        define_unit_group Allies MAu22
    
    
        label_unit 0 0 22 MAu23
        ai_gta_add_unit_label 0 0 MAu23
        define_unit_group Allies MAu23
    
    
        label_unit 0 0 23 MAu24
        ai_gta_add_unit_label 0 0 MAu24
        define_unit_group Allies MAu24
    
    
        label_unit 0 0 24 MAu25
        ai_gta_add_unit_label 0 0 MAu25
        define_unit_group Allies MAu25
    
    
        label_unit 0 0 25 MAu26
        ai_gta_add_unit_label 0 0 MAu26
        define_unit_group Allies MAu26
    
    
        label_unit 0 0 26 MAu27
        ai_gta_add_unit_label 0 0 MAu27
        define_unit_group Allies MAu27
    
    
        label_unit 0 0 27 MAu28
        ai_gta_add_unit_label 0 0 MAu28
        define_unit_group Allies MAu28
    
    
        label_unit 0 0 28 MAu29
        ai_gta_add_unit_label 0 0 MAu29
        define_unit_group Allies MAu29
    
    
        label_unit 0 0 29 MAu30
        ai_gta_add_unit_label 0 0 MAu30
        define_unit_group Allies MAu30
    
    
        label_unit 0 0 30 MAu31
        ai_gta_add_unit_label 0 0 MAu31
        define_unit_group Allies MAu31
    
    
        label_unit 0 0 31 MAu32
        ai_gta_add_unit_label 0 0 MAu32
        define_unit_group Allies MAu32
    
    
        label_unit 0 0 32 MAu33
        ai_gta_add_unit_label 0 0 MAu33
        define_unit_group Allies MAu33
    
    
        label_unit 0 0 33 MAu34
        ai_gta_add_unit_label 0 0 MAu34
        define_unit_group Allies MAu34
    
    
        label_unit 0 0 34 MAu35
        ai_gta_add_unit_label 0 0 MAu35
        define_unit_group Allies MAu35
    
    
        label_unit 0 0 35 MAu36
        ai_gta_add_unit_label 0 0 MAu36
        define_unit_group Allies MAu36
    
    
        label_unit 0 0 36 MAu37
        ai_gta_add_unit_label 0 0 MAu37
        define_unit_group Allies MAu37
    
    
        label_unit 0 0 37 MAu38
        ai_gta_add_unit_label 0 0 MAu38
        define_unit_group Allies MAu38
    
    
        label_unit 0 0 38 MAu39
        ai_gta_add_unit_label 0 0 MAu39
        define_unit_group Allies MAu39
    
    
        label_unit 0 0 39 MAu40
        ai_gta_add_unit_label 0 0 MAu40
        define_unit_group Allies MAu40
    
    
    ; *********************************************************************************************
    ;    Enemy Unit Labels
    ; *********************************************************************************************
    
    
        label_unit 1 0 0 Eu1
        ai_gta_add_unit_label 1 0 Eu1
        define_unit_group Enemy Eu1
    
    
        label_unit 1 0 1 Eu2
        ai_gta_add_unit_label 1 0 Eu2
        define_unit_group Enemy Eu2
    
    
        label_unit 1 0 2 Eu3
        ai_gta_add_unit_label 1 0 Eu3
        define_unit_group Enemy Eu3
    
    
        label_unit 1 0 3 Eu4
        ai_gta_add_unit_label 1 0 Eu4
        define_unit_group Enemy Eu4
    
    
        label_unit 1 0 4 Eu5
        ai_gta_add_unit_label 1 0 Eu5
        define_unit_group Enemy Eu5
    
    
        label_unit 1 0 5 Eu6
        ai_gta_add_unit_label 1 0 Eu6
        define_unit_group Enemy Eu6
    
    
        label_unit 1 0 6 Eu7
        ai_gta_add_unit_label 1 0 Eu7
        define_unit_group Enemy Eu7
    
    
        label_unit 1 0 7 Eu8
        ai_gta_add_unit_label 1 0 Eu8
        define_unit_group Enemy Eu8
    
    
        label_unit 1 0 8 Eu9
        ai_gta_add_unit_label 1 0 Eu9
        define_unit_group Enemy Eu9
    
    
        label_unit 1 0 9 Eu10
        ai_gta_add_unit_label 1 0 Eu10
        define_unit_group Enemy Eu10
    
    
        label_unit 1 0 10 Eu11
        ai_gta_add_unit_label 1 0 Eu11
        define_unit_group Enemy Eu11
    
    
        label_unit 1 0 11 Eu12
        ai_gta_add_unit_label 1 0 Eu12
        define_unit_group Enemy Eu12
    
    
        label_unit 1 0 12 Eu13
        ai_gta_add_unit_label 1 0 Eu13
        define_unit_group Enemy Eu13
    
    
        label_unit 1 0 13 Eu14
        ai_gta_add_unit_label 1 0 Eu14
        define_unit_group Enemy Eu14
    
    
        label_unit 1 0 14 Eu15
        ai_gta_add_unit_label 1 0 Eu15
        define_unit_group Enemy Eu15
    
    
        label_unit 1 0 15 Eu16
        ai_gta_add_unit_label 1 0 Eu16
        define_unit_group Enemy Eu16
    
    
        label_unit 1 0 16 Eu17
        ai_gta_add_unit_label 1 0 Eu17
        define_unit_group Enemy Eu17
    
    
        label_unit 1 0 17 Eu18
        ai_gta_add_unit_label 1 0 Eu18
        define_unit_group Enemy Eu18
    
    
        label_unit 1 0 18 Eu19
        ai_gta_add_unit_label 1 0 Eu19
        define_unit_group Enemy Eu19
    
    
        label_unit 1 0 19 Eu20
        ai_gta_add_unit_label 1 0 Eu20
        define_unit_group Enemy Eu20
    
    
        label_unit 1 0 20 Eu21
        ai_gta_add_unit_label 1 0 Eu21
        define_unit_group Enemy Eu21
    
    
        label_unit 1 0 21 Eu22
        ai_gta_add_unit_label 1 0 Eu22
        define_unit_group Enemy Eu22
    
    
        label_unit 1 0 22 Eu23
        ai_gta_add_unit_label 1 0 Eu23
        define_unit_group Enemy Eu23
    
    
        label_unit 1 0 23 Eu24
        ai_gta_add_unit_label 1 0 Eu24
        define_unit_group Enemy Eu24
    
    
        label_unit 1 0 24 Eu25
        ai_gta_add_unit_label 1 0 Eu25
        define_unit_group Enemy Eu25
    
    
        label_unit 1 0 25 Eu26
        ai_gta_add_unit_label 1 0 Eu26
        define_unit_group Enemy Eu26
    
    
        label_unit 1 0 26 Eu27
        ai_gta_add_unit_label 1 0 Eu27
        define_unit_group Enemy Eu27
    
    
        label_unit 1 0 27 Eu28
        ai_gta_add_unit_label 1 0 Eu28
        define_unit_group Enemy Eu28
    
    
        label_unit 1 0 28 Eu29
        ai_gta_add_unit_label 1 0 Eu29
        define_unit_group Enemy Eu29
    
    
        label_unit 1 0 29 Eu30
        ai_gta_add_unit_label 1 0 Eu30
        define_unit_group Enemy Eu30
    
    
        label_unit 1 0 30 Eu31
        ai_gta_add_unit_label 1 0 Eu31
        define_unit_group Enemy Eu31
    
    
        label_unit 1 0 31 Eu32
        ai_gta_add_unit_label 1 0 Eu32
        define_unit_group Enemy Eu32
    
    
        label_unit 1 0 32 Eu33
        ai_gta_add_unit_label 1 0 Eu33
        define_unit_group Enemy Eu33
    
    
        label_unit 1 0 33 Eu34
        ai_gta_add_unit_label 1 0 Eu34
        define_unit_group Enemy Eu34
    
    
        label_unit 1 0 34 Eu35
        ai_gta_add_unit_label 1 0 Eu35
        define_unit_group Enemy Eu35
    
    
        label_unit 1 0 35 Eu36
        ai_gta_add_unit_label 1 0 Eu36
        define_unit_group Enemy Eu36
    
    
        label_unit 1 0 36 Eu37
        ai_gta_add_unit_label 1 0 Eu37
        define_unit_group Enemy Eu37
    
    
        label_unit 1 0 37 Eu38
        ai_gta_add_unit_label 1 0 Eu38
        define_unit_group Enemy Eu38
    
    
        label_unit 1 0 38 Eu39
        ai_gta_add_unit_label 1 0 Eu39
        define_unit_group Enemy Eu39
    
    
        label_unit 1 0 39 Eu40
        ai_gta_add_unit_label 1 0 Eu40
        define_unit_group Enemy Eu40
    
    
    ; *********************************************************************************************
    ;    FaW - Active
    ; *********************************************************************************************
    
    
    ; *********************************************************************************************
    ;    FaW - Player
    ; *********************************************************************************************
    
    
            unit_set_fire_at_will_mode MAu1 on
            unit_set_fire_at_will_mode MAu2 on
            unit_set_fire_at_will_mode MAu3 on
            unit_set_fire_at_will_mode MAu4 on
            unit_set_fire_at_will_mode MAu5 on
            unit_set_fire_at_will_mode MAu6 on
            unit_set_fire_at_will_mode MAu7 on
            unit_set_fire_at_will_mode MAu8 on
            unit_set_fire_at_will_mode MAu9 on
            unit_set_fire_at_will_mode MAu10 on
            unit_set_fire_at_will_mode MAu11 on
            unit_set_fire_at_will_mode MAu12 on
            unit_set_fire_at_will_mode MAu13 on
            unit_set_fire_at_will_mode MAu14 on
            unit_set_fire_at_will_mode MAu15 on
            unit_set_fire_at_will_mode MAu16 on
            unit_set_fire_at_will_mode MAu17 on
            unit_set_fire_at_will_mode MAu18 on
            unit_set_fire_at_will_mode MAu19 on
            unit_set_fire_at_will_mode MAu20 on
            unit_set_fire_at_will_mode MAu21 on
            unit_set_fire_at_will_mode MAu22 on
            unit_set_fire_at_will_mode MAu23 on
            unit_set_fire_at_will_mode MAu24 on
            unit_set_fire_at_will_mode MAu25 on
            unit_set_fire_at_will_mode MAu26 on
            unit_set_fire_at_will_mode MAu27 on
            unit_set_fire_at_will_mode MAu28 on
            unit_set_fire_at_will_mode MAu29 on
            unit_set_fire_at_will_mode MAu30 on
            unit_set_fire_at_will_mode MAu31 on
            unit_set_fire_at_will_mode MAu32 on
            unit_set_fire_at_will_mode MAu33 on
            unit_set_fire_at_will_mode MAu34 on
            unit_set_fire_at_will_mode MAu35 on
            unit_set_fire_at_will_mode MAu36 on
            unit_set_fire_at_will_mode MAu37 on
            unit_set_fire_at_will_mode MAu38 on
            unit_set_fire_at_will_mode MAu39 on
            unit_set_fire_at_will_mode MAu40 on
    
    
    ; *********************************************************************************************
    ;    FaW - AI
    ; *********************************************************************************************
    
    
            unit_set_fire_at_will_mode Eu1 on
            unit_set_fire_at_will_mode Eu2 on
            unit_set_fire_at_will_mode Eu3 on
            unit_set_fire_at_will_mode Eu4 on
            unit_set_fire_at_will_mode Eu5 on
            unit_set_fire_at_will_mode Eu6 on
            unit_set_fire_at_will_mode Eu7 on
            unit_set_fire_at_will_mode Eu8 on
            unit_set_fire_at_will_mode Eu9 on
            unit_set_fire_at_will_mode Eu10 on
            unit_set_fire_at_will_mode Eu11 on
            unit_set_fire_at_will_mode Eu12 on
            unit_set_fire_at_will_mode Eu13 on
            unit_set_fire_at_will_mode Eu14 on
            unit_set_fire_at_will_mode Eu15 on
            unit_set_fire_at_will_mode Eu16 on
            unit_set_fire_at_will_mode Eu17 on
            unit_set_fire_at_will_mode Eu18 on
            unit_set_fire_at_will_mode Eu19 on
            unit_set_fire_at_will_mode Eu20 on
            unit_set_fire_at_will_mode Eu21 on
            unit_set_fire_at_will_mode Eu22 on
            unit_set_fire_at_will_mode Eu23 on
            unit_set_fire_at_will_mode Eu24 on
            unit_set_fire_at_will_mode Eu25 on
            unit_set_fire_at_will_mode Eu26 on
            unit_set_fire_at_will_mode Eu27 on
            unit_set_fire_at_will_mode Eu28 on
            unit_set_fire_at_will_mode Eu29 on
            unit_set_fire_at_will_mode Eu30 on
            unit_set_fire_at_will_mode Eu31 on
            unit_set_fire_at_will_mode Eu32 on
            unit_set_fire_at_will_mode Eu33 on
            unit_set_fire_at_will_mode Eu34 on
            unit_set_fire_at_will_mode Eu35 on
            unit_set_fire_at_will_mode Eu36 on
            unit_set_fire_at_will_mode Eu37 on
            unit_set_fire_at_will_mode Eu38 on
            unit_set_fire_at_will_mode Eu39 on
            unit_set_fire_at_will_mode Eu40 on
    
    
    ; *********************************************************************************************
    ;    Skirmish Mode - Active
    ; *********************************************************************************************
    
    
            unit_set_skirmish_mode Eu1 on
            unit_set_skirmish_mode Eu2 on
            unit_set_skirmish_mode Eu3 on
            unit_set_skirmish_mode Eu4 on
            unit_set_skirmish_mode Eu5 on
            unit_set_skirmish_mode Eu6 on
            unit_set_skirmish_mode Eu7 on
            unit_set_skirmish_mode Eu8 on
            unit_set_skirmish_mode Eu9 on
            unit_set_skirmish_mode Eu10 on
            unit_set_skirmish_mode Eu11 on
            unit_set_skirmish_mode Eu12 on
            unit_set_skirmish_mode Eu13 on
            unit_set_skirmish_mode Eu14 on
            unit_set_skirmish_mode Eu15 on
            unit_set_skirmish_mode Eu16 on
            unit_set_skirmish_mode Eu17 on
            unit_set_skirmish_mode Eu18 on
            unit_set_skirmish_mode Eu19 on
            unit_set_skirmish_mode Eu20 on
            unit_set_skirmish_mode Eu21 on
            unit_set_skirmish_mode Eu22 on
            unit_set_skirmish_mode Eu23 on
            unit_set_skirmish_mode Eu24 on
            unit_set_skirmish_mode Eu25 on
            unit_set_skirmish_mode Eu26 on
            unit_set_skirmish_mode Eu27 on
            unit_set_skirmish_mode Eu28 on
            unit_set_skirmish_mode Eu29 on
            unit_set_skirmish_mode Eu30 on
            unit_set_skirmish_mode Eu31 on
            unit_set_skirmish_mode Eu32 on
            unit_set_skirmish_mode Eu33 on
            unit_set_skirmish_mode Eu34 on
            unit_set_skirmish_mode Eu35 on
            unit_set_skirmish_mode Eu36 on
            unit_set_skirmish_mode Eu37 on
            unit_set_skirmish_mode Eu38 on
            unit_set_skirmish_mode Eu39 on
            unit_set_skirmish_mode Eu40 on
    
    
    ; *********************************************************************************************
    ;    AI Run & Shootout
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour < 2
    and not I_BattlePlayerArmyIsAttacker
    and I_BattlePlayerArmyPercentageOfUnitClass missile > 25.0
    
    
            unit_order_move_to_missile_range Eu1 MAu1 run
            unit_order_move_to_missile_range Eu2 MAu1 run
            unit_order_move_to_missile_range Eu3 MAu1 run
            unit_order_move_to_missile_range Eu4 MAu2 run
            unit_order_move_to_missile_range Eu5 MAu2 run
            unit_order_move_to_missile_range Eu6 MAu2 run
            unit_order_move_to_missile_range Eu7 MAu3 run
            unit_order_move_to_missile_range Eu8 MAu3 run
            unit_order_move_to_missile_range Eu9 MAu3 run
            unit_order_move_to_missile_range Eu10 MAu4 run
            unit_order_move_to_missile_range Eu11 MAu4 run
            unit_order_move_to_missile_range Eu12 MAu4 run
            unit_order_move_to_missile_range Eu13 MAu5 run
            unit_order_move_to_missile_range Eu14 MAu5 run
            unit_order_move_to_missile_range Eu15 MAu5 run
            unit_order_move_to_missile_range Eu16 MAu6 run
            unit_order_move_to_missile_range Eu17 MAu6 run
            unit_order_move_to_missile_range Eu18 MAu6 run
            unit_order_move_to_missile_range Eu19 MAu7 run
            unit_order_move_to_missile_range Eu20 MAu7 run
            unit_order_move_to_missile_range Eu21 MAu8 run
            unit_order_move_to_missile_range Eu22 MAu8 run
            unit_order_move_to_missile_range Eu23 MAu9 run
            unit_order_move_to_missile_range Eu24 MAu9 run
            unit_order_move_to_missile_range Eu25 MAu10 run
            unit_order_move_to_missile_range Eu26 MAu10 run
            unit_order_move_to_missile_range Eu27 MAu10 run
            unit_order_move_to_missile_range Eu28 MAu11 run
            unit_order_move_to_missile_range Eu29 MAu12 run
            unit_order_move_to_missile_range Eu30 MAu13 run
            unit_order_move_to_missile_range Eu31 MAu14 run
            unit_order_move_to_missile_range Eu32 MAu15 run
            unit_order_move_to_missile_range Eu33 MAu16 run
            unit_order_move_to_missile_range Eu34 MAu17 run
            unit_order_move_to_missile_range Eu35 MAu18 run
            unit_order_move_to_missile_range Eu36 MAu19 run
            unit_order_move_to_missile_range Eu37 MAu20 run
            unit_order_move_to_missile_range Eu38 MAu20 run
            unit_order_move_to_missile_range Eu39 MAu20 run
            unit_order_move_to_missile_range Eu40 MAu20 run
    
    
    terminate_monitor
    end_monitor
    
    
    ; *********************************************************************************************
    ;    Free At Last
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattleStarted
    
    
            release_unit Eu1
            release_unit Eu2
            release_unit Eu3
            release_unit Eu4
            release_unit Eu5
            release_unit Eu6
            release_unit Eu7
            release_unit Eu8
            release_unit Eu9
            release_unit Eu10
            release_unit Eu11
            release_unit Eu12
            release_unit Eu13
            release_unit Eu14
            release_unit Eu15
            release_unit Eu16
            release_unit Eu17
            release_unit Eu18
            release_unit Eu19
            release_unit Eu20
            release_unit Eu21
            release_unit Eu22
            release_unit Eu23
            release_unit Eu24
            release_unit Eu25
            release_unit Eu26
            release_unit Eu27
            release_unit Eu28
            release_unit Eu29
            release_unit Eu30
            release_unit Eu31
            release_unit Eu32
            release_unit Eu33
            release_unit Eu34
            release_unit Eu35
            release_unit Eu36
            release_unit Eu37
            release_unit Eu38
            release_unit Eu39
            release_unit Eu40
            release_unit MAu1
            release_unit MAu2
            release_unit MAu3
            release_unit MAu4
            release_unit MAu5
            release_unit MAu6
            release_unit MAu7
            release_unit MAu8
            release_unit MAu9
            release_unit MAu10
            release_unit MAu11
            release_unit MAu12
            release_unit MAu13
            release_unit MAu14
            release_unit MAu15
            release_unit MAu16
            release_unit MAu17
            release_unit MAu18
            release_unit MAu19
            release_unit MAu20
            release_unit MAu21
            release_unit MAu22
            release_unit MAu23
            release_unit MAu24
            release_unit MAu25
            release_unit MAu26
            release_unit MAu27
            release_unit MAu28
            release_unit MAu29
            release_unit MAu30
            release_unit MAu31
            release_unit MAu32
            release_unit MAu33
            release_unit MAu34
            release_unit MAu35
            release_unit MAu36
            release_unit MAu37
            release_unit MAu38
            release_unit MAu39
            release_unit MAu40
    
    
    terminate_monitor
    end_monitor
    
    
    ; *********************************************************************************************
    ;    Initialize Battle Plans & Monitors
    ; *********************************************************************************************
    
    
    ; *********************************************************************************************
    ;    Shared Reinforcements Monitor
    ; *********************************************************************************************
    
    
    monitor_event BattleReinforcementsArrive
    
    
    ;AI Reinforcements
    inc_counter AI_reinforcements 1
    
    
    end_monitor
    
    
    ; *********************************************************************************************
    ;    AI Unit Monitors & AI Reactions
    ; *********************************************************************************************
    
    
    monitor_conditions I_UnitEnemyUnitInRadius Eu1 50
    
    
    inc_counter AI_GRP 1
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    
    
    terminate_monitor
    end_monitor
    
    monitor_conditions BattleIsMeAttack
    and BattleDirectionOfAttack = rear
    and I_BattlePlayerArmyIsAttacker
    
    
    inc_counter AI_GDEP 1
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    
    
    terminate_monitor
    end_monitor
    
    
    monitor_conditions BattleIsMeAttack
    and BattleDirectionOfAttack = flank
    and I_BattlePlayerArmyIsAttacker
    
    
    inc_counter AI_GFP 1
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    
    
    terminate_monitor
    end_monitor
    
    
    monitor_conditions I_PercentageUnitKilled Eu1 > 5
    
    
    inc_counter AI_GRP 1
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    
    
    terminate_monitor
    end_monitor
    
    
    ; *********************************************************************************************
    ;    AI Reinforcements Defend Plan 
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour < 2
    and I_CompareCounter AI_reinforcements > 0
    and I_TimerElapsed AI_reinforcements_timer < 180000
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 DEFEND_LINE 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 DEFEND_FEATURE
    
    
    end_monitor
    
    
    ; *********************************************************************************************
    ;    Player AI Reinforcements Defend Plan 
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour < 1
    and I_CompareCounter AI_reinforcements > 0
    and I_TimerElapsed AI_reinforcements_timer < 180000
    
    
    ;Player Units Battle Objective
    ai_gta_add_objective 0 DEFEND_LINE 999
    ;Player Alliance Battle Objective
    ai_gta_plan_set 0 DEFEND_FEATURE
    
    
    end_monitor
    
    
    ; *********************************************************************************************
    ;    AI Reinforcements Attack Plan
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour < 2
    and I_CompareCounter AI_reinforcements > 0
    and I_TimerElapsed AI_reinforcements_timer > 180000
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    
    
    
    
    end_monitor
    
    
    ; *********************************************************************************************
    ;    Player AI Reinforcements Attack Plan
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour > 0
    and I_CompareCounter AI_reinforcements > 0
    and I_TimerElapsed AI_reinforcements_timer > 180000
    
    
    ;Player Units Battle Objective
    ai_gta_add_objective 0 ATTACK_ENEMY_BATTLEGROUP 999
    ;Player Alliance Battle Objective
    ai_gta_plan_set 0 ATTACK_ALL
    
    
    
    
    end_monitor
    
    
    ; *********************************************************************************************
    ;    Normal Attack Plan
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour < 2
    and not I_BattlePlayerArmyIsAttacker
    and I_ConflictType Normal
    and I_CompareCounter AI_reinforcements = 0
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    
    
    end_monitor
    
    
    ; *********************************************************************************************
    ;    Normal Defend Plan
    ;    Recurring counters fire above 0
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour > 0
    and I_BattlePlayerArmyIsAttacker
    and I_CompareCounter AI_reinforcements = 0
    and I_ConflictType Normal
    
    
    if I_CompareCounter AI_GRP = 0
    ;AI Units Battle Objective
    ai_gta_add_objective 1 DEFEND_LINE 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 DEFEND_FEATURE
    end_if
    
    
    if I_CompareCounter AI_GRP > 0
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    end_if
    
    
    if I_CompareCounter AI_GDEP > 0
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    end_if
    
    
    if I_CompareCounter AI_GFP > 0
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    end_if
    
    
    end_monitor
    
    
    ; *********************************************************************************************
    ;     Assault Crossing
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour < 2
    and not I_BattlePlayerArmyIsAttacker
    and I_BattleIsRiverBattle
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ASSAULT_CROSSING 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_ALL
    
    
    end_monitor
    
    
    ; *********************************************************************************************
    ;     Assault Settlement 
    ; *********************************************************************************************
    
    
    monitor_conditions I_BattlePlayerAllianceOddsInFavour < 2
    and not I_BattlePlayerArmyIsAttacker
    and I_BattleIsSiegeBattle
    
    
    ;AI Units Battle Objective
    ai_gta_add_objective 1 ATTACK_ENEMY_BATTLEGROUP 999
    ;AI Alliance Battle Objective
    ai_gta_plan_set 1 ATTACK_SETTLEMENT
    
    
    end_monitor
    
    
    while I_InBattle
    end_while
    
    
    end_script
    Last edited by z3n; December 01, 2015 at 12:34 AM.
    The AI Workshop Creator
    Europa Barbaroum II AI/Game Mechanics Developer
    The Northern Crusades Lead Developer
    Classical Age Total War Retired Lead Developer
    Rome: Total Realism Animation Developer
    RTW Workshop Assistance MTW2 AI Tutorial & Assistance
    Broken Crescent Submod (M2TW)/IB VGR Submod (BI)/Animation (RTW/BI/ALX)/TATW PCP Submod (M2TW)/TATW DaC Submod (M2TW)/DeI Submod (TWR2)/SS6.4 Northern European UI Mod (M2TW)

  12. #12
    Cesco's Avatar Decanus
    Join Date
    Apr 2013
    Location
    Italia
    Posts
    595

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Thank you, i really appreciate your support.
    As i said i'm not able to understand this argoument yet, but i will try and experiment something. When you' are done with the tutorial about battle scripting, please give me a shout.
    Huic ab adulescentia bella intestina, caedes, rapinae, discordia civilis grata fuerunt ibique iuventutem suam exercuit

  13. #13
    +Mr.Crow+'s Avatar VIVERE MILITARE EST
    Join Date
    Aug 2009
    Location
    Apulia, Kingdom of Sicily
    Posts
    1,359

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Hi z3n,

    i've a question for you about CAI. After reading through the AI LTGD logs while doing some tests, i noticed that sometimes the game do the decision entries evaluations more then one time in the same turn for the same faction.

    For example, if i've the faction X, the faction Y and the faction Z and i play 6 turns, it can happen that during the turn 2 (for example) both defensive and invasion decision entries for the faction X are evaluated 2 times instead one.

    Do you know why?
    BELLUM CRUCIS 7.0 Co-Director
    PERSONAL PROJECTS: CSUR || WARWAGON

    Quote Originally Posted by Cyprian2 View Post
    As far as I'm concerned, you've done something that CA should have thought of a long time ago. You should be on their pay-roll!

  14. #14
    z3n's Avatar State of Mind
    Moderator Emeritus

    Join Date
    Aug 2011
    Posts
    4,640

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Yes, diplomatic change results in the game state changing which means the LTGD has to reevaluate its stance against factions so it runs through the decision entries again.

    (From CA's notes in the file, I really recommend reading through their notes in the descr_campaign_ai_db, they're invaluable)

    Code:
            // The ai_labels specify the structure of the long term goal director (LTGD) which drives the high level campaign AI.
            // At the start of every factions turn (or when diplomacy changes), the LTGD is re-evaluated as follows:
                // for every target faction (all other factions), evaluate the defend decisions
                // for every target faction, evaluate the invasion decisions
                // any invasion priorities are modified by the faction standing (relationship) towards the target
                // depending on current game state, a new target with a high invasion priority may be selected to invade
            // the LTGD can be debugged with the preferences '[log] level = ai.ltgd trace' and '[ai] ltgd_logging = true.
    The AI Workshop Creator
    Europa Barbaroum II AI/Game Mechanics Developer
    The Northern Crusades Lead Developer
    Classical Age Total War Retired Lead Developer
    Rome: Total Realism Animation Developer
    RTW Workshop Assistance MTW2 AI Tutorial & Assistance
    Broken Crescent Submod (M2TW)/IB VGR Submod (BI)/Animation (RTW/BI/ALX)/TATW PCP Submod (M2TW)/TATW DaC Submod (M2TW)/DeI Submod (TWR2)/SS6.4 Northern European UI Mod (M2TW)

  15. #15
    Junaidi83 de Bodemloze's Avatar Dont Mess With Me
    Join Date
    Feb 2011
    Location
    Indonesia
    Posts
    2,616

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Absolutely mind blowing stuff
    Modding is like accursed wine, you try a sip and you ended empty the whole glass
    Under Proud Patronage of Shankbot de Bodemloze

  16. #16

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Quote Originally Posted by z3n
    In my own opinion CA does a rather good job at explaining how the file works which is worth a read through if you've never unpacked MTW2 and read through their explanations.
    For the most part I would agree with this statement, however through studying the AI LTGD log (from way back when it was first available, before 'kingdoms' was even around) I have to say some of their descriptions are off, at the least misleading. I've a more detailed explanation here.
    http://rtw.heavengames.com/cgi-bin/f...ct&f=9,6718,,1
    Also you may want to note my observations on coordinating attack and defend decisions.

    Quote Originally Posted by z3n
    Some common myths about the battle analyzer is that it crosses over to the campaign AI as well
    Yea, that could well be my fault...

  17. #17
    z3n's Avatar State of Mind
    Moderator Emeritus

    Join Date
    Aug 2011
    Posts
    4,640

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    I don't think I've read this one before, interesting information here.
    Thanks!

    And there's no problem there, it would be an alternative and reasonable conclusion but I found it wrong during the testing. So I've been trying to figure out what exactly controls the exact figure the AI uses when calculating whether it attacks. I've come to the conclusion that it is based upon these, unfortunately the AI will still attack before it's entirely ready from time to time despite this.

    Code:
          <att_str_modifier float = "1.0"/>            <!-- modifies the effective attackers strength when determining the priority of making attack decision (i.e. att_def_strength_ratio = ((att_str*att_str_modifier)/def_str) -->
          <siege_att_str_modifier float="1.0"/>      <!-- modifies the effective sieging attackers strength when determining the priority of making attack decision -->
          <crusade_att_str_modifier float = "1.0"/><!--modifies the effective crusading sieging attackers strength when determining the priority of making attack decision -->
          <sally_att_str_modifier float="1.0"/>      <!-- modifies the effective sallying attackers strength when determining the priority of making attack decision -->
          <ambush_att_str_modifier float = "1.2"/><!--modifies the effective ambushing attackers strength when determining the priority of making attack decision -->
          <str_limit_weak float = "0.99"/>              <!-- min ideal strength ratio modifier for determining when an army is far too weak for an attack ( att_def_strength_ratio < (ideal_str_ratio*str_limit_weak) ) -->
          <str_limit_strong float = "1.01"/>            <!-- max ideal strength ratio modifier for determining when an army is far too strong for a fair attack ( att_def_strength_ratio > (ideal_str_ratio*str_limit_strong) ) -->
    However, from a test, when setting str_limit_weak to 0.01 the AI would attack/besiege regardless of it's army strength (1 unit vs 10 units for example). What this suggests, is that the AI has a function in the exe that tells the AI a certain army has a certain 'strength' once the unit is one turn away. I believe this is to prevent too many units being assigned to a stack at once, resulting in stack overflow. If unit strength was calculated on the same turn as a buildup/invasion decision multiple units might be assigned to the same army all at once, resulting in a less efficient distribution of forces across the board.

    I really wish we could have the AI constantly re-evaluate the game state and redistribute its forces actively according to the short term as well rather than just long term but unless we constantly flipped every factions diplomatic state from at war to neutral to at war all based on counters. (which would probably be a huge CPU drain in itself) we can't really force constant AI reevaluations of the game state. And I'm not even sure if that would work as I hope.

    Sorry I got distracted with that new idea, and forgot to add what I'm saying is that tests I've run (giving the AI the option to attack with a huge stack or one unit stack vs 10 units) the str_limit_weak is the absolute minimum strength it must be from one turn away. It would use the one unit stack when the str_limit_weak was at 0.01 , but the 19 unit stack (and add the 1 unit stack into it) when the str_limit_weak was .99. I've been unable to figure out what exactly the str_limit_strong is or whether they intended to disable it as there is no discernible effect raising it or lowering it in conjunction with str_limit_weak. Maybe you might have better luck or have an idea.

    edit: Actually I did read it but it was on this link which looks different.
    http://medieval2.heavengames.com/m2t...ay/index.shtml
    Last edited by z3n; February 06, 2016 at 12:49 AM.
    The AI Workshop Creator
    Europa Barbaroum II AI/Game Mechanics Developer
    The Northern Crusades Lead Developer
    Classical Age Total War Retired Lead Developer
    Rome: Total Realism Animation Developer
    RTW Workshop Assistance MTW2 AI Tutorial & Assistance
    Broken Crescent Submod (M2TW)/IB VGR Submod (BI)/Animation (RTW/BI/ALX)/TATW PCP Submod (M2TW)/TATW DaC Submod (M2TW)/DeI Submod (TWR2)/SS6.4 Northern European UI Mod (M2TW)

  18. #18

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    I have tried several methods to get the AI to re-evaluate the game state for short term goals, the main thing I was looking for is the AI's tendency to order an attack from multiple turns away, without considering the conditions when their army actually arrives. It can actually be done, using freeze_faction_ai in the campaign script, unfortunately it only works if that faction actually has a turn with it's AI frozen, so it's really a non-option. Otherwise, so far, I've not had any luck, the best thing I've found for my specific problem is your pathfinding 'fix' of increasing the base character starting_action_points, this, at least, allows the AI to re-enforce an army enroute.

    I did some testing the str_limit settings when they became available with 'Kingdoms' and have reached the same conclusion you have. While setting 'weak' to higher then 1 seems tempting, it has a tendency to create decisively limit AI activity. As far as strong goes, I think it has to do with intercepting armies. So if the AI has an enemy army in it's territory and has a powerful army nearby, if that AI's army strength is greater then the str_limit_strong vs the enemy army, the AI will not move it's larger army to intercept because it's "too strong" to fight that enemy. But tbh I have done only limited testing on this theory as, if it's correct, it would make str_limit_strong only more useless then it seems to be already, it also dose not seem to effect attacking at all, so the AI will still attack an enemy army if it's too strong, it just wont defend against one.

    It is important to stress that everything in my link was done before Kingdoms, and a good portion of it is outdated by Kingdoms 'improved' AI. (improved in ' because it is only better in comparison to what it was before Kingdoms.) One BIG example of outdated info is my comment on descr_character about the individual movement rates being broken, that's because, at the time I was doing this research, if you selected part of an army that did not include the lead unit (captain or general) that army would have movement range equal to the default (ie pathfinding) movement range, this has since been fixed. There are also a great deal of options in descr_campaign_db I did not get into, because they simply did not exist at the time (including the str_limit options, or the entire <ai> section for that matter.)

  19. #19
    z3n's Avatar State of Mind
    Moderator Emeritus

    Join Date
    Aug 2011
    Posts
    4,640

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    I have tried several methods to get the AI to re-evaluate the game state for short term goals, the main thing I was looking for is the AI's tendency to order an attack from multiple turns away, without considering the conditions when their army actually arrives. It can actually be done, using freeze_faction_ai in the campaign script, unfortunately itonly works if that faction actually has a turn with it's AI frozen, so it's really a non-option. Otherwise, so far, I've not had any luck, the best thing I've found for my specific problem is your pathfinding 'fix' of increasing the base character starting_action_points, this, at least, allows the AI to re-enforce an army enroute.
    Did you test freezing the AI PreFactionTurnStart then turning it back on on CharacterTurnStart?
    That might work.


    If you did try that already I guess the next step is to try forcing the diplomatic stance for slaves to neutral, then to hostile every turn. In theory that should result in the game state changing which means every turn the AI will re-evaluate the game state every turn, which may be better than randomly. The reason I am thinking this would also work is due to diplomatic changes forcing a re-evaluation of the game state.

    I did some testing the str_limit settings when they became available with 'Kingdoms' and have reached the same conclusion you have. While setting 'weak' to higher then 1 seems tempting, it has a tendency to create decisively limit AI activity. As far as strong goes, I think it has to do with intercepting armies. So if the AI has an enemy army in it's territory and has a powerful army nearby, if that AI's army strength is greater then the str_limit_strong vs the enemy army, the AI will not move it's larger army to intercept because it's "too strong" to fight that enemy. But tbh I have done only limited testing on this theory as, if it's correct, it would make str_limit_strong only more useless then it seems to be already, it also dose not seem to effect attacking at all, so the AI will still attack an enemy army if it's too strong, it just wont defend against one.
    So what you're saying is that we might as well place a value of 999 there in hopes of disabling it completely or at least raising the limit so high it's effectively nullified? As it seems like an entirely useless value, or perhaps just a mistake by CA and they didn't place an extra 9 on the end of the default 99 value.
    The AI Workshop Creator
    Europa Barbaroum II AI/Game Mechanics Developer
    The Northern Crusades Lead Developer
    Classical Age Total War Retired Lead Developer
    Rome: Total Realism Animation Developer
    RTW Workshop Assistance MTW2 AI Tutorial & Assistance
    Broken Crescent Submod (M2TW)/IB VGR Submod (BI)/Animation (RTW/BI/ALX)/TATW PCP Submod (M2TW)/TATW DaC Submod (M2TW)/DeI Submod (TWR2)/SS6.4 Northern European UI Mod (M2TW)

  20. #20

    Default Re: Understanding MTW2/Kingdoms.exe Pathfinding, XML and the AI

    Quote Originally Posted by z3n
    If you did try that already I guess the next step is to try forcing the diplomatic stance for slaves to neutral, then to hostile every turn. In theory that should result in the game state changing which means every turn the AI will re-evaluate the game state every turn, which may be better than randomly. The reason I am thinking this would also work is due to diplomatic changes forcing a re-evaluation of the game state.
    No, I have not tried pre-faction turn start. Changing the diplomatic stance of the rebel faction would only force the AI to re-evaluate the Long Term, which is already done every faction turn start anyway. It would have no effect on the short term as that is only considered at the start of the faction turn, and is only effected by major changes in the long term (ie ceasfire or alliance with a faction that an attack against has already been issued.) This is why stationary armies might move to intercept incoming enemy armies, but armies that have already received orders usually will not.

    Quote Originally Posted by z3n
    So what you're saying is that we might as well place a value of 999 there in hopes of disabling it completely or at least raising the limit so high it's effectively nullified? As it seems like an entirely useless value, or perhaps just a mistake by CA and they didn't place an extra 9 on the end of the default 99 value.
    Yes mostly. I believe the intent was to prevent large armies from hunting down small armies in it's area, to keep them free for more effective orders such as attacking, defending or intercept larger armies. For example, a human player will not have a full stack army ready to attack use half it's movement in the opposite direction from the intended attack to take out a 1 or 2 unit enemy army, instead it will order the full stack to attack and have a smaller (or separate a small force) attack said one or two units. I believe that is the intended purpose of str_limit_strong, the whole thing was either disabled completely (as I said I've only done limited testing) or they effectively disabled it (99) because it did not work as intended.

Page 1 of 11 12345678910 ... LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •