[Dev] Civic dependency Tree (Parent)

keldath

LivE LonG AnD PrOsPeR
Joined
Dec 20, 2005
Messages
7,367
Location
israel
Heya dear civ4 modders,

lately i have been developing a new-old civic dependency system.
f1rpo0 has been helping me and directing me, but i have decided to put it here to get more help/opinions.
maybe vene some modders would like to use this as well later on.

key description of my system:
a civic can have children civics, aka Dependent civics
Code:
<CivicChild>
                <CivicTypes>CIVIC_R1</CivicTypes> (civic option 1)
                <CivicTypes>CIVIC_R2</CivicTypes> (civic option 1)
                <CivicTypes>CIVIC_ANARCHIZEM</CivicTypes> (civic option 2)
                <CivicTypes>CIVIC_TRADITIONAL</CivicTypes> (civic option 2)
</CivicChild>

here there are 2 civics to choose from in one civic option that are both children civics for the Parent civic (civic option 0).

ill talk about AI logic, as the human is easier.
AI will "
check if a civic is a parent ->
evaluate the parent value + evaluate all the dependencies that belong to it.from the 4 dependent civics above , it will take one civic from the 2 civic options.
meaning the total value of the tested civic will be :
parent + child 1 or 2 + child 3 or 4.

after that, it will choose the best parent out of all the parents in the civic 0 option.
once the code reaches to perform revolution,
another function will take the list of civics the ai picked,
and for the parent that is in that list - the code will force replace the children civic options 1 + 2 --> to the best children civics that are dependent on this parent.
it will replace the civics that got picked for civic option 1+2 in the normal per civic evaluation in vanilla code.

here are the functions i wrote:

CvPlayerAI::AI_civicValue (original function was renamed to AI_civicValue_original
Spoiler :

Code:
/* doto Civics Dependency (Asaf) - start )
    hi jack of AI_civicValue original fn.
    this will value a parent civic with its best children dependant civics.
    if its not a parent civic, normal value will be returned e,g AI_civicValue AI_civicValueParent
 */
int CvPlayerAI::AI_civicValue(CivicTypes eCivic) const
{
    PROFILE_FUNC();
 
    CivicTypes eParent = getCivicParent(eCivic);
 
    int eBestChild = 0;
 
    if (eParent != NO_CIVIC)
    {
        CvCivicInfo kCivic = GC.getCivicInfo(eParent);
        int childrenCivics = kCivic.getNumParentCivicsChildren();
        int eTotalCivicChildrenValue = 0; //should be one per civ option
   
        if (childrenCivics > 0) //gotta be above 0 cause of getCivicParent call above
        {
            //evaluate all childcivics and save their values
            //this should save some loops below.
            int* m_atemp = new int[childrenCivics];
            for (int i = 0; i < childrenCivics; i++)
            {
                m_atemp[i] = AI_civicValue_original(kCivic.getParentCivicsChildren(i));
            }
            int eTotalValueOfChildrenBestCivics = 0; //total dependant children 1, per civic option for a parent civic.
       
            ///this should be saved in the playerai cache so it will know
            //to which dependant civics the revolution should take place
            //once a parent was deemed worthi for revolution.
            //placed here for now for codde example
            //ArrayEnumMap<CivicOptionTypes,CivicTypes> m_aChildCivicsToAutoSwitch; // advc.130w
 
            //for every civicoption - lets find the best child civic with the better value
            for (int i = 0; i < GC.getNumCivicOptionInfos(); i++)
            {
                int eTempBestCivicChildOption = 0;
                for (int J = 0; J < childrenCivics; J++)
                {
                    CivicTypes childCivicType = kCivic.getParentCivicsChildren(J);
                    if (GC.getInfo(childCivicType).getCivicOptionType() == (CivicOptionTypes)i)
                    {
                        //if another civic child belongs to the same civic option
                        if (m_atemp[J] > eTempBestCivicChildOption)
                        {
                            // save the value -> if there is more childs to this civic option
                            eTempBestCivicChildOption =    m_atemp[J];
                            //m_aChildCivicsToAutoSwitch.set((CivicOptionTypes)i, childCivicType); // see above comment
                            m_aChildCivicsToAutoSwitch[(CivicOptionTypes)i] = childCivicType;
                        }
                    }
                }
                // the best civic's value that was found for a civic option
                eTotalValueOfChildrenBestCivics += eTempBestCivicChildOption;
            }
            SAFE_DELETE_ARRAY(m_atemp);
            return AI_civicValue_original(eParent) + eTotalValueOfChildrenBestCivics;
        }
    }
    return AI_civicValue_original(eCivic); //normal civic value calc.
}
/* doto Civics Dependency (Asaf) - end ) */


CivicMap CvPlayerAI::forceChildCivics --> very similar code to the civicvalue, with different return
Spoiler :

Code:
CivicMap CvPlayerAI::forceChildCivics(CivicMap ePreRevolutionMap) const;
{
    PROFILE_FUNC();
 
    FOR_EACH_ENUM(CivicOption)
    {
        CivicTypes eCivicePreRevolutionMap.get(eLoopCivicOption) //get the civic for the itered c option
        CivicTypes eParent = getCivicParent(eCivic); //check if the civic is a parent
   
        int eBestChild = 0;
   
        if (eParent != NO_CIVIC)
        {
            //since this is a parent civic
            //the code below, will find its children
            // best children for each civic option that the children belongs to
            // and it will update the ePreRevolutionMap with the best children.
            //this will ensure that if a parent civic was chosen, it will force select its
            // dependant children, and will switch from current none dependant children
            CvCivicInfo kCivic = GC.getCivicInfo(eParent);
            int childrenCivics = kCivic.getNumParentCivicsChildren();
           
            if (childrenCivics > 0) //gotta be above 0 cause of getCivicParent call above
            {
                //evaluate all childcivics and save their values
                //this should save some loops below.
                int* m_atemp = new int[childrenCivics];
                for (int i = 0; i < childrenCivics; i++)
                {
                    m_atemp[i] = AI_civicValue_original(kCivic.getParentCivicsChildren(i));
                }
           
                //for every civicoption - lets find the best child civic with the better value
                for (int i = 0; i < GC.getNumCivicOptionInfos(); i++)
                {
                    int eTempBestCivicChildOption = 0;
                    for (int J = 0; J < childrenCivics; J++)
                    {
                        CivicTypes childCivicType = kCivic.getParentCivicsChildren(J);
                        if (GC.getInfo(childCivicType).getCivicOptionType() == (CivicOptionTypes)i)
                        {
                            //if another civic child belongs to the same civic option
                            if (m_atemp[J] > eTempBestCivicChildOption)
                            {
                                // save the value -> if there is more childs to this civic option
                                eTempBestCivicChildOption =    m_atemp[J];
                                ePreRevolutionMap.set((CivicOptionTypes)i, childCivicType);
                            }
                        }
                    }
                }
            }
        }
    }
 
    return ePreRevolutionMap;
}


CvPlayer::getCivicParent
Spoiler :

Code:
/* Civics Dependency (Asaf) - start */
CivicTypes CvPlayer::getCivicParent(CivicTypes eCivic) const
{
    if (GC.getCivicInfo(eCivic).getNumParentCivicsChildren() > 0)
        return eCivic; // if the same civic is a parent
 
    for (int iI = 0; iI < GC.getNumCivicInfos(); ++iI)
    {
        int childrenCivics = GC.getCivicInfo((CivicTypes)iI).getNumParentCivicsChildren();
        if (childrenCivics > 0)
        {
            for (int J = 0; J < childrenCivics; J++)
            {
                if ((CivicTypes)J == eCivic)
                    return (CivicTypes)iI; //return the parent of the child civic
            }
        }
    }
    return NO_CIVIC; //the civic is not a parent nor a child
}
/* Civics Dependency (Asaf) - End */


CvPlayerAI::CvPlayerAI(
Spoiler :

Code:
/* doto Civics Dependency (Asaf) - Start */
    m_aChildCivicsToAutoSwitch = new int[GC.getNumCivicOptionInfos()];;
    for (int i = 0; i < GC.getNumCivicOptionInfos(); i++)
    {
        m_aChildCivicsToAutoSwitch[i] = -1;
    }
/* doto Civics Dependency (Asaf) - end */


CvPlayerAI::AI_uninit()
Spoiler :

Code:
/* doto Civics Dependency (Asaf) - Start */   
    SAFE_DELETE_ARRAY(m_aChildCivicsToAutoSwitch);
/* doto Civics Dependency (Asaf) - end */


CvPlayerAI::AI_reset
Spoiler :

Code:
/* doto Civics Dependency (Asaf) - Start */
    //FAssert(m_aChildCivicsToAutoSwitch == NULL);
    m_aChildCivicsToAutoSwitch = new int[GC.getNumCivicOptionInfos()];
    for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
    {
        m_aChildCivicsToAutoSwitch[iI] = -1;
    }
/* doto Civics Dependency (Asaf) - end */


CvPlayerAI::AI_doCivics()
Spoiler :

Code:
    /* doto Civics Dependency (Asaf) - Start */
    // get the civics with forced children
    aeBestCivic_updated = forceChildCivics(aeBestCivic);
    /* doto Civics Dependency (Asaf) - end */




I would love to hear ways to improve this code and make it better.
my logic is probably not well design in terms of code.
so please help out with any ideas or code changes :)


this is the civic screen i did to display the civic 0 (goverment ) and its 2 civic options dependents.
Capture8.PNG
 
Some questions:

1. How many children can civics have? Is it always 2 or anything from 1 to "infinite" ?
2. Are grandchildren and great-grandchildren and so on possible? E.g. Despotism allows Bureaucracy and Nationhood, and Nationhood allows Slavery and Proletariat.
3. Is your civic screen required for this system or works with different screens too? E.g. the one in AND.
4. Is it compatible with the forbidden civics in AND2?
 
hey,
sure.

1. for now i limited it to 4 child tags, without a limitation, you could have 2-2, 3-1 or 4.
thats a work in progress,
2. not gonna do more than parent child , at least until i know the system is working well and the ai can use it well.
3. civic screen is for now just cosmetics, but i think for the human player ill implement some logic. ill try to avoid python logic and only do cosmetics.
4. for now, stuff are a bit of hard coded, as said, once the system works ill make it completely generic for other usages.
I haven't given thought to forbidden civics, but i really like that, so ill also implement it (for examples, some governments will be banned for some civs).

im open to suggestions and also civic screen layout changes of course.
thanks for the interest.
 
Top Bottom