Results 1 to 19 of 19

Thread: Rome 2 Scripting tutorial reloaded

  1. #1

    Default Rome 2 Scripting tutorial reloaded

    Hey guys,
    I’m by no means a scripting pro – quite the contrary: at the moment, I’m trying to get into this rather complicated field of modding a bit farther, so I know quite well that there’s almost no support for future scripters from the community out there (anymore). For that reason, I thought it might be helpful to share my recently acquired knowledge with you, being it as insignificant as it could be. My main goal is to provide some more detailed explanation and examples of the way scripts work in Rome 2, so the start is less tough for a beginner. As I will hopefully progress with my scripting skills, I will expand this tutorial further. Please note: while I won't be covering basic programming principles (like for example loops, functions etc.) here, I will show how to apply them in practice and point to some Lua-specific topics when needed.


    First off, here’s a table of content:

    1. Tools and resources – the environment section
    2. Basic introduction to Rome 2 Lua scripts in general – in addition to existing tuts
    3. Concrete examples
    4. Hints and observations regarding various built-in functions and how to use them (or not).
    5. Maybe some advanced scripting stuff, but this is arguable at the moment.

    So let's check it out.


    Tools and resources – the environment section

    When I said that there's very little support for scripting in Rome 2, I actually meant that unfortunately most skilled modders have advanced to other games or (temporarily) retired. Further, there have never been that many who really had an extensive clue of scripting and shared their knowledge broadly with noobs like me ;-P However, there do exist some good tutorials which must be mentioned here, because they provide a solid base knowledge: honour to whom honour is due.


    Magnar's Introduction to Scripting

    Great stuff, very essential. Daruwind has added some important links including other helpful minor tutorials/instructions, such as scripting an army and adding money to AI factions. I won't repeat these links here, just scroll to the second post in the link above.


    A tutorial by Mitch on political parties (that's rather complicated stuff, be warned!)

    [More to come if I can find it]



    Sadly, this covers imho the most important tutorials yet; however, there's more stuff that a Rome 2 scripter will find helpful:


    TW Attila's scripting documentation, mainly the last two points on the list („Campaign Script Interface“ and „Extra Scripting Guide“.

    This is MOST vital, something you'll probably use A LOT and a thing I've missed for years. It's not a guide in fact (as the name suggests), but a documentation of the Lua functions supported by the game and, most important, the arguments that these functions take and which interface provides them. I will explain that later in more detail. Oh, and don't let the fact that it's for Attila disturb you. Some Lua functions are in use since Empire and still work. Some of the functions listed here are not implemented any more (meaning you can't call them ingame), others are Attila specific (which ones is quite obvious), so if you constantly fail to get a function to work, it's probably due to the function not being supported anymore.
    A general advice on the list:
    apparently, it was written up in a somewhat sloppy manner, meaning that I have encountered some inconsistencies and typos in the functions. At some points, I'm pretty sure the list is plainly wrong (for example, UNIT_LIST_SCRIPT_INTERFACE:item_at() is said to return a UNIT_LIST_SCRIPT_INTERFACE – which wouldn't make any sense. Although I haven't tested it, it's rather a UNIT_SCRIPT_INTERFACE, which makes quite a difference). So use with caution!

    Given the importance of this documentation, I've found its formatting to be quite a disgrace. You have to cross-search it a lot, but the respective sections are not linked to each other as they should, making you rape your scroll wheel each time you search something...
    For that reason, I've created an excel version of the documentation, which you can find here. Hope you find it useful.


    A Lua syntax checker

    Using this website you can check your code for syntax errors, without doing lots of manual search and try/error testing (thats sooooooo annoying!). Of course, this won't cover logical errors, like functions that aren't implemented in the fame any more or wrong arguments.


    Other modder's scripts: no link here

    The main source of knowledge is existing scripts by CA or other modders. Do not take that as an invitation to just copy their work and pretend it's yours! But in most cases, seeing how someone else solved a problem and then mimicking him is the best (or only) way to learn something. Therefore, I recommend you to download DeI and save their scripts as a reference on your local disc. That is probably the most advanced and holistic scripting you can get for Rome 2 – just brilliant work. Do the same for CA's scripts, especially the prologue campaign's scripts (the prologue is heavily scripted and provides surprisingly many examples of the use of important functions), as well as the CiG and HatG stuff. Hint: if you don't possess these DLCs, you can check various mods – some of them have the lua files of the DLC campaigns imported, so you can extract them.

    More on scripting tools and related stuff my come, but the most important points have been convered imho so far. Feel free to give me feedback if you think something important is missing or lave a general comment.
    I will post the other sections of the tutorial one by one later, as they aren't completely written up yet.
    Last edited by Fridericus Secundus; February 28, 2019 at 07:17 AM.

  2. #2

    Default Re: Rome 2 Scripting tutorial reloaded

    Basic introduction to Rome 2 scripts

    So let's get started with the interesting stuff. Personally, I think it's advisable to have at least a basic understanding of what you're actually doing when you run a script in Rome 2, so let's have a closer look at that first of all.
    As Magnar pointed out in his tutorial, scripting is based around certain events like a turn being ended or a new campaign being started. You could expand this info a bit by saying that the game provides a bunch of so-called “interfaces” a modder can use to analyse the current state of the game - and then modify it. Events are one of these interfaces (at least of some kind). That means that you cannot influence every single aspect of the game (that's a basic but quite important info) – you have to check for an appropriate opportunity provided by the game, which is represented by an event. Magnar said that all events that are supported by Rome 2 are listed in events.lua, which is more or less correct. However, I've come by some events that in fact do not work at all, or I was just too silly to get them to work in several hours of trying... So handle the events list with a bit of caution: for example, the “settlementOccupied” event is unfortunately not implemented, or not the way we'd assume it works. Anyway, in most cases, the very basic events are perfectly sufficient. These are:
    factionTurnStart – fires every time a faction ends its turn, covering every singe AI faction as well (not automatically the human player's turn!)
    factionTurnEnd – same as above, but at the end if a turn
    newCampaignStarted – fires during the loading screen before you get the faction intro. Imho, here's the point where the map is being generated and all AI factions perform their first turn (please correct me id that's not right, I'm not 100% sure. But if I remember correctly, AI factions continue with turn == 2 when the player clicks “end turn” for the first time)
    characterSelected – when the player clicks on any character
    settlementSelected – same as above for settlements
    You can also find a bit of info on the more sophisticated events at the top of the campaign script guide for Attila in the TW Wiki (at least before it was removed... maybe there is a backup somewhere?). What could be useful and is worth noting here:
    gameSaved – fires once the game was saved, e.g. at every auto save, quick save and regular save. This is VERY useful because it allows a modder to store script variables inside the savegame file and reload them later
    gameLoaded – do something when the game was loaded. There's a caveat for the built-in save/load functions, but we'll discuss that later.
    [tbc if recommended]
    If you've found the event fitting best to your planned script, you can connect both by adding a so-called callback. That works similar to a newspaper subscription: you subscribe and now you'll get a paper every time it comes out. So if you write
    Code:
    scripting.gameInterface:addEventCallback(name_of_event, your_function_name)
    you tell the game to try executing your function (“your_function_name”) every time the specified event (name_of_event) takes place – like, for example, on every single faction’s turn start.
    That being said, we can start right off writing our first small script. For demo purposes – and because it's quite useful for the further work. It will also give me the opportunity to explain the details of how the other interfaces of the game engine (apart from events) do work:
    Code:
    -- --------------------------------------------------------
     -- -- FOW - Remove the fog of war
     -- ---------------------------------------------------------
     --
     local function removeFOW(context)
              local component = UIComponent(context.component):Id()
                  if component == "button_hud_chat" then
                          scripting.game_interface:show_shroud(false)
                  end
     end
     
    
     
     local function recoverFOW()
              if scripting.game_interface:model():is_player_turn() == true then
                      scripting.game_interface:show_shroud(true)
              end --if
     end
     
    
     
     scripting.AddEventCallBack("ComponentLClickUp", removeFOW)
     scripting.AddEventCallBack("FactionTurnEnd", recoverFOW);


    The first of these two functions will remove the fog of war from the campaign map – that is, once the “chat” icon in upper left corner is clicked. At the end of each turn, the second one recovers FOW to the normal state. Of course I wrote that SOLELY for debugging purposes and NOT AT ALL for cheating :-D But honestly: if you want to check certain things like whether a scripted army did actually spawn as intended or not, this is most helpful. We’ll add another script later that enhances this one – a debug spy.
    Let’s leave the commented stuff on top aside for the moment– however, I would generally strongly recommend to comment your code for documentation reasons! This is not only a matter of coding style, but also essential if you want to get back to your scripts after a long break and you can’t remember everything in detail.
    The first line declares a new local function with the name ‘removeFOW’, accepting the variable ‘context’ as an argument. There is a caveat with the keyword ‘local’ in LUA, since it restricts a function to a certain scope. It’s a bit wordy to explain – this Q/A on Stackoverflow however explains it perfectly. In short terms, be careful when you call local functions from other local functions – this might not work, unless you have placed the target function in your code above the calling function (LUA is generally parsed top down). If you don’t like that, you could remove the ‘local’ keyword from the target function, making it a global function that can be accessed from anywhere in your script, but that could be considered bad practice in some cases. Why I’m harping on that point so much is because I’ve spent hours trying to find out why a specific function was never called, although my code was alright… Generally, most functions I’ve encountered so far were declared local, which in my understanding is generally good style. If it’s needed just locally, keep it local and don’t mess around with global stuff.

    Same principle is for the variable ‘component’ on the next line. It’s needed just inside that local function and not elsewhere in the script, so I’ve marked it local. This assures that the variable and its value will be know to the programme just when it executes the function. Having ended, the programme ‘forgets’ the variable ever existed. Useful side effect is that you can reuse the variable name another time and there won’t be confusion.
    The ‘Component’ variable is filled with the return value (a String in this case) of a function called id() which is itself executed on the return value of a function called UIComponent(). This function call apparently requires a parameter of type ‘component’. No idea how to obtain that value? Well, luckily we have ‘context’. Context is what I call a ‘Gandalf feature’: sometimes there, sometimes not, if there it’s mostly helpful but all in all its a rather misty thing. In my understanding, context is some kind of object (in an Object Orientated Design sense) that is generated on the event calling the function. So basically, scripting.AddEventCallBack() is what must be generating the context object and therefore, every callback function has to provide an argument for it. From context, you can get directly a bunch of info about the current state of the game when the event fired. In the example above, it is the (UI) component the user clicked on. Obviously, the stuff that can be obtained from context varies depending on the event that was used before; in our case that is ‘ComponentLClickUp’. Hence, it is quite obvious that context contains information named ‘component’. On the other hand, don’t expect something like ‘FactionTurnEnd’ to provide component (thus, UI related) stuff. Unfortunately, I haven’t found a suitable documentation on context, so its use comes down to a game of guessing and try / error in the end.
    In the next step we check for a certain value, a UI component (button) named ‘button_hud_chat’. But once again: where do I get that name from? Well, I wrote a small, custom logging function that prints stuff from the game to a plain text file. We’ll get to that later, for the moment it’s sufficient to know that in the first place, I decided to link that FOW removal with the chat button – something I will never use in a singleplayer campaign for sure, so I can avoid unwanted side effects. Basically, the first version of the function looked like that:
    Code:
    local function removeFOW(context)
              log(UIComponent(context.component):Id());
     end
    Very simple: log() is what my logging tool is called, so after clicking on the ‘Chat’ button ingame I had what I needed.
    Inside the if condition is the interesting stuff, because it contains a rather easy sample entry into the world of Rome 2 scripting interfaces. As said above, the game provides a bunch of interfaces a modder can use to interact with the game. There are two main ways to obtain them: either using the mentioned context variable, which is a more situational approach depending on the preceding event; or using ‘Campaign Script Interface’ as it’s called in CA’s documentation. (Note: it’s also called like that in my scripting spreadsheet, where it is also the initial table after opening the file). In the game scripts, this interface is called or accessed by the term scripting.game_interface() - mostly without the brackets but followed by double points and more function calls. Trust me, you’ll write that A LOT. It provides the central interface from which you can obtain any other, specialized interface at any point (hence not depending on the current conditions like context). If you decide to put your scripts into a separate file instead of appending it to scripting.lua (which is advisable, we’ll get back to that later), make sure you write/copy this line to the top of your file:
    Code:
    local scripting = require "lua_scripts.EpisodicScripting"
    This imports (‘require’) another file called EpisodicScripting, located in a folder called lua_scripts into your current file, so the game knows that it has to take into account ALL of the stuff inside EpisodicScripting when executing your code. Without that line of code, most likely no single function in your script will work!
    OK, so we have imported EpisodicScripting, and we have now a brand new game_interface object. What to do next? Looking at the spreadsheet’s Campaign Script Interface section, we see a lot of functions – basically, you can now execute any of them by appending them to your call, e.g.
    Code:
    scripting.game_interface: apply_effect_bundle("effect_bundle_key", "faction_key", turns)
    or show_shroud(false) as we do in our script above. Almost all of these functions have direct effects, described in the column next to their name in the spreadsheet. There’s one exception, however, and that is the ‘model()’ function. This is where you can get access to the remaining interfaces, because it actually returns a model_script_interface object. If you take a look at the list of interfaces on the left side of the spreadsheet, you’ll notice an according item. Take a look at this interface; it provides much essential information about the current state of the game, with a broader scope than the context interface. Inspecting the column with the return values, you’ll notice several functions returning other interfaces also listed in the index. And that’s how you get to the stuff you need.
    In most cases, if you intend to script a certain feature, you’ll first want to find out which function meets your intention. Then you go and check out the interface containing the needed function, and last of all, you try to find a way to access that interface from the campaign script downwards. A bit like pathfinding: what’s the best (shortest) way from campaign script to, say, building_script_interface… Note that there will probably be many cases where more than one path leads to your goal. Let me illustrate this with another example: for one function I needed a list of all regions currently under control by Carthage. Precisely, I needed the concrete amount, a number, to iterate over it. So I wrote:
    Code:
    scripting.game_interface:model():world():faction_by_key("rom_carthage"):region_list():num_items();
    I had to connect campaign_script_interface with region_list_interface, with the condition that at some point the owning faction (Carthage) had to come in. This is the way I solved it, maybe I could have used other paths as well. This is left to your creativity ;-)
    Last edited by Fridericus Secundus; February 28, 2019 at 07:25 AM. Reason: Added part 2

  3. #3
    Jake Armitage's Avatar Artifex
    Patrician

    Join Date
    Apr 2011
    Location
    apartment 6
    Posts
    4,694

    Default Re: Rome 2 Scripting tutorial reloaded

    That's much appreciated!

    Scripting could be a real pain in the.
    I'm doing them only by mockering and trials.

    Well, that excel sheet is really useful. Thank you!

  4. #4

    Default Re: Rome 2 Scripting tutorial reloaded

    Quote Originally Posted by Jake Armitage View Post
    That's much appreciated!

    Scripting could be a real pain in the.
    I'm doing them only by mockering and trials.

    Well, that excel sheet is really useful. Thank you!
    Thanks Jake! Totally agree with you - scripting is a really rough field, but extremely powerful if done correctly.
    I've not forgotten this thread and the second part is well on its way, but I'm a married father of a little son and that's plenty of work, too :-P Further, I was quite busy over the last weeks with other tasks and projects, so the tutorial took longer than expected. But I will post the other parts, just give me a week or two.

  5. #5
    Jake Armitage's Avatar Artifex
    Patrician

    Join Date
    Apr 2011
    Location
    apartment 6
    Posts
    4,694

    Default Re: Rome 2 Scripting tutorial reloaded

    Notice that in your list there are functions that does not fit Rome 2

  6. #6

    Default Re: Rome 2 Scripting tutorial reloaded

    Thanks for the great resources! One issue:

    Quote Originally Posted by Fridericus Secundus View Post
    TW Attila's scripting documentation, mainly the last two points on the list („Campaign Script Interface“ and „Extra Scripting Guide“.
    While archive.org indicates that this link used to work, it seems that you now need to add .html to the URL:

    TW Attila's scripting documentation

    Oh, and it seems that the links in that page are similarly broken; you need to add .html to the URLs.
    Last edited by ♔Greek Strategos♔; April 21, 2022 at 10:30 AM. Reason: Double post.

  7. #7

    Default Re: Rome 2 Scripting tutorial reloaded

    Quote Originally Posted by Jake Armitage View Post
    Notice that in your list there are functions that does not fit Rome 2
    Thanks for pointing out - that's (unfortunately) correct. As mentioned above, the list has been written for Attila and therefore provides anachronistic functions. I didn't have the time to filter them out, but most are rather obvious IIRC.I do have a bit of spare time right now and I'll try to continue this tutorial, although some of the knowledge mentioned above about specific scripting tweaks and issues is probably lost :-(

    Fixed broken Link and added part two. Please feel free to correct any errors you encounter (I'm really no pro and some things are just how I personally figured them out) or give feedback!
    Last edited by ♔Greek Strategos♔; April 21, 2022 at 10:31 AM. Reason: Double post.

  8. #8
    ~Seleukos.I.Nikator~'s Avatar Campidoctor
    Join Date
    Nov 2010
    Location
    The United Europe, currently residing in Norway
    Posts
    1,642

    Default Re: Rome 2 Scripting tutorial reloaded

    Hey Fridericus,

    Thanks for doing this! It's a very important contribution as there is so little help that one can find online with respect to scripting in lua for Rome 2.

    Please, keep it up and I will certainly have some of my thoughts to share here as well.

    One of the things that I guess might be quite useful would be to collect all the lua scripts from Rome 2 and all its dlcs in one text file so that one can easily find any references to how certian functions are used in the vanilla game.

    What do you, guys, think?

  9. #9

    Default Re: Rome 2 Scripting tutorial reloaded

    Quote Originally Posted by ~Seleukos.I.Nikator~ View Post
    Hey Fridericus,

    Thanks for doing this! It's a very important contribution as there is so little help that one can find online with respect to scripting in lua for Rome 2.

    Please, keep it up and I will certainly have some of my thoughts to share here as well.

    One of the things that I guess might be quite useful would be to collect all the lua scripts from Rome 2 and all its dlcs in one text file so that one can easily find any references to how certian functions are used in the vanilla game.

    What do you, guys, think?
    Many thanks, Seleukos, and feel free to contribute - that's very much appreciated! Good idea with the script collection, btw... I'll gather that together, but I can just add vanilla and Empire Divided scripts since I do not possess the others. Which is odd, because CiG e.g. has some very decent scripts explaining stuff like army spawning quite well. Just thinking how to do that in a usable way, because honestly I think that a large txt file would be very hard to search.
    Currently, I'm writing part three and thinking about a new script - both for demo purposes and my own pleasure, because one of the major problems in ROme II (as well as any TW game so far) is late game challenge. Or, more precisely, the lack of it; you basically drown in money and armies once you have survived early and beginning of mid game. No AI faction can withstand, so I'm considering to boost the civil war feature or separately script usurpators with strong armies somewhere in your hinterland, forcing you to plan army locations more carefully.

  10. #10
    Jake Armitage's Avatar Artifex
    Patrician

    Join Date
    Apr 2011
    Location
    apartment 6
    Posts
    4,694

    Default Re: Rome 2 Scripting tutorial reloaded

    Quote Originally Posted by ~Seleukos.I.Nikator~ View Post
    Hey Fridericus,

    Thanks for doing this! It's a very important contribution as there is so little help that one can find online with respect to scripting in lua for Rome 2.

    Please, keep it up and I will certainly have some of my thoughts to share here as well.

    One of the things that I guess might be quite useful would be to collect all the lua scripts from Rome 2 and all its dlcs in one text file so that one can easily find any references to how certian functions are used in the vanilla game.

    What do you, guys, think?
    I've begun to do something similar into this excel sheet months ago but I stopped, don't even remember what scripts I checked, honestly.
    Probably all data_Rome2 vanilla script with the exceptions of some export scripts

    https://www.mediafire.com/file/ne2hz..._v1.1.ods/file
    Last edited by Jake Armitage; March 11, 2019 at 06:51 PM.

  11. #11

    Default Re: Rome 2 Scripting tutorial reloaded

    Hello,

    I am trying to follow as many examples and instructions i can find. but i cant make it right :
    - i want the correct syntax to check if the region where an OnPendingBattle event takes place and compare it to a list ( to make sure it's on said list, wich is based on factions starting regions) => the goal is to alter auto-resolved battles outcome with bonuses or penalties depending on the conditions which are the current factions involved as attacker/defender and the current region where the battle takes place

    I have tried many times but it does nothing ingame when testing.
    - How do i set up the correct syntax for checking if current event region matches at least ONE of the names in a list/array (of names like "rom_regionname") ?

    As an example :

    One of my "arrays" :

    core_regions_seleucid = {
    "emp_syria_antioch",
    "emp_syria_tarsus",
    "emp_syria_thapsacus",
    "emp_mesopotamia_ctesiphon",
    "emp_mesopotamia_charax",
    "emp_mesopotamia_hatra",
    "emp_mesopotamia_edessa",
    "emp_palmyra_dura",
    "emp_palmyra_palmyra"
    };

    one of my current check :

    if attacking_faction:name() == "rom_seleucid" and scripting.game_interface:model():world():region_manager():region_by_key(core_regions_seleucid[i])

    What am i doing wrong. I can use the DeI script for auto-resolve to alter the results of AI battles, but i cant find how to add this region check to make my own specific script ... I know the scripts is read everytime i reload the game because i have seen the changes ( good or bad). Many examples are confusing or incomplete, at best they offer some form of info but then a bit of code that dosent do what i want shows up and breaks my understanding of what it does or how i could manipulate it ...

    Thank you for your help.
    Last edited by Sylla; July 26, 2021 at 12:50 PM.

  12. #12
    Jake Armitage's Avatar Artifex
    Patrician

    Join Date
    Apr 2011
    Location
    apartment 6
    Posts
    4,694

    Default Re: Rome 2 Scripting tutorial reloaded

    for saying it quickly, you'll have to link the region name to attacker/defender character

    conceptually:

    -- this is the variable we'll need to register the region name
    local _VAR_Attacker_Region_Name = context:pending_battle():attacker():region():name()

    -- then you put the region library as a condition

    if core_regions_seleucid[_VAR_Attacker_Region_Name]
    then...

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

    you may want to check testudo's autoresolve script, is more elaborated than vanilla dei's and is using faction libraries as conditions
    by following those samples and adding region conditions you should solve the puzzle
    https://www.mediafire.com/file/josp9...olve.pack/file
    Last edited by Jake Armitage; July 27, 2021 at 06:32 AM.

  13. #13

    Default Re: Rome 2 Scripting tutorial reloaded

    Hi,

    So i have been following your examples and came up with this function but it still wont work as intended. While the vanilla dei script and testudo scripts brings many examples, it's never quite what i want so i'm still confused with the code. I tried to do my own following my original logic : if the attacker/defender in any AI auto-battle is in its own regions (definded by a fixed list) then large AR bonus to whichever situation (defense / attack).

    Code:
    function CORE_REGION_AUTORESOLV(context)
    
        local attacker = context:pending_battle():attacker():faction() -- defines the attacking faction name
        local defender = context:pending_battle():defender():faction() -- defines the defending faction name
        local attacked_region = context:pending_battle():attacker():region():name() -- defines the region where the attacker is in
        local defended_region = context:pending_battle():defender():region():name() -- defines the region where the defender is in
        local attacker_is_local = false -- variables for checking if attacker and / or defender is local to the region or not
        local defender_is_local = false -- variables for checking if attacker and / or defender is local to the region or not
        
        
    if attacker ~= is_human() and defender ~= is_human() 
     then Log("attacker / defender is AI") -- if player not involved apply bonuses / penalties to AR
        if core_regions_seleucid[attacked_region] and attacker == "rom_seleucid" -- attacker is seleucid in seleucid home region
        then 
           attacker_is_local = true
           Log("attacker home region")
           scripting.game_interface:modify_next_autoresolve_battle(7, 0.3, 0.3, 7, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
        elseif core_regions_seleucid[defended_region] and defender == "rom_seleucid" -- defender is seleucid in seleucid home region
        then
           defender_is_local = true
           Log("defender home region")
           scripting.game_interface:modify_next_autoresolve_battle(0.3, 9, 9, 0.3, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
        elseif attacker_is_local == false and defender_is_local == false -- attacker / defender is foreign to the region, let the AR proceed as normal
        then   
           Log("foreign faction in foreign region") -- no actions for AI vs AI in foreign regions
        end
    else
        Log("attacker / defender is Human") -- if player involved no bonuses / penalties to AR
    end
    end
    The syntax seems ok (as a LUA), but still ...

    I have a test battle between an AI emerging faction (osroene) and seleucids at the edessa settlement and the rebels wins the battle everytime. If i use the original script from dei or the one from testudo the rebels never win this battle. Another thing bothers me, i tried to use some form of logging but it only records one line, not the entire information ( i think it records only the last log entry of whichever script ran last ...). Without proper logging it's hard to see where exactly my code fails if it is even called at some point ...

  14. #14
    Jake Armitage's Avatar Artifex
    Patrician

    Join Date
    Apr 2011
    Location
    apartment 6
    Posts
    4,694

    Default Re: Rome 2 Scripting tutorial reloaded

    the variable
    "local attacker = context:pending_battle():attacker():faction()"
    is ok when you define the "is_human()" condition
    bot not when you use it for registering the name, here
    "and attacker == "rom_seleucid"

    you'll need to build another variable like
    "local attacker_name = context:pending_battle():attacker():faction():name()"

  15. #15

    Default Re: Rome 2 Scripting tutorial reloaded

    Ok so i made a few changes, remove my placeholder variables which are not useful atm. However, still no luck with the test battle. There is still the other issue. the script has a basic logging function, but it doesnt record anything regarding my autoresolv function, meaning the script fails even before its supposed to do anything ?

    I have defined the attacker/defender faction name and the attacker/defender region names as well as the attacker / defender basic nature to check if human.
    So first test :
    - is attacker or defender a human ?
    - - if no then proceed

    Second test i chose to check immediately if the attacker region is inside a previously defined list (which is above my main function atm = does it need to be inside the function instead ?)
    - - if region_list contains my attacker region name AND if the attacker faction name is the supposed owner of said region (actual or previous) then AR bonus for attacker (in order to help the owner / previous owner to do battle in this region since its/was theirs in the first place)

    Things may then get repeated for the defender trigger which is the same but with the defender condition = maybe i misunderstand what the attacker_region = context:pending_battle():attacker():region():name() represents ... and so it fails as a correct trigger / condition later ?


    Code:
    local function OnPendingBattle(context)
        local attacker = context:pending_battle():attacker():faction()
        local defender = context:pending_battle():defender():faction()
        local attacker_name = context:pending_battle():attacker():faction():name()
        local defender_name = context:pending_battle():defender():faction():name()
        local attacker_region = context:pending_battle():attacker():region():name()
        local defender_region = context:pending_battle():defender():region():name()
    
    
    if attacker ~= is_human() and defender ~= is_human() 
     then Log("attacker / defender is AI") -- if player not involved apply bonuses / penalties to AR
        -- SELEUKIDAI --
        if core_regions_seleucid[attacker_region] and attacker_name == "rom_seleucid" -- attacker is seleucid in seleucid home region
        then 
           Log("attacker in home region")
           scripting.game_interface:modify_next_autoresolve_battle(7, 0.3, 0.3, 7, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
        elseif core_regions_seleucid[defender_region] and defender_name == "rom_seleucid" -- defender is seleucid in seleucid home region
        then
           Log("defender in home region")
           scripting.game_interface:modify_next_autoresolve_battle(0.3, 9, 9, 0.3, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
        else  
           Log("foreign faction in foreign region") -- no actions for AI vs AI in foreign regions
        end
    else
        Log("attacker / defender is Human") -- if player involved no bonuses / penalties to AR
    end
    end
    -- Event callbacks --
    scripting.AddEventCallBack("PendingBattle", OnPendingBattle);
    I did a simplified version for the sake of testing if something was wrong with th array or something else, no results with a simplified check. Below i just wanted to target a specific settlement for the next AR battle, if region is edessa but attacker isnt seleucid then penalty for the attacker :

    Code:
    function OnPendingBattle(context)    local attacker = context:pending_battle():attacker():faction()
        local defender = context:pending_battle():defender():faction()
            local attacker_name = context:pending_battle():attacker():faction():name()
        local defender_name = context:pending_battle():defender():faction():name()
            local attacker_region = context:pending_battle():attacker():region():name()
            local defender_region = context:pending_battle():defender():region():name()
    
    
    if attacker ~= is_human() and defender ~= is_human() 
     then Log("attacker / defender is AI") -- if player not involved apply bonuses / penalties to AR
        -- SELEUKIDAI --
        if attacker_region == "emp_mesopotamia_edessa" and attacker_name ~= "rom_seleucid" -- attacker is seleucid in seleucid home region
        then 
           Log("EDESSA REGION")
           scripting.game_interface:modify_next_autoresolve_battle(0.3, 7, 7, 0.3, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
        else  
           Log("NOT EDESSA") -- no actions
        end
    else
        Log("attacker / defender is Human") -- if player involved no bonuses / penalties to AR
    end
    end
    -- Event callbacks --
    scripting.AddEventCallBack("PendingBattle", OnPendingBattle);
    This is driving me crazy but i guess thats because i really want it to work :p

    Ok so, after comparing my code with the (mind blowingly amazing) ones from DeI, i was successful in a first real attempt it seems :

    Code:
    local function CORE_REGION_AR_BONUS(context)    local attacker = context:pending_battle():attacker():faction()
        local defender = context:pending_battle():defender():faction()
        local attacker_name = context:pending_battle():attacker():faction():name()
        local defender_name = context:pending_battle():defender():faction():name()
        local attacker_region = context:pending_battle():attacker():region():name()
        local defender_region = context:pending_battle():defender():region():name()
        local homeland = false
        
    if attacker:is_human() == false and defender:is_human() == false then
    ------ SELEUKIDAI ------
        if core_regions_seleucid[attacker_region] or core_regions_seleucid[defender_region]
        then
            local homeland = true
            if attacker_name == "rom_seleucid" and homeland == true then -- attacker is local 
               Log("SELEUCID REGION ATTACKED BY SELEUCIDS") -- LOCAL BONUS
               scripting.game_interface:modify_next_autoresolve_battle(7, 0.3, 0.3, 7, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
              else -- attacker is foreigner
                 Log("SELEUCID REGION DEFENDED BY FOREIGNERS") -- FOREIGNER PENALTY
                 scripting.game_interface:modify_next_autoresolve_battle(0.3, 7, 7, 0.3, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES  
              end
            if defender_name == "rom_seleucid" and homeland == true then -- defender is local
               Log("SELEUCID REGION DEFENDED BY SELEUCIDS") -- LOCAL BONUS
               scripting.game_interface:modify_next_autoresolve_battle(0.3, 7, 7, 0.3, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
              else -- defender is foreigner
                 Log("SELEUCID REGION DEFENDED BY FOREIGNERS") -- FOREIGNER PENALTY
                 scripting.game_interface:modify_next_autoresolve_battle(7, 0.3, 0.3, 7, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
              end
        else
            homeland = false
            Log("FOREIGN REGION") -- NO BONUS
        end -- IF REGION
    else
        Log("ATTACKER / DEFENDER IS HUMAN") -- NO BONUS
    end -- IF HUMAN
    end
    
    
    -- Event callbacks --
    
    
    scripting.AddEventCallBack("PendingBattle", CORE_REGION_AR_BONUS);
    My main erros where the check is_human (bad way to do it ...) and also my arrays of data for the settlements, they were built on an example i found on the web rather than inside another game script so it was missing a few things like the [] and =true, which i noticed in the testudo script "librairies"

    Code:
    --=======================---- Seleucid CORE REGIONS --
    --=======================--
    
    
    -- SELEUCID --  
        core_regions_seleucid = {
            ["emp_syria_antioch"]=true,
            ["emp_syria_tarsus"]=true,
            ["emp_syria_thapsacus"]=true,
            ["emp_mesopotamia_ctesiphon"]=true,
            ["emp_mesopotamia_charax"]=true,
            ["emp_mesopotamia_hatra"]=true,
            ["emp_mesopotamia_edessa"]=true,
            ["emp_palmyra_dura"]=true,
            ["emp_palmyra_palmyra"]=true,
        };
    I tested this during a rebel vs seleucid battle at edessa :
    - without the AR bonus, the rebels would always win (and not losing a single unit btw ...)
    - with the above code (although i'm not sure all the conditions works yet) seleucid defenders kept the city and the rebels were reduced from 10 units to three very badly hurt

    I'm going to try this with other factions that tends to disappear too soon or lose territory to "not really historically accurate" factions.

    EDIT: During the same turn of my Edessa test, an army of rebel cypriots were also trying to take over Salamis. I added Salamis to their own homeland and the script allowed them to take over the settlement (while before they would lose the battle everytime against the ptolemy). It seems the module is working for both attack and defense situation. All i have to do now is expand the number of "protected" land for specific factions as much as i can and see the result during a grand campaign for a good 25 to 50 turns at least. It's amateur work, but at least it works as intended for now :p !
    Last edited by ♔Greek Strategos♔; April 21, 2022 at 10:35 AM. Reason: Double post.

  16. #16
    Jake Armitage's Avatar Artifex
    Patrician

    Join Date
    Apr 2011
    Location
    apartment 6
    Posts
    4,694

    Default Re: Rome 2 Scripting tutorial reloaded

    well done

  17. #17

    Default Re: Rome 2 Scripting tutorial reloaded

    Hi,

    After long testing with different difficulty settings etc. i noticed its not fully working as intended :
    - first of all some faction still disappear very early on, the Katpatuka/rom_cappadocia for example gets wiped out very early by the Galatians or other neighbors. The celtici often gets destroyed by the lusitanians or their turdetani neighbor too ...
    - It seems some battles are going on with "vanilla" outcome => i did some errors in the spelling of some factions/regions and fixed as many as i could find (which did change the outcome of battles, but not always !?)
    - Even by having overwhelming AR odds against it, the AI can still conquer a "protected" region with ease (and a single army vs decent garrison + small to medium local army) : assaulter does take damage (previously i saw AI armies being in pristine condition after an assault), but it seems so does the defenders => when two enemy armies attack the same region, one of them sometimes takes massive damage and is lost but the second one succeed at taking over the settlement => which means that the code either breaks / doesn't work for multiple armies (but I don't think so) or the multipliers of the auto-resolve are indeed in favor of the defenders but in very negligible numbers (like the defender has an advantage but only by a thin margin and not a very large abyss like i wanted) => i corrected my code with values between 0-1 instead of the ridiculous 10-90 i used at first but it doesn't show major improvements

    Is it possible that all I'm doing is tweaking values that are not THAT significant with the AR results ? I know there is tables with multipliers for the autoresolver but I'm not sure which one i should edit to make results completely broken (for the sake of testing the odds, especially with multiple armies involved) ...

    I also simplified my faction/region blocs to a more compact form :

    Code:
    ------ DAORSI ------
        elseif core_regions_daorsi[attacker_region] or core_regions_daorsi[defender_region]
        then
            if attacker_name == "rom_daorsi" then -- attacker is local 
                Log("DAORSI REGION ATTACKED BY DAORSI") -- LOCAL ATTACK BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.95, 0.01, 0.01, 0.95, true) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            elseif defender_name == "rom_daorsi" then -- defender is local
                Log("DAORSI REGION DEFENDED BY DAORSI") -- LOCAL DEFENSE BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.01, 0.95, 0.95, 0.01, true) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            else -- attacker / defender is foreigner
                Log("DAORSI REGION DEFENDED BY FOREIGNERS") -- FOREIGNER PENALTY
            end
    The result of one of my testing was two armies of delmatae vs a small daorsi army and their capital garrison / one full army of liguria vs the veneti capital garrison :
    - After i fixed a spelling error in my code the Daorsi were able to defend their territory but the veneti lost the battle and were either conquered by liguria or replaced by their local emergent faction => in theory, even with a full army the ligurians should lose the battle considering the crazy odds against them.

    I checked the code and my region blocs etc to make sure i just didn't make more mistakes of region/faction spelling, but for those two factions everything seems OK. Soooo, what gives ?

    Lately, something even weirder happened on normal difficulty :
    - Kypros/gen_cyprus managed to take over salamis => this was the intended result => OK (normally they would lose the fight against ptolemy)
    - Sardeis/gen_sardes took over pessinous => they were also a faction i gave an advantage to emerge in their local region => OK (normally they would lose the fight against Lydia/rom_sardes)

    However !!!

    Osroene managed to take over Edessa and thus emerge against the Seleucids, which is not supposed to happen => Two possibilities (the fog of war was on so i'm not sure what happened) :
    - Another faction liberated the place (and thus conquered it, somewhat, against the odds) => very likely to be the reason
    - The rebels took over by winning the battle themselves (Edessa was definitely protected as per my early tests already showed) => very unlikely because rebels tend to attack with barely the necessary forces to conquer anything

    This should not happen for my protected regions, but it still does, only not all the time. The logs also still only record one line of code so i do see data from various factions blocs (meaning the code is probably read all the way to the end without errors) but it prevents me from debugging a particular battle.

    Hello,

    Thanks to the work of several other modders and the precious help of Jake i was able to complete a working autoresolve script which does the following :
    - protects regions from being conquered either by boosting the local owner attack/defense AR odds (except if attacks comes by sea unfortunately, creating some sort of weakness for coastal settlement but the drawback is minor after testing ...)
    - provide a crude but readable log (thanks to Zerk-Jenkins) which allowed me for the first time to record everything happening in my script and not one line (this was a frustrating issue, i assure you, so thanks man ! and thanks to Jake for bringing me this info)
    - allow some form of AI war deadlock, preserving many factions and their territories (almost all the starting ones, and also some emergent ones should they be able to, well, emerge !)

    The aim :

    Prevents the constant "faction bleeding away" and fading into nothing long before the player can interact / trade / fight them on the field (if he wants it, personally i like to vassalize as much as possible for a specific reason i'll state below) => trying to take over Gaul during the time of Caesar ?? Too late !! The Iceni have conquered it already after destroying of their Britannic neighbors ?? CAI Help me !!)
    - Seleucids gettings wiped out or reduced to Antioch only after a few dozen turns ?? Not anymore you pesky Ptolemies !!!
    - Lusitanians crushing all of spain and, why not, half of Gaul ?? Nope, you stay in your fishing village please, thank you ! And leave the Cantabri, Gallaeci alone please, i'm supposed to meet them in a few hundred turns :p !
    - Armenia getting destroyed by Kartli ?? Nope ! Neither is Armenia conquering all of steppe areas !! Just give up Armenian cataphracts, you are too slow for Scythians and Sarmatians horses !!
    - Illyrians factions getting destroyed by Odryssia ? ... Yeah, right, and like a hundred years before Rome even invaded the place ? I don't think so ! Turn 70 => Delmatae, Daorsi and Ardiaei alive and kicking (each other mainly) !!

    The main reason :

    As a fervent Rome & DeI mod fan i was amazed by the story driven / historical inputs given during the campaign through the mini-story scripts related to war with Carthage or Seleucids rebellion etc and all the other historical info message ingame.

    However, the grand campaign allows the AI to snowball their neighbor easily, and most of the time the outcome of my VH/N campaign was the same. Some factions, both "major" (historically speaking) or "minor" would disappear or be replaced by the emergent variant :
    - This deprives the player of a possible somewhat story-driven campaign ==> aiming to reenact some battles (like the siege of Agrigentum around 262BC or the fall of Saguntum during the Punic wars).
    - You can also have a proper conquest of Gauls and their many tribes, while previously, the local major faction would destroy most of them (the aedui were known as allied of Rome i think at the time of Ceasar and would have helped in its campaigns, but there is historical records of many meetings and interactions between Rome and the Gauls.

    There is the many small campaigns / mods depicting the Gallic wars or the punic wars, as an example. But what about having the opportunity (with more or less accuracy, depending of gameplay of course) to make your own actual mini-campaign by waiting a specific timeline and engaging specific factions, just for the sake of following history books as close as manageable considering gameplay ? Take your time and pass the time if you want, conquer and finish the game if you want, freedom is key in this game.

    This has always been my wish : after years of playing, i know the game (and its weird AI shenanigans), so i wanted something more casual and "scenaristic" but without the constraints of the standalone campaigns from the various DLC / mods.

    I don't know the correct rules for sharing a proper script and cannot take credit for the results because it's based on everything i saw in Litharion and the DeI team scripts, also in other mods i found here and there as well, which were tweaking Autoresolve bonus. Therefore i will simply share the structure of it :

    Code:
    --  CORE REGIONS --
    
    
    -- SELEUCID --
        core_regions_seleucid = {
        ["emp_syria_antioch"]=true,
            ["emp_syria_tarsus"]=true,
            ["emp_syria_thapsacus"]=true,
            ["emp_mesopotamia_ctesiphon"]=true,
            ["emp_mesopotamia_charax"]=true,
            ["emp_mesopotamia_hatra"]=true,
            ["emp_mesopotamia_edessa"]=true,
            ["emp_palmyra_dura"]=true,
            ["emp_palmyra_palmyra"]=true,
        };
    -- PTOLEMY --
        core_regions_ptolemy = {
        ["emp_aegyptos_alexandria"]=true,
            ["emp_aegyptos_diospolis"]=true,
            ["emp_aegyptos_memphis"]=true,
            ["emp_aegyptos_myos_hormos"]=true,
            ["emp_judea_jerusalem"]=true,
            ["emp_judea_tyros"]=true,
            ["emp_galatia_et_cappadocia_side"]=true,
            ["emp_libya_ammonium"]=true,
            ["emp_libya_paraitonion"]=true,
        };
    -- CARTHAGE --
        core_regions_carthage = {
        ["emp_africa_carthago"]=true,
            ["emp_africa_leptis_magna"]=true,
            ["emp_africa_hadrumentum"]=true,
            ["emp_africa_macomades"]=true,
            ["emp_sicily_agrigentum"]=true,
            ["emp_sicily_panormus"]=true,
            ["emp_corsica_et_sardinia_aleria"]=true,
            ["emp_corsica_et_sardinia_caralis"]=true,
            ["emp_hispania_ibossim"]=true,
            ["emp_hispania_nova_carthago"]=true,
            ["emp_mauretania_siga"]=true,
            ["emp_mauretania_tingis"]=true,
            ["emp_baetica_gades"]=true,
            ["emp_numidia_iol"]=true,
            ["emp_hispania_saguntum"]=true,
        };
    -- MACEDON --
        core_regions_antigonidai = {
        ["emp_macedonia_thessalonica"]=true,
            ["emp_macedonia_pharsalus"]=true,
        };
    -- ATHENS --
        core_regions_athens = {
        ["emp_achaia_athenae"]=true,
        };
    -- EPIRUS --  
        core_regions_epirus = {
        ["emp_macedonia_apollonia"]=true,
            ["emp_macedonia_pharsalus"]=true,
            ["emp_italia_brundisium"]=true,
        };
    
    
    -- BAKTRIA --
        core_regions_baktria = {
        ["emp_bactria_baktria"]=true,
            ["emp_bactria_eucratides"]=true,
            ["emp_transoxania_maracanda"]=true,
            ["emp_transoxania_bukhara"]=true,
        };
    
    
    -- My above "home regions" per factions are arbitrary, based on mostly what the game sets up at start of the DeI grand campaign startpos.esf, but also depending on my preferences
    -- Some starting territory of a large faction might not be protected, for the sake of randomness and to allow some liberties for the AI to conquer or liberate an emergent faction
    
    
    -- PENDING BATTLE, AUTORESOLVER BONUSES   --
    
    
    local function CORE_REGION_AR_BONUS(context)
        local attacker = context:pending_battle():attacker():faction() -- To check if human
        local defender = context:pending_battle():defender():faction()
        local attacker_name = context:pending_battle():attacker():faction():name() -- To check the factions involved by names
        local defender_name = context:pending_battle():defender():faction():name()
        local attacker_region = context:pending_battle():attacker():region():name() -- To check the current region of the battle, i havent tried it but i wish i could reduce this to an easier/simple "context:pending_battle():region():name()"
        local defender_region = context:pending_battle():defender():region():name()
    
    
    -- I exclude the player from the conditions, the original script checked if player was nearby or if faction was allied 
    -- But since my scripts protects factions regardless of their previous or current "importance" it doesn't matter because they will be protected in their homeland or crushed in (most) of their neighbors territory
    
    
    if attacker:is_human() == false and defender:is_human() == false then
    
    
    -- I then start a first "faction bloc", of whatever list i fancy, i can simply add factions to the bloc or make several blocs, i chose to do this by culture except for the first (my "major factions")
    
    
    ------ SELEUKIDAI ------
        if core_regions_seleucid[attacker_region] 
        or core_regions_seleucid[defender_region]
        
        then
            
            if attacker_name == "rom_seleucid" then -- attacker is local
                
                LuaLog("ASSAULT OF "..context:pending_battle():attacker():faction():name().." VS "..context:pending_battle():defender():faction():name().." IN "..context:pending_battle():attacker():region():name())
                LuaLog("LOCAL ATTACK BONUS FOR "..context:pending_battle():attacker():faction():name()) -- LOCAL ATTACK BONUS
                scripting.game_interface:modify_next_autoresolve_battle(100, 0.01, 0.01, 100, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            elseif defender_name == "rom_seleucid" then -- defender is local
                
                LuaLog("DEFENSE OF "..context:pending_battle():defender():faction():name().." VS "..context:pending_battle():attacker():faction():name().." IN "..context:pending_battle():defender():region():name())
                LuaLog("LOCAL DEFENSE BONUS FOR "..context:pending_battle():defender():faction():name()) -- LOCAL DEFENSE BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.01, 100, 100, 0.01, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            else -- attacker / defender is foreigner
                
                LuaLog("FOREIGN FACTION "..context:pending_battle():attacker():faction():name()" GETS NO BONUS") -- FOREIGNER PENALTY
            
            end
    ------ PTOLEMY ------
        elseif core_regions_ptolemy[attacker_region] 
        or core_regions_ptolemy[defender_region]
        
        then
            
            if attacker_name == "rom_ptolemaics" then -- attacker is local
                
                LuaLog("ASSAULT OF "..context:pending_battle():attacker():faction():name().." VS "..context:pending_battle():defender():faction():name().." IN "..context:pending_battle():attacker():region():name())
                LuaLog("LOCAL ATTACK BONUS FOR "..context:pending_battle():attacker():faction():name()) -- LOCAL ATTACK BONUS
                scripting.game_interface:modify_next_autoresolve_battle(100, 0.01, 0.01, 100, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            elseif defender_name == "rom_ptolemaics" then -- defender is local
                
                LuaLog("DEFENSE OF "..context:pending_battle():defender():faction():name().." VS "..context:pending_battle():attacker():faction():name().." IN "..context:pending_battle():defender():region():name())
                LuaLog("LOCAL DEFENSE BONUS FOR "..context:pending_battle():defender():faction():name()) -- LOCAL DEFENSE BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.01, 100, 100, 0.01, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            else -- attacker / defender is foreigner
                
                LuaLog("FOREIGN FACTION "..context:pending_battle():attacker():faction():name()" GETS NO BONUS") -- FOREIGNER PENALTY
            
            end
    ------ CARTHAGE ------
        elseif core_regions_carthage[attacker_region] 
        or core_regions_carthage[defender_region]
        
        then
            
            if attacker_name == "rom_carthage" then -- attacker is local
                
                LuaLog("ASSAULT OF "..context:pending_battle():attacker():faction():name().." VS "..context:pending_battle():defender():faction():name().." IN "..context:pending_battle():attacker():region():name())
                LuaLog("LOCAL ATTACK BONUS FOR "..context:pending_battle():attacker():faction():name()) -- LOCAL ATTACK BONUS
                scripting.game_interface:modify_next_autoresolve_battle(100, 0.01, 0.01, 100, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            elseif defender_name == "rom_carthage" then -- defender is local
                
                LuaLog("DEFENSE OF "..context:pending_battle():defender():faction():name().." VS "..context:pending_battle():attacker():faction():name().." IN "..context:pending_battle():defender():region():name())
                LuaLog("LOCAL DEFENSE BONUS FOR "..context:pending_battle():defender():faction():name()) -- LOCAL DEFENSE BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.01, 100, 100, 0.01, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            else -- attacker / defender is foreigner
                
                LuaLog("FOREIGN FACTION "..context:pending_battle():attacker():faction():name()" GETS NO BONUS") -- FOREIGNER PENALTY
            
            end
    ------ ANTIGONIDAI ------
        elseif core_regions_antigonidai[attacker_region] 
        or core_regions_antigonidai[defender_region]
        
        then
            
            if attacker_name == "rom_macedon" then -- attacker is local
                
                LuaLog("ASSAULT OF "..context:pending_battle():attacker():faction():name().." VS "..context:pending_battle():defender():faction():name().." IN "..context:pending_battle():attacker():region():name())
                LuaLog("LOCAL ATTACK BONUS FOR "..context:pending_battle():attacker():faction():name()) -- LOCAL ATTACK BONUS
                scripting.game_interface:modify_next_autoresolve_battle(100, 0.01, 0.01, 100, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            elseif defender_name == "rom_macedon" then -- defender is local
                
                LuaLog("DEFENSE OF "..context:pending_battle():defender():faction():name().." VS "..context:pending_battle():attacker():faction():name().." IN "..context:pending_battle():defender():region():name())
                LuaLog("LOCAL DEFENSE BONUS FOR "..context:pending_battle():defender():faction():name()) -- LOCAL DEFENSE BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.01, 100, 100, 0.01, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            else -- attacker / defender is foreigner
                
                LuaLog("FOREIGN FACTION "..context:pending_battle():attacker():faction():name()" GETS NO BONUS") -- FOREIGNER PENALTY
            
            end
    ------ EPIRUS ------
        elseif core_regions_epirus[attacker_region] 
        or core_regions_epirus[defender_region]
        
        then
            
            if attacker_name == "rom_epirus" then -- attacker is local
                
                LuaLog("ASSAULT OF "..context:pending_battle():attacker():faction():name().." VS "..context:pending_battle():defender():faction():name().." IN "..context:pending_battle():attacker():region():name())
                LuaLog("LOCAL ATTACK BONUS FOR "..context:pending_battle():attacker():faction():name()) -- LOCAL ATTACK BONUS
                scripting.game_interface:modify_next_autoresolve_battle(100, 0.01, 0.01, 100, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            elseif defender_name == "rom_epirus" then -- defender is local
                
                LuaLog("DEFENSE OF "..context:pending_battle():defender():faction():name().." VS "..context:pending_battle():attacker():faction():name().." IN "..context:pending_battle():defender():region():name())
                LuaLog("LOCAL DEFENSE BONUS FOR "..context:pending_battle():defender():faction():name()) -- LOCAL DEFENSE BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.01, 100, 100, 0.01, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            else -- attacker / defender is foreigner
                
                LuaLog("FOREIGN FACTION "..context:pending_battle():attacker():faction():name()" GETS NO BONUS") -- FOREIGNER PENALTY
            
            end
    ------ ATHENAI ------
        elseif core_regions_athens[attacker_region] 
        or core_regions_athens[defender_region]
        
        then
            
            if attacker_name == "rom_athens" then -- attacker is local
                
                LuaLog("ASSAULT OF "..context:pending_battle():attacker():faction():name().." VS "..context:pending_battle():defender():faction():name().." IN "..context:pending_battle():attacker():region():name())
                LuaLog("LOCAL ATTACK BONUS FOR "..context:pending_battle():attacker():faction():name()) -- LOCAL ATTACK BONUS
                scripting.game_interface:modify_next_autoresolve_battle(100, 0.01, 0.01, 100, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            elseif defender_name == "rom_athens" then -- defender is local
                
                LuaLog("DEFENSE OF "..context:pending_battle():defender():faction():name().." VS "..context:pending_battle():attacker():faction():name().." IN "..context:pending_battle():defender():region():name())
                LuaLog("LOCAL DEFENSE BONUS FOR "..context:pending_battle():defender():faction():name()) -- LOCAL DEFENSE BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.01, 100, 100, 0.01, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            else -- attacker / defender is foreigner
                
                LuaLog("FOREIGN FACTION "..context:pending_battle():attacker():faction():name()" GETS NO BONUS") -- FOREIGNER PENALTY
            
            end
    ------ BAKTRIA ------
        elseif core_regions_baktria[attacker_region] 
        or core_regions_baktria[defender_region]
        
        then
            
            if attacker_name == "rom_baktria" then -- attacker is local
                
                LuaLog("ASSAULT OF "..context:pending_battle():attacker():faction():name().." VS "..context:pending_battle():defender():faction():name().." IN "..context:pending_battle():attacker():region():name())
                LuaLog("LOCAL ATTACK BONUS FOR "..context:pending_battle():attacker():faction():name()) -- LOCAL ATTACK BONUS
                scripting.game_interface:modify_next_autoresolve_battle(100, 0.01, 0.01, 100, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            elseif defender_name == "rom_baktria" then -- defender is local
                
                LuaLog("DEFENSE OF "..context:pending_battle():defender():faction():name().." VS "..context:pending_battle():attacker():faction():name().." IN "..context:pending_battle():defender():region():name())
                LuaLog("LOCAL DEFENSE BONUS FOR "..context:pending_battle():defender():faction():name()) -- LOCAL DEFENSE BONUS
                scripting.game_interface:modify_next_autoresolve_battle(0.01, 100, 100, 0.01, false) -- ATT, DEF, ATT LOSSES, DEF LOSSES
            
            else -- attacker / defender is foreigner
                
                LuaLog("FOREIGN FACTION "..context:pending_battle():attacker():faction():name()" GETS NO BONUS") -- FOREIGNER PENALTY
            
            end
        
        else
            
            LuaLog("BATTLE REGION IS NOT MAJOR EMPIRE") -- NO BONUS
        
        end -- IF REGION
    
    
    ------ FACTION BLOCS END => Create as many blocs as you wish but there is probably better ways to set this up
    ------ In my case i choose to make factions blocs as "if then - elseif then else end" by my own standards => my "major factions" are arbitrary, depending on my own preferences
    ------ This is because i thought i was restricted to a certain number of loops / if - elseif => mostly the reason was because i misspelled region/factions provoking code failures
    ------ So it seems that if syntax is correct, you can go a lot farther than the mere 6 - 8 factions blocs i made or the larger 12 - 15 for celts / gauls for example
    
    
     else -- IF HUMAN
    
    
        LuaLog("BATTLE BETWEEN "..context:pending_battle():attacker():faction():name().." AND "..context:pending_battle():defender():faction():name().." IN "..context:pending_battle():attacker():region():name())
     
     end -- IF HUMAN
    
    
    end -- END ONPENDINGBATTLE FUNCTION
    
    
    -- Event callbacks => of course, to make sure the script runs for every battles --
    
    
    scripting.AddEventCallBack("PendingBattle", CORE_REGION_AR_BONUS);
    This code should work properly, if yes, then for AI autobattle (ON LAND only, because i don't know yet how to manage the naval battles, if at all possible ...) the bonus/penalty will apply on selected regions for specified factions. Base line is => region owner gets bonus to attack / defense, it doesn't matter where as long as the region is on their ownership list.

    The log is completely the module of this link : https://www.twcenter.net/forums/show...1#post16022431
    - to have only the events of my script logged i set the two variables debug to false in the beginning of the log module / lua script inside it.
    - i added the log script into my own to make things simple but it is not necessary for it to work on its own

    Result should look like this :

    Code:
    BATTLE REGION IS NOT MAJOR EMPIRE
    BATTLE REGION IS NOT GREEK / COLONIES
    BATTLE REGION IS NOT ILLYRIAN
    BATTLE REGION IS NOT THRACIAN
    BATTLE REGION IS NOT IBERIAN
    BATTLE REGION IS NOT BRITANIC
    BATTLE REGION IS NOT CELTIC
    BATTLE REGION IS NOT GERMANIC
    BATTLE REGION IS NOT PANNONIAN
    BATTLE REGION IS NOT DACIAN
    DEFENSE OF rom_kartli VS rom_trapezos IN emp_caucasia_mtskheta
    LOCAL DEFENSE BONUS FOR rom_kartli
    BATTLE REGION IS NOT NOMADIC
    BATTLE REGION IS NOT DESERT KINGDOM -- This is the end of my faction blocs so i know the game has finished checking the current battle and will then check the next one (starts with "major empire" check)
    BATTLE REGION IS NOT MAJOR EMPIRE
    DEFENSE OF rom_cimmeria VS rom_siraces IN emp_bosporus_tanais
    LOCAL DEFENSE BONUS FOR rom_cimmeria
    BATTLE REGION IS NOT ILLYRIAN
    BATTLE REGION IS NOT THRACIAN
    BATTLE REGION IS NOT IBERIAN
    BATTLE REGION IS NOT BRITANIC
    BATTLE REGION IS NOT CELTIC
    BATTLE REGION IS NOT GERMANIC
    BATTLE REGION IS NOT PANNONIAN
    BATTLE REGION IS NOT DACIAN
    BATTLE REGION IS NOT EASTERN
    BATTLE REGION IS NOT NOMADIC
    BATTLE REGION IS NOT DESERT KINGDOM
    The result is a stream of checks throurgh the factions blocs, above, we can see two battles, the first ones the game detected a fight of eastern factions (kartli defending in their homeland), the output of the battle and the protagonists replace the "BATTLE REGION IS NOT EASTERN" since, well, it is a battle in eastern territory :p

    The second battle is taking place in a region i tied to greeks colonies, the cimmerians / bosporus defends against nomadic siraces in tanais, so the output BATTLE REGION IS NOT GREEK / COLONIES is replaced because it is a fight in what i defined as a greek colony territory.

    If anyone has questions on this, or knows how to improve this system further (notably for naval battles), any help will be appreciated. I hope i made this as easily readable as possible.
    Last edited by ♔Greek Strategos♔; April 21, 2022 at 10:36 AM. Reason: Double post.

  18. #18

    Default Re: Rome 2 Scripting tutorial reloaded

    Quote Originally Posted by Fridericus Secundus View Post
    A Lua syntax checker

    Using this website you can check your code for syntax errors, without doing lots of manual search and try/error testing (thats sooooooo annoying!). Of course, this won't cover logical errors, like functions that aren't implemented in the fame any more or wrong arguments.
    I have had a very good time loading up a broken script file and a recent backup in Meld. If I'm shown where the changes are, syntax errors usually stick out.

  19. #19

    Default Re: Rome 2 Scripting tutorial reloaded

    looks like debug module is no longer available on mediafire, would you happen to know if there's another download link?

Posting Permissions

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