Making a list for Reference (Traits)

phungus420

Deity
Joined
Mar 1, 2003
Messages
6,296
I'm trying to clone some revolutions functions that exist for Civics into traits. Unfortunately jdog has made this require Python and SDK. The SDK stuff is easy, just setting up XML tags and exposing them to Python. This is done. Now I need to get the stuff to work functionally, which unfortunately requires python. Just cloning the functions is not effective as they don't relate well. Right here at the start I've ran into a problem, I just need to get the list for reference so that I can clone the functionality functions, and get the XML tags I've added to do stuff (modify Revolution penalties/bonuses).

Here is what I'm stuck on (and this is the beggining of the file... so I gots a ways to go).

Code:
# civicsList[0] is a list of all civics of option type 0
civicsList = list()


def initCivicsList( ) :

    CvUtil.pyPrint("  Rev - Initializing Civics List")

    global civicsList

    for i in range(0,gc.getNumCivicOptionInfos()) :
        civicsList.append(list())

    for i in range(0,gc.getNumCivicInfos()) :
        civicInfo = gc.getCivicInfo(i)
        civicsList[civicInfo.getCivicOptionType()].append(i)

There is nothing equivalent for traits to getNumCivicOptionsInfos() so this is where I am stuck. Anyone know how to get this list to work?
 
What exactly do you want this list to hold? A list of lists makes no sense for traits. You need to see further down how the list is used to figure out what you want it to hold for traits. if you just want it to hold a list of trait IDs you can use

Code:
traitsList

def initTraitsList():
    CvUtil.pyPrint("  Rev - Initializing Traits List")
    global traitsList

    traitsList = range(gc.getNumTraitInfos())

The reason I can think of to have the civics list is so that you can quickly and easily loop over all civic types for a given civic option type. But for traits there is no correlation.
 
Actually there sort of is.

What I need is a list of traits, then to loop over those traits and search their atributes. It's the atributes that are important, these are the XML tags I cloned from CivicInfos. This way I can make it so that traits, like Civics, carry with them Revolution penalties/bonuses (namely bonuses).

Once I get RevCivicUtils to be able to search the traits I will clone functions like this:

Code:
def getCivicsHolyCityEffects( iPlayer ) :

    pPlayer = gc.getPlayer(iPlayer)

    if( pPlayer.isNone() ) :
        return [0,0]

    if( pPlayer.getNumCities() == 0 ) :
        return [0,0]

    goodEffect = 0
    badEffect = 0

    for i in range(0,gc.getNumCivicOptionInfos()) :
        iCivic = pPlayer.getCivics(i)
        if( iCivic >= 0 ) :
            civicInfo = gc.getCivicInfo(iCivic)
            goodEffect += civicInfo.getRevIdxHolyCityGood()
            badEffect += civicInfo.getRevIdxHolyCityBad()

    return [goodEffect,badEffect]

Only make it apply to the Trait Revolution Tag I just added.
 
My point is that two Civics are related to each other (and grouped in this data structure) because they are in the same Option Type (e.g. Legal). There is no such grouping for Traits. Without seeing how civicsList is used later, I can't tell you how to create traitsList other than what I showed.

Yes, each Trait has attributes you inspect--the same with Civics I expect. But notice that civicsList doesn't match up Civics with the same attributes. The same will be true for Traits I expect.
 
OK. Here is some code in RevCivicUtils where it is used:

Code:
def getCivicsRevIdxLocal( iPlayer ) :

    pPlayer = gc.getPlayer(iPlayer)

    if( pPlayer.isNone() ) :
        return [0,list(),list()]

    if( pPlayer.getNumCities() == 0 ) :
        return [0,list(),list()]

    if( len(civicsList) < gc.getNumCivicOptionInfos() ) :
        initCivicsList()

    localRevIdx = 0
    posList = list()
    negList = list()

    for i in range(0,gc.getNumCivicOptionInfos()) :
        iCivic = pPlayer.getCivics(i)
        if( iCivic >= 0 ) :
            civicInfo = gc.getCivicInfo(iCivic)
            civicEffect = civicInfo.getRevIdxLocal()

            # Effect doubles for some when a much better alternative exists
            if( civicEffect > 0 and civicInfo.getRevLaborFreedom() < -1 ) :
                for j in civicsList[civicInfo.getCivicOptionType()] :
                    if( pPlayer.canDoCivics(j) ) :
                        jInfo = gc.getCivicInfo(j)
                        if( jInfo.getRevLaborFreedom() > 1 ) :
                            civicEffect = 2*civicEffect
                            #CvUtil.pyPrint("  Rev - Effect of %s doubled to %d because can do %s"%(civicInfo.getDescription(),civicEffect,jInfo.getDescription()))
                            break

            if( civicEffect > 0 and civicInfo.getRevDemocracyLevel() < -1 ) :
                for j in civicsList[civicInfo.getCivicOptionType()] :
                    if( pPlayer.canDoCivics(j) ) :
                        jInfo = gc.getCivicInfo(j)
                        if( jInfo.getRevDemocracyLevel() > 1 ) :
                            civicEffect = 2*civicEffect
                            #CvUtil.pyPrint("  Rev - Effect of %s doubled to %d because can do %s"%(civicInfo.getDescription(),civicEffect,jInfo.getDescription()))
                            break

            if( civicEffect > 0 ) :
                negList.append( (civicEffect, civicInfo.getDescription()) )
            elif( civicEffect < 0 ) :
                posList.append( (civicEffect, civicInfo.getDescription()) )

            #CvUtil.pyPrint("  Rev - %s local effect: %d"%(civicInfo.getDescription(),civicEffect))

            localRevIdx += civicEffect

    return [localRevIdx,posList,negList]

Basically what I am trying to do is clone some of the functions in RevCivicUtils that are designed to modify revolution effects based on civics into Traits. This will allow me to modify the distance penalties, state religion bonuses etc by setting values in TraitInfos. I've already got these tags in the XML and they load into CvTrait (or whatever it's called in CvInfos), and they are exposed to python through CyInfoInterface3. So I need something like this:
Loop over player Traits: Find XML value of of localRevIdx in TraitInfos, if not zero do this function (which will be a virtual copy of the function above just applied to traits).

The thing is I'm pretty sure I need to set up a similar list as in the OP for these functions to work, as it appears they are needed for the Civic Tags, so I assume it is similar if I set up similar stuff for Traits. But Traits aren't comparable, I can't figure out how to set up a similar list... There is no equivalent of gc.getNumCivicOptionInfos() for traits, so I can't just copy the def initCivicsList( ) function, and modify it to apply to traits. I'm sure it can be done, I just don't know how.
 
Nope, you don't need a list of traits at all. You could build one to speed up the checking, but it's not necessary and not used by the civics code so let's start easy.

Code:
def getTraitsRevIdxLocal( iPlayer ) :
	pPlayer = gc.getPlayer(iPlayer)

	if( pPlayer.isNone() ) :
		return [0,list(),list()]

	if( pPlayer.getNumCities() == 0 ) :
		return [0,list(),list()]

	localRevIdx = 0
	posList = list()
	negList = list()

	for i in range(gc.getNumTraitInfos()):
		if pPlayer.hasTrait(i):
			traitInfo = gc.getTraitInfo(i)
			traitEffect = traitInfo.getRevIdxLocal()
			if( traitEffect > 0 ) :
				negList.append( (traitEffect, civicInfo.getDescription()) )
			elif( traitEffect < 0 ) :
				posList.append( (traitEffect, civicInfo.getDescription()) )

			#CvUtil.pyPrint("  Rev - %s local effect: %d"%(traitInfo.getDescription(),traitEffect))

			localRevIdx += traitEffect

	return [localRevIdx,posList,negList]

Do traits have the two other fields from Civics: getRevLaborFreedom() and getRevDemocracyLevel()? The reason for the civics lists is so that it can check if you are running civic A but can switch to another civic B in the same option group with a better effect. You can't switch traits nor are they related in groups.
 
Oh, cool then. Thanks EF. And no, I didn't clone some of the tags, because I didn't think they would apply to Traits. Namely iRevIdxSwitchTo, fRevViolentMod, iReligiousFreedom, iLaborFreedom, iEnvironmentalProtection, iRevDemocracyLevel, and all the booleans were left out, because I don't see how they would apply (or can't apply like SwitchTo).
 
Here is what I have for RevCivicUtils.py:

Code:
########################## Traits effect helper functions #####################

def getTraitsRevIdxLocal( iPlayer ) :
	pPlayer = gc.getPlayer(iPlayer)

	if( pPlayer.isNone() ) :
		return [0,list(),list()]

	if( pPlayer.getNumCities() == 0 ) :
		return [0,list(),list()]

	localRevIdx = 0
	posList = list()
	negList = list()

	for i in range(gc.getNumTraitInfos()):
		if pPlayer.hasTrait(i):
			traitInfo = gc.getTraitInfo(i)
			traitEffect = traitInfo.getRevIdxLocal()
			if( traitEffect > 0 ) :
				negList.append( (traitEffect, civicInfo.getDescription()) )
			elif( traitEffect < 0 ) :
				posList.append( (traitEffect, civicInfo.getDescription()) )

			#CvUtil.pyPrint("  Rev - %s local effect: %d"%(traitInfo.getDescription(),traitEffect))

			localRevIdx += traitEffect

	return [localRevIdx,posList,negList]


def getTraitsRevIdxNational( iPlayer ) :
	pPlayer = gc.getPlayer(iPlayer)

	if( pPlayer.isNone() ) :
		return [0,list(),list()]

	if( pPlayer.getNumCities() == 0 ) :
		return [0,list(),list()]

	localRevIdx = 0
	posList = list()
	negList = list()

	for i in range(gc.getNumTraitInfos()):
		if pPlayer.hasTrait(i):
			traitInfo = gc.getTraitInfo(i)
			traitEffect = traitInfo.getRevIdxNational()
			if( traitEffect > 0 ) :
				negList.append( (traitEffect, civicInfo.getDescription()) )
			elif( traitEffect < 0 ) :
				posList.append( (traitEffect, civicInfo.getDescription()) )

			#CvUtil.pyPrint("  Rev - %s local effect: %d"%(traitInfo.getDescription(),traitEffect))

			localRevIdx += traitEffect

	return [localRevIdx,posList,negList]


def getTraitsHolyCityEffects( iPlayer ) :

    pPlayer = gc.getPlayer(iPlayer)

    if( pPlayer.isNone() ) :
        return [0,0]

    if( pPlayer.getNumCities() == 0 ) :
        return [0,0]

    goodEffect = 0
    badEffect = 0

    for i in range(gc.getNumTraitInfos()):
		if pPlayer.hasTrait(i):
			traitInfo = gc.getTraitInfo(i)
			
            goodEffect += traitInfo.getRevIdxHolyCityGood()
            badEffect += traitInfo.getRevIdxHolyCityBad()

    return [goodEffect,badEffect]


def getTraitsNationalityMod( iPlayer ) :

    pPlayer = gc.getPlayer(iPlayer)

    if( pPlayer.isNone() ) :
        return 0

    if( pPlayer.getNumCities() == 0 ) :
        return 0

    natMod = 0

    for i in range(gc.getNumTraitInfos()):
		if pPlayer.hasTrait(i):
			traitInfo = gc.getTraitInfo(i)
            natMod += traitInfo.getRevIdxNationalityMod()

    return natMod


def getTraitsReligionMods( iPlayer ) :

    pPlayer = gc.getPlayer(iPlayer)

    if( pPlayer.isNone() ) :
        return [0,0]

    if( pPlayer.getNumCities() == 0 ) :
        return [0,0]

    goodMod = 0
    badMod = 0

    for i in range(gc.getNumTraitInfos()):
		if pPlayer.hasTrait(i):
			traitInfo = gc.getTraitInfo(i)
            goodMod += traitInfo.getRevIdxGoodReligionMod()
            badMod += traitInfo.getRevIdxBadReligionMod()

    return [goodMod,badMod]


def getTraitsDistanceMod( iPlayer ) :

    pPlayer = gc.getPlayer(iPlayer)

    if( pPlayer.isNone() ) :
        return 0

    if( pPlayer.getNumCities() == 0 ) :
        return 0

    distMod = 0

    for i in range(gc.getNumTraitInfos()):
		if pPlayer.hasTrait(i):
			traitInfo = gc.getTraitInfo(i)
            distMod += traitInfo.getRevIdxDistanceMod()

    return distMod

I've piggybacked this on the end of RevCivicUtils. Now here is the thing. I'm thinking that the return functions are going to just overwrite the variables like distmod set up by civic functions. Obviously I don't want this I want to add them. Any advice on how to get this functioning correctly? And are there any obvious errors? BtW the variables like distMod are referenced in another python file, Revolution.py, which is why I think that this will overwrite the values assigned by the civic functions and so it wol't work properly. (also the indentation looks wierd when I cut and pasted it, but it's fine, it's just these forums...)
 
All the += lines are using spaces for indentation whereas the rest of the lines use tabs. Never mix spaces and tabs unless you want to go insane. Set up your editor to use tabs and you'll be a happy camper since (most) all the Civ4 files use tabs.

Those variables like natMod and distMod are local to each function. They return the value from the function, so you need to check where you call those functions and add them to the existing values as needed.

For example, find where getCivicsRevIdxLocal() is called currently. You'll need to combine the results from that function with those from getTraitsRevIdxLocal() somehow. The return value from those functions isn't a single value but rather a tuple (3 values in a list) of different types: an int and two lists. You probably want to add up the first value from each function call and probably concatenate (add) the lists of each:

Code:
civicsIdxLocal = getCivicsRevIdxLocal(iPlayer)
traitsIdxLocal = getTraitsRevIdxLocal(iPlayer)
idxLocal = (civicsIdxLocal[0] + traitsIdxLocal[0], civicsIdxLocal[1] + traitsIdxLocal[1], civicsIdxLocal[2] + traitsIdxLocal[2])
 
OK, that makes sense. I knew there had to be an easy way to do it. Thanks again.
 
Actually the local RevIndex was simpler then that, and I assume the distance mod will be as well.

However the Religion stuff is a list, and a couple more of these are lists as well, so I can't just take my variable and say += to whatever it was going to do with it. I'm not sure how to handle lists. Here is the code I'm looking at for Holy cities:

Code:
                    [holyCityGood,holyCityBad] = RevUtils.getCivicsHolyCityEffects(iPlayer)
                    if( not hasLiberalism and stateRel >= 0 ) :
                        if( pCity.isHasReligion(stateRel) ) :
                            stateHolyCity = game.getHolyCity( stateRel )
                            if( not stateHolyCity.isNone() ) :
                                holyCityOwnerID = stateHolyCity.getOwner()
                                if( (not holyCityGood == 0) and (holyCityOwnerID == iPlayer) ) :
                                    posList.append( (-holyCityGood, localText.getText("TXT_KEY_REV_WATCH_HOLY_CITY",())) )
                                    relGoodIdx += holyCityGood
                                elif( (not holyCityBad == 0) and (not gc.getPlayer(holyCityOwnerID).getStateReligion() == stateRel) ) :
                                    negList.append( (holyCityBad, localText.getText("TXT_KEY_REV_WATCH_HEATHENS",())) )
                                    localRevIdx += holyCityBad

                    [relGoodMod,relBadMod] = RevUtils.getCivicsReligionMods(iPlayer)

                    relBadIdx = int(math.floor(relBadIdx*(1.0 + relBadMod) + .5))
                    relGoodIdx = int(math.floor(relGoodIdx*(1.0 + relGoodMod) + .5))

How would I add the trait effect for holy cities in there to relBadIdx and relGoodIdx? I'm sure once I see this for holy cities I can figure the rest of the lists out. Should I just clone this whole thing? That seems wrong, I want to add the effect of the trait, I don't want to double up the penalty or bonus for a non state/state holy city.
 
It looks like the lists hold descriptions of the bonuses, so you'd add the traits' bonuses to the same lists (good or bad list depending on bonus).
 
I don't know the proper syntax for it, because I have no knowledge of lists. How could I do something like this:

Code:
					[holyCityGood,holyCityBad] = RevUtils.getCivicsHolyCityEffects(iPlayer)
					if( not hasLiberalism and stateRel >= 0 ) :
						if( pCity.isHasReligion(stateRel) ) :
							stateHolyCity = game.getHolyCity( stateRel )
							if( not stateHolyCity.isNone() ) :
								holyCityOwnerID = stateHolyCity.getOwner()
								if( (not holyCityGood == 0) and (holyCityOwnerID == iPlayer) ) :
									[I][B]#[Add the value of holyCityGood from traits to the value from civics,Add the value of holyCityBad from traits to the value from civics] = RevUtils.getCivicsHolyCityEffects(iPlayer)[/B][/I]
									posList.append( (-holyCityGood, localText.getText("TXT_KEY_REV_WATCH_HOLY_CITY",())) )
									relGoodIdx += holyCityGood
								elif( (not holyCityBad == 0) and (not gc.getPlayer(holyCityOwnerID).getStateReligion() == stateRel) ) :
									negList.append( (holyCityBad, localText.getText("TXT_KEY_REV_WATCH_HEATHENS",())) )
									localRevIdx += holyCityBad

					[relGoodMod,relBadMod] = RevUtils.getCivicsReligionMods(iPlayer)
 
Assuming getCivicsHolyCityEffects() and getTraitsHolyCityEffects() return two lists you'd append them together using this:

Code:
traitHolyCityGood, traitHolyCityBad = getTraitsHolyCityEffects(iPlayer)
holyCityGood += traitHolyCityGood
holyCityBad += traitHolyCityBad
 
OK, cool. So I can just manipulate variables in a list, just like any old variable then. I was thinking that since they were in a list they had to be handled differently.
 
The += in the two lines above concatenate two lists together; they don't add the individual values inside the list.

Code:
> x = [1, 2]
> y = [3, 4]
> x + y
[1, 2, 3, 4]
> x += y
> x
[1, 2, 3, 4]
> y
[3, 4]

I highly recommend skimming the Python tutorial, especially the section on lists.

Install Python (Civ4 uses version 2.4, but you can install the latest) and play around with IDLE. It has an interactive shell so you can enter Python statements as above and see the results immediately.
 
I missed something. Where did hard-coding enter into the mix? The modifier values are stored in the XML for Traits so the player/modder is free to change them easily enough.
 
Yeah, I find it better in the long run to softcode if you have the option. It's usually not much more work, and allows for easier tweaking in the future. This is why I prefer to set things up in the SDK with XML tags to control the values, instead of hardcoding functions in python. For the record I was doing similar stuff with Python, but I didn't like having the traits and their effects hardcoded.
 
Back
Top Bottom