Results 1 to 6 of 6

Thread: Descr_campaign_ai_db. My observations.

  1. #1

    Default Descr_campaign_ai_db. My observations.

    While in my case, working with the campaign AI proved to be a fruitless and unrewarding task (I discovered there were many factors that were ultimately influencing the behavior of the AI, factors external to the xml directives I was trying to modify) I did learn a thing or two about how the campaing_ai_db file works, and how it interfaces with the LTGD. I decided to share some of my findings here in case someone finds them useful. Before venturing any further, I will preemptively state that these are conclusions to which I arrived based on my observations, and experiences and while I believe I'm correct, short of looking at the game's source or using a disassembler, we can't be 100% certain of anything here.

    Before starting, I will boldly state that after spending hours messing around with all this, I've no Idea what it is the invade decisions do exactly. Honestly, it's perplexing. I do know that they are not responsible for the merging of armies in any capacity, though. It might seem so at times, because, often a particular invasion directive can exacerbate the merging issue, but I am certain the root of the problem is elsewhere. I also believe that, although the invasion decisions influence the size of the stacks of AI armies, the biggest factor is something else entirely. It might not be be a single setting, but a combination of them, and they might even be on multiple files. So if you're looking to mod the campaing_ai_db file in hopes of addressing these two *big* issues, I suggest looking somewhere else first...

    Also, I'd strongly recommend you to create a hotseat game, choosing whatever faction you want to monitor, select the skip_ai_movement option, use the toggle_fow command and finally give the control of your faction to the AI by typing control factionname. Where factioname is the name of the faction. This is the best way to observe the disaster that is the campaign AI and all the stupid glitches and bugs that make it perform so poorly. As you'll soon realize, most these have little to do with the campaing_ai_db file.


    Behavior:

    The way the AI behaves is mostly dictated internally, we can only give it general guidelines on what to do. Crusades and Jihads are completely handled internally, we have absolutely no control over it and can't make a faction join a crusade much less create a crusade army. Some AI config files waste many entries with several conditions trying to 'detect' crusades. this of course is a complete waste of space and processing cycles. On the other hand, can_force_invade does have an effect on crusades, since, when disabled, crusade armies will not attack the target faction. Internal code will create an army, often taking vital forces that could be used elsewhere, they will march the army all the way to the destination, and then just stand there, wasting time and money until can_force_invade equals true or the crusade ends.

    Similarly, naval invasions are handled by internal engine code. We can't make a faction launch a naval invasion any more than we can make it join a crusade. Although we can completely disable the internal system which automatically selects a target faction to attack, by setting can_force_invade to false.

    Vassal factions are not processed as normal factions. Once a faction becomes a vassal, it's invasion processing is disabled by the LTGD(with the exception of crusades which are handled internally anyway). This means that no matter how we much we try, we can't make a vassal launch an invasion, even to help their overlords. Only defend decisions are evaluated for these factions.

    Destroyed and yet to be spawned factions, do not get processed by the LTGD. Hoewever, other factions still analyze them. There's no sure way to determine if a faction is destroyed or not yet on the map, to circumvent its processing.

    Slave factions are, despite what one may think, processed sort of like a normal faction and both invade and defend decisions are evaluated by the LTGD. However, because of the peculiar situation they find themselves in, being spread around the world and at war with everyone, the game seems not to evaluate any invasion target for them and the data obtained from them is completely bogus most of the time. We occasionally have the same problem with other factions, which is why is important to evaluate the data we are receiving to see if it falls within "normal" range, and ignore it otherwise. So all this means that trying to make the slave faction do somehting meaningful as a whole is a fool's errand. I've had much success with them by using an extremely simple configuration, with only a couple of entries, that sees them defend their lands when attacked and venture causing devastation and often besieging and taking weak settlements.
    Code:
    <faction_ai_label name="slave_faction">
    
            <defend_decisions>
    
                <decision_entry>
                    <min_entry is_neighbour="true"/>
                    <faction_attitude defense="defend_deep"/>
                </decision_entry>
            
            </defend_decisions>
    
    
            <invasion_decisions>
                
                <decision_entry>
                    <min_entry is_neighbour="true"/>
                    <faction_attitude invade="invade_raids" invade_priority="512"/>
                </decision_entry>
    
            </invasion_decisions>
            
        </faction_ai_label>

    Decision entries:

    Decision entries are evaluated sequentially, for each faction toward other factions. When an entry is found in which all tests are successful, the entry's parameters are read into the game and further evaluations for that target faction are aborted(unless continue is set to true).

    Min and Max:

    Min-max entries are akin to greater or equal than, less or equal than comparison operators, respectively. That is to say:
    Code:
    <decision_entry>
        <min_entry military_balance="0.5"/>
        <max_entry military_balance="1.0"/>
    </decision_entry>
    would be equivalent to the following expresion: "if (military_balance >= 0.5 && military_balance <= 1.0)" in a typical scripting language. Or: "if military balance is greater or equal to 0.5 and less or equal to 1.0" in plain English.

    Decision entries that have a "true" or "false" value are interpreted as if their value is 1 for true and 0 for false. As such, there's no need to compare both ends of the spectrum, in this manner:
    Code:
    <decision_entry>
            <min_entry is_neighbour="true"/>
            <max_entry is_neighbour="true"/>
        </decision_entry>
    That is the same as saying: if neighbour is >= 1 and neighbour <= 1, which is not only redundant but wasteful of both space and posibly CPU cycles as each of the conditions specified here are parsed to later be evaluated during the game; and judging by the size of the original campaign AI I doubt the develoopers bothered to perform optimizations in this regard. A simple is_neighbour >= 1 will do if you want to test if it's a neighbour or, is neighbour <= 0 if you want to know if it is not a neighbour. Going back to the example above, that would be:
    Code:
    <decision_entry>
            <min_entry is_neighbour="true"/>            ; is_neighbour >= 1
        </decision_entry>
    OR if you want to see if it's not a neighbour:
    Code:
    <decision_entry>
            <max_entry is_neighbour="false"/>            ; is_neighbour <= 0
        </decision_entry>
    Other decision entries behave just like that. In fact everything that is assigned to a decision entry (="xxxx") has a numerical value, yes that includes religion and faction names. For example, stance modifiers' values are: Allied = 0, Neutral = 1, AtWar = 2. Knowing this can be very helpful for properly setting up conditions and reducing redundant comparisons. eg:
    Code:
    <decision_entry>
            <min_entry stance="AtWar"/>
            <max_entry stance="AtWar"/>
        </decision_entry>
    isn't necessary. Simply writing:
    Code:
    <decision_entry>
            <min_entry stance="AtWar"/>
        </decision_entry>
    will suffice, because AtWar is the highest value and there are no other modifiers >= AtWar(2). Similarly, if you are looking for Allied this will do the trick:
    Code:
    <decision_entry>
            <max_entry stance="Allied"/>
        </decision_entry>
    But if you want to find Neutral, specifically, then a range comparison is necessary because its value, 1, is between the others (0 and 2):
    Code:
    <decision_entry>
            <min_entry stance="Neutral"/>
            <max_entry stance="Neutral"/>
        </decision_entry>
    Religion has the following integral values: catholic = 0, orthodox = 1, islam = 2, pagan = 3

    Rand:
    The rand entry tests a random value between 0 and 1.0. Any value between that range has an equal chance of occurring. For example, the following would have an 80% chance of triggering:
    Code:
    <decision_entry>
            <min_entry rand="0.2"/>
        </decision_entry>
    because there's an 80% chance for rand to return a number greater or equal to 0.2 (1.0 - 0.2 = 0.8).
    I've seen people do things like:
    Code:
    <decision_entry>    
            <min_entry rand="0.2"/>
            <max_entry rand="0.4"/>
        </decision_entry>
    to get a 20% chance of the condition to trigger; which completely boggles my mind. That is not necessary. The developer's themselves give plenty example on how to use rand in the vanilla campaign_ai file.


    Default values:

    Every decision entry comes initialized with a default value. What that value is, can be observed by enabling LTGD log. Knowing what the default values are can be important to avoid creating unnecessary entries / comparisons. All entries are reset to their default value after every turn. Some of the default values: invade_priority = -1, invade = invade_none, defend = defend_normal, continue = false.

    Assigning entries their default value, isn't necessary, but contrary to tests, which have to be evaluated every turn by the game, this won't affect affect turn times because it's done while parsing the file and is a one off operation. At times, I explicitly set an entry to its default value. I do this to be explicit and help those reading the file, not because there's a need for it.


    Misleading Decision entries:
    num_enemies:
    Contrary to what its name might indicate, this doesn't tell how many factions a faction is at war with, as shown in the diplomacy screen, but rather with how many factions they're military engaged with. You can be "at war" with 5 factions but only really fighting one, and num_enemies will correctly report 1.

    can_force_invade:
    Allows or denies the game to perform automatic invasions. The LTGD picks targets based on internal code, and seems to choose the one with highest score. These invasions are the only way to allow naval attacks. Invasions initiated by the "invade" directive will never be carried through the sea. Certain locations seem to have hardcoded naval invasions and will always occur regardless if this setting is enabled or disabled.

    at_war:
    A mystifying entry. After setting it to true, subsequent tests for stance="AtWar" will be true -even if the factions are not at war at all- for the duration of the turn. Yet there seems to be more to it, as my tests show factions to be more effective and aggressive when fighting rebels. In fact, when toggled, no invade directive seems necessary; factions will attack the nearest rebel settlements immediately and with great force. I've yet to determine if this occurs for non rebel settlements as well. It could be that the setting merely toggles on can_force_invade internally.
    PlainEdit Multipurpose editor designed to automate repetitive text modification tasks.

  2. #2
    z3n's Avatar State of Mind
    took an arrow to the knee

    Join Date
    Aug 2011
    Posts
    4,640

    Default Re: Descr_campaign_ai_db. My observations.

    This is a really good tutorial and contains a lot of important information from a programming perspective, thanks for posting it.

    A mystifying entry. After setting it to true, subsequent tests for stance="AtWar" will be true -even if the factions are not at war at all- for the duration of the turn. Yet there seems to be more to it, as my tests show factions to be more effective and aggressive when fighting rebels. In fact, when toggled, no invade directive seems necessary; factions will attack the nearest rebel settlements immediately and with great force. I've yet to determine if this occurs for non rebel settlements as well. It could be that the setting merely toggles on can_force_invade internally.
    1) This part in particular stands out for me. I didn't realize it increases aggression but I can say it doesn't act as can_force_invade internally (or at least not in full), when I did some tests with it set but can_force_invade set to false, I didn't see forced invasions occurring.


    can_force_invade:
    Allows or denies the game to perform automatic invasions. The LTGD picks targets based on internal code, and seems to choose the one with highest score. These invasions are the only way to allow naval attacks. Invasions initiated by the "invade" directive will never be carried through the sea. Certain locations seem to have hardcoded naval invasions and will always occur regardless if this setting is enabled or disabled.

    2) Very interesting that in the vanilla map (I assume this is what you reference), naval invasions are carried out regardless of the setting. Maybe this is the true purpose of those hidden resources (could they act as a flag?). I never test on vanilla map, only the EBII map or other mod.


    num_enemies:
    Contrary to what its name might indicate, this doesn't tell how many factions a faction is at war with, as shown in the diplomacy screen, but rather with how many factions they're military engaged with. You can be "at war" with 5 factions but only really fighting one, and num_enemies will correctly report 1.
    I did not know this, definitely explains some current behaviour I observed.


    ___________


    Before starting, I will boldly state that after spending hours messing around with all this, I've no Idea what it is the invade decisions do exactly. Honestly, it's perplexing. I do know that they are not responsible for the merging of armies in any capacity, though. It might seem so at times, because, often a particular invasion directive can exacerbate the merging issue, but I am certain the root of the problem is elsewhere. I also believe that, although the invasion decisions influence the size of the stacks of AI armies, the biggest factor is something else entirely. It might not be be a single setting, but a combination of them, and they might even be on multiple files. So if you're looking to mod the campaing_ai_db file in hopes of addressing these two *big* issues, I suggest looking somewhere else first...

    You're going to get a lot of flak about this one. But yeah I agree, from a code perspective I don't really know what these invasion decisions do either for merging. They definitely don't control the merge part of the algorithm, which is pretty haphazard.

    In later TW games the devs simply gave up getting armies to merge and forced all armies to group on a single general ; furthermore they even forced a limit to the amount of armies a faction can have. So they never solved the merge problem either, they just went around it.


    I don't think merge is really a solvable problem. One time I even tried to set the descr_campaign_db army strength variables to some absurd numbers. I thought maybe this would force the AI into stacking troops but it actually just cripples them from taking action. And when you set it upwards, they still often use multiple armies to attack instead of merging into one stack.

    Essentially you can't really directly influence the merge part.
    Last edited by z3n; May 07, 2022 at 08:34 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

    Default Re: Descr_campaign_ai_db. My observations.

    If I am not mistaken I would add to the Misleading Decision entries: frontline balance. I check the log and see it really gets crazy values, either minimal or very big - thousands. Event if this value sums the allied forces surrounding a territory, I don't think it explains the values like 3.18086e-005 or 41000 (real examples).

  4. #4
    bitterhowl's Avatar Campidoctor
    Join Date
    Feb 2011
    Location
    Russian Feodality
    Posts
    1,695

    Default Re: Descr_campaign_ai_db. My observations.

    at_war:
    A mystifying entry.
    The mystery is simple - it may be changed only at defend_decisions part. So when one want to change them in invade_decision code engine will not toggle it to false/true as it written in code.

    My sister, do you still recall the blue Hasan and Khalkhin-Gol?
    Russian warship is winning. Proofs needed? Go find yourself!

  5. #5

    Default Re: Descr_campaign_ai_db. My observations.

    Quote Originally Posted by bitterhowl View Post
    The mystery is simple - it may be changed only at defend_decisions part. So when one want to change them in invade_decision code engine will not toggle it to false/true as it written in code.
    Can you explain a little bitterhowl ? Do you mean that by setting it 'true' the war may be started ?

  6. #6
    bitterhowl's Avatar Campidoctor
    Join Date
    Feb 2011
    Location
    Russian Feodality
    Posts
    1,695

    Default Re: Descr_campaign_ai_db. My observations.

    I mean that at_war parameter could be changed only in defend_decisions part of code. When you try to change it in invade_decision part engine will not execute this changes. That's why some people say they can't understand how engine operates with it, they say standings are neutral or allied but at_war is "true".

    My sister, do you still recall the blue Hasan and Khalkhin-Gol?
    Russian warship is winning. Proofs needed? Go find yourself!

Posting Permissions

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