Results 1 to 13 of 13

Thread: Get AI Army to build more siege equipment

  1. #1

    Default Get AI Army to build more siege equipment

    I've been using a combination of several mods (and modding myself) and fixing issues that I find. One issue is that the AI keeps launching assaults with too few pieces of equipment. Ideally, I could just tell them to use ladders and they'd build 4+ ladders and some rams instead of ~1 of each, but that doesn't look like it's an option given all the hard-coded things.

    I've been searching around quite a bit to try any way to force the AI to have more siege equipment before launching an assault (or spawn it during the assault):
    • I thought maybe I could "add ladders" to an army at the beginning of a battle. AFAICT you can't add these as mentioned here.
      Seems the only way to do this would be editing values in memory, and that's outside my wheelhouse.
    • I noticed that in the link above (and related links) regarding spawning scripted sieges, you could tell an army to "maintain" a siege, which might be sufficient to get it to build more equipment.
    • Similar to the prior bullet, I considered specifying that an army with less than TotalSiegeWeapons should not assault. This requires knowing which army IsBesieging.


    So right now I have a bunch of pieces to a puzzle that I can't quite make sense of. A sufficient piece of psuedocode might look like the below

    Code:
    if I_SettlementUnderSiege <settlement> ; is the settlment under siege
    AND IsBesieging <army>                 ; is this army besieging
    AND ! ArmyIsLocal <army>               ; is this an ai army? (possibly redundant with the below)
    AND SettlementIsLocal <settlement>     ; is this a human's settlement?
    
    if random < 66%
    Code:
    siege_settlement <army>, <settlement>, maintain
    endif
    


    As the above would cause a delay *most* of the time, and it might make the player (myself at the moment) more likely to attack their besieger rather than suffer attrition. This would be sufficient for enjoyment while being an "easy" mediocre solution.
    Note: If someone wants a better solution, the issue becomes that you need sufficient counters to check if an army already waited or not and other counters to see how many armies you already have being counter'd.

    The issues I need to solve (and that might be unsolvable) are:
    • How do I get the army? I was trying to understand labeling. I did find some tutorial about spawning a general in a region, moving him to trigger an event, and then using that event to check things about the region, but it doesn't seem like what I'm looking for.
    • Do I really need to right this for <x hundred> settlements? Is there really no event-driven way to derive a settlement nor a way to loop over all existing settlements/armies?

    Thank you for any information/assistance you're able to provide.

  2. #2

    Default Re: Get AI Army to build more siege equipment

    After posting this, I have come across another idea that might work that I'm currently psuedo-coding - effectively an Anti-Garrison script.

    This script would function by first defining a set of infantry with a "free_siege" attribute (or some such) that are effectively Town Militia with Ladders/Siege Towers of the appropriate size. (I have no idea if creating a unit like this this would work, but will try it tonight)

    Then (if able) add them to the opposing army (or as this thread talks about, add it as a helping army.)

    The psuedo-code for that looks something like

    Code:
               monitor_event FactionTurnStart <faction> 
                        and not FactionIsLocal
                        and I_FactionBesieging <faction>
                        and I_FactionBesieged <player_faction>
    
                        ; figure out where to spawn these units
                        create_unit <faction>, <free-siege unit>, num <x>, exp 3, arm 0, wep 0
                        end_monitor
                        
                        monitor_event FactionTurnEnd <faction>  
                        and not FactionIsLocal
                        destroy_units <faction>, free_siege
                        end_monitor
    This has some mild bugs, such as spawning these free units against other AI opponents when they are besieging the player, but maybe someone can help me with that bit of logic. A key issue here is that I still need to somehow know the army's location if I want to try to add them to that army. I read that that might not be possible, in which adding them as a helping army is a bit lame, but it's *something*

    Other things worth noting:

    * This can probably be implemented with an existing Garrison script without adding new monitors, by pushing some of the logic inside of the monitor_event

    * In order to deal with different size walls, I'll have to have logic that spawns the appropriate ladders/towers probably by checking settlement buildings. This means having (at least) 1 Ram unit, 3 Ladder Units, and 3 Tower units - plus I'll need them for each culture (e.g. ME, EE, SE, and NE) - probably using Town Militia as my default.

    * This creates the units at beginning of their turn only for besieged settlements (when the logic is all there) and then removes them regardless at EoT. This should mean that you never have these goofy units outside of these siege battles but you always have them during siege battles

  3. #3

    Default Re: Get AI Army to build more siege equipment

    Several small updates:
    * Creating an infantry with a Ram or ladder "siege engine" works as one would expect, which is handy.
    * Grabbed a garrison script and have formed a few ideas for what might work if I go the unit spawning route. Looks something like this for each settlement
    Code:
        if I_SettlementUnderSiege <settlement>
        monitor_event FactionTurnStart <faction A>
        and ! FactionIsLocal <faction A>
            if I_IsCharacterTypeNearTile <faction A>, named_character, <settlement location>
                spawn_army
                    <faction A units>
                
            if I_IsCharacterTypeNearTile <faction A>, captain?, <settlement location>
                spawn_army
                    <faction A units>
    NOTE: The above will only spawn them nearby, does not yet take into account wall height, nor does it take into account which units to spawn based on which faction. We might be able to merge them with the original army as well by spawning slave merchants (as the previously linked thread mentioned) on top of the city to block out the tiles not covered by armies, then simply spawn armies and hope they combine with the original (have no idea if that works.) If they don't combine with the original, then spawning an allied force with a bunch of ladders and rams will probably not perform amazing, but might be worth the trouble.

    * Biggest Update: I looked around and found M2TWEOP which is an overhaul project. One of the things it allows you to do is change the cost of siege engines. My assumption is that if I change the cost to around 0-1 that the comp will always build at least 6 engines (as it tends to build the max that it can and players are limited to 6 engines per turn.) I'll test this out; especially since siege towers are effectively worse than ladders anyway and this is the least amount of actual code changes I'd have to do (install the mod, combine it with mine, write a simple lua script that adjusts the costs.)

    Presumably with the above approach I could set the cost to be the normal cost on my turn and the AI cost on theirs; leaving the player with the normal balance numbers.

  4. #4

    Default Re: Get AI Army to build more siege equipment

    I had not seen that tutorial by Archaon nor any mention of 'sieging' or 'garrisoned_army' elsewhere on TWC, so thanks for bringing this to my attention. Standard practice for spawning an army that is meant to siege a settlement has always been to include at least one artillery unit in the army. This is an interesting new trick; however, I'm inclined to believe Withwnar, who has an impeccable record of thoroughly testing scripts, when he suggests that 'sieging' isn't doing anything and it's just the maintain command that's allowing the AI time to build more equipment.

    This is actually good news for you because it means that 1) the maintain command can be issued before the AI makes its own attack/maintain decision at the start of their turn and 2) the AI will continue building siege equipment at least up to whatever hard coded limit they may have.

    Quote Originally Posted by tescrin View Post
    Presumably with the above approach I could set the cost to be the normal cost on my turn and the AI cost on theirs; leaving the player with the normal balance numbers.
    I'm glad you found EOP on your own. You're asking about using iterative methods to get arbitrary objects and this is only currently possible in Lua. The conventional campaign script's conditions and commands generally only accept constants as arguments which is why people write ridiculous scripts with thousands of if-statements to test every possible value (settlement name, character name, etc.).

    The fact that we can use 'sieging' with 'siege_equipment' to add equipment to an army in descr_strat.txt, but not in campaign_script.txt, leads me to believe it might be possible for the folks working on EOP to extend that feature beyond descr_strat.txt, or better yet, to add a siege equipment field to the unit structure.

    In the meantime, since we know the AI will build equipment if given the chance, the best plan is your current one: use iteration to get the sieging army, check the number of siege equipment using e.g. the TotalSiegeWeapons condition, maintain siege and reduce equipment build points cost if # equipment < some threshold, maybe the threshold is based on total number of equipment-capable infantry units in the army. If the build point reduction doesn't work during the AI's turn for some reason (I suspect it will work without a problem), you could also look into the SiegeEngineering character attribute, each point of which corresponds to 1 build point for siege equipment. Setting this attribute in Lua should work for captains as well as generals as both are named characters.

    Good luck, and please post your final result here for others to benefit from!

  5. #5

    Default Re: Get AI Army to build more siege equipment

    Quote Originally Posted by Callistonian View Post
    This is actually good news for you because it means that 1) the maintain command can be issued before the AI makes its own attack/maintain decision at the start of their turn and 2) the AI will continue building siege equipment at least up to whatever hard coded limit they may have.
    The issue is actually figuring out which army is doing the sieging. I'm looking through the M2TWEOP docs since they have a feature that iterates the armies and you can walk the ones that are sieging. Given this, there's two solutions:

    * Ask it to maintain the siege (this might be possible in current lua scripting though I haven't gone that far yet)

    * Go back to generating "equipment units", but I only have to write the code once rather than for 150 settlements or w/e haha.


    Quote Originally Posted by Callistonian View Post
    The fact that we can use 'sieging' with 'siege_equipment' to add equipment to an army in descr_strat.txt, but not in campaign_script.txt, leads me to believe it might be possible for the folks working on EOP to extend that feature beyond descr_strat.txt, or better yet, to add a siege equipment field to the unit structure.
    I've posted a feature request to their discord regarding this and might try to learn how to do this and do a pull request for their repo. I do find it a bit obnoxious that the devs thought of this for descr_strat and not the campaign_script lol.

    Quote Originally Posted by Callistonian View Post
    In the meantime, since we know the AI will build equipment if given the chance, the best plan is your current one: use iteration to get the sieging army, check the number of siege equipment using e.g. the TotalSiegeWeapons condition, maintain siege and reduce equipment build points cost if # equipment < some threshold, maybe the threshold is based on total number of equipment-capable infantry units in the army. If the build point reduction doesn't work during the AI's turn for some reason (I suspect it will work without a problem), you could also look into the SiegeEngineering character attribute, each point of which corresponds to 1 build point for siege equipment. Setting this attribute in Lua should work for captains as well as generals as both are named characters.
    So far I've only seen it early game (my save was incompatible with m2tweop and converting it didn't work.) They built 4 rams; which is not 6 lol. That said, I haven't seen them siege a larger city yet. I'm playing as HRE hoping that one of the factions nearby gets antsy! Venice just started war :p. If it takes too long maybe I'll write a spawned army that sieges me at the beginning; but I'm hoping for an organic result that lets me play the game haha.

    That all said, I think the ideal situation is actually *more* than 6 pieces. When I siege the comp, I normally run 2-4 rams, 4-8 ladders + towers. If the comp assaulted 4-8 of your walls and had sufficient rams, rather than globbing at your front door in a ridiculous pushing match, I bet things would play out in a much more interesting way. The main issue with the ram/tower/ladder unit-spawning method is that we're looking at 4 units minimum, maybe even 5 for the base game to have a reasonable looking unit to spawn with the equipment, then you multiply it by 2 ladder sizes, a tower, and a ram (at minimum) for ***20*** units in the export_descr_units, 30 if you want to cover the other tower heights. That's pretty gross IMO.


    Quote Originally Posted by Callistonian View Post
    Good luck, and please post your final result here for others to benefit from!
    For sure! My ideal is to take the cobbled together thing and release it; though I need to acquire the various permissions from others to do so for the full thing.

    For the record, and something mildly productive; here is the lua code I'm testing with.

    Code:
    -- Fires when the plugin is first loaded at game start or restarted with restartLua()
    function onPluginLoad()
        M2TWEOP.unlockGameConsoleCommands();
        M2TWEOP.setEquipmentCosts(0,1);
        M2TWEOP.setEquipmentCosts(1,1);
        M2TWEOP.setEquipmentCosts(2,1);
    
    end
    I picked 1 as a starting number since 0 might cause a divide by zero or some other bug. Assuming that this is sufficient, I'll simply move this to a FactionTurnStart and change it based on whether the FactionIsLocal or not.

  6. #6

    Default Re: Get AI Army to build more siege equipment

    BIG UPDATE Here's the working M2TWEOP code. Obviously you can adjust the values, change the settlement.level stuff yourself to suit your tastes. You can also remove the line for whether you're a player if you want to be chalk full of siege equipment. But gol' darn it - looks like we're in business! Now I just gotta get one of these sieges so I can see how the comp does with a boat load of equipment lol!
    Code:
     function onSettlementTurnStart (eventData)     local settlement = eventData.settlement     for i = 0, settlement.siegesNum-1 do         local siege=settlement:getSiege(i);         local army = siege.besieger;         if army then             print('Found sieging army')             if army.faction.isPlayerControlled == 1 then return end;              print('Found sieging AI army')             if army.rams < 4 then                 print('Added rams to a comp army')                 army.rams = 4;                             end             if settlement.level >= 4 then                 if army.towers < 6 then                     print('Added towers to a comp army')                     army.towers = 6                 end             end             if settlement.level < 4 then                 if army.towers < 4 then                     print('Added towers to a comp army')                     army.towers = 4                 end                 if army.ladders < 6 then                     print('Added ladders to a comp army')                     army.ladders = 6                 end             end         end     end end
    Last edited by tescrin; April 08, 2024 at 05:54 PM.

  7. #7

    Default Re: Get AI Army to build more siege equipment

    Here is an example script with a lot of stuff left for you to do.

    Example

    Code:
    function onPreFactionTurnStart(eventData)
        if eventData.faction.isPlayerControlled == 0 then
            M2TWEOP.setEquipmentCosts(0, 1) --ram --I don't know what the scale of the cost is here or if 0 is allowed
            M2TWEOP.setEquipmentCosts(1, 1) --ladder
            M2TWEOP.setEquipmentCosts(2, 1) --tower
        else
            M2TWEOP.setEquipmentCosts(0, 25) --ram --I don't know what the original costs were
            M2TWEOP.setEquipmentCosts(1, 25) --ladder
            M2TWEOP.setEquipmentCosts(2, 25) --tower
        end
    end
    
    function onCharacterTurnStart(eventData)
        local siegeEquipmentThreshold = eventData.character.character.besiegingSettlement.level + 2
        if ((eventData.faction.isPlayerControlled == 0)
        and (eventData.character.character.besiegingSettlement ~= nil)
        and (M2TWEOP.condition("TotalSiegeWeapons", character.character) < siegeEquipmentThreshold) --I'm not sure this will work, I've never used M2TWEOP.condition
        ) then
            local originalLabel = eventData.character.label
            eventData.character.label = "siegeAttacker1"
            stratmap.game.scriptCommand("siege_settlement", tostring("siegeAttacker1 "..eventData.character.character.besiegingSettlement.name.." maintain")
            eventData.character.label = originalLabel
        end
    end


    To set the number of siege equipment the AI needs before attacking, I've simply set it to the settlement level + 3 so that e.g. sieging a small town will require 4 engines, this obviously doesn't work for villages, I would also recommend implementing some randomness here, so you have some work to do in how you want to set this threshold. I also don't know how much the siege equipment costs by default. And remember, this all assumes that giving the AI more time is enough to prompt them to build more equipment.

    I'm not sure if there is a way to prevent the AI from dropping their other equipment once the first ram has breached the first gate. I agree, that is very annoying behavior. AI scripting isn't really my forte, my only idea is increasing the gate health so that it takes longer to break through.

    Edit: Just saw your last post. I like how you're counting individual siege engines using those fields from the stack structure. That will give you more fine grained control.
    Last edited by Callistonian; April 04, 2024 at 04:46 PM.

  8. #8

    Default Re: Get AI Army to build more siege equipment

    Alright, mostly minor updates:

    * My account was all goofed for a bit. Sorry about the slow return.

    * editing my previous post to correct a coding error caused all the whitespace to goof. Trying to edit again shows a blank message lol.

    * I've gotten to play around with the aforementioned fix a bit. It seems that the AI works differently than the player. When they spawn in equipment (or when it comes from this method of doing so?) they only bring to the battle what they *could* man. E.G. if you tell it to have 4 rams, 6 ladders, and 4 siege towers, they will short themselves some rams to keep the ladders, even if they would've left the rams un attended anyway.

    * Similar to the above, if you sally-out, they seem to be missing the new equipment. Not a huge deal, but a little odd.

    It does look like the battles (when you have real walls) will be nuts though. Having 6-10 units climbing up and down your walls is bound to screw up the "lol i have 3-5 spearmen at the gate and 2 archers on the walls, come-at-me-bro" defense. And the good news is that they did bring most of the equipment.

    I'll have to tune it a bit as Cal was mentioning. Here's an updated piece of code that spawns it from the *armies* perspective:

    Code:
    functiononFactionTurnStart(eventData)
    localfaction=eventData.faction
    
    ForceSiegeAttack(faction) 
    end
    
    
    functionForceSiegeEquipment(faction)
    
    iffaction.isPlayerControlled==1thenreturnend;
    
    -- walk each army
    localarmiesNum=faction.stacksNum;
    
    forj=0, armiesNum-1do
    
    localarmy=faction:getStack(j);
    
    
    
    ifarmy.siegethen
    
    localsett=army.siege.besiegedSettlement;
    
    print('Found sieging army at ', sett.name)
    
    
    addRams(army);
    addTowers(sett, army);
    
    addLadders(sett, army);
    
    end
    end
    
    end
    
    

  9. #9

    Default Re: Get AI Army to build more siege equipment

    I see this is coming along pretty well. What do your addRams, addTowers, and addLadders functions look like?

  10. #10

    Default Re: Get AI Army to build more siege equipment

    Quote Originally Posted by Callistonian View Post
    I see this is coming along pretty well. What do your addRams, addTowers, and addLadders functions look like?
    The same as they did, posted here for posterity
    Code:
      function addRams(army)     -- always bring enough rams     if army.rams < 4 then         print('Added rams to a comp army')         army.rams = 4;     end end  function addLadders(settlement, army)     -- ladders only make sense between level 2 and 4, bring a lot                 if settlement.level > 1      and settlement.level < 4     and army.ladders < 6 then         print('Added ladders to a comp army')         army.ladders = 6     end end  function addTowers(settlement, army)     -- big settlements can only get towers, so bring enough of them     if settlement.level >= 4      and army.towers < 6 then         print('Added towers to a comp army')         army.towers = 6     end      -- siege towers only make sense for level 3 and higher     if settlement.level > 2      and army.towers < 4 then         print('Added towers to a comp army')         army.towers = 4     end end
    I'll make them more interesting at some point, but they do the job. I was mostly splitting them into functions because the IDE was whining about function complexity lol. This is a bit on the backburner as I'm attempting to deal with some other siege mechanics I'll post in a later thread about. Namely - when a comp sieges, it sticks around and doesn't abandon the siege; or when a crusading army gets to its target, it'll hopefully do the job rather than sit there after chickening out haha. About to try out your scriptCommand/label method above and see if that does it.

  11. #11

    Default Re: Get AI Army to build more siege equipment

    Ah, I didn't see that you were assigning the number of equipment. Really cool that it works out, but I wonder how the game decides which units get the equipment. And what happens if you assign more equipment than you have infantry units or more equipment than you have total units?

    You should make sure these assignments are saved in the campaign file. If not, it might affect when you want to do the function calls.

  12. #12

    Default Re: Get AI Army to build more siege equipment

    Quote Originally Posted by Callistonian View Post
    A) but I wonder how the game decides which units get the equipment.

    B) And what happens if you assign more equipment than you have infantry units or more equipment than you have total units?

    C) You should make sure these assignments are saved in the campaign file. If not, it might affect when you want to do the function calls.
    A) I haven't quite figured this one out. I adjusted the formation but that seems to only affect the default setup that the player uses. The AI then rejiggers it to what they want during deployment. Which is to say that they ignore the formation and do their own formation somehow before the battle starts. Though i could be wrong about this?

    B) It will spawn equipment for each infantry unit, whether they decide to use it or not. E.G. if you have 2 rams, 4 ladders and 4 towers and you have 10 infantry units, it will spawn all the equipment but the AI still backlogs a ram rather than pushing both forward. (When I siege with rams, I bring 3-4 and push them all at the same time so that they are unlikely to all get set on fire before being relatively safe by the wall)

    On your other note here, if you spawn more equipment than they have infantry, they keep 1 ram (?) and then spawn the equipment used by the rest of the force. The rest goes unspawned. My guess on this is that the ram is the one produced by the AI where as the other equipment is what I spawned? Not sure. But it means I'll evolve the logic so that they always have 2-3 rams and the rest will scale the walls up to some max value.

    The sieges are interesting - the comp (as others have stated in various threads) will only do one ladder set or tower per wall section, so it spreads quite far as the amount of ladders/towers goes up. Great for an outnumbered or outclassed player; and (if nothing else) more interesting for the player.

    C) This is worth checking, but I think the only time it could get goofed is if the player saves right before the battle and reloads, since it's spawned right before they'd launch an attack. (I have adjusted it to be OnCharacterTurnStart instead of OnSettlementTurnStart) On the other news: I'll post a thread (in this subforum?) with an example script to cause sticky-sieges or force an army to launch its siege. Just got it working this morning, now just have to tinker with it. In essence, crusading armies will launch their attack ASAP, and other armies will no longer lift sieges; but might maintain them to cause attrition until they hit some threshold win%. Finally, I'm hoping to have armies that are standing near a settlement siege it rather than just stand in your way "thinking about sieging you" while you build up a big defense; or make the AI more aggressive about sieging a settlement in general. Anyway, Lots to do!

  13. #13

    Default Re: Get AI Army to build more siege equipment

    As requested, here is the more refined script and I don't intend to update it here further.
    Code:
    function addAiSiegeEqiupment(settlement)
        for i = 0, settlement.siegesNum-1 do
            local siege=settlement:getSiege(i);
            local army = siege.besieger;
            if army then
                if army.faction.isPlayerControlled == 1 then return end;
                
                local numinfantry = numInfantryUnits(army);
                print('Found sieging ai army - adding equipment. Num infantry: ', numinfantry);
                
                -- track how many things we've added so additional rams still spawn
                numinfantry = addRams(army, numinfantry);
                numinfantry = addTowers(settlement, army, numinfantry);
                addLadders(settlement, army, numinfantry);
            end
        end
    end
    
    
    -- rams are unique in that we want buffer troops for the extra rams to spawn.
    function addRams(army, numinfantry)
        if not numinfantry then return 0 end
        --print('Added rams to a comp army')
        if numinfantry > 7 then
            army.rams = 3;
            return numinfantry - 3
        end
        army.rams = 2;
        return numinfantry - 2
    end
    
    
    function addTowers(settlement, army, numinfantry)
        if not numinfantry then return 0 end
    
        -- siege towers only make sense for level 3 and higher
        if settlement.level > 2 
        and army.towers < (settlement.level + 3) then        
            local maxtowers = math.min(numinfantry, math.random(settlement.level + 3))
            local oldTowers = army.towers
            army.towers = math.max(army.towers, maxtowers)      -- never reduce the number of towers
            --print('Added towers to a comp army. Old towers '..oldTowers..' New towers '..army.towers)
            return numinfantry - army.towers
        end
    
        return numinfantry
    end
    
    
    function addLadders(settlement, army, numinfantry)
        if not numinfantry then return 0 end
    
        -- ladders only make sense between level 1 and 5, bring a lot            
        if settlement.level >= 1 
        and settlement.level < 5
        and army.ladders < numinfantry then
            local newnumladders = math.min(numinfantry, 6)
            --print('Added ladders to a comp army, total = '..newnumladders)
            army.ladders = newnumladders
        end
    end
    Works great. I've lost a normal siege or two even with adequate garrisons. If nothing else they're much more interesting in the average case.

    Some might thing this is a tad too much, if so they can modify numInfantry directly or subtract more in the AddRams stage so that less ladders/towers are produced.

Posting Permissions

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