1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Building a new Tag in C2C code

Discussion in 'C2C mod mods' started by Thunderbrd, Dec 29, 2018.

  1. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    Recently, a few budding programmers on the team have expressed some interest in some projects that will require the creation of a new tag. I read a guide similar to this to get started with coding and I've been meaning to do a walkthrough in C2C for a while. So when a new tag need suddenly came up for me, I figured I'd walk through the creation process as an example and then invite you to try to make one of your own and I could be here to help to advise on how to work through the unique aspects of that particular tag generation.

    The tag that I'm wanting to build today is one that will enable a promotion to give a combat modifier against units based on the state religion of their owner or specific religion of the units they fight.

    I'm sure that brings to mind lots of very specific questions about the intention like 'how do you define the religion of the unit itself' for which there are answers and we'll explore that as we go but the point of this thread is not so much to discuss the concept of the tag itself but to expose the process of implementing a tag in general.

    Promotions are a good place to start because they are both simple and also expose some extra steps that some other tags don't have. That may sound like an interesting contradiction but you should see what I mean as we go.

    I know that @KaTiON_PT is also wanting to input a new tag in promotions so once I've completed this procedure, I'd like to invite him to try working through that tag and showing his steps here to reinforce things for those to follow and to allow for comments where an adjustment should be made to the approach.

    Ok, so where do we begin?

    I'll start with an overview of steps (You can use this as a checklist to make sure you've covered everything):
    1. Declare and apply the new tag into the schema that this tag applies to.
    2. Declare the variable and function for calling on the tag's information in CvInfos.h
    3. Setup the tag in the appropriate CvInfos.cpp/CvBuildingInfos.cpp/CvUnitInfos.cpp file.
    4. Apply the tag to the game function - program how it works into the processing of the game itself.
    5. Address display factors for the tag, usually in CvGameTextMgr.cpp.
    6. Program tag AI where necessary.
    7. Expose to python if needed.

    Clearly, there are numerous sub-steps that can vary by what sort of tag you are inserting and we'll explore those as we go. I'm going to reserve quite a few posts here and then come back around to show exactly what steps I'm taking in deeper detail as I program this new tag.

    In the meantime, as you can see, the first step is to insert the tag into the schema. This is not quite as necessary as it used to be and you CAN have a tag in use that isn't declared in the schema since it's now more of a guide than an enforced set of rules for the xml as it is for normal CivIV modding. It IS possible to have a tag in use that is NOT in the schema now. But it's not good form as it can confuse folks. There's a time and place for that sort of thing and it's not generally within the scope of this discussion.

    I wrote a whole post on the subject of understanding the XML to the point of understanding the schema. This is really good stuff for ALL modders, whether you want to learn to program a tag or not. So before we proceed further, please try to absorb the material HERE. It's important to understand this post before proceeding. Once you read that, return and we can continue with this example.

    EDIT: Actually I think it may be better to now just keep reading on rather than referring to that link unless you want more indepth info that I somehow miss in the next section.
     
    Last edited: Dec 29, 2018
    raxo2222 and KaTiON_PT like this.
  2. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    Declare and Apply in the Schema

    Ok, so in rereading that post myself it's certainly a little hard to follow and it's focus is more on how to use the schema if you're working with an XML object.

    The most important part to understand for tag creation is that here is where we are mapping out our new tag for others to be able to apply it in an informed manner to the object its intended for and to explain what objects it applies to.

    It's where we 'name' the tag basically.

    For Promotion Tags and the sake of this particular walkthrough:
    Spoiler :

    I'm trying to make a promotion that can give a combat modifier based on religious differences.

    One thing to consider about promotion tags in particular is that it's usually good form to first ask yourself if this is ONLY ever an ability that a promotion can give or if it should also be an ability that a unit base definition can have or possibly a unitcombat (unit category) can give all units in that category. It may be rare that you can never see the potential for a unit to come with this as a base definition.

    In this particular case, I CAN imagine a situation where a unit type itself should be able to provide this ability. Perhaps it should be given to Crusader units directly! That would be cool. Not what I'm going to do right now but I should pave the way as I go. Maybe there should be a fanatical unitcombat, or perhaps some religions should innately bring that kind of ability and apply it by a religious unitcombat to all units you train when you have that religion as a state religion. So yeah, I can see the point of also making it a unitcombat tag.

    Though it adds more memory allocation to make the full triad of tags (this is what I consider the standard unit tag triad, units, promotions and unitcombats) it actually can add some effort to be more limited about it and can greatly frustrate later when you do see the need to add it to units and unitcombats.

    The same thinking can apply if you are looking to add a basic unit tag too... will a promo or unitcombat be able to manipulate this value on a unit in the game? If you can imagine the potential of that, you should probably just set it up on the full triad. If it's really hard to envision the situation, then it's fine not to. (@Kation - the XP tag might be tough to imagine as something you'd want on the unit or the unitcombat so you could probably get away without adding it to those.)


    So there are four substeps here:
    1. Determine which schema to use
    2. Declare the tag
    3. Apply the tag
    4. Update modular schemas

    Determining which schema to use:

    At first, always program into the core schemas, those in the Assets->XML folders. Programming into a modular schema is a very bad idea and we decided as a team long ago that if a tag should exist it should exist in the core schema and then be updated to the modular schema if needed there.

    As a useful programming convention, in the XML folders, there is only one schema in each and it is used by all the info files in that folder. It is not necessary for it to work like this and often in modules it doesn't. So you should be aware that the info file, at the beginning, refers directly to which schema it applies to.

    Therefore, in Assets->XML->CIV4PromotionInfos.xml you'll see at the top the schema declared for this file:

    <Civ4PromotionInfos xmlns="x-schema:C2C_CIV4UnitSchema.xml">

    C2C_CIV4UnitSchema.xml is thus the schema file we want to manipulate if we're making a tag for this folder.

    You'll also notice that every other file in this XML file also uses the same schema:
    Example:
    <Civ4UnitInfos xmlns="x-schema:C2C_CIV4UnitSchema.xml">

    Again, in a modules file you may find multiple schemas handling multiple types of info files and if you make your own module file it's helpful to know you need to get your xml files to refer to the correct local schema that exists in the same folder as your info file.



    So to add the tags here we will be placing them into the Assets->XML->Units->C2C_CIV4UnitSchema.xml.

    In the spoiler above I've determined that my example will be adding tags for Units, Promotions AND Unitcombats, all of which use the same schema file.


    Declare the tag:

    You'll see in the schema that there are two ways a tag appears,
    <ElementType name=
    and
    <element type=

    The first is a declaration line and the second is an application line. The way they start is exactly how you will know the difference. The easiest way to remember it at a glance is both start with element but only the declaration starts with the E capitalized. Applications also don't include 'name=' because that's very specifically what we are doing with a declaration, defining the name of the tag.

    All tags that are going to be used will need to be declared anywhere in the schema but can only be declared once in the schema ever.

    A declaration line can be a very simple single line:
    Code:
    <ElementType name="bAltDown" content="textOnly" dt:type="boolean"/>
    or it can be more complex, with a start line and a stop line and with nested tag applications within the two:
    Code:
        <ElementType name="TerrainStructs" content="eltOnly">
            <element type="TerrainStruct" minOccurs="0" maxOccurs="*"/>
        </ElementType>
    Those usually start with the content="eltOnly" at the end of the first line.

    There are numerous things that can go after the declaration of the name, definition of content is usually either textOnly or eltOnly depending on whether it's going to have tags nested within this tag or not. Then dt:type= refers to the type of data, if that's clear enough to declare, such as "boolean", "int" etc... As a rule, find a similar tag to what you're trying to do and use the same syntax you see there and you should be alright. Just make sure to name it how you wish!

    Again, the most important thing to know here is it's going to give you an error if you declare a tag more than once in a schema. So when you declare a tag, it's a good idea to ctrl-f search for the name you've chosen for that tag to ensure it's not in use elsewhere already. If you are using an existing tag within a nested tag you're declaring, make sure to discover if you need to declare a new tag for that nested tag or if it's already declared somewhere in the schema before you apply it.

    You'll notice in the schema that some of us have grouped and organized our tags. It's a courtesy to other modders to try to follow these examples to do this as much as possible for yourself as well. You'll see comment lines like:

    Code:
        <!--TB's Tags begin-->
        <!--Textual References -->
    that represent this kind of organization efforts. Not all modders have used the same formatting on this so just do your best to help us all out. Also helps to document what you're responsible for and in a way take credit where it's due.


    Apply the tag:

    If you notice, the schema is a huge nesting map in general. Even UnitInfos is itself a tag that is declared and has the individual UnitInfo tag nested within it. Within that, where UnitInfo is declared, is nested all the tags that can appear within a UnitInfo entry.

    Thus, you'll see in the unit XML Info file that all entries are nested within these tags:
    Code:
    
        <UnitInfos>
            <UnitInfo>
                <Class>UNITCLASS_BAND</Class>
                <Type>UNIT_BAND</Type>
    UnitInfos encompasses all the entries and each entry is individually nested within its own <UnitInfo></UnitInfo> statement. And in the above example, <Class> is also a tag and it is the first tag declared in the schema under UnitInfo:
    Code:
        <ElementType name="UnitInfo" content="eltOnly">
            <element type="Class" minOccurs="0"/>
    Again, lines that start with <element type= are application lines.

    So anywhere we wish to apply our tag, we must include such an application line. If we want to include a tag in UnitInfos, we've got to place our application line for our new tag within the nested declaration statement for UnitInfo:
    <ElementType name="UnitInfo" content="eltOnly">

    Example spoiler (some discussion on naming conventions as well):
    Spoiler :

    So in my example, I want to give this religious combat modifier ability to units, promotions and unitcombats. So I need a name. With this sort of 'triad' programming, the naming convention is to make the unit tag differ from the promotion and unitcombat tag, which helps later on to keep things a bit more sorted out in the head as you interact with these tags.

    Thus, I'm naming the Unit tag: iReligiousCombatModifier and the Promotion and UnitCombat tags iReligiousCombatModifierChange.

    Breaking this naming convention down, the "i" indicates that this is an Integer tag. If you were using a float (number with decimals) you'd call it an "f". If you were setting up a Boolean tag (0 or 1 indicates false or true only) then you'd start it off with a "b". Most nested tags don't start with any type identifier, nor do text only reference tags (such as a reference to a Type of another game object, like you'd see in a TechPrereq tag.)

    The 'Change' portion is referring to it being a tack on value that will add to the base value on the unit tag in the code on a unit in the game when the promotion or unitcombat values are applied.

    You might see a tag saying 'Type' and that indicates that we'll be declaring, either through a nested tag to guide the definition of a second nested tag like an integer, or all on its own, a <Type> tag reference to another game object. Thus the tag <MissionType> indicates by its name that we'll be taking a MISSION_BLAHBLAH entry where that mission has a whole type entry in the mission XML info file.

    A plural 's' at the end is generally an indication that you've set things up for multiple entries of that sort with the name existing without the 's' nested within. Thus you'd totally expect UnitTypes to have UnitType as the nested applied tag within and in the UnitType definition, you'd find more tags nested within that apply to each individual type referred to. One of the first tags would usually be a <Type> if you are defining a new object or a <XType> tag if you are referring to what happens when that type somehow applies.

    For example, in <ReligionSpreads> you find <ReligionSpread> and under <ReligionSpread> you immediately find <ReligionType> that defines what religion the next value applies to, that next value being <iReligionSpread>, the integer that gives the base chance of this unit spreading a religion.

    Why are we not making this new tag specific to the religion you may be wondering? Because it will base it's function first on what religious unitcombat it may have assigned to it and any disagreement that may have with the unit it is in combat with and if either one doesn't have an overriding religious unitcombat (the religion can be defined on it's unitcombat type but only one religion can be 'true' on a given unit) then it will base its behaviors on the state religions of the owner(s) of the units in combat. Thus we do not have to specify which religion this particular tag works on as it works on ANY defined religious disagreement with the unit this unit finds itself in conflict with.

    Thus a simple straightforward integer is all we need here. Thus iReligiousCombatModifier.

    Another IMPORTANT note on naming conventions:
    Modifier refers to a percentage adjustment.
    Change refers to a +/- adjustment


    So my new tags must be declared and applied, thus, to declare them, I add under my own Integers section:
    Code:
        <ElementType name="iReligiousCombatModifier" content="textOnly" dt:type="int"/>
        <ElementType name="iReligiousCombatModifierChange" content="textOnly" dt:type="int"/>
    
    and inside UnitInfo wherever it seems appropriate:
    Code:
            <element type="iReligiousCombatModifier" minOccurs="0"/>
    and inside UnitCombatInfo and PromotionInfo, again wherever it seems appropriate (after a corresponding tag to the one we placed iReligiousCombatModifier if possible):
    Code:
            <element type="iReligiousCombatModifierChange" minOccurs="0"/>


    Once you've taken this step, if you are new to this process, it is HIGHLY advised that you save the schema and immediately load the mod to ensure that you haven't made a mistake here. Your tag won't do anything on its own being declared and applied in the schema but you may find an error here and it's best to find them and correct them as you go if you can. At least until you get familiar enough with the way mistakes will manifest themselves and have overcome quite a few of your own self inflicted lumps and will know how to sort out the difference between one mistake form and another.


    Update modular schemas

    This step is completely optional and depends on whether you intend to use this tag in some modular xml that already exists or not. Otherwise, every now and then we just go through and update schemas and it'll get caught the next time this happens. Usually, just opening the core schema and then opening up the modular schemas you want to update, copying the core entirely over the modular content and then saving the modular schema is all that's necessary to updat a schema in the module. Do not rename the schema in the module because the info xmls there are referring to that name to call on it properly. But they are, inside, literally a carbon copy of the core one in the xml once properly updated.
     
    Last edited: Dec 29, 2018
    KaTiON_PT likes this.
  3. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    Declare the Variable and Function in CvInfos.h


    OK, so as you probably know by now, .h files are header files, where we declare global variables and functions in a particular class, whereas .cpp files are where we program the functions themselves.

    So the next step in programming a tag is to define the function and the variable inside CvInfos.h.

    One should probably do all this in Visual Studio.

    Go into CvInfos.h and do a search to find the section dealing with the Info type that we are working with on this tag. To search, just ctrl-f and enter in the type, such as 'CvPromotionInfo'.

    In my example, the first one I'm looking at is CvUnitInfo so I do a search for that.

    It will take me to a portion that looks like this:
    Code:
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //
    //  class : CvUnitInfo
    //
    //  DESC:   
    //
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    class CvArtInfoUnit;
    class CvUnitInfo : public CvHotkeyInfo
    {
    //---------------------------------------PUBLIC INTERFACE---------------------------------
    public:
        CvUnitInfo();
        virtual ~CvUnitInfo();
    
    Beneath that, we see lines like these:
    Code:
    
        int getAIWeight() const;                // Exposed to Python
        int getProductionCost() const;                // Exposed to Python
        int getHurryCostModifier() const;                // Exposed to Python
        int getAdvancedStartCost() const;                // Exposed to Python
        int getAdvancedStartCostIncrease() const;                // Exposed to Python
        int getMinAreaSize() const;                // Exposed to Python
        int getMoves() const;                // Exposed to Python
        int getAirRange() const;                // Exposed to Python
        int getAirUnitCap() const;                // Exposed to Python
        int getDropRange() const;                // Exposed to Python
        int getNukeRange() const;                // Exposed to Python
        int getWorkRate() const;                // Exposed to Python
        int getBaseDiscover() const;        // Exposed to Python
        int getDiscoverMultiplier() const;        // Exposed to Python
        int getBaseHurry() const;                // Exposed to Python
        int getHurryMultiplier() const;                // Exposed to Python
        int getBaseTrade() const;                // Exposed to Python
    These are the function declarations. Our tag needs one of these. The 'int' declaration states, of course, that the return on this function call will be an integer. There are also bool declarations and many other types of declarations in use that get into more advanced territory we can cover later.

    For now, we're working on the simple Integer for our example.

    So I need to add a line here for my unit tag. You notice in this function declaration that 'i' has been replaced by 'get', which is the in-code naming for a function that 'gets' an integer value and returns that integer value where this function has been called.

    Thus, my function declaration will be:
    int getReligiousCombatModifier() const;

    const is a big subject itself but, generally, it means that you cannot perform any calculations that would change any variable data within this function so you can know, when you call this function, that it's going to be a pure response and it helps us to avoid writing code into infinite loop circles and such.

    Generally, most 'get' functions are constants (const) functions.

    The function is capped by () which is where you'd declare any parameters to feed the function when it is called that may adjust how the function performs when called or be the input the function needs to give the integer answer it's going to deliver.

    With these kinds of basic int calls from core infos, you don't usually want to provide any parameters BUT you might find a few interesting cases where there are some in use. I know I pulled a trick or two with turning off tags with or without certain options in use with these parameters.

    The ; portion is the way we put a period at the end of a line of code.

    Now, once you've added this line for your tag, you can't compile and expect it to go fine until you've taken care of the .cpp step in this process. You've just declared that you will give the program a new function and if you don't, you'll now trip up the compiler's expectations.



    Now you need to make a protected class variable (one that can only be accessed when accessing information on this particular class object , such as, in this example case, the information on a given unit, within the corresponding .cpp file to this .h file.

    Huh? Ok, let's just explain it by scrolling down and showing you. Scroll down through all these function declarations until you see some lines that look like this:

    Code:
    
    protected:
        std::vector<int> m_aiUpgradeUnitClassTypes;
        bool* m_pbPassableRouteNeeded;
        int* m_piPrereqOrVicinityBonuses;
        bool m_bWorkerTrade;
        bool m_bMilitaryTrade;
        bool m_bForceUpgrade;
    The portion that states 'protected:' states all the declarations past this point are protected and can only be locally accessed. Here, we aren't defining the function that will return the proper information but rather we are defining the storage variable that said function will refer to for the data it will return when called.

    You'll notice that these declarations follow a typecast declaration, name, ; format.

    Our example new tag will look like this:

    int m_iReligiousCombatModifier;

    I'll put that at the end of the normal list of integer declarations if I can identify that cleanly to any degree, otherwise, I'll just try to fit it in at the end of the list somewhere. Some more generic cleanup to keeping all types next to each other in these areas might be in order though there are different reasons for conflicting organization methods, such as wanting to keep some tags that work together next to each other and so on. Organizing things here is hard to perfect.

    Misc clarifications:
    Spoiler :

    You might see some lines like this later:
    Code:
    public:
    /************************************************************************************************/
    /* Afforess                         END                                                            */
    /************************************************************************************************/
    
    
    /************************************************************************************************/
    /* DCM                                     04/19/09                                Johny Smith  */
    /************************************************************************************************/
        // Dale - RB: Field Bombard START
        int getDCMBombRange() const;
        int getDCMBombAccuracy() const;
    The public: part is a declaration that we're back to declaring the function calls again for a while. You can see in the /****** lines that there was a popular voluminous note method being employed back when this programming was done - this was at a time when coding was more easily compared to original vanilla files so that folks could use a compare program to extract certain aspects of the mod and apply it to their code. Koshling and I gave up on this in recognition that C2C's dll is now way tooo different to make it possible to get any usefulness out of any kind of compare to original files now. Usually we delete these notes now when we see them and feel they are cluttering things up. There's only so many lines a given cpp file can have and we've breached that limit before on CvInfos.cpp, thus why we now have CvBuildingInfos.cpp and CvUnitInfos.cpp.

    Then we switch back with 'protected:' to declaring the variables again.

    Code:
    protected:
        int m_iDCMBombRange;
        int m_iDCMBombAccuracy;


    So for my example, I'm declaring the variable for my tag as:
    int m_iReligiousCombatModifier;

    And I'm doing it at the end of a long string of int declarations where it seems appropriate to place it. I have a section for my own tags so I'll put it at the end of the integers there.

    Now I also have to add these function and variable declarations in the sections for CvPromotionInfo and CvUnitCombatInfo. I have to remember that I'm programming these in as 'change' promotions rather than the same naming I used here where the term 'change' is not included because this tag is here on the unit to establish a base value upon which promos and unitcombats are optional to combine into.

    Thus, under CvPromotionInfo and CvUnitCombatInfo, I find where I have setup my own tag lists and at the end of those integer lists I add
    Code:
        int getReligiousCombatModifierChange() const;
    and
    Code:
        int m_iReligiousCombatModifierChange;
    The next step is then to program the info.cpp file(s) with what we just declared. This step will draw the information from the XML into the RAM during the game load process and make it available to the program during the game.

    This is where each tag we add does add to the RAM overhead. Not only does every defined unit type, and all other game objects, have rapidly indexable base information stored in RAM during play, each individual unit also has defined combined variables. This is what really adds up and can invite memory allocation failures as the game gets larger.

    So it's possible we're going to have to eventually get a bit more streamlined with some of these tags. When we've thought we've hit this barrier before, we've found ways to buy a lot more room and there are still a few tags that can be reprogrammed to give us a ton more space here as well.

    Yes, there is a hard memory budget we cannot exceed so there are limits to what we will be able to achieve within this CivIV mod environment that we hit at some point. Therefore, not only are there things we should do to streamline further, we should also resist adding tags if not necessary to what we really wish to accomplish. Never know... the tag I'm making today might go up on the chopping block someday.
     
    Last edited: Dec 29, 2018
    KaTiON_PT likes this.
  4. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    CvInfos.cpp/CvBuildingInfos.cpp/CvUnitInfos.cpp

    All three of these files use the same CvInfos.h file as their header files but only CvBuildingInfos and CvUnitInfos types will be processed into the game through their special .cpp files. Every other type currently works through the general CvInfos.cpp file.

    So I'm going to be working in both the CvInfos.cpp for the Promotion and UnitCombat tags, and the CvUnitInfos.cpp file for the Unit tag.

    What I usually do here is in the CvInfos.h file, I'll go and highlight the variable name above the one that I've just declared. Thus, in my example, in the CvUnitInfo section I have declared my variable at the end of the list of int variables so that it now ends like this:
    Code:
        int m_iAggression;
        int m_iAnimalIgnoresBorders;
        int m_iReligiousCombatModifier;
    So I'm going to highlight m_iAnimalIgnoresBorders and then hit ctrl-f, which puts it into the search panel. In the search panel, I select to look through the Entire Solution. Then I hit the arrow that enacts the search and of course I find this reference, as well as a few that have 'Change' added to the end of it, then at a point I pass the end of these references in this .h file and it looks through the rest of the files in the C2C code set and it finds the variable being used in the CvUnitInfos.cpp file. It should find it from the top down of the file but I just take the steps wherever they show up when adding a tag.

    Each of these places it stops will be an example of what you need to do with your own tag, which in my case is m_iReligiousCombatModifier. If you are using an example tag search like this for, say, promotions only, make sure to ensure that you are in the promotions section rather than the unitcombat section. We already know that many promotion tags are in use in unitcombats as well.


    Default Declaration:

    Anyhow, just to walk through each place we have to do something and cover notes on those, the first spot that comes up for me here is in a list like this:
    Code:
    m_iNumTriggers(0),
    m_iAnimalIgnoresBorders(0),
    m_iAggression(5),
    Scrolling up, you'll see this list begins with a class declaration above:
    Code:
    CvUnitInfo::CvUnitInfo() :
    m_iDCMBombRange(0),
    m_iDCMBombAccuracy(0),
    m_bDCMAirBomb1(0),
    m_bDCMAirBomb2(0),
    (Note that I did a little rearranging and took out some unnecessary notation to clean this up a bit for this example.)

    What you are looking at is a default declaration portion. The number or variable within the ()s is the default you intend for this tag. It's not the only way you need to set things up to establish a default for the variable but it's one peg in doing so and it's a non-optional step. The order of variables here technically doesn't matter but we should try to keep it organized, ints with ints and so on. So I add my new variable right after my example(searched for) variable.

    Code:
    m_iAnimalIgnoresBorders(0),
    m_iAggression(5),
    m_iReligiousCombatModifier(0),
    The final variable in this list should not have a comma after it. Some of these lists have been setup to put the comma before the variable for that reason.


    Program the retrieval function:

    Continuing our search, we find the next place m_iAnimalIgnoresBorders comes up is the next step in the process, where the function itself that we declared in the header file is programmed.

    Code:
    int CvUnitInfo::getAggression() const
    {
        return m_iAggression;
    }
    
    int CvUnitInfo::getAnimalIgnoresBorders() const
    {
        return m_iAnimalIgnoresBorders;
    }
    
    This is not always what you'll next have to do... in vector forms of data storage, there's another step in here and you don't declare the default the same either. But we'll address those later as needed. We're just covering simple tags, ints and bools pretty much. Using similar tricks to finding examples, you can probably work through nearly anything else after this.

    Anyhow, our new function needs to be programmed here so it will go in right after these functions. Now it looks like this:
    Code:
    int CvUnitInfo::getAnimalIgnoresBorders() const
    {
        return m_iAnimalIgnoresBorders;
    }
    
    int CvUnitInfo::getReligiousCombatModifier() const
    {
        return m_iReligiousCombatModifier;
    }

    Checksum
    Up until now, these steps have taken a universal order of appearance regardless of what type of info class you're programming the tag for. From here on in you'll find that these steps are ordered in different sequences for whatever reason and some of them are not even there. If that's the case for the class you're working on, just know that's ok and plug on. This is why I suggest to search through the steps with an example variable, as we've been doing, so that you won't miss any and you won't double guess yourself once you've completed the search loop.

    The Unit tag I'm working on is a good example for these steps because UnitInfos takes all the usual steps you can find here.

    In units, the next step to come up is the Checksum stage. You'll see lines like these:
    Code:
        CheckSum(iSum, m_iNumTriggers);
        CheckSum(iSum, m_iAggression);
        CheckSum(iSum, m_iAnimalIgnoresBorders);
    Recently Alberts2 clarified the purpose of this step for me, which is used by the program in 'sensing' that a change has been made in the assets that may precipitate the need for a reaction, such as a recalc. It's not working perfectly and perhaps we'll remove these or figure out why at some point. But it doesn't mean we should ignore this step. What it's doing is, during the mod load sequence, adding up all the values here and comparing it to the last time it was totalled to see if changes took place. To add your tag into this, just add a new line (or in some cases 'lines') and follow the example of the type of tag you are adding. So for this integer we're adding, we just add in our own line:
    Code:
        CheckSum(iSum, m_iAggression);
        CheckSum(iSum, m_iAnimalIgnoresBorders);
        CheckSum(iSum, m_iReligiousCombatModifier);[code]
    Order of appearance does NOT matter.  What matters is that all tag variables in the class ARE here to give an accurate total.  The likely bug we have in the trigger mechanism for the recalc popup to come up may well be a missing tag or bad reference in one of these checksum lists.
    
    
    Upload the values from the XML into the variable.
    Clearly this is the MOST important step and I'm SO glad that I get to show an example of setting this up with a default establishment, since usually the default is 0 so it doesn't need that second step in establishing an abnormal default.
    
    Ok, so continuing our searchthrough, we find lines that look like this:
    [code]
        pXML->GetOptionalChildXmlValByName(&m_iNumTriggers, L"iNumTriggers");
        pXML->GetOptionalChildXmlValByName(&m_iAggression, L"iAggression", 5);
        pXML->GetOptionalChildXmlValByName(&m_iAnimalIgnoresBorders, L"iAnimalIgnoresBorders");
    These are function calls in the pXML class that I have never taken a lot of time to evaluate deeply. The more advanced guys than I can play in this realm if they wish. Just duplicate the way your type of tag example is programmed and add another line for your variable. Generally.

    As with any function call, within the () go the parameters and there is an option parameter in use here that you can see on the iAggression tag. The first parameter we've entered is the mapping to the variable we want to fill with our tag data: &m_iAggression.

    The second parameter is the name you'd find the tag referred to in the XML, looking to match character for character from the Left most position within the <>'s in the xml (indicated by the L - which is not necessary for all forms here.)

    The third parameter, showing 5 in that entry, is the default you want to load for that tag if you don't find it in use on the type being loaded. If left blank, it defaults to 0 on ints and bools and NONE on type reference strings. Apparently we wanted iAggression to default to 5, and that also means where we are declaring defaults above in the parenthesis in the first section of this cpp searchthrough you should see (5) instead of the usual (0) after m_iAggression. If you look above, you'll see that is indeed the case.

    USUALLY you won't need to establish a non-0 default, though -1 is not uncommon when you need 0 to mean something meaningful but negative numbers would indicate a complete lack of tag application. You should be able to find some examples of that in our tag sets if you're curious when to employ this trick.

    You'll notice that there is MUCH more involved programming when you are importing data into more complex nested tags. Again, we'll cover that later but there's lots of existing examples to help be a guide. Int and bool types are very easy one liners here.

    Adding in my tag to this stage we have:
    Code:
        pXML->GetOptionalChildXmlValByName(&m_iAggression, L"iAggression", 5);
        pXML->GetOptionalChildXmlValByName(&m_iAnimalIgnoresBorders, L"iAnimalIgnoresBorders");
        pXML->GetOptionalChildXmlValByName(&m_iReligiousCombatModifier, L"iReligiousCombatModifier");
    I'm wanting a 0 as a default here.

    Again, order doesn't matter, per se, but keeping things nice and clean and organized is always of value.


    Copy Non-Defaults

    Here is where the magic happens that enables <Type> references that are defined later in the reference stream to override, on a tag by tag basis, the definitions established earlier in the stream. In most cases, this simply means this is the portion that enables modules to override core definitions only on the tags they provide their own definitions for. Though, there are also some places in the XML where a conditional additional edit entry is used after the core original <Type> is defined the first time. Either way, this is the section that enables the replacement of the original information if you are presenting the tag with non-default value.

    Different classes will show slightly different syntaxes on how this is done and I've been told before that one is a better form than another but I cannot recall which way it SHOULD be. Anyhow, I THINK that the unit infos shows the proper method. Either way, simply take your example and replicate it for your tag, adding your tag's application of this step right after your example.

    The lines in this section look like this:
    Code:
        if ( m_iNumTriggers == iDefault ) m_iNumTriggers = pClassInfo->getNumTriggers();
        if ( m_iAggression == iDefault ) m_iAggression = pClassInfo->getAggression();
        if ( m_iAnimalIgnoresBorders == iDefault ) m_iAnimalIgnoresBorders = pClassInfo->getAnimalIgnoresBorders();
    Which is saying, if our tag currently has a default value from the entry we JUST loaded in (the one that's currently being uploaded), then replace that value with anything we might have PREVIOUSLY loaded in. The magic is in the logic that it also means that if there isn't a default value that we just loaded from this current entry, we keep it instead of anything that came beforehand.

    The one weakness to this system is that you cannot give a default value in a modular edit and expect it to override the value established in the core - one reason to use the replacement XML method if needed. (That's potentially a whole 'nother subject so if that's confusing, let's just leave that alone for now.)

    Again, in practical terms, this is as simple as duplicating the example and replacing the variables with your own new tag's terminology. Thus, my tag adds in right after m_iAnimalIgnoresBorders, ending up looking like this:
    Code:
        if ( m_iAnimalIgnoresBorders == iDefault ) m_iAnimalIgnoresBorders = pClassInfo->getAnimalIgnoresBorders();
        if ( m_iReligiousCombatModifier == iDefault ) m_iReligiousCombatModifier = pClassInfo->getReligiousCombatModifier();
    
    And there you go. That's all the steps. For me, I need to do all this in CvInfos.cpp for UnitCombats and Promotions, adding in the details for ReligiousCombatModifierChange rather than just ReligiousCombatModifier. If you're only building a tag for, say, promotions, or buildings, you would be done at this stage and ready to move on. So I'll just wrap up this segment here.

    Don't be afraid to ask clarifying questions. I may have overlooked mentioning something or my wording might need improvement. Let me know!

    EDIT:
    Words to the wise - when you are done here, compile the DLL. Always compile the final release after deleting the final release file in the assets folder. If it finds errors in a given file, it will stop on that file so you can focus on solving those errors. If you made a mistake in the .h file, delete the final release folder again before beginning the compile again. Otherwise, fix what you find in the .cpp file and again compile and it will either find the unaddressed or new errors in the .cpp file or will get through it and continue on.

    If you try building the debug dll first, you'll have to wait until it tries to compile every file and then list off all the errors in the entire dll as they came up so it can be a bit of a search to figure out where the errors were and harder to know for sure that you've solved all the errors in that file before moving on.

    Before publishing to the SVN, always clean compile both the dll and debug dll by deleting the final release and debug files again and compiling once more, knowing there will be no interruptions in the compile process. Not taking these clean compile steps can result in errors that exist in the compiling of the code, even if the code itself is good. These can lead to crashes where the code doesn't show that such a problem should exist.

    Use the new dll to run the mod to make sure you haven't screwed up somewhere. Always good to take sanity checks whenever you can to keep your debugging to a more limited selection of changes made.
     
    Last edited: Dec 29, 2018
    KaTiON_PT likes this.
  5. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    Program the Tag into the Game Stream

    At this point we are ready to start working our tag into the flow of the game itself. Here's where there are a lot of steps that will vary a great deal from one tag to another. Therefore this is the step where we go swimming out into the big ocean of code and need to have some idea of the larger picture of the DLL structure overall. But it's not as confusing as it might seem and there are some standard steps you will likely take here.

    Almost always, you'll have to work the tag into being applied to the object IN the game that it's supposed to apply to.

    For example, if you have a building tag, you'll need to set it up so that the values of that tag will add to the city when the building using that tag is constructed - making it's value removed when the building is lost somehow is usually part of that step too. This step is called the processX step. In such an example, processBuilding is actually the name of the function where this takes place.

    Even before you do this, you've probably gotta set up the game object with the ability to keep track of the value that your new tag brings to that object. That step is a matter of setting up a variable for the object type in question, then programming functions to add or subtract values of that variable, and retrieve those values when the game needs them. This whole process is much like the previous step except that it applies to the object in the game, not just the type of base object it derives its initial values from.

    For example, we just setup a UnitInfos function, getReligiousCombatModifier, but that is to be called when a unit needs to know what amount that variable is defined as on its base unit TYPE, whereas now we need to make it possible to add in promotion and unitcombat change amounts to a final total for each individual unit and make that total something the game can reference.

    We also need to setup the save and load sequence for those new variables.

    Then we can move into applying the sum total of your tag variables where it actually counts. In the case of my ReligiousCombatModifier, I'll setup where it applies as a modifier in combat and the filters that determine if the unit gets the benefit (or penalty) of this modifier when and where combat modifiers are being compiled for application in establishing combat odds and during actual combat. Similar things would take place for just about any tag, plugging the final compiled variable for that game object into where it will apply to make its effect happen in the game.

    That's also the part I can't explain everything about. It takes some searching through the code to figure out where to apply those tag values. You get more familiar over time but there are some ways to look through the function lists and some naming conventions on functions that make this a little easier to figure out how to research into fast enough to make it possible to keep from losing your way - and your mind.

    So let's start here by listing the above sub-steps and we'll work through them for my example.
    1. Setup the variable and functions for compiling the variable on the applicable game object.
    2. Initialize the default data on your variable
    3. Include the variable in the read/write wrapper
    4. processObject - insert your info.cpp variable into the process stage so that it adds/subtracts when the object, such as trait, building, promotion, unitcombat etc... is added or lost.
    5. Apply your total to where it interacts with the game process itself

    Establish the Variable and Functions for calculating and returning that value
    In our example, I'm adding a set of tag effects to units. Therefore, I'm going to now go to CvUnit.h and CvUnit.cpp to compile and track my values on the actual units in the game. Note the difference between CvUnitInfos.cpp and CvUnit.cpp. The first is for accounting for the base unit type but the second is for accounting for (and working the game processing functions on) the actual unit in the game. Thus one Spearman unit can differ in abilities to another Spearman unit based on the specific level and promotions it has taken, whether it's been damaged, etc... CvUnit.cpp tracks that specific in-game unit where all it's variables are uniquely compiled.

    This portion of the process, however, is much like the Infos setup. We must declare the variable and declare the functions that track, return and manipulate that variable all in the header file (.h). Then we program those functions in the .cpp file. If you were doing this for a building, you'd do this all in CvCity.cpp/.h. If it was for a trait or civic you're adding things to the player level so CvPlayer.cpp/h. For a Feature tag or a Terrain tag or an Improvement or Bonus (that affects the plot) you'd do all this in CvPlot.cpp. Usually, if you don't know where to go, research the flow of tags that work in a similar manner to the one you want to use to determine where you need to compile the tag in these files.

    So I start in CvUnit.h, and find a similar variable to the one I want to insert. This will remain a simple integer, and the naming convention for these integers is to, as in the Infos files, start them with m_i. So I can simply search for m_i and find a similar variable I want to follow the steps this variable takes. A simple conditional combat modifier would work very well to model this after so I'll use m_iExtraVSBarbs as my model to work from. This is a combat modifier tag that applies in combat only against non-animal barbarians and neanderthals (collectively known as Hominids in the code).

    You will notice the 'Extra' term being added at this step. This is because the base value is going to be directly derived from the UnitInfo and for the actual unit, here we only need to track the EXTRA amounts that the unit's unitcombats and promotions are adding(or subtracting) to the unit so that we can provide a sum totaling function for retrieving the total amount the unit has. This is also one helpful way of distinguishing between the various steps we're working on is to vary the naming conventions a little. You might notice quite a few little naming convention tricks being employed here and I'll try to point out those we encounter as we go.

    In the case of a promotion-only tag, you may not want to use the Extra moniker. You'll still need to create a variable for the unit to store the total amounts of that value that promotions have given to the unit - remember that multiple promotions can still add up this value.

    There are SOME cases where you might want the value to be the highest or lowest of all the promotion sources the unit has processed into it instead of being a tally, and with some searching you can find some examples of tags that work like this as well.

    Either way, the only difference here when you are not using the 'triad' of unit modifiers, is that you aren't going to have a base unit value or combat class source to tally into this variable total. Thus, a promotion tag alone is nearly going to mean the same amount of data reserved per unit as a unit/promotion/unitcombat set does, which is why I usually am liberal about doing all three if I can even imagine ever wanting the tag in use on the other types.

    So I have found in the CvUnit.h file where the m_iExtraVSBarbs is declared and I add my variable declaration right afterwards so now it looks like this:
    Code:
        int m_iExtraPursuit;
        int m_iExtraEarlyWithdraw;
        int m_iExtraVSBarbs;
        int m_iExtraReligiousCombatModifier;
    You'll notice this section where the variables are being declared is also located beneath a protected: designation, just as it is in the Infos file, and the functions are declared under a public: designation.

    So my next step is to declare the necessary functions for compiling, calculating and reporting this variable. So I look for 'ExtraVSBarbs for where the set of functions for this variable are declared and I'll just copy those declarations and replace the terminology of VSBarbs with ReligiousCombatModifier.

    So the first line I find is:
    Code:
    int vsBarbsModifier() const;   
    This is the function that provides the grand total of this modifier for this unit when it is called, the one that really gets applied into the code stream where you want the effect to be included. I know because it uses the naming convention of lower case letters to start it off and doesn't utilize 'get' or 'is' or 'has' or anything like that. Often it will also end with the word Total but this was a tag I did in earlier days of modding and hadn't yet decided to add that to make it more clear. This function is also the one we call when we're building the display for this value on the in-game unit help tool / mouse hover information.

    Anyhow, I'll need to duplicate this declaration and rename it for my new tag. I'm going to add the Total term for the sake of clarity as well.

    int religiousCombatModifierTotal() const;

    Again, const is for retrieving values and means the variables in these functions cannot be manipulated within the function (though localized variables can be which is sometimes useful for these totaling functions.)

    Then I continue my search. In newer tag sets you'll probably find this totaling function is right next to the rest of the functions regarding the tag but earlier examples have the total functions separated out. This could all do for some re-organizing of the .h/.cpp but it really doesn't ultimately matter what order anything appears in so it's hard to see the value in reorganizing this.

    The next place I find VSBarbs applied is here:
    Code:
        int getExtraVSBarbs (bool bIgnoreCommanders = false) const;
        void changeExtraVSBarbs (int iChange);
    These are the functions that adjust and return the total of our variable. The first is used in the totalling function above where it is added to the base value from the unit and the total is returned.

    The parameter, bool bIgnoreCommanders = false, is completely optional and you only need to include it if you want this tag to be something a field commander would pass along to those within his command rather than adds to himself. You may already be aware that a Field Commander unit cannot have a tag for himself AND pass that value along to those under his command.

    The inclusion of ' = false ' in this parameter is an established default for when this function is called so that the function can be called without having to specify this parameter at all if it's calling it in the 'usual' manner.

    The actual programming of this function will be altered a bit in a commander applicable tag vs one that isn't. It should also be noted that I did not realize that the use of commander application does cause a little processing delay that can add up unnecessarily so at some point we may want to audit the necessity of applying this option and try to eliminate some of them, but it cuts down on our options for commander promos when we do really and I honestly feel that we haven't even begun to tap the potential for cool commander promos yet.

    The second is where the processPromotion or processUnitCombat function will add or subtract the values from the promotion or unitcombat when those objects are processed onto the unit. That second function is thus declared a void function, as it is a call to manipulate the variable rather than return its value, (it's an action command rather than a call for information), and it has a parameter of iChange, where the amount to manipulate the variable by is supplied in the process stage where it calls this function.

    So I duplicate these function declarations for my example:
    Code:
        int getExtraReligiousCombatModifier(bool bIgnoreCommanders = false) const;
        void changeExtraReligiousCombatModifier(int iChange);
    Then I find I loop back around to the declaration of the VS Barbs variable so I know I'm done with my work in the CvUnit.h file. I move on to CvUnit.cpp and continue, from the top down, my search for VSBarbs and I'll end up hitting all other steps I gave above, just maybe not in the order I assigned them, which is not entirely necessary to program them in that order - I gave that order above for the sake of following in the order the system would process the data flow rather than exactly the order in which I address these steps exactly.


    Initialize the default data on your variable

    So from here I let the search guide my path. The first instance of VSBarbs I find in CvUnit.cpp is:
    Code:
        m_iExtraVSBarbs = 0;
    This appears in a long string of similar value assignments. In C++ you must give your variables a default value when you initialize them into the game. Therefore, here is where, when a unit is born, it's initial default value is applied to all it's tags. Actually, this first instance of this is under void CvUnit::reset(yada yada) so it's not only called during unit manifestation but also during resets such as what happens during a recreation of the unit that takes place when the unit is upgraded.

    So I add a similar line to establish the default value for the tag here.
    Code:
        m_iExtraReligiousCombatModifier = 0;
    Sometimes you'll find more than one function where this needs to take place in the class you're working in, which is why I am teaching to follow previous examples. Where recalculations take place there is a similar function that resets the variables to 0 usually. Units don't get recalculated by the recalc feature so don't have that second spot for this.

    Note:
    As you continue to search, sometimes you'll find some results that contain the term you're searching for but have nothing to do with the actual tag you're looking at. Watch out for those and ignore them. For example, I just found:
    Code:
            if (isDead())
            {
                if (isNPC())
                {
                    GET_PLAYER(pDefender->getOwnerINLINE()).changeWinsVsBarbs(1);
                }
    becuase 'changeWinsVsBarbs' has VSBarbs in it but this has little to do with the value manipulations we need to address right now.


    Apply your total to where it interacts with the game process (part I)

    Rather out of order here to logical coding sequence I find:
    Code:
            if (isHominid())
            {
                //TB Combat Mods Begin
                iExtraModifier2 = -pAttacker->vsBarbsModifier();
    within the

    int CvUnit::maxCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails, bool bSurroundedModifier) const

    function. This is where the combat modifier total is being conditionally added and I might as well just take the step to add my total combat modifier conditionally here. THIS is not as simple as copying my example because I need to build my filter for when this modifier applies here and that filter isn't a terribly simple one. This example also isn't being applied quite where this new one needs to go. There's a lot of little intricacies to cover in this function - it's really kind of messy with tons of different ways the function can be called. But suffice it to say, the modifiers are tallied here and reported into the combat help hover and even into the EXE in a list of specific variables you cannot add to. So I want this to be a conditional normal combat modifier so I want to find where the actual usual generic combat modifier is being added and add to that total with this value WHEN it should apply.

    So I'll look for that moment here in this function. I find it's being applied up above in a completely non-conditional placement where we don't have to know what the opponent unit is. So I do have to add it to that variable but I have to add it below, where we are only adding the modifier on the basis of knowledge of what unit it is we're fighting and variables on that unit or its owner.

    What is clear from finding where the normal combat modifier is applied is the variable we have to add our total to, pCombatDetails->iExtraCombatPercent

    So now, armed with that knowledge, I have to now look further down to find where we start adding values based on the knowledge we can have of our opponent.

    All modifiers are applied to the defender so under this line:
    Code:
            // only compute comparisons if we are the defender with a known attacker
            if (!bAttackingUnknownDefender)
            {
    is where we'll find where we can add our variable.

    Here's a good example of the format to apply things:
    Code:
    
                iExtraModifier = unitClassDefenseModifier(pAttacker->getUnitClassType());
                iTempModifier += iExtraModifier;
                if (pCombatDetails != NULL)
                {
                    pCombatDetails->iClassDefenseModifier = iExtraModifier;
                }
    
                iExtraModifier = -pAttacker->unitClassAttackModifier(getUnitClassType());
                iTempModifier += iExtraModifier;
                if (pCombatDetails != NULL)
                {
                    pCombatDetails->iClassAttackModifier = iExtraModifier;
                }
    
    The first section there is applying any applicable modifier vs UnitCombat from the defender based on the attacker and the second is applying any applicable modifier vs UnitCombat from the attacker based on the defender.

    This filter for religious combat modifier will be similar in complexity. I'm going to want to build the filter into the totaling function for this value to determine when it should apply. This means I'm going to go back and add a parameter to send the determined 'religion' of the opponent to the function so that the total is only returned IF the opponent's religion is contradictory to our unit's religion. (This is where my specific example gets a bit more complicated.) So I'll have to go back to the .h file and change the declaration there for the totaling function to:

    int religiousCombatModifierTotal(ReligionTypes eReligion) const;

    I'm also going to need a whole new function to draw upon for units to 'getReligion()' for that unit specifically so I can define there what kind of religion the unit is and be able to place that function in the new parameter I just added in the totaling function.

    So for that I go all the way to the bottom of CvUnit.cpp and go to the declaration of the last new function and add beneath that the declaration for my new function:
    Code:
        ReligionTypes getReligion() const;
    I'll program this as part of the last step since it will be added to the functions at the end of the .cpp.

    So my application here in the combat modifier tally in int CvUnit::maxCombatStr is as follows:
    Code:
                iExtraModifier = religiousCombatModifierTotal(pAttacker->getReligion());
                iTempModifier += iExtraModifier;
                if (pCombatDetails != NULL)
                {
                    pCombatDetails->iExtraCombatPercent+= iExtraModifier;
                }
    
                iExtraModifier = -pAttacker->religiousCombatModifierTotal(getReligion());
                iTempModifier += iExtraModifier;
                if (pCombatDetails != NULL)
                {
                    pCombatDetails->iExtraCombatPercent+= iExtraModifier;
                }
    iExtraModifier is a variable that is a temporary platform upon which we are placing the modifier variable before applying it to the end combat modifier total (pCombatDetails->iExtraCombatPercent).

    Sometimes you will see iExtraModifier set to 0 before a new section because it should always be completely reset before we use it in another segment here, but for this, we just set it directly to the amount our totaling function returns when we refer to our unit and qualify the value by supplying the religion of the opponent (and in that function it will ask us our own and compare our religion vs theirs to see if the modifier applies.) We do that because there's no need to set to 0 since the function return will fill it with 0 if our function returns no value.

    iTempModifier is taking a running tally of all applicable combat modifiers through this section of the code and actually adds this end total to the actual combat system itself. Therefore, if we find an applicable combat modifier, it gets added in here. Being able to add the iExtraModifier derived here AND to the combat details tally (for the help display later) is why we use the iExtraModifier platform to place our results onto, thus it can be called on twice without calculating everything with function calls both times.

    You will notice that the value assigned to iExtraModifier is negative in the second step of this process where we are deriving the value for the attacker's religious combat modifier. That's because this running total for iTempModifier is all going to apply in the end to the defender only - yep that's how this crazy combat system works and it can be a little disorienting to work with for a while until it gets comfortable.

    To explain another way, positive combat modifiers for an attacker are reflected as negative total combat modifiers for the defender in the underlying combat engine.

    You might not need to know that for the tag you're working on but there are probably going to be some specific quirks like these to cover for each unique project. I can't prepare someone for every unique facet of the core game design, unfortunately. So if you hit a barrier, which you likely will when you reach this stage of application, I'm more than happy to try to guide you through the specifics.

    You can see (if you're following me in the code and I've already committed it) that after my added section for the religious combat modifier, moving into the processing of the unitcombat combat modifiers, the platform variable of iExtraModifier is immediately reset to 0.

    At this point, I'm going to take a break from the application portion, as I've done what I need to do to apply the religious combat modifier into the game engine. I won't even have to do some more complex work with the combat help because that pCombatDetails bit feeds directly into that section - this will look like a basic combat modifier when it applies. I'll wrap things up when I program the getReligion function and I'll do that when I find the totaling function and insert where I need to call for that information on our unit in question.

    I next find vsBarbs being applied in Air Combat modifier totals and I think I'm going to keep the religious combat modifier out of the realm of air combat because of the impersonal nature of air strikes. So I'll ignore that second application.

    Part II
    My next search result finds the vsBarbsModifier totalling function. So it looks like I'll be keeping with the application portion of our segment here.

    Here's the function for our example tag:
    Code:
    int CvUnit::vsBarbsModifier() const
    {
        return (m_pUnitInfo->getVSBarbs() + getExtraVSBarbs());
    }
    So when this function is called, it's taking the getVSBarbs() function value from CvUnitInfo.cpp getVSBarbs() function for this unit, thus within CvUnit.cpp, m_pUnitInfo-> means get the tag data pointed to by the arrow from the base unit definition for this particular unit. This is part of why the tag for the unit, from the very beginning, is usually named without the 'Change' denomination.

    THIS is where the base is pulled in and added to all extra sources, promos and combat classes, that have been processed onto the unit, which is what getExtraVSBarbs() returns.

    By not including a pointer setup before the function call, getExtraVSBarbs() obviously is a function that exists within CvUnit.cpp. Within the AI files you can also call directly to these functions without a pointer setup as well. This works here and in the CvUnitAI.cpp so long as you're getting the information for the unit you have in focus when this portion of the code is processing. Otherwise, if you're looking for information on another unit, such as one we're in battle with, then you need to ask for the function return for that unit, and you thus may call something like pAttacker->getUnitClassType() as we did in the above example. pAttacker was a variable that was setup to refer to that specific unit that was the attacker in that case.

    Ok, that's probably a bunch of stuff you didn't need to know or already do.

    Anyhow, my example will be a little more involved here than the normal totaling function like this one because it's going to include filtering it's response in a manner that depends on what religion the opposition is.

    One thing I forgot to do earlier when I changed the declaration of our new totaling function to take a Religion input in the parameters was to give that parameter a default value. If I do this, then I don't HAVE to specify the religion. I mentioned how to establish a default for a parameter earlier as well if you recall. So now my function declaration should look like this:
    Code:
    int religiousCombatModifierTotal(ReligionTypes eReligion = NO_RELIGION) const;
    The term NO_RELIGION is pretty much used in any kind of indexed Type call as a default - just change the name after NO_ to match the object. These are macro defined definitions elsewhere in the code that mean a flat type declaration with a no type definition. It would be otherwise expressed as: (ReligionTypes)-1.

    I'm also going to add a boolean parameter here that enables me to call this function and always get an answer as to what the total amount is without having to provide a religion to do it. I'm going to call that bDisplay.

    Wherever you start declaring defaults on parameters, every parameter listed afterwards must also declare a default. I want one for this anyhow, false, so that the assumption is that we're looking for the value in relation to an opponent, as we've already programmed that portion and this boolean is here more specifically for display reasons so the help hovers can show the unit's accumulated Religious Combat ability when its not in the context of a potential battle.

    So now our declaration looks like this:
    Code:
        int religiousCombatModifierTotal(ReligionTypes eReligion = NO_RELIGION, bool bDisplay = false) const;
    Code:
    int CvUnit::religiousCombatModifierTotal(ReligionTypes eReligion, bool bDisplay) const
    {
        if (bDisplay || (getReligion() != NO_RELIGION))
        {
            if (bDisplay || getReligion() != eReligion)
            {
                return (m_pUnitInfo->getReligiousCombatModifier() + getExtraReligiousCombatModifier());
            }
            else if (getReligion() == eReligion)
            {
                return -(m_pUnitInfo->getReligiousCombatModifier() + getExtraReligiousCombatModifier());
            }
        }
        return 0;
    }
    Note that when you are setting up the function programming, you don't put the defaults as declared in the .h with =NO_RELIGION and =false. Just leave them out in this first line of the function programming. You can always see what the defaults are by right clicking on the function address line or on the function wherever it is being called in the code and selecting 'go to declaration'.

    So you can see that I'm only returning the total if this unit's religion differs from the religion that its opponent, projected or real, has, and to make this tag really interesting, if, instead, they have the same religion, the combat bonus becomes a penalty instead (reluctant to hurt those who share the same beliefs.)

    If the function is being called for display purpose only, then it will quickly return the base value for the unit plus all extra sources.

    If your unit has no religion then this combat modifier is useless and will always return 0.

    If your unit has a religion but your enemy does not, we just see that as a disagreement between religious views just as if the enemy had a religion, and we apply the totals.

    Note that this is a const and nowhere in here are we actually changing any variables. We could get away with being able to manipulate a variable that's totally local to this function within a const, but we CANNOT assign a change to any variable declared outside this function directly, such as m_iReligiousCombatModifier. This is JUST for returning the value that exists, dependant on the filter conditions we established here. When you run afoul of const and have a hard time with the compiling of the code as a result, it helps to understand this. Also, you can only call for a const value within a const function like this. Therefore, if m_pUnitInfo->getReligiousCombatModifier() was not also declared as a const function, you would error out during compile. It's all to keep the program on protected tracks really.

    Also keep in mind that keepReligion() has been declared at this point but I still need to program it. I'll again do that at the end here since we're searching through the code for VSBarbs to guide our efforts still.


    Part III

    Here's the next spot to address that our example of VSBarbs provides.
    Code:
    int CvUnit::getExtraVSBarbs (bool bIgnoreCommanders) const
    {
        if (!bIgnoreCommanders && !isCommander()) //this is not a commander
        {
            CvUnit* pCommander = getCommander();
            if (pCommander != NULL)
            {
                return    m_iExtraVSBarbs + pCommander->getExtraVSBarbs();
            }
        }
        return m_iExtraVSBarbs;
    }
    
    void CvUnit::changeExtraVSBarbs(int iChange)
    {
        m_iExtraVSBarbs +=iChange;
        FAssert(getExtraVSBarbs() >= 0);
    }
    To do this super simple, just copy and paste this example and switch out every instance of VSBarbs with RelgiousCombatModifier. Ultimately that's all we're doing here. But what do these functions mean? What are they saying?

    Again, the first one is an integer call to the total of all extra sources of VSBarbs that have been assigned to this unit. Usually, it's only ever called in the totaling function we just did.

    The parameter of bIgnoreCommanders is declared with a = false default so it's going to assume we should check for a commander that is in command of this unit giving it some value to add to here from his own amount tallied up in this function for him. getCommander() searches for an applicable local commander that has enough command points and influence range to contribute his abilities to this unit this round and is heavily cached for optimum performance so is not a simple function call.

    Our new tag will allow commanders to become holy inspired leaders with this tag in use on commander promotions so we'll keep all that in place for our corresponding function here. Ignoring the commander is setup for calls to this function where that may be a desireable thing to bypass, not that this is usually used in any kind of display text anywhere.

    This commander stuff will get a little trickier with domain-specific commanders being a project to address at some point. (For admirals, generals, and aviators to only command units of their own domain type.)

    The second function receives the amount to change our unit's storage variable by and adjusts it (from a default we established above as 0) whenever we hit the point of processing a promotion or unitcombat that gives or manipulates this ability on a unit.

    The Fassert is saying throw an assert when the debug dll is running if this total comes up as less than 0 because really, that shouldn't be a valid value in the XML so the XML should be reviewed to see how it got that way and what the thinking was and perhaps we should allow it but enforce that the return on the total extra amount instead be limited to a minimum of 0 even if the variable comes up as less than that. If you make this change, by having the first 'get' function here return std::min(0,m_iExtraVSBarbs) , then remove the FAssert here.

    So our corresponding functions now look like:
    Code:
    int CvUnit::getExtraReligiousCombatModifier(bool bIgnoreCommanders) const
    {
        if (!bIgnoreCommanders && !isCommander()) //this is not a commander
        {
            CvUnit* pCommander = getCommander();
            if (pCommander != NULL)
            {
                return    m_iExtraReligiousCombatModifier + pCommander->getExtraReligiousCombatMoidifier();
            }
        }
        return m_iExtraReligiousCombatModifier;
    }
    
    void CvUnit::changeExtraReligiousCombatModifier(int iChange)
    {
        m_iExtraReligiousCombatModifier += iChange;
        FAssert(getExtraReligiousCombatModifier() >= 0);
    }
    I added them in right below the VSBarbs functions and that's perfectly ok to do, to add your tag processing functions right after the best example you find to base your work on.

    You could also do them all in a group at the end of the function and you'll see I've done that numerous times when I wasn't working with a template example but was just programming it out. Took me a bit to get comfortable enough to be able to do this.

    Following previous examples are particularly important when you start interacting with mapped variables that Koshling put in CvUnit, which he did to give us tons more data room over the way it was in Firaxis's original work, where they were all stored in data costly arrays. The syntax for interacting with these is tricky for me still. They come up when the tag also declares a specific type that it applies to, like a combat modifier vs a particular unitcombat type for example. Had I made this ReligiousCombatModifier capable of specifying specific religions you'd get a modifier against, I'd have pushed us into this realm of complexity. At some point, you may wish to 'go there' in some way, and again, the best advice is to find an example and make your processing analogous to it, particularly until it really starts making sense how it works.

    Even now knowing how to usually go about things without an example, I still often follow them just to make my task faster, easier and less error-prone. Thus my most common error is failing to swap out a term in copied and pasted code for the new tag's terminology. Thankfully the compiler often catches this. I also am sure to reread everything I do numerous times before moving on.

    Moving on...

    PromotionValidity checks you may need to look at:
    I find VSBarbs here in this section which is a bit unique for promotion tags:
    Code:
    if (isSpy())
        {
            if (promotionInfo.getAttackCombatModifierChange() != false ||
                promotionInfo.getDefenseCombatModifierChange() != false ||
                promotionInfo.getPursuitChange() != 0 ||
                promotionInfo.getEarlyWithdrawChange() != 0 ||
                promotionInfo.getVSBarbsChange() != 0 ||
    This list is found in
    bool CvUnit::isPromotionValid(PromotionTypes ePromotion, bool bKeepCheck) const
    and it indicates that promotions with this value are not valid for spy units, which do not take normal promotion tags in the normal manner - usually they mean something completely different for spies. Spies are tricksy little dudes and a whole subject of study in and of themselves. If you aren't programming a tag specifically to be used for spy promos, include your promotion's get function call here on a new line somewhere in the middle here. Thus, right after the line I stopped at above, I'm adding:
    Code:
    
                promotionInfo.getReligiousCombatModifierChange() != 0 ||
    I'm also adding a similar line under the same basic syntax I found before under another validity check designed to turn off promotions that use certain tags for all units that don't have any strength score. OBVIOUSLY a combat modifier of any kind won't apply to 0 strength units.
    Code:
        if (getDomainType() != DOMAIN_AIR)
        {
            if (baseCombatStr() < 1 && m_iBaseCombat < 1 && !isCommander())
            {
                if (promotionInfo.getInterceptChange() != 0 ||
                    promotionInfo.getEvasionChange() != 0 ||
                    promotionInfo.getWithdrawalChange() != 0 ||
    
    starts off this list and it's in the same validity function.

    processUnitCombat and processPromotion

    Next on the search I find the line that processes in our values from the unitcombat tag applications as we're adding or removing unitcombats that use this tag:
    Code:
        changeExtraVSBarbs(kUnitCombat.getVSBarbsChange() * iChange);//no merge/split
    
    In void CvUnit::processUnitCombat(UnitCombatTypes eIndex, bool bAdding, bool bByPromo)

    At the beginning of this function we are setting up whether we are adding or subtracting the value and that will determine whether iChange is 1 or -1. When iChange is 1, the tag is being added if there is any value in that tag on the unitcombat being added. If the unitcombat is being removed from the unit, iChange will be -1 and thus the call to changeExtraVSBarbs will include a negative parameter that will remove the amount this unitcombat added to the unit when it was originally assigned.

    So our new tag needs a line for this since we've included the full triad of unit tags and have made it possible for unitcombats to influence Religious Combat Modifiers.
    Code:
        changeExtraReligiousCombatModifier(kUnitCombat.getReligiousCombatModifierChange() * iChange);//no merge/split
    Remember we just established the changeExtraReligiousCombatModifier function above.

    If you do not have this tag in use for unit combats, you don't include this step. Again this gets a bit trickier for more involved data types so follow examples given and you should be able to work through it properly.

    The //no merge/split comment was added to help identify those tags that won't have this data calculated in another after-stage if the unit is merged or split. Combat Modifiers don't adjust, for example, on a split or merge in size matters, but the actual unit strength very much does. The work involved on those tags that would be influenced by a merge or split gets much more intense. Again, follow examples (even I have to with this go off of what took many hours of headaches to perfect.) It's nice to explicitly state here that this tag is NOT needing these extra efforts.

    It should come as NO surprise that you're going to find the same exact sort of line in processPromotion next and need to take an almost identical step there if you are working with a promotion tag.
    Code:
    
        changeExtraVSBarbs(kPromotion.getVSBarbsChange() * iChange);
        changeExtraReligiousCombatModifier(kPromotion.getReligiousCombatModifierChange() * iChange);
    You'll notice the only difference here (aside from not having put the merge/split comment) is that we're calling for kPromotion rather than kUnitCombat. The promotion or unitcombat that is being processed in is referred to this way to setup the call to that promo or unitcombat's information on this particular tag.


    Read/Write Wrappers

    Continuing our search through for our example of VSBarbs, we find:
    Code:
    WRAPPER_READ(wrapper, "CvUnit", &m_iExtraVSBarbs);
    Here's where we need to be a little careful.

    This is the read wrapper. What it does is load in the unit's saved value from a game save. This IS the read sequence for each unit. When values are stored for a unit in the save file, they are saved in something similar to a comma delineated sequence. So this is the #1 most important thing to know here:
    THE ORDER MATTERS! THE ORDER IS EVERYTHING! DO NOT DISTURB THE EXISTING ORDER!

    Always add to the end of the read list. So take this example of code and paste it to the end of the WRAPPER_READ list and replace with your tag instead.

    BUT, how do you know where the end of the list is? It seems to start and stop a LOT. It sorta does and then again all it's really doing is pausing to process a few things needed after loading a tag's data on occasion.

    THE end of the list is where you find this statement:
    Code:
    WRAPPER_READ_OBJECT_END(wrapper);
    So place your tag's variable into the read stream at the very end, just before this. Always at the end. No matter how crazy complex your data model requires, always the end. Ok, there ARE a few exceptions but for now let's not get into that and confuse things.

    So now at the end of the read sequence, I've added:
    Code:
    WRAPPER_READ(wrapper, "CvUnit", &m_iExtraReligiousCombatModifier);
    And I'm not getting into HOW all this works. I've allowed it to largely remain mysterious for me and procedural and I'll let a more advanced programmer answer more advanced questions about these streams. But I WILL say that if you ever mess this up you will create the most headache-inducing bugs I have ever had to work on. Audit the order of reads vs the order of writes once more at the end of your process before compiling. It's just so important to get it right.


    The next thing you'll find is going to be the write wrapper sequence and where our example is placed there. AGAIN, the sequence is everything and you need to put your tag's write line(s) at the END of the write stream. The write stream is usually cleaner.

    So now the end of my write stream looks like:
    Code:
        WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraGatherHerdCount);
        WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraReligiousCombatModifier);
        WRAPPER_WRITE_OBJECT_END(wrapper);
    }
    There is one Cv type that this end is a little harder to find and all the read and write lines are in a conditional nesting within an if{} statement. Be sure to include yours within that nesting as well. I think it was CvPlayer.cpp. Just a warning to stay sharp in passing - every warning has a story behind it ;)

    Another Misc application issue for our example tag
    vsBarbs shows up in a few applications for checking whether to apply anti-barbarian handicaps or not, which has nothing to do with our example tag nor our tags.

    But it does also show up in the section for battlefield promotions, where if the promotion has some NEGATIVE value against barbarians, victorious units (both attackers and defenders are specified in different sections here) shouldn't be able to get this promotion automatically from a battlefield promotion event (free promo when winning a tough fight).

    In this section if you want to make it conditionally possible to have a promotion with this tag be potentially handed to the victor, you can specify the tag and the condition of the battle. I'm going to keep this Religious Combat tag completely out of this space but not ban a promo for having it from being awardable in this context either. Thus, while promos with getVSBarbsChange at less than 0 are set to be impossible to earn via battlefield promos, I won't be adding my new tag anywhere in this function's many filters.

    Here is the section where it's setting up the positive value of anti-barbarian combat modifiers to be capable of making their promotion applicable to a victorious attacker:
    Code:
                        //TB Combat Mods Begin * anti-barbarian combat mod
                        if (kPromotion.getVSBarbsChange()>0 &&(pDefender->isHominid()))
                        {
                            aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
                        }
    It's saying if our unit attacked a Hominid (non-animal barbarian or neanderthal) and the promotion has some positive anti-barbarian value, then add it to the list of considered possible promotions to be added if we're earning a battlefield promotion here.

    Finale
    At this point, we've reached the end of the CvUnit.cpp file where all of the instances of our example (VSBarbs) have been found and mimicked where appropriate to do so. Thus, we've completed this... wait no we haven't! I personally still need to program the getReligion() function for the unit which plays into how this tag works!

    Ok, so I scroll to the bottom of CvUnitInfo and add my function - though when I go to do so I realize that this is going to take some caching so that the unit only has to infrequently figure out what religion it is - and that takes setting up a few new variables and another function entirely for that. I won't get too in detail as to what I did here - I'll let you ask if you wish to understand deeper ;)
    Code:
    void CvUnit::defineReligion()
    {//call this when a unitcombat that has a religion is processed in and for all units when the state religion is changed.
        //Check for dedicated faith by unit type, assign it and let it not be changeable unless the unit type changes
        if (!m_bIsReligionLocked)//purely meaning the unit has an overriding religious unitcombat in its base definition (like a missionary, crusader or hellsmouth dog would)
        {
            if (m_eReligionType == NO_RELIGION)
            {
                for (int iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
                {
                    if (m_pUnitInfo->hasUnitCombat((UnitCombatTypes)iI))
                    {
                        ReligionTypes eOriginalCombatReligion = GC.getUnitCombatInfo((UnitCombatTypes)iI).getReligion();
                        if (eOriginalCombatReligion != NO_RELIGION)
                        {
                            m_eReligionType = eOriginalCombatReligion;
                            m_bIsReligionLocked = true;
                            break;
                        }
                    }
                }
            }
            //if not locked by innate type, after changes in unitcombat process function we'll call this function IF the unitcombat has a religion.
            //This function is also called if the state religion changes so if we find a unit combat has defined m_eReligionType then we'll not bother with switching to the state religion so check here first
            bool bFound = false;
            for (int iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
            {
                if (isHasUnitCombat((UnitCombatTypes)iI))
                {
                    ReligionTypes eUnitCombatReligion = GC.getUnitCombatInfo((UnitCombatTypes)iI).getReligion();
                    if (eUnitCombatReligion != NO_RELIGION)
                    {
                        m_eReligionType = eUnitCombatReligion; //Let's assume there's only going to be one of these on a unit ever - it only ever comes up if the unit isn't locked with a pre-defined one anyhow
                        //and unitcombats that assign a religion should be rare to assign unless we are more advanced into the Ideas project where the city will assign its religion type to all units that it produces.
                        //There could be promos that assign overriding religious types but we'll cross that bridge when we get there.
                        bFound = true;
                        break;//thus we stop at the first one we find
                    }
                }
            }
            if (!bFound)
            {
                m_eReligionType = GET_PLAYER(getOwner()).getStateReligion();//NO_RELIGION is a perfectly satisfactory answer here.
            }
        }
        //else do nothing - if the religion is locked we're done here.
    }
    
    ReligionTypes CvUnit::getReligion() const
    {
        return m_eReligionType;
    }
    At this point, you definitely want to compile the finalrelease code again to make sure you haven't pooed the scrooch. You don't have to run the mod, just look for compiler errors so they don't get overwhelming later. You're doing pretty dang good if you haven't messed up any syntax anywhere!
     
    Last edited: Dec 31, 2018
    KaTiON_PT likes this.
  6. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    CvGameTextMgr.cpp

    The main thing with a set of unit, promotion and unitcombat tags is to include text lines for help displays. You'll want those for each of the above that applies individually, and one for the total value that will show up on a unit.

    So for units, this line will show up on the unit type information in the pedia and will show up on the proper hot key page for the unit hover information when viewing that info by hovering over the unit icon in the city build selection panel.

    For unitcombats, your line will show up in the unitcombat information in the pedia (combat class is what these are called in-game) and the details for tags like these display on units that come with those unitcombats in the pedia and on the 'shift' hotkey hover data on the unit as well (though that is a list of unitcombats and if you keep holding shift and taking the mouse off hover then back onto the icon, it cycles through the tag breakdowns on these unitcombats and what they offer.)

    For Promotions, your line will show up on the promotion icon hover help panel and in the pedia as well.

    The code and python UI directs when these lists of tags display but the lines themselves are defined in CvGameTextMgr.cpp and in the Text files.

    The easiest thing to do to go straight to the spot you'll need to insert your new text lines is to find another tag in the same class as the one you are needing to add a new text display section for that is very similar in nature to the tag you are working with. (Does that sound familiar by now?) In VS, go to the function declaration of that example in CvInfos.h, right click on that function and select 'Find all references'. In the Output box in VS(usually at the bottom though VS can be configured numerous ways) you will find the full list of everywhere that function is found in the solution files. Look for where it is found in CvGameTextMgr.cpp and pattern your new tag after that fashion.

    There are a lot of potential quirks in going about this, depending on what kind of game objects you are using. Promotions in particular have some very intriguing details as to how text is delivered. I'll go through this in my example here since I've programmed a triad set of unit tags. But buildings, traits, civics, all have their own unique considerations to make so you'll find there's the need to really look at the intricacies of the functions in which these display lines are delivered and try to figure out what's taking place.

    Building tags, for example, may be included in or excluded from being active when the building is religiously disabled, or only inactive when the building is obsoleted, though there are sections you'd place these lines within to indicate that even if the building is obsoleted you still display the tag.

    All these intricacies often have 'context' as being the complexity to consider, when and how and why to display. Display during pedia view only? Display during a hotkey display page only (you'll see this on unit tags)? These kinds of considerations must be understood for how the code is working with them. My point here is that although I can warn you to take some care and read the function through enough to understand its unique facets, I can't prepare you for every concern you'll find.


    So to continue with the example I'm using to show how this goes, I figured out that thematically, VSBarbs is a good tag to work with as my template for ReligiousCombatModifier. This would be true, here, as well. In units, if you are working with a combat effect, the tag goes in a special section where combat manipulators are supposed to be placed for text displays so that they only show when the ctrl key is depressed (on help hovers). So for a combat modifier, following another conditional combat modifier is a good idea for this part of the project.

    So I find int CvUnitInfo::getVSBarbs() cost and I show all instances of getVSBarbs() throughout the project.

    The first reference I find is to CvGameTExtMgr.cpp Line 19396.. I double click on that to go right there and start taking a look at the function this is coming up in.

    Immediately the text display programming for getVSBarbs() is shown:
    Code:
            if (kUnit.getVSBarbs() != 0)
            {
                szBuffer.append(NEWLINE);
                szBuffer.append(gDLL->getText("TXT_KEY_UNIT_VSBARBS", kUnit.getVSBarbs()));
            }
    And, scrolling up, I find that this appears within
    Code:
    
        //bTBUnitView1 = (Combat)
        if (bTBUnitView1)
        {
    Brackets. This is where we began only listing off those display lines that go within the Combat help page (ctrl hover where the filter applies).

    Now we also know that bTBUnitView1, when true, means the ctrl button is depressed as we hover over a unit icon to get the help info up.

    And scrolling up further, we find that this display line is set within this function:
    void CvGameTextMgr::setBasicUnitHelpWithCity(CvWStringBuffer &szBuffer, UnitTypes eUnit, bool bCivilopediaText, CvCity* pCity, bool bConscript, bool bTBUnitView1, bool bTBUnitView2, bool bTBUnitView3)

    So we're setting up the city help panel - the hover over info for the unit icons found there. The naming on functions is thankfully established for the sake of sanity. It COULD have been named anything but it's sure nice they used the 'set' naming convention and stated almost as point blank as possible what text they are setting up. (If you ever have to add your own text help function, which you may have to do with some projects, try to follow similar naming conventions.)

    Anyhow, I've explained the programming in a text call line recently but I'll do it again here because this post should be able to work any new tag builder through the whole process.

    I'm going to copy the vsBarbs stuff and paste it in just above it and adjust it for my Religious Combat Modifier. Why above it? Because I see that below VSBarbs is the listing for getAnimalCombatModifier and it seems to make more sense for that to be closer related to VSBarbs, even though all 3 of these tags are just combat modifiers one way or another.

    Anyhow my new text display command now looks like this:
    Code:
            if (kUnit.getReligiousCombatModifier() != 0)
            {
                szBuffer.append(NEWLINE);
                szBuffer.append(gDLL->getText("TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER", kUnit.getReligiousCombatModifier()));
            }
    So this line is saying, if this unit (kUnit is the unit under evaluation for this display call, as shown in this line above:
    CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
    This line makes kUnit a macro representation of GC.getUnitInfo(eUnit). You could go through and replace kUnit with GC.getUnitInfo(eUnit) anywhere in this function and it would work the same but it wouldn't be as fast - once this macro is setup, since it's being called a lot, it shortcuts the manner in which the unit is referenced since the code doesn't have to run through establishing which unit it is referring to each time - it now ran that once and remembers it for the rest of the function, applying that established reference anywhere kUnit is used until the function is finished running.

    In this initial 'if' filter here at 'if (kUnit.getReligiousCombatModifier() != 0)' we see != (if doesn't equal) in use. Some will say > 0 instead when it should really be impossible to setup a negative value. I would do that here except that I'd like to be able to spot a problem in the XML easier from within the game rather than setting it up to be invisible if the unit was given a negative amount of Relgious Combat Modifier. With this filter being applied to != 0, it means if the ability isn't used, we don't show it in the display sequence at all.

    szBuffer.append(NEWLINE);
    szBuffer is the display string we're building and this command to it adds a hard return.

    szBuffer.append(gDLL->getText("TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER", kUnit.getReligiousCombatModifier()));
    Here we add the actual text to the display string. gDLL->getText is a function call that adds the text from the XML to the display string we're building. The text entry it calls in the text files (which can be found anywhere in any text file in the mod) and here it is looking for the text indicated with the label, TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER.

    This is followed by ',kUnit.getReligiousCombatModifier()'. The commas past the point of the text label call are delineating the data that would be inserted into the variable calls found in the TXT_KEY itself. Any number of commas and function calls can be supplied here and they will insert themselves in the order of the variable calls found in the text itself.

    At this point I need to define TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER in the text files. I usually do this as soon as I create the definition for this label in the code so I will never forget to add the text I'm referring to.

    I want to make sure that my new label is to be found in an appropriate text file so I'll grep search through the Assets->XML->Text folder for the original text label I'm pattering this after, TXT_KEY_UNIT_VSBARBS. I find it in
    C:\Program Files (x86)\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Caveman2Cosmos\Assets\XML\Text\Units_CIV4GameText.xml
    So I open that file in notepad++ and search for the TXT_KEY_UNIT_VSBARBS directly there.

    So I find:
    Code:
        <TEXT>
            <Tag>TXT_KEY_UNIT_VSBARBS</Tag>
            <English>[ICON_BULLET]%d1_Amount%% VS Non-Animal Barbarians</English>
            <French>[ICON_BULLET]%d1_Amount%% contre les unités barbares non-animales.</French>
            <German>[ICON_BULLET]%d1_Amount%% gegen Barbaren</German>
            <Spanish>[ICON_BULLET]%d1_Amount%% contro barbari non-animali.</Spanish>
            <Polish>[ICON_BULLET]%d1_Amount%% przeciwko nie-zwierzęcym Barbarzyńcom</Polish>
        </TEXT>
    I start by copying and pasting just beneath this portion, everything I'm showing there, in the text file.

    I then delete all the lines but English (to let interpreters be guided to needing to add their interps here when they can - I certainly cannot do them.)

    I change the TXT_KEY label to the one I'm calling for in the code: TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER, and I reword everything past the %d1_Amount%% (which is a numeric variable (%d) call that will come in as a % - the first% ends the name of the variable and the second expresses an actual % character into the text at that point, so %d1_Amount% will deliver the first called numeric value after the comma in the parameters of the text display call, which in this case will be the amount found in kUnit.getReligiousCombatModifier() and then be immediately followed with a % character, like any 'modifier' probably should be.

    Note that %D (capitalized) will automatically result in the addition of a '+' if it's a positive amount. That's a neat trick to know about.

    Code:
        <TEXT>
            <Tag>TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER</Tag>
            <English>[ICON_BULLET]%d1_Amount%% Religious Combat Modifier.  Valid against units that have a difference in Religion.  Inverses to a penalty against units with this unit's religion.</English>
        </TEXT>
    So I've setup this text call and I need to move on now. Looking for the next getVSBarbs to be found in theCvGameTextMgr.cpp I find the second call right where I was here. There's no other displays for this particular function call so I move on.

    I have done the UnitInfo call. Promotions, UnitCombats and the final unit total still need to be done. Since this is a combat modifier, I would have needed to add a line into the combat help but we just piggybacked this modifier onto the normal combatmodifier tag when it applies so it might not be as explanatory to the player during a combat help hover but it could still be determined by an observant player if and when it comes into effect. It's not the only combat modifier I've piggybacked onto the normal combat modifier total.

    So on to the Promotion display line. Again, I go to CvInfos.h and find the VSBarbsChange call there and repeat what I did with VSBarbs in Units. Finding int getVSBarbsChange() const; in the Promotion section, I search for all instances of getVSBarbsChange().

    As I warned earlier, promotions are a very different cat to the normal display setup you find for UnitInfo. The first line I find in CvGameTextMgr.cpp is this:
    Code:
            iVSBarbsChange += GC.getPromotionInfo(linePromotionsOwned[iI]).getVSBarbsChange();
    So what has happened here is in this function,
    void CvGameTextMgr::parsePromotionHelpInternal(CvWStringBuffer &szBuffer, PromotionTypes ePromotion, const wchar* pcNewline, bool bAccrueLines)

    just above this line, we find that a local integer variable for this tag has been defined and initially established as 0.
    Code:
        int    iVSBarbsChange = 0;
    This is all part of how promotions are setup for displaying the total of themselves plus everything in the promotions prior to them in the same promotionline, or in other contexts JUST their own values.

    So without trying to explain everything upfront about that, procedurally, we just add another line here to declare our own new tag's local integer variable. It doesn't have to go in order but it should be in the appropriate section (there's a section for bools, one for ints, and beyond that for more complicated stuff.) So I add my own int declaration here:
    Code:
        int    iEarlyWithdrawChange = 0;
        int    iVSBarbsChange = 0;
        int    iReligiousCombatModifierChange = 0;
    Then I go down below and add another line like the one we found for iVSBarbsChange:
    Code:
            iEarlyWithdrawChange += GC.getPromotionInfo(linePromotionsOwned[iI]).getEarlyWithdrawChange();
            iVSBarbsChange += GC.getPromotionInfo(linePromotionsOwned[iI]).getVSBarbsChange();
            iReligiousCombatModifierChange += GC.getPromotionInfo(linePromotionsOwned[iI]).getReligiousCombatModifierChange();
    
    At this point, you'll notice we're still just compiling the value that will be used in the display line and we haven't inserted an actual display call for this promo tag yet. We're not finding another call to getVSBarbs, and we won't because it's been transferred to our new variable, iReligiousCombatModifierChange - so now we need to search CvGameTextMgr.cpp below this point for instances where iVSBarbsChange is found.

    We find:
    Code:
        if (iVSBarbsChange != 0)
        {
            szBuffer.append(pcNewline);
            szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_VSBARBS_TEXT", iVSBarbsChange));
        }
    Which is exactly where the promotion text display is delivered. You'll notice that the variable supplied in the parameter call is our local variable rather than a call to the promotion directly.
    I repeat similar steps to what I did in the Unit tag and I come up with code that pastes in immediately below the example shown above.
    Code:
        if (iReligiousCombatModifierChange != 0)
        {
            szBuffer.append(pcNewline);
            szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_RELIGIOUS_COMBAT_MODIFIER_TEXT", iReligiousCombatModifierChange));
        }
    The Text reference itself needs to be created as well. The VSBARBS example is grepped and found inside Promotions_CIV4GameText.xml. I copy and paste it and take the same steps as I did in the Unit display and end up with this:
    Code:
        <TEXT>
            <Tag>TXT_KEY_PROMOTION_RELIGIOUS_COMBAT_MODIFIER_TEXT</Tag>
            <English>[ICON_BULLET]%D1_Change%% Religious Combat Modifier</English>
        </TEXT>
    I'm letting the more full description of the tag ability remain on the original unit tag only. Promos are best to leave as short as possible.

    And there you have it. The promo tag is complete. Onto the UnitCombat tag. Again, going back to CvInfo.h and searching for the next instance of getVSBarbsChange, I quickly find the function declaration as it stands under CvUnitCombatInfos section and search 'Find All References' for that function. The few references it finds to this function being called in CvGameTextMgr.cpp leads me to this:
    Code:
        if (info.getVSBarbsChange() != 0)
        {
            if (bFirstDisplay)
            {
                szBuffer.append(NEWLINE);
                szBuffer.append(gDLL->getText("TXT_KEY_FROM_COMBAT_CLASS"));
                szBuffer.append(info.getDescription());
                bFirstDisplay = false;
            }
            szBuffer.append(NEWLINE);
            szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_VSBARBS_TEXT", info.getVSBarbsChange()));
        }
    in this function.
    void CvGameTextMgr::setUnitCombatHelp(CvWStringBuffer &szBuffer, UnitCombatTypes eUnitCombat, bool bCivilopediaText, bool bFromUnit)

    Looks like we're exactly where we should be.
    Again, the syntax is a little unique for UnitCombats. Copy and paste the whole thing and replace the VSBarbsChange with our own terminology. Note that this UnitCombat text uses the same text display line that the promotion one calls for.
    Code:
        if (info.getReligiousCombatModifierChange() != 0)
        {
            if (bFirstDisplay)
            {
                szBuffer.append(NEWLINE);
                szBuffer.append(gDLL->getText("TXT_KEY_FROM_COMBAT_CLASS"));
                szBuffer.append(info.getDescription());
                bFirstDisplay = false;
            }
            szBuffer.append(NEWLINE);
            szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_RELIGIOUS_COMBAT_MODIFIER_TEXT", info.getReligiousCombatModifierChange()));
        }
    The (bFirstDisplay) section is pretty much the same in every text display for unitcombat lines because the setup is a little different for these and it deals with how the first tag in use, if there IS one, will setup the whole text with a little tack on before the tag is displayed. It's just a unique facet of UnitCombat tags and goes further towards explaining what I meant at the beginning of this discussion that all display types have their own idiosynracies.


    The last type we must address is the easiest to forget and it's the one you always need for any unit/promo/unitcombat tag, the display for the totaling function found in CvUnit.cpp so that a unit IN the game will show it's grand total of this ability in a hover display.

    The easiest thing to do here is go back to our VSBarbs example and find the totaling function declaration in CvUnit.h (int vsBarbsModifier() const;) and find all references on that so that the CvGameTextMgr.cpp reference to it can be found as our template to work off of again.

    Note that for this function, I added a boolean for use in display calls for the unit so it always gives the full value in that case, not the conditional value of + or - that's based on what the opposing unit actually is. So in this display call asking for this function's value, I want to denote this boolean as true (its default is false).

    Our example template search finds:
    Code:
                if (pUnit->vsBarbsModifier() > 0)
                {
                    if (bShort)
                    {
                        szString.append(NEWLINE);
                        szString.append(gDLL->getText("TXT_KEY_UNIT_VSBARBS_MODIFIER_SHORT", pUnit->vsBarbsModifier()));
                    }
                    else
                    {
                        szString.append(NEWLINE);
                        szString.append(gDLL->getText("TXT_KEY_UNIT_VSBARBS_MODIFIER", pUnit->vsBarbsModifier()));
                    }
                }
    Under the function:
    void CvGameTextMgr::setUnitHelp(CvWStringBuffer &szString, const CvUnit* pUnit, bool bOneLine, bool bShort)

    Looks like we're right where we should be.

    You'll notice (those idosynchracies again!) that there is a difference between a 'short' call and a normal call here. You're going to want the short call to find a shorter text to deliver, one that doesn't explain the rule details on the tag, just expresses the amount the unit has in total. In the normal call we are bringing this up in contexts where we have more room to deliver information so we'll add the full explanation on the tag values here. So my code, which I copy/paste/edit for my own tag will be pasted right beneath this VSBarbs call, and comes out looking like this:
    Code:
                if (pUnit->religiousCombatModifierTotal(NO_RELIGION, true) != 0)
                {
                    if (bShort)
                    {
                        szString.append(NEWLINE);
                        szString.append(gDLL->getText("TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER_SHORT", pUnit->religiousCombatModifierTotal(NO_RELIGION, true)));
                    }
                    else
                    {
                        szString.append(NEWLINE);
                        szString.append(gDLL->getText("TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER", pUnit->religiousCombatModifierTotal(NO_RELIGION, true)));
                    }
                }
    
    pUnit->religiousCombatModifierTotal(NO_RELIGION, true)
    I fed the first parameter in my totaling function a default value because I had to specify that since I had to enter something there so I could give a non-default value of true in the second parameter.

    Unlike my VSBarbs example, I also duplicate my call to the text key I made earlier for the longer display on Religious Combat Modifier in the UnitInfo. I realize now that it's unnecessary for there to have been a whole new text for that.

    IF I was worried about speed here much (and it might be nice to help the pedia get even faster still) then I would restructure this function so that calls to the totaling functions in CvUnit.cpp are only made once for a given type by defining a local variable and calling for that function to fill that variable then applying it both to the filter check,

    if (pUnit->religiousCombatModifierTotal(NO_RELIGION, true) != 0)

    which would then look something like

    if (iReligiousCombatModifier != 0)

    AND
    szString.append(gDLL->getText("TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER_SHORT", pUnit->religiousCombatModifierTotal(NO_RELIGION, true)));
    would become:
    szString.append(gDLL->getText("TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER_SHORT", iReligiousCombatModifier));

    There are areas of the code where this small time optimization could make a huge difference. This hasn't been perfected yet here because it wouldn't mean much since displays are never called during end of turn processing and they already come up pretty quickly on hovering for the 'tool tip'.

    So now, once I add TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER_SHORT to the text file xml:
    Code:
        <TEXT>
            <Tag>TXT_KEY_UNIT_RELIGIOUS_COMBAT_MODIFIER_SHORT</Tag>
            <English>[ICON_BULLET]%d1_Amount%% Religious Combat Modifier</English>
        </TEXT>
    I'm completely finished now with text display issues for my new tag.

    Woohoo! Next up, AI factors. That will wrap up all work on this tag.
     
    Last edited: Jan 1, 2019
  7. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    AI

    This is the most difficult portion to prepare anyone for in tag design. The reason for this is because each tag type is added to AI considerations in their own unique way.

    Thankfully, in this case, our work for this example tag set will be pretty simple.

    Unit tags are already caught in a state of underdevelopment with AI so we won't add any valuation to units from our new tag, because if we do at this point without taking the added time to make sure all unit tags are considered where they should be and how, we will likely only be making units consider the value of this new tag too much compared to other tags with even more importance that have no current consideration.

    Promotion and Unitcombat tags are usually evaluated the same way, just in different specific functions for them, just as there are different but almost identical functions for processPromotion and processUnitCombat.

    You can again use an example tag with a similar effect to base our model of adding value for the AI to consider these tags. For Promotions and Unitcombats, this programming is found in CvPlayerAI.cpp. Find all References on the promotion function in CvInfo.cpp and look for where it comes up in CvPlayerAI.cpp.

    For getVSBarbsChange() in Promotions, we find it appears in CvPlayerAI.cpp here:
    Code:
        iTemp = kPromotion.getVSBarbsChange();
        if (iTemp != 0)
        {  
            if (eUnitAI == UNITAI_COUNTER ||
                eUnitAI == UNITAI_CITY_DEFENSE ||
                eUnitAI == UNITAI_CITY_COUNTER ||
                eUnitAI == UNITAI_ESCORT_SEA ||
                eUnitAI == UNITAI_EXPLORE_SEA ||
                eUnitAI == UNITAI_EXPLORE ||
                eUnitAI == UNITAI_PILLAGE_COUNTER ||
                eUnitAI == UNITAI_HUNTER ||
                eUnitAI == UNITAI_HUNTER_ESCORT ||
                eUnitAI == UNITAI_GREAT_HUNTER ||
                eUnitAI == UNITAI_ESCORT )
            {  
                int iEraFactor = 10 - (int)getCurrentEra();
                iTemp *= iEraFactor;
                iTemp /= 9;
                if (pUnit != NULL && !pUnit->isHominid())
                {
                    iValue += iTemp;
                }
            }
        }
    To explain, in this very long function:
    int CvPlayerAI::AI_promotionValue(PromotionTypes ePromotion, UnitTypes eUnit, const CvUnit* pUnit, UnitAITypes eUnitAI, bool bForBuildUp) const

    iTemp is the temporary value platform that we build the value that the tag's volume will add to the overall value for the unit to select this promotion.

    iValue is the variable that is compiling the total promotion value for the unit to select. So once iTemp has been given its value based on all the conditions necessary to consider to vary the value of our tag's numeric volume with, iTemp is added to iValue.

    Before another tag is evaluated for worth, iValue is cleared to 0 or re-assigned the base value of the new tag.

    So the way that getVSBarbsChange works is that it first defines iTemp as it's own value.

    Then if there is any value in iTemp at that point (which means there IS something to evaluate here in this tag's section, otherwise move on immediately to the next tag to evaluate) then let's take a look at how to really value this amount.

    For some sake of reference, IIRC, 30 is what a normal Combat I promotion is going to be valued at for normal combat units because it simply multiples the value of the promotion's Combat Modifier by 3 for units that are generally intended for combat (identified by the UnitAI type of the unit). 100 is the value that some very very very potent tags, like bBlitz, bring to the value of the promo and you'll only see that as a cap in some special cases.

    Many tags are given greater value the higher the unit's overall amount of that ability becomes so as to suggest to the AI that it should focus its units into various specialties. Thus, the more % combat vs a particular UnitCombat type a unit has, the more the value of that ability is worth.

    In the case of this VSBarbs value, if the unit is of a particular list of AI types that highly value this ability then the value of iVSBarbs becomes greater, but loses that boost of value as the game gets further and further in, thus you see how the era plays a role in reducing the boosted amount - the higher the era of the game, the less extra value we give this tag.

    For iVSBarbs, there appears to be no consolation value if you aren't working with a unit that would want this tag's benefits but many tags to give alternative lesser amounts of worth for all other units.

    You'll notice that if (pUnit != NULL is a statement added before !pUnit->isHominid() has a chance to be called. If you're going to assume we're looking at assigning value based on a call to a detail about the possible unit that we're asking this value to be compiled for, you MUST include a pre-check to ensure that there IS a unit defined as pUnit right now. This function is often called without ever needing to define a specific unit but just to look for the base value of the promotion, not specific to any particular unit. So if you don't include that 'if(pUnit != NULL portion before any other pUnit-> call, you'll cause a crash. I cannot tell you how many times I brought this on myself early on. There are stories behind all warnings!

    If you take some time and look through how other tags are valued, you'll see a wide assortment of methods. It can help you to figure out how you want to guess at the best way to go about valuing your tag. This section is used for buildup intended tags as well but there is a different spot in CvUnitAI where values on statuses are more specifically programmed. Those are usually much more specific to the situation for the unit.

    But you'll see some of the sections here are very intricate, looking for situation specifics in the game environment to influence the value a given tag adds.

    Mine is currently intended for use by a trait so I could potentially get away with adding no consideration for my tag BUT that's bad form because if anyone used this effect on a skill promotion (one that units select as a result of levelling up) they would be adding a tag the AI is blind to. So some basic generic value is good to assign here. Since it can have a backlash effect, I'm thinking each point of Religious Combat Modifier should be worth about 2/3rds of a normal Combat Modifier amount. So for units intended for combat, we can add the value of the tag times 2.

    For a list of UnitAIs to consider for your filter uses, rightclick on any UNITAI_ macro value and select Go to Declaration, which should take you to the CvEnums.h file right where all UnitAIs are listed out.

    I'm going to want to include value on this tag for
    UNITAI_ATTACK,
    UNITAI_ATTACK_CITY,
    UNITAI_COLLATERAL,
    UNITAI_PILLAGE,
    UNITAI_RESERVE,
    UNITAI_COUNTER,
    UNITAI_CITY_DEFENSE,
    UNITAI_CITY_COUNTER,
    UNITAI_ATTACK_SEA,
    UNITAI_RESERVE_SEA,
    UNITAI_ASSAULT_SEA,
    UNITAI_ATTACK_CITY_LEMMING

    There's a few others that could use it but I'd prefer not to include it on for more thematic and value of general use assumptions.

    So this very basic AI value comes up with this coding:
    Code:
      
        iTemp = kPromotion.getReligiousCombatModifierChange();
        if (iTemp != 0)
        {
            iExtra = pUnit == NULL ? kUnit.getReligiousCombatModifier() : pUnit->getExtraReligiousCombatModifier();
            iTemp *= (100 + iExtra * 2);
            iTemp /= 100;
    
            if (eUnitAI == UNITAI_ATTACK ||
                eUnitAI == UNITAI_ATTACK_CITY ||
                eUnitAI == UNITAI_COLLATERAL ||
                eUnitAI == UNITAI_PILLAGE ||
                eUnitAI == UNITAI_RESERVE ||
                eUnitAI == UNITAI_COUNTER ||
                eUnitAI == UNITAI_CITY_DEFENSE ||
                eUnitAI == UNITAI_ATTACK_SEA ||
                eUnitAI == UNITAI_RESERVE_SEA ||
                eUnitAI == UNITAI_ASSAULT_SEA ||
                eUnitAI == UNITAI_ATTACK_CITY_LEMMING)
            {
                iTemp *= 2;
                iValue += iTemp;
            }
        }
    
    Sometimes simple is best. Particularly to start with. You can always come back in here and make adjustments and I'm sure there are still a lot of things that can be done to constantly improve on how certain tags obtain AI value for selection.

    iExtra = pUnit == NULL ? kUnit.getReligiousCombatModifier() : pUnit->getExtraReligiousCombatModifier();
    iTemp *= (100 + iExtra * 2);
    iTemp /= 100;
    is included to add that extra amount of value for units that already start with or have developed some of this ability previously, giving the hint to the AI to continue developing this ability if there already is a head start for this unit type.


    UnitCombat AI -

    So we should usually carbon copy our valuation programming on any promotion tag and use an almost exact replica of it for UnitCombat AI, which is to be found in the next section. Highlight your AI coding and copy it, then highlight the tag we're basing things on and find the next place in CvPlayerAI.cpp that it shows up with an almost identical bit of programming for value again.

    The ONLY difference you'll see there is that kPromotion references have been changed to kUnitCombat references.

    So paste in your new section for the new tag and change those kPromotion references to kUnitCombat references and you're good to go in 9 out of 10 cases. If you see a good reason for values to change in Unit Combat evaluations, by all means adjust that in the programming here, but usually this is only called when considering the values of tags on a unitcombat when the unitcombat would be added by a promotion, therefore, even here it all relates to other sources of value on a normal promotion. It's pretty much just a nested extension of promotion valuation in the way it comes up anyhow.

    So once I found the next instance of getVsBarbsChange() here in CvPlayerAI.cpp, I add my new getReligiousCombatModifierChange() value programming and change kPromotion to kUnitCombat and I've completed the AI work on my tags.

    Code:
    
    
        iTemp = kUnitCombat.getVSBarbsChange();
        if (iTemp != 0)
        {  
            if (eUnitAI == UNITAI_COUNTER ||
                eUnitAI == UNITAI_CITY_DEFENSE ||
                eUnitAI == UNITAI_CITY_COUNTER ||
                eUnitAI == UNITAI_ESCORT_SEA ||
                eUnitAI == UNITAI_EXPLORE_SEA ||
                eUnitAI == UNITAI_EXPLORE ||
                eUnitAI == UNITAI_PILLAGE_COUNTER ||
                eUnitAI == UNITAI_HUNTER ||
                eUnitAI == UNITAI_HUNTER_ESCORT ||
                eUnitAI == UNITAI_GREAT_HUNTER ||
                eUnitAI == UNITAI_ESCORT_SEA ||
                eUnitAI == UNITAI_ESCORT )
            {  
                int iEraFactor = 10 - (int)getCurrentEra();
                iTemp *= iEraFactor;
                iTemp /= 9;
                if (pUnit != NULL && !pUnit->isHominid())
                {
                    iValue += iTemp;
                }
            }
        }
    
        iTemp = kUnitCombat.getReligiousCombatModifierChange();
        if (iTemp != 0)
        {
            iExtra = pUnit == NULL ? kUnit.getReligiousCombatModifier() : pUnit->getExtraReligiousCombatModifier();
            iTemp *= (100 + iExtra * 2);
            iTemp /= 100;
    
            if (eUnitAI == UNITAI_ATTACK ||
                eUnitAI == UNITAI_ATTACK_CITY ||
                eUnitAI == UNITAI_COLLATERAL ||
                eUnitAI == UNITAI_PILLAGE ||
                eUnitAI == UNITAI_RESERVE ||
                eUnitAI == UNITAI_COUNTER ||
                eUnitAI == UNITAI_CITY_DEFENSE ||
                eUnitAI == UNITAI_ATTACK_SEA ||
                eUnitAI == UNITAI_RESERVE_SEA ||
                eUnitAI == UNITAI_ASSAULT_SEA ||
                eUnitAI == UNITAI_ATTACK_CITY_LEMMING)
            {
                iTemp *= 2;
                iValue += iTemp;
            }
        }

    Building tags are a lot more difficult to work with. Civics tags may be the worst, which is why I really do NOT like working on civic tags. As I've stated before, Civics consider conditional factors on whether something is beneficial or not to perhaps the deepest degree possible.

    I realize I'm probably leaving a lot of questions more than giving answers so again, I'll try to answer any questions y'all may have.

    For now, until we take a look at some more complex examples later, this completes the tutorial on adding a new tag.

    See if you can work through a new tag project for yourself!
     
    Last edited: Jan 2, 2019
  8. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
  9. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
  10. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
  11. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
  12. KaTiON_PT

    KaTiON_PT Chieftain

    Joined:
    Jan 2, 2010
    Messages:
    842
    Location:
    Portugal
    Appreciate all the work you're having to compile this guide. Thank you. :hug:
     
  13. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    Anyone who can work through this stuff is 75% of the way to being a great C2C code programmer - it at least plants the seed in REALLY good soil.

    I'll do another one soon for defining a new UnitAI type because I really need some help in that department - not just defining one but correcting those we have - a lot. The easiest way to learn how to edit something is to learn how to make it from scratch. If I can teach those who want to learn I'll be easing up on my own headaches a lot I think - y'know the whole give a man a fish vs teaching a man how to fish thing.

    Then we can cover other subjects as they come up and y'all will find the limitations of my own knowledge. But what I do know, I should share I think.
     
    KaTiON_PT likes this.
  14. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    Ok, for now that does it until we need to look at another example situation. The next time I do something more complicated or with a tag in a different type, I'll add another section for that discussion, thus I am leaving some posts reserved.

    Let me know if I lose you anywhere as you work through adding a tag for yourself!
     
  15. sorcdk

    sorcdk Chieftain

    Joined:
    Jun 26, 2014
    Messages:
    59
    Location:
    Denmark
    I am having some problems with compiling the .dll with visual studio (2017, since that is the latest of the .slns on SVN). I have tried to look up general guides for how to handles this, but they are for really old versions of VS so there might be some details that are different. I have set up TOOLKIT, PSDK, and CIVINSTALL in the makefile, but I get errors related to "include MakefilePaths", based on it not being able to be found.
    I may have missed it, but it would be nice if you could point to a guide or explain yourself how to make sure that we do to be able to compile the dll for caveman 2 cosmos.
     
  16. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    I will try to compile some instructions on getting things setup. I'm using 2017. It was not an easy setup and I am already forgetting a lot of the little things I had to do to get it up and running.
     
  17. Anq

    Anq Chieftain

    Joined:
    Apr 14, 2019
    Messages:
    47
    Gender:
    Male
    You don't need the MakefilePaths if you've set up the Paths section in your makefile already.
    Here, from Thunderbrd's post, is his MakefilePaths file. Its content is just the same as the Paths section.

    I don't know if fastdep.exe is optional, anyhow this is where I downloaded it. New makefile (faster compilation, profiling and more)
    I don't know if VS 2017 needs additional setup, I just use VS 2010 found in this article. [BTS] The Easiest Way to Compile a New DLL

    I used VS2010 Express to open "C2C (VS 2010).sln". At first I let it do auto-conversion. Then everything was ready and I could successfully compile it to both debug and release targets.

    I used it today to apply a fix suggested by Thunderbrd (post) for myself, that bug was what I was most concerned about. I didn't want to bother them to get this done for me...
     
    Last edited: May 7, 2019
  18. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    It was already fixed, just not the way you thought it should've been done.
     
  19. Anq

    Anq Chieftain

    Joined:
    Apr 14, 2019
    Messages:
    47
    Gender:
    Male
    I don't see in the log any change in CvCity.cpp. And I actually agreed with your comment and changed the code on my end that way.

    Update: double checked to see with 10566 dll, problem persists, while with mine it is fixed. Testing by adding/removing specialists quite a few times. It's not that the source change was unpublished, but at that revision the only change was about the colorless GP calculation.

    Test detail:
    To begin with, my capital has 50557 points toward a doctor. With +2 from trait and +3 from one specialist, the next turn I should have 50562. Applying your fix, it resulted in exactly that number, while with 10566 dll, it gained more, ending up with 50608 points (random value because I clicked on specialists a random number of times.)
     
    Last edited: May 7, 2019
  20. Thunderbrd

    Thunderbrd C2C War Dog

    Joined:
    Jan 2, 2010
    Messages:
    24,728
    Gender:
    Male
    Location:
    Las Vegas
    Did I never put that on the SVN? Jeez... I thought I'd done it already. I'll check on that later today and make sure it gets implemented. Sorry for that!

    By the way, you have been able to get setup to mod the dll, which many have wanted to do but never had the carry through to actually do it - one reason I'm usually reluctant to spend the time to try to trace out the steps again. Have you been considering joining us on the team here? You sound like you've got a bit of experience in programming. We could use the help, particularly since I'm getting a little distracted lately - as you can see.
     

Share This Page