Question about Civics

zulu9812

The Newbie Nightmare
Joined
Jan 29, 2002
Messages
6,388
Location
Athens of the North
I want to add a new range of civics - namely, military/warfare/haven'tdecidedonanameyet. Anyway. I want to add a Blitzkrieg civic that gives armour units (not decided if only new units, or all) the March promotion. Does anyone have any idea how to do this?

Secondly, I'm looking to add a Peacekeeping civic that lowers war weariness (easy enough) but also makes other civs friendlier to you. Any ideas on how to achieve this?
 
Upon further reflection, I would like the Blitzkrieg civic to give all armour units (i.e. including those built before the civic was enacted) the March promotion, but only for as long as the Blitzkrieg civic is in play (i.e. the promotion is taken away when the player switches to another civic).
 
The promotion thing should definitely be possible with python, and I think it would pretty simple too. I'm actually right in the middle of learning it though, so I'm not quite confident enough to tell you how to do it myself. If Kael or TheLopez or some other good python modder stops by this thread he should be able to show you.

Edit: Ok, I'm actually going to try and figure out how to do this by myself. I think it will be a good learning experience. I have the general idea of how to do it, I just need to look up what each call is actually named.
 
Aha! I think I've done it!

Code:
def onEndPlayerTurn(self, argsList):
		'Called at the end of a players turn'
		iGameTurn, iPlayer = argsList
                player = gc.getActivePlayer(iPlayer)
                unitList = PyPlayer(iPlayer).getUnitList()
                
		if (player.isCivic(gc.getInfoTypeForString("CIVIC_BLITZKREIG"))):
                    for pUnit in unitList:
                        if pUnit.getUnitCombatType() == gc.getInfoTypeForString("UNITCOMBAT_ARMOR"):
                            pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), true)
                else:
                    if player.isAnarchy() == False:
                        for pUnit in unitList:
                            if pUnit.isHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH")):
                                pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), false)
This is the first bit of python code I've written, so don't trust it completely. :) Hopefully someone knowledgeable will come around here and see if what I did looks good.
 
Looks like I made in error in the code, sorry. In its current state the bonus will only apply to human players, not the AI. Here's the (hopefully) correct version:

Code:
def onEndPlayerTurn(self, argsList):
		'Called at the end of a players turn'
		iGameTurn, iPlayer = argsList
                player = gc.getPlayer(iPlayer)
                unitList = PyPlayer(iPlayer).getUnitList()
                
		if (player.isCivic(gc.getInfoTypeForString("CIVIC_BLITZKREIG"))):
                    for pUnit in unitList:
                        if pUnit.getUnitCombatType() == gc.getInfoTypeForString("UNITCOMBAT_ARMOR"):
                            pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), true)
                else:
                    if player.isAnarchy() == False:
                        for pUnit in unitList:
                            if pUnit.isHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH")):
                                pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), false)
 
Gunner said:
Looks like I made in error in the code, sorry. In its current state the bonus will only apply to human players, not the AI.

I've not tested your updated code, but it didn't work. I suspect that this is because I had another section in my event manager beginning "def onEndPlayerTurn(self, argsList):"

Do you know how I would integrate the two?

EDIT: figured it'd be helpful to know what the other section said

Code:
	def onEndPlayerTurn(self, argsList):
		iGameTurn, iPlayer = argsList
		realSlavery.onEndPlayerTurn(argsList)
		self.parent.onEndPlayerTurn
 
Hmm, well once again, I don't really know what I'm doing too much. It would be very helpful if we got someone else here to look at everything.


One thing I just realized is that I spelled Blitzkrieg wrong in my original code. That might have caused some problems.

How did you try to merge the code before? I would try something like this:
Code:
def onEndPlayerTurn(self, argsList):
		'Called at the end of a players turn'
		iGameTurn, iPlayer = argsList
                player = gc.getPlayer(iPlayer)
                unitList = PyPlayer(iPlayer).getUnitList()

                realSlavery.onEndPlayerTurn(argsList)
                self.parent.onEndPlayerTurn
                
		if (player.isCivic(gc.getInfoTypeForString("CIVIC_BLITZKRIEG"))):
                    for pUnit in unitList:
                        if pUnit.getUnitCombatType() == gc.getInfoTypeForString("UNITCOMBAT_ARMOR"):
                            pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), true)
                else:
                    if player.isAnarchy() == False:
                        for pUnit in unitList:
                            if pUnit.isHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH")):
                                pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), false)
Good luck :)
 
Gunner said:
Code:
def onEndPlayerTurn(self, argsList):
		'Called at the end of a players turn'
		iGameTurn, iPlayer = argsList
                player = gc.getPlayer(iPlayer)
                unitList = PyPlayer(iPlayer).getUnitList()
                
		if (player.isCivic(gc.getInfoTypeForString("CIVIC_BLITZKRIEG"))):
                    for pUnit in unitList:
                        if pUnit.getUnitCombatType() == gc.getInfoTypeForString("UNITCOMBAT_ARMOR"):
                            pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), true)
                else:
                    [B]if player.isAnarchy() == False:[/B]
                        for pUnit in unitList:
                            if pUnit.isHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH")):
                                pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), false)
Why have you specified the Civilization not to be in Anarchy? Surely it could be in anarchy moving away from blitzkreig, and this wouldn't work until a civic was assigned. I'm not sure if isCivic(...) would pick up civics while in anarchy, so you may either lose, or still have the promotions in anarchy. I don't know.

This code also has the problem that it will remove the march promotion from any unit, even if the promotion wasn't given by the civic. I'm not sure if this is intented or not. Seems like it might be an issue.

It also has some optimisation problems. It wouldn't be too hard to only run it on the turn after the civic changes, avoiding all the unit cycling every turn. You probably wouldn't notice the difference - but it would start to get in the order of seconds added between turns on maps with large amounts of units.

To expand on the base, I'd use something similar to this:
Code:
# THIS BIT GOES ABOVE THE CLASS DEFINITION
iNumPlayers = gc.getGame.countCivPlayersEverAlive()
bBkChange = []
marchList = []

while iNumPlayers <= len(bBKChange):
	bBKChange.append(1)
	marchList.append([])
#----


# Whatever the class is
class [...]:

	# Other events
	[...]

	def onEndPlayerTurn(self, argsList):
		'Called at the end of a players turn'
		iGameTurn, iPlayer = argsList
		player = gc.getPlayer(iPlayer)
		unitList = PyPlayer(iPlayer).getUnitList()
                
		if (player.isCivic(gc.getInfoTypeForString("CIVIC_BLITZKRIEG"))):
			if bBKChange[iPlayer]:
				marchList[iPlayer] = []
		       		for pUnit in unitList:
		       			if pUnit.getUnitCombatType() == gc.getInfoTypeForString("UNITCOMBAT_ARMOR"):
		       				pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), true)
						marchList[iPlayer].append(pUnit)
				bBKChange[iPlayer] = 0
		else:
			if not bBKChange[iPlayer]:
               			for pUnit in marchList[iPlayer]:
               				pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_MARCH"), false)
				bBKChange[iPlayer] = 1
This fixes all the problems I mentioned above. This should track all the units which have been given the march promotion by this code using a list of lists, and also keep track of whether the promotion need be tested for or not (both when you have the civic, and when not). Not very big changes, but the little things make all the difference ;).

Note that the bit at the top goes above the class definition, and the rest goes in "def onEndPlayerTurn(...)"

I'm pretty sure it'll work, but if it doesn't, the code posted by Gunner should be fine.

The AI will not recognise the importance of this change.

As for merging, I'd suggest:
Code:
	def onEndPlayerTurn(self, argsList):
		# INSERT CODE HERE (removing duplicate iGameTurn, iPlayer = argsList)
		realSlavery.onEndPlayerTurn(argsList)
		self.parent.onEndPlayerTurn

Oh - and if you don't have it, I would suggest enabling python error popups. They give some indication of where/what the problem is, and make the whole thing alot easier to debug. (HidePythonExceptions = 0 in the main Civ4 ini file should do it)

BTW: Gunner - if you don't get any of the above I would be happy to explain.



About the other civic - making the AI's friendlier is best done in the SDK, otherwise you don't get the mouseover info. Kael has written a tutorial covering something similar.
 
Thanks for helping TGA. I need all the help I can get :D

The reason I put the anarchy thing in was that I was thinking that the promotion should be kept if the player was changing a category unrelated to the blitzkrieg one. So for example if the player was changing a civic in the Legal category, that would cause anarchy, but the anarchy wouldn't lose the promotions for the units. I don't know if the way I implemented it would have worked as intended. Not really a big deal though.

Other than that I think that everything you changed is much better. That was a pretty big oversight by me regarding how it removed all of the march promotions that had been granted :mischief:

I think I pretty much understand the code now, but it took me a good while to figure out. Here are the assumptions I had to make:
1) It is possible for a list (in this case the marchList) to hold other lists within itself.
2) The statement "if bBKChange[iPlayer]:" is telling the program to advance to the next level down if the result of bBKChange[iPlayer] equals 1.


What would happen if bBKChange[iPlayer] equaled some number other than one or zero? How would the "if" statement respond then? I guessed that the b in bBKChange[iPlayer] stands for boolean, but that isn't an official designation is it?

If you reversed this statement:
Code:
while iNumPlayers <= len(bBKChange):
	bBKChange.append(1)
	marchList.append([])
to
Code:
while iNumPlayers <= len(marchList):
        marchList.append([])
        bBKChange.append(1)
would it still work? I guess I'm wondering if an empty list inside another list is treated the same as a regular number or string.


Once again, I really do appreciate the help you've given me in learning this python.
 
Gunner said:
I think I pretty much understand the code now, but it took me a good while to figure out. Here are the assumptions I had to make:
1) It is possible for a list (in this case the marchList) to hold other lists within itself.
Yup. It can go as deep as you like, with each embedded list holding lists (which in turn can have embedded lists)
Gunner said:
2) The statement "if bBKChange[iPlayer]:" is telling the program to advance to the next level down if the result of bBKChange[iPlayer] equals 1.

What would happen if bBKChange[iPlayer] equaled some number other than one or zero? How would the "if" statement respond then? I guessed that the b in bBKChange[iPlayer] stands for boolean, but that isn't an official designation is it?
Strictly it's checking that the result of bBKChange[iPlayer] is non-zero it will continue. Any value other than zero will cause this to work. I'm using b to designate boolean much like is done in XML tags. It helps a little bit in keeping track of things.
Gunner said:
If you reversed this statement:
Code:
while iNumPlayers <= len(bBKChange):
	bBKChange.append(1)
	marchList.append([])
to
Code:
while iNumPlayers <= len(marchList):
        marchList.append([])
        bBKChange.append(1)
would it still work? I guess I'm wondering if an empty list inside another list is treated the same as a regular number or string.
I would be much more certain of it the other way around, but this should work too.

Lists are one of the best things about python IMO. It's very easy to stick information in them, and then rip it out. You can manipulate them alot more easily than you can similar structures in other languages.
 
The Great Apple said:
About the other civic - making the AI's friendlier is best done in the SDK, otherwise you don't get the mouseover info. Kael has written a tutorial covering something similar.

Thanks for your help. Following Kael's tutorial, I was able to sucessfully create the .dll and the diplomatic screen shows the bonus whilst the Peacekeeping civic is enacted. However, it is giving the bonus twice - +4 (as I wanted) but also an additional +1: i.e., it says "+4 You help with peacekeeping operations" AND "+1 You help with peacekeeping operations". Any way to limit the bonus to only a single instance of +4?

Additionally, my Blitzkrieg and Peacekeeping civics obviously don't show the bonuses on the Civics screen. However, I'd like them to. Any way to do this?

Lastly, the AI doesn't seem to be care much for the Peacekeeping civic. In my test, it had no prerequisite and so was available from the beginning of the game. I was the only one who chose that civic, although all of the other civs were perfectly ameanable to change to that civic when I asked them to (they didn't even ask for anything in return). Is there a way to nudge the AI towards seeing the value of the civic?
 
zulu9812 said:
Thanks for your help. Following Kael's tutorial, I was able to sucessfully create the .dll and the diplomatic screen shows the bonus whilst the Peacekeeping civic is enacted. However, it is giving the bonus twice - +4 (as I wanted) but also an additional +1: i.e., it says "+4 You help with peacekeeping operations" AND "+1 You help with peacekeeping operations". Any way to limit the bonus to only a single instance of +4?
No idea why you're getting the bonus twice. Would guess that you implemented it wrong. I'd ask Kael in the thread about it.
zulu9812 said:
Lastly, the AI doesn't seem to be care much for the Peacekeeping civic. In my test, it had no prerequisite and so was available from the beginning of the game.
You'll probalby have to get at the AI code fo civic selection in the SDK. It'll be in CvPlayerAI.ccp, but I don't have the file on me, so I don't know what function you would have to edit.
 
The Great Apple said:
This fixes all the problems I mentioned above. This should track all the units which have been given the march promotion by this code using a list of lists, and also keep track of whether the promotion need be tested for or not (both when you have the civic, and when not). Not very big changes, but the little things make all the difference ;).

Note that the bit at the top goes above the class definition, and the rest goes in "def onEndPlayerTurn(...)"

I'm pretty sure it'll work, but if it doesn't, the code posted by Gunner should be fine.

Mmm...I'm still not getting that March promotion for Armour units. What the code is doing it to remove wonder splashes. That is to say, I still get a message saying that I've built the wonder, but no splash and no wonder movie. Additionally, my ability to select units within a stack is all screwed up. All I can select is the visible unit: I can see the other units lined up at the bottom of the screen where they should be, but clicking on them doesn't select them, it merely highlights the second unit in addition to the visible unit (that can be selected) but the action buttons are still only for the visible unit.

Here is my event manager. I suspect that the second problem might be a conflict with 12Monkey's plot list enhancement.

http://www.civfanatics.net/uploads12/CvSevoEventManager.zip
 
I doubt it's a conflict - probably my code causing errors. Do you have python error enabled? Did you get a popup? Can you post the log?

EDIT: Spotted one capitilization error.
Code:
iNumPlayers = gc.getGame.countCivPlayersEverAlive()
bBkChange = []
marchList = []

while iNumPlayers <= len(bBKChange):
	bBKChange.append(1)
	marchList.append([])
should read
Code:
iNumPlayers = gc.getGame.countCivPlayersEverAlive()
bB[B]K[/B]Change = []
marchList = []

while iNumPlayers <= len(bBKChange):
	bBKChange.append(1)
	marchList.append([])
 
Another silly error.

Code:
iNumPlayers = gc.getGame.countCivPlayersEverAlive()

should be:
Code:
iNumPlayers = gc.getGame().countCivPlayersEverAlive()
 
I corrected those 2 errors, but I then had to take the whole section out because it was preventing Civ from launching (it would freeze at the "init python" stage
 
Right. Yes. I feel silly.
Code:
while iNumPlayers [B]<= [/B]len(bBKChange):
should read
Code:
while iNumPlayers [b]>[/b] len(bBKChange):

Also, it would appear that the function to count the amount of alive players isn't working (else it would never have gotten caught in the loop in the first place) so I would suggest using:

Code:
while len(bBKChange) < 19:
and scrapping the line where iNumPlayers is defined.

If that doesn't work - I'm getting home tomorrow and should be able to sort it out with access to a debugger. If you want something to use then Gunner's code should definately work. I really shouldn't do this sorta stuff without access to a debugger, as I'm very good at writing bad code...
 
Alrightey, I can now start up the game, but I'm still not getting that free March promotion for Armour units. Bugger. Incidentally, i was curious about this:

The Great Apple said:
Also, it would appear that the function to count the amount of alive players isn't working (else it would never have gotten caught in the loop in the first place)

Why is it important for python to know how many players are left in the game?
 
Back
Top Bottom