[Python] NextWar's resource depletion code

jkp1187

Unindicted Co-Conspirator
Joined
Aug 29, 2004
Messages
2,496
Location
Pittsburgh, Pennsylvania
Hello again. Just wondering if someone could help me understand how this code works. Couldn't see anything relating to this in the SDK nor in the API.

The code starts off with this function and follows with what appears to be a list of resources with varying numbers (abbreviated for space) from CvEventManager.py:

Code:
def doCheckDepletion(self):

        self.doCheckWorkedResource("BONUS_ALUMINUM", "IMPROVEMENT_MINE", 880)
        self.doCheckWorkedResource("BONUS_COAL", "IMPROVEMENT_MINE", 880)
        self.doCheckWorkedResource("BONUS_COPPER", "IMPROVEMENT_MINE", 880)
        self.doCheckWorkedResource("BONUS_CORN", "IMPROVEMENT_FARM", 1120)
        self.doCheckWorkedResource("BONUS_MARBLE", "IMPROVEMENT_QUARRY", 880)
        self.doCheckWorkedResource("BONUS_OIL", "IMPROVEMENT_WELL", 880)
        self.doCheckWorkedResource("BONUS_OIL", "IMPROVEMENT_OFFSHORE_PLATFORM", 880)
        self.doCheckWorkedResource("BONUS_PIG",         self.doCheckWorkedResource("BONUS_URANIUM", "IMPROVEMENT_MINE", 880)
        self.doCheckWorkedResource("BONUS_WHALE", "IMPROVEMENT_WHALING_BOATS", 560)
        self.doCheckWorkedResource("BONUS_WHEAT", "IMPROVEMENT_FARM", 1120)
        self.doCheckWorkedResource("BONUS_WINE", "IMPROVEMENT_WINERY", 1120)

Then it follows up with what appears to be the code controlling when the resources deplete:

Code:
    def doCheckWorkedResource(self, bonus, improvement, randomInt):
            
        iBonus = CvUtil.findInfoTypeNum(gc.getBonusInfo, gc.getNumBonusInfos(), bonus)
        
        if (iBonus == -1):
            return
        
        lValidPlots = self.getPlotListbyBonus(iBonus)
        
        if len(lValidPlots) == 0:
            return

        for plot in lValidPlots:
            if plot.getImprovementType() == CvUtil.findInfoTypeNum(gc.getImprovementInfo, gc.getNumImprovementInfos(), improvement):
                if self.getRandomNumber(randomInt) == 0:
                    
                    pBonusInfo = gc.getBonusInfo(iBonus)
                    
                    plot.setBonusType(-1)
                    
                    szTitle = localText.getText("TEXT_KEY_NEXT_WAR_RESOURCE_DEPLETED_TITLE", ())
                    #szText = localText.getText("TEXT_KEY_NEXT_WAR_RESOURCE_DEPLETED", (pBonusInfo.getDescription(), plot.getX(), plot.getY()))
                                        
                    CyInterface().addMessage(plot.getOwner(), False, gc.getEVENT_MESSAGE_TIME(), szTitle, "AS2D_DISCOVERBONUS", InterfaceMessageTypes.MESSAGE_TYPE_MINOR_EVENT, pBonusInfo.getButton(), gc.getInfoTypeForString("COLOR_GREEN"), plot.getX(), plot.getY(), True, True)    
                    
                    

    def getPlotListbyBonus(self, iBonus):
        lPlots = []
        totalPlots = gc.getMap().numPlots()
        
        for i in range(totalPlots):
            plot = gc.getMap().plotByIndex(i)
            iOwner = plot.getOwner()
            if (iOwner != -1):
                if plot.getBonusType(gc.getPlayer(iOwner).getTeam()) == iBonus:
                    lPlots.append(plot)
        return lPlots

    def getRandomNumber(self, int):
        return CyGame().getSorenRandNum(int, "Next War")

My question is: how can I adjust the likelihood of a resource depleting? I figure that this code was written more or less with the scenario in mind, not so much the epic-style game....so I probably want to make the resources LESS likely to deplete. I further assume that the numbers next to the resource names weigh on this factor -- but does a higher number mean a higher chance of depletion?

Anything that can be offered to help me understand this would be appreciated!
 
Hello again. Just wondering if someone could help me understand how this code works. Couldn't see anything relating to this in the SDK nor in the API.

The code starts off with this function and follows with what appears to be a list of resources with varying numbers (abbreviated for space) from CvEventManager.py:

My question is: how can I adjust the likelihood of a resource depleting? I figure that this code was written more or less with the scenario in mind, not so much the epic-style game....so I probably want to make the resources LESS likely to deplete. I further assume that the numbers next to the resource names weigh on this factor -- but does a higher number mean a higher chance of depletion?

Anything that can be offered to help me understand this would be appreciated!

Increase the number to decrease the chance, it generates a random integer between 0 and that number, if the result is zero it depletes the resource. Basically, it's a one in this number chance of occuring. And, it will only deplete the listed resource if the listed improvement is present. So having a windmill on a iron resource won't deplete the iron.

Interestingly enough, this means the with the BtS forts giving access to resources they will never deplete with this code, is that a low enough chance for you? :)

Another thing you could do it add in a check for the games current era so that this code would be skipped entirely until you hit a specific era.
 
Increase the number to decrease the chance, it generates a random integer between 0 and that number, if the result is zero it depletes the resource. Basically, it's a one in this number chance of occuring. And, it will only deplete the listed resource if the listed improvement is present. So having a windmill on a iron resource won't deplete the iron.

Cool. Thanks for the help!


Interestingly enough, this means the with the BtS forts giving access to resources they will never deplete with this code, is that a low enough chance for you? :)

Wow. Just...wow. I figured the code here wasn't quite ready for prime-time..... but not including "IMPROVEMENT_FORT"???? Just...wow. (Of course, the worst part is that I missed it....)


Another thing you could do it add in a check for the games current era so that this code would be skipped entirely until you hit a specific era.

Eras are Civ-specific, right? So I'd have to run a check for the Civ that owns the resource first?
 
Waitaminit...if I'm reading this right, then building a city on a resource would ALSO protect that resource from depletion! :crazyeye: Wow. Double wow.

For purposes of this bit o' code, Is a city considered a terrain feature?
 
The problem cities will give you is that they are always worked and you can't avoid working them. So you should probably write your own method to handle cities differently than 'normal' resources. Cities, BTW, are cities... that is they aren't improvements or features.

You have different versions of getCurrentEra() available. One is per player (maybe team, I don't recall) and the second is for the game itself. The current era for the players & teams is pretty simple as it's based on their techs, the game's current era is calculated from all of the players. I believe it uses an average but I haven't look at it in a while, should be in CvGame.cpp. You see the effect of the game's current era with roads and improvements outside of cultural borders :)

In this case you can just use the current era for the player since this will only happen on owned tiles (somebody has to own it or it can't be worked).
 
The problem cities will give you is that they are always worked and you can't avoid working them. So you should probably write your own method to handle cities differently than 'normal' resources. Cities, BTW, are cities... that is they aren't improvements or features.

Are cities considered to be 'working' a bonus resource while the revealing technology is undiscovered. So if you build a city on uranium, would the game consider you to be working that resource?
 
Bonuses don't get worked, plots do. That's why the original code you posted not only checks to see if the plot is worked but also checks to see if they have the required improvement. Now I don't see a check in there for whether or not they can use the bonus so in the case of cities it's entirely possible that using that exact code would cause resources to disappear before you even knew they were there.
 
Two questions:

1.). How did you figure out how the numbers controlled the probability of the resource disappearing? (I want to be able to figure these things out for myself where possible....)

2.) I'm starting to wonder if maybe it would be better to put a limitation on the resource depletion from firing based on whether or not the civ has the enabling tech for that resource/luxury. If i did that, do you think that I'd be able to bypass the necessity for the city-specific code?

(I'm skittish about writing the city code bc I'm not sure where to start for that.)
 
Two questions:

1.). How did you figure out how the numbers controlled the probability of the resource disappearing? (I want to be able to figure these things out for myself where possible....)
I followed the code :)

The important part was this line:
yGame().getSorenRandNum(int, "Next War")

The 'int' argument here is the number at the end of the line for each resource/improvement combo. The way this works is a random number is generated between 0 and, in this case, 'int' later the code looks to see if the result == 0, since there's only one zero that means you have a 1 in 'int' chance of it being true.

2.) I'm starting to wonder if maybe it would be better to put a limitation on the resource depletion from firing based on whether or not the civ has the enabling tech for that resource/luxury. If i did that, do you think that I'd be able to bypass the necessity for the city-specific code?
It's not that complicated, you're already checking for the improvement all you need to do is add a condition if it's a city or the improvement AND you have the required tech. The city part is easy, it's something like pPlot.isCity() :)

The original code is already checking for visibility, that's handled automatically with getBonusType(gc.getPlayer(iOwner).getTeam()). There should be another quick and easy way to see if you have the tech to connect the resource but I don't know it off the top of my head.
 
For the city check, would I add another line for each resource under doCheckDepletion(self)?

So:

Code:
if pPlot.isCity():
    self.doCheckWorkedResource("BONUS_ALUMINUM", -1, 1760)
    self.doCheckWorkedResource("BONUS_BANANA", -1, 2240)
etc.

(I'm assuming "-1" is the value to insert for improvement, since there is no improvement on the plot where there's a city....)


It's not that complicated, you're already checking for the improvement all you need to do is add a condition if it's a city or the improvement AND you have the required tech. The city part is easy, it's something like pPlot.isCity() :)

The original code is already checking for visibility, that's handled automatically with getBonusType(gc.getPlayer(iOwner).getTeam()). There should be another quick and easy way to see if you have the tech to connect the resource but I don't know it off the top of my head.

That's good. As long as I don't have to worry about someone unknowingly building a fort or mine on a resource only to have it 'disappear' before they even know it's there, that's fine.
 
Actually, what you'd want to do is change the doCheckWorkedResource() function itself instead. Something like:
Code:
    def doCheckWorkedResource(self, bonus, improvement, randomInt):
            
        iBonus = CvUtil.findInfoTypeNum(gc.getBonusInfo, gc.getNumBonusInfos(), bonus)
        
        if (iBonus == -1):
            return
        
        lValidPlots = self.getPlotListbyBonus(iBonus)
        
        if len(lValidPlots) == 0:
            return

        for plot in lValidPlots:
[B]            bValid = False    
            if plot.getImprovementType() == CvUtil.findInfoTypeNum(gc.getImprovementInfo, gc.getNumImprovementInfos(), improvement):
                bValid = True
            elif plot.isCity():
                bValid = True

            if bValid = True:[/B]
                if self.getRandomNumber(randomInt) == 0:
                    
                    pBonusInfo = gc.getBonusInfo(iBonus)
                    
                    plot.setBonusType(-1)
                    
                    szTitle = localText.getText("TEXT_KEY_NEXT_WAR_RESOURCE_DEPLETED_TITLE", ())
                    #szText = localText.getText("TEXT_KEY_NEXT_WAR_RESOURCE_DEPLETED", (pBonusInfo.getDescription(), plot.getX(), plot.getY()))
                                        
                    CyInterface().addMessage(plot.getOwner(), False, gc.getEVENT_MESSAGE_TIME(), szTitle, "AS2D_DISCOVERBONUS", InterfaceMessageTypes.MESSAGE_TYPE_MINOR_EVENT, pBonusInfo.getButton(), gc.getInfoTypeForString("COLOR_GREEN"), plot.getX(), plot.getY(), True, True)
To handle the tech issue, you'd need to check for the proper technology also which i would do before calling doCheckWorkedResource()
 
The original code is already checking for visibility, that's handled automatically with getBonusType(gc.getPlayer(iOwner).getTeam()). There should be another quick and easy way to see if you have the tech to connect the resource but I don't know it off the top of my head.



I ain't no programmer or nothing, but I'm guessing the answer is somewhere in here....

Code:
    if (NO_BONUS != kEvent.getBonusRevealed())
    {
        if (GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getBonusInfo((BonusTypes)kEvent.getBonusRevealed()).getTechReveal()))
        {
            return false;
        }

        if (GET_TEAM(getTeam()).isForceRevealedBonus((BonusTypes)kEvent.getBonusRevealed()))
        {
            return false;
        }
    }
 
Yeah, that's the C++ code for it, I just have no clue what it would be in python. I'll look that up for you on Sunday if you haven't found it by then :)
 
Nichts ser gut. "getBonusRevealed()" is listed in the API, but I am getting this error:

Traceback (most recent call last):

File "CvEventInterface", line 23, in onEvent

File "CvEventManager", line 199, in handleEvent

File "CvEventManager", line 371, in onBeginGameTurn

File "CvEventManager", line 1120, in doCheckDepletion

File "CvEventManager", line 1193, in doCheckWorkedResource

AttributeError: 'int' object has no attribute 'getBonusRevealed'
ERR: Python function onEvent failed, module CvEventInterface


I may just have to brute-force it and individually list every starting tech with an IF statement in the code....

This is the code used:
Code:
## JKP1187/Seven05 new code

        def doCheckWorkedResource(self, bonus, improvement, randomInt):
            
            iBonus = CvUtil.findInfoTypeNum(gc.getBonusInfo, gc.getNumBonusInfos(), bonus)
        
            if (iBonus == -1):
                return
        
            lValidPlots = self.getPlotListbyBonus(iBonus)
        
            if len(lValidPlots) == 0:
                return

##Do you have the revealing tech for iBonus?  If not, return.
            for plot in lValidPlots:
                pPlayer = plot.getOwner()
                TechBonus = iBonus.getBonusRevealed()

                if not pPlayer.isHasTech(TechBonus):
                    return

##If you have the requisite improvement (incl. fort) or if the plot is being worked by a City, continue.
            
             
            for plot in lValidPlots:
                bValid == False
                if plot.getImprovementType() == CvUtil.findInfoTypeNum(gc.getImprovementInfo, gc.getNumImprovementInfos(), improvement):
                    bValid = True
                elif plot.isCity():
                    bValid = True

                if bValid == True:
                    if self.getRandomNumber(randomInt) == 0:
                    
                        pBonusInfo = gc.getBonusInfo(iBonus)
                    
                        plot.setBonusType(-1)
                    
                        szTitle = localText.getText("TEXT_KEY_NEXT_WAR_RESOURCE_DEPLETED_TITLE", ())
                        #szText = localText.getText("TEXT_KEY_NEXT_WAR_RESOURCE_DEPLETED", (pBonusInfo.getDescription(), plot.getX(), plot.getY()))
                        #Not sure what the above commented out code for -- left in from original NextWar.  JKP1187
                        
                        CyInterface().addMessage(plot.getOwner(), False, gc.getEVENT_MESSAGE_TIME(), szTitle, "AS2D_DISCOVERBONUS", InterfaceMessageTypes.MESSAGE_TYPE_MINOR_EVENT, pBonusInfo.getButton(), gc.getInfoTypeForString("COLOR_GREEN"), plot.getX(), plot.getY(), True, True)		

				

### Below are additional functions necessary to the activity of the resource depl'n code:					

	def getPlotListbyBonus(self, iBonus):
		lPlots = []
		totalPlots = gc.getMap().numPlots()
		
		for i in range(totalPlots):
			plot = gc.getMap().plotByIndex(i)
			iOwner = plot.getOwner()
			if (iOwner != -1):
				if plot.getBonusType(gc.getPlayer(iOwner).getTeam()) == iBonus:
					lPlots.append(plot)
		return lPlots

	def getRandomNumber(self, int):
		return CyGame().getSorenRandNum(int, "Next War")
 
That problme is cause by trying to treat an integer (real number) like a pointer, which doesn't work so well :)

You have:

iBonus = CvUtil.findInfoTypeNum(gc.getBonusInfo, gc.getNumBonusInfos(), bonus)

That sets the variable 'iBonus' to equal the infotype number for the bonus, much like using GC.getInforTypeFromString() returns a number. In order to use member functions you need a pointer to the object, not the number of the infotype :)

So this line:

TechBonus = iBonus.getBonusRevealed()

That needs to be changed so you're trying to use the getBonusRevealed() function on a bonus objects, not a bonus infotype number. To do this you need to grab the bonus info first...

pBonus = gc.getBonusInfo(iBonus)

And then get the reveal tech...

TechBonus = pBonus.getBonusRevealed()

Assuming getBonusRevealed returns a tech and works on bonuses since the code you pulled that from was for events. Let me take a look in the SDK and see what we have for that.

Ok, for bonuses you have getTechReveal() so try:

TechBonus = pBonus.getTechReveal()

So the whole code section would be something like:
Code:
## JKP1187/Seven05 new code

        def doCheckWorkedResource(self, bonus, improvement, randomInt):
            
            iBonus = CvUtil.findInfoTypeNum(gc.getBonusInfo, gc.getNumBonusInfos(), bonus)
        
            if (iBonus == -1):
                return
        
            lValidPlots = self.getPlotListbyBonus(iBonus)
        
            if len(lValidPlots) == 0:
                return

##Do you have the revealing tech for iBonus?  If not, return.
            for plot in lValidPlots:
                pPlayer = plot.getOwner()
                pBonus = gc.getBonusInfo(iBonus)
                TechBonus = pBonus.getTechReveal()

                if not pPlayer.isHasTech(TechBonus):
                    return
If that works all you have to do is go back and clean it up a little ;)
 
Cool. I will give this a try tonight. (Stupid work, always taking up my free time during the day.)

Question: what on earth is a "pointer"?
 
A pointer points to things :)

Every variable/object occupies memory and every block of memory has an address. When you create a variable you get a new block of memory for your data, when using multiple functions this can be a problem since they won't share data stored in variables unless that data is passed and an argument and then the return value is used to adjust the variable in the first function. A pointer, on the other hand, isn't a new block of memory, instead it is a pointer to the memory address of the data. This means you can access and modify data directly in memory from within multiple functions. Civ4 uses a lot of pointers so it has plenty of examples :)

So you can think of a pointer as a reference to the location of data rather than actual data. Although it's not technically a reference as that is yet another thing for you to learn about later, but still fundamentally very similar to a pointer. I believe python actually handles most variables more like references but I'm not a python guru, in fact my python experience can best be summed up as, "OMG this is too slow!" followed by changing the code in the SDK instead :D
 
No joy on the code. Error message:

Traceback (most recent call last):

File "CvEventInterface", line 23, in onEvent

File "CvEventManager", line 199, in handleEvent

File "CvEventManager", line 371, in onBeginGameTurn

File "CvEventManager", line 1120, in doCheckDepletion

File "CvEventManager", line 1205, in doCheckWorkedResource

AttributeError: 'int' object has no attribute 'isHasTech'
ERR: Python function onEvent failed, module CvEventInterface


Code:
##Do you have the revealing tech for iBonus?  If not, return.  getBonusRevealed not working!  JKP
            for plot in lValidPlots:
                pPlayer = plot.getOwner()
                pBonus = gc.getBonusInfo(iBonus)
                TechBonus = pBonus.getTechReveal()
                
                if not pPlayer.isHasTech(TechBonus):
                    return

What was interesting is that I was not getting an error message with this code:

Code:
# Random factor: 50% chance of depletion code check NOT happening, if player is running Environmentalism.
            for plot in lValidPlots:
                pOwner = plot.getOwner()
                pPlayer1 = gc.getPlayer(pOwner)
                depleteRandNum = (gc.getGame().getSorenRandNum(10, "D10 Roll"))
                if (pPlayer1.getCivics (3) == 19):#forces game to check: (3) = ECON CIVIC, 19 = ENVIRONMENTALISM
                    if (depleteRandNum < 5):
                            return

I'd originally tried:

Code:
for plot in lValidPlots:
   pPlayer = plot.getOwner()
   depleteRandNum = (gc.getGame().getSorenRandNum(10, "D10 Roll"))
   if (pPlayer1.getCivics (3) == 19):#forces game to check: (3) = ECON CIVIC, 19 = ENVIRONMENTALISM
                    if (depleteRandNum < 5):
                            return

...but when I tried the latter, I ended up with the identical error message that I got with the tech code. (When I changed the environmentalism code to the former, the thing worked as intended.)

I then tried, for the tech code:

Code:
pOwner1 = plot.getOwner()
pPlayer2 = gc.getPlayer(pOwner1)
pBonus = gc.getBonusInfo(iBonus)
TechBonus = pBonus.getTechReveal()

if not pPlayer2.isHasTech(TechBonus):
    return

Unfortunately, I got the same error message.

I'm sure there's more variations to try, but it's too late, have to go exercise. :P
 
Ok, random thoughts...

isHasTech() is this function for players or teams? I believe it's a method in the CvTeam class in the sdk, not the CvPlayer class. The various tech methods (isHasTech, canResearch, canEverResearch, etc) are all mixed up with some being in the player class and others in the team class. Try using a team instead of a player, so pOwner = pPlot.getTeam() I believe.

I don't know, I'm just taking shots in the dark here and you're new to programing so this is like the blind leading the blind :)
 
Back
Top Bottom