[PYTHONCOMP] Tentative True Disarmament Mod

JoshW

Chieftain
Joined
Aug 17, 2006
Messages
23
Location
Richmond, VA, US


I have just completed a tentative version of a Python-based mod which allows the UN to implement limited universal disarmament. Just as the vanilla UN can ban the production of nukes, this version can also ban production of:
-weapons that cause collateral damage (artillery, bombers, etc.)
-invisible units (spies, submarines)
-primarily-offensive weapons (tanks, gunships, etc.)
Another set of resolutions destroys all weapons on the map which are abolished by those resolutions. One resolution destroys all nuclear weapons.
When both types of resolutions are passed for a weapons class (i.e. invisible units), all the weapons are gone and none can be built -- true disarmament.

Notes:
-- Abolished weapons will not disappear from the map unitl a city screen has been checked (just an idiosyncracy of using the CvGameUtil's cannotTrain function -- suggestions for revision appreciated)
-- This mod is 100% Mac-compatible



Download at:

http://forums.civfanatics.com/downloads.php?do=file&id=2596


Thanks:
My deep thanks go to Gerikes for instrumental help and advice writing the Python code. Thanks Gerikes! :)
 
Woah this has merit....

How does the A.I. handle this type of thing, especialy something that just removes a good portion of the military away does the A.I. take into consideration what this could do?

Be interesting just for the nuke stuff....I'll prob use it for that :)
Since right now nukes in my game are seriously deadly stuff.
 
Of course I have to ask, does anyone know where Civ4VoteInfo.XML was moved to for warlords as that file does not exist in the warlords install, so I assume it was merged somewhere.
 
Perhaps it would be better if there was a possibility of not submitting to the resolution, which would entail detoriation of diplomatic relations with nations that did obey? Like, you are asked whether you obey. If yes, you could get a few turns to remove the abolished weapons. In a few turns time you would be punished if you still possess any.
 
Ishon said:
Perhaps it would be better if there was a possibility of not submitting to the resolution, which would entail detoriation of diplomatic relations with nations that did obey? Like, you are asked whether you obey. If yes, you could get a few turns to remove the abolished weapons. In a few turns time you would be punished if you still possess any.

UN-Council: So that settles it, we will universally disarm ourselves and live in peace.
King Vehem: No, I don't want to.
UN-Council: But you must! We'll give you 3 years to reconsider or there will be dire consequences!

--- 3 years pass as the UN resolution is put into effect. King Vehem stands alone and defiant ---

UN-Council: King Vehem, the deadline approaches. Will you see reason and submit to the resolution of this council?
King Vehem: I will not.
UN-Council: Then it is with deep regret that I must expel you from this council and inform you that a state of war shall exist between your nation and those of our member states until such time as you have been disarmed.

--- King Vehem goes on to conquer the newly demilitarized world - Domination Victory 1954 ---
 
And how will the UN members accomplish winning a war against anyone refusing to comply with such a resolution, if they themselves have complied with the resolution and disarmed themselves? :lol:
 
I agree partly -- while there are methods for accomplishing such disarmament in the real world (a few political scientists have devoted a great deal of time to that question), to do it in the game as it would be in the real world would be too complicated. I.e.:

1945: Disarmament resolution passes for phased disarmament
1946: Computer verifies compliance: 3 civs in noncompliance
Disarmament halts
1947: Sanctions applied to 3 civs
1948: 3 civs begin phased disarm
1949: Computer waits for 3 civs to reach disarm level of others
1950: Computer verifies compliance: All civs in compliance
Universal disarmament restarts
Etc.

However it might be interesting to have the computer hand over control of your civ to the AI for a few turns until the disarmament process is completed if you refuse to comply. (You have been "arrested" for violation of international law.)
 
Ket said:
Woah this has merit....
Thank you!

Ket said:
How does the A.I. handle this type of thing, especialy something that just removes a good portion of the military away does the A.I. take into consideration what this could do?

Truth be told, the AI has no inkling of how to handle these resolutions, except for the one destroying all nukes, which uses the bNoNukes tag. The mod is sort of added to the game via python, it doesn't touch the SDK code inside. The AI judges whether it wants to do a resolution based on the tags activated in the VoteInfos XML file. Unfortunately those same tags actually cause the resolution in question to do what the tag says (i.e., bNoNukes, bForceCivic) and we can't do that because there are already resolutions for those purposes. The new disarmament resolutions have NO xml tags acivated in them (except for the Destroy Nukes one, and the AI will in that case react properly.) For the others the AI sees the resolution XML and says "well this one doesn't accomplish anything, so I might as well vote an enthusiastic YES."
 
I would like to revise the Disarmament mod and I need some help since my knowledge of Python coding is rather limited.

First:

I'd like to have it so that the abolished weapons disappear at the beginning of the turn after the resolution passes. So far I've put the code in the definition of the cannotTrain function:

Spoiler :
Code:
def cannotTrain(self,argsList):

		pCity = argsList[0]

		eUnit = argsList[1]

		bContinue = argsList[2]

		bTestVisible = argsList[3]
		if not bTestVisible:
			for iVote in range(gc.getNumVoteInfos()):
				if gc.getGame().isVotePassed(iVote):
					if self.m_dResolutionUnitAbol.has_key(iVote):
						# Go through all of the players in the game.
						for iLoopPlayer in range(gc.getMAX_PLAYERS()):

							if (gc.getPlayer(iLoopPlayer).isAlive() and not gc.getPlayer(iLoopPlayer).isBarbarian()):									
								for szAbolishedUnit in self.m_dResolutionUnitAbol[iVote]:
											
									loopCount = 0

									killList = PyPlayer(iLoopPlayer).getUnitsOfType(gc.getInfoTypeForString(szAbolishedUnit))

									for pUnit in killList:
										
										loopCount += 1

										pUnit.kill(true, iLoopPlayer)
									break
Apparently this function is only called when a city screen is checked, so the units only disappear then. I tried putting it in the onGameTurnEnd and such funcitons in the EventManager file, but this seems to have no effect in-game - no errors, but the weapons do not disappear. So if anyone knows of another function I can put the code in, or some way to make my own separate file do the job, I would appreciate it.

Another thing I would like to do is have alerts announce what type of weapons have been eliminated. I tried inserting the following code into the CannotTrain function, but it didn't appear and it makes the disarm not work.
 
I believe a U.N. resolution like this would be ideal for disarmament of nuclear, chemical & biological weapons but I don't like including conventional weapons like bombers & tanks, ect. I also think that a civ should be able to defy the international community & keep it's W.M.D. stockpiles without having the game forcibly take them. If you refuse to hand over your W.M.D. weapons (delete units)..... economic sanctions follow & no civ is allowed to trade with you. :)
 
JoshW said:
I would like to revise the Disarmament mod and I need some help since my knowledge of Python coding is rather limited.

First:

I'd like to have it so that the abolished weapons disappear at the beginning of the turn after the resolution passes. So far I've put the code in the definition of the cannotTrain function:

snip

Apparently this function is only called when a city screen is checked, so the units only disappear then. I tried putting it in the onGameTurnEnd and such funcitons in the EventManager file, but this seems to have no effect in-game - no errors, but the weapons do not disappear. So if anyone knows of another function I can put the code in, or some way to make my own separate file do the job, I would appreciate it.

Another thing I would like to do is have alerts announce what type of weapons have been eliminated. I tried inserting the following code into the CannotTrain function, but it didn't appear and it makes the disarm not work.

Cannot train is called on every single unit (twice actuallym once with bVisible True, once with it False) every time the buttons in the city bar need to be redrawn (basically, ever time a city is selected). The results are used to determine whether to not to allow for the unit to be shown on the training screen, then if it's trainable, but all this only for that point in time. It's checked dynamically every single time, and relies nothing on previous history to determine it's outcome.

No matter what, you're going to have to place code into this function to disable the unit in order to keep this python-only. The problem is that the vote system is set up so that vote effects happen immediately. The best alternative would be to keep track of what turn the vote passed, and then inside the cannotTrainUnit function also have a check that you have gone on to the next turn before returning True to disable the unit.

There is no way to check if a vote that has passed or failed did so on the current turn (that I know of, I might be wrong though), so you'll probably have to change the onBeginGameTurn function to check all the votes that are passed and store them into some array, then use that arrays values rather than the "if gc.getGame().isVotePassed(iVote):".

So, you would in the __init__ method where you have your abolished units dictionary you would have an array definition:

Code:
# Creates a list of booleans, one for each resolution.
# Defaults each resolution to False
self.pResolutionPassed = [False] * gc.getNumVoteInfos()


onBeginPlayerTurn would have something like this:

Code:
# Updates all the True/False values of if a vote is passed.
for iVote in range(gc.getNumVoteInfos()):
	self.pResolutionPassed[iVote] = gc.getGame().isVotePassed(iVote)

Then edit the cannotTrain function:

Code:
def cannotTrain(self,argsList):
	pCity = argsList[0]
	eUnit = argsList[1]
	bContinue = argsList[2]
	bTestVisible = argsList[3]
	if not bTestVisible:
		for iVote in range(gc.getNumVoteInfos()):
[B]			if self.pResolutionPassed[iVote]:[/B]
				if self.m_dResolutionUnitAbol.has_key(iVote):
					# Go through all of the players in the game.
					for iLoopPlayer in range(gc.getMAX_PLAYERS()):
						if (gc.getPlayer(iLoopPlayer).isAlive() and not gc.getPlayer(iLoopPlayer).isBarbarian()):														for szAbolishedUnit in self.m_dResolutionUnitAbol[iVote]:
								loopCount = 0
								killList = PyPlayer(iLoopPlayer).getUnitsOfType(gc.getInfoTypeForString(szAbolishedUnit))
								for pUnit in killList:
									loopCount += 1
									pUnit.kill(true, iLoopPlayer)
									break

Edit: Also, you might want to consider putting all the unit killing into onBeginPlayerTurn. Something like:

Code:
for iVote in range(gc.getNumVoteInfos()):
	bOldVote = self.pResolutionPassed[iVote]
	self.pResolutionPassed[iVote] = gc.getGame().isVotePassed(iVote)

	# Determine if the vote just changed this turn.
	if bOldVote != self.pResolutionPassed[iVote]:
		# Determine if the vote is now in the state of "passed"
		if self.pResoultionPassed[iVote]:
			[I]# Put code to kill all units here.[/I]
 
Gerikes said:
self.pResolutionPassed = False * gc.getNumVoteInfos()

Small typo, you forgot the brackets to make it a list. ;)


Code:
self.pResolutionPassed = [False] * gc.getNumVoteInfos()
 
I'd like to preface this by saying that I won't ask for any more help after this issue gets cleared up! :D Promise. :)

First I'd like to say that I have added a thank-you section to the first post of this thread thanking Gerikes for the great help.

Speaking of thanks, I really appreciate the hard work in the last post, Gerikes, but I think there might have been a misunderstanding.

What I was really going for was some way for "abolished" (vs. production-banned) units to disappear (get killed) from the map when the resolution passes, rather than having to check the city screen.

In order to have the units killed on the passage of the resolution, I have indeed tried placing the unit-killing code inside the onBeginPlayerTurn and/or the other on_____Turn as well as the dictionaries in the init of the EventManager file, but as before, though there are no errors the units don't get killed.
 
I would also like to throw some props at Gerikes, his info on this thread and many others is very informative. :)
 
Jeckel said:
Small typo, you forgot the brackets to make it a list. ;)


Code:
self.pResolutionPassed = [False] * gc.getNumVoteInfos()

Ah, thank you much. I fixed it.

JoshW said:
What I was really going for was some way for "abolished" (vs. production-banned) units to disappear (get killed) from the map when the resolution passes, rather than having to check the city screen.

In order to have the units killed on the passage of the resolution, I have indeed tried placing the unit-killing code inside the onBeginPlayerTurn and/or the other on_____Turn as well as the dictionaries in the init of the EventManager file, but as before, though there are no errors the units don't get killed.

Hmm, that was what I was trying to do, move the unit-killing to the start of the next turn, so that you're not going through killing all the bannished units every time you open the city screen (which would be a waste of time, since if you do it once they shouldn't be able to be built until the resolution is revoked).

You can try throwing in some print statements and follow the code to see if it ever reaches the unit-killing code. If you're still having trouble post what you have.
 
I have used print statements at every step in the abolishment code section and have narrowed down the list of suspects to one: the self.m_dAbol dictionary which contains the resolutions and the units they abolish. The program seems to work fine except when that is called. I.E. the print statements work until the code hits that self.m_dAbol statement. Thus it may have something to do with the self.m_dAbol statement or the (iVote) invariable contained within those statements. The following is the code:

Spoiler :
Code:
def __init__(self):
	
		self.m_dAbol = {
			gc.getInfoTypeForString("VOTE_ABOL_NUKES") : ("UNIT_ICBM",),

			gc.getInfoTypeForString("VOTE_ABOL_SEIGE") : ("UNIT_BOMBER","UNIT_STEALTH_BOMBER","UNIT_ARTILLERY","UNIT_BATTLESHIP"),
			gc.getInfoTypeForString("VOTE_ABOL_SPIES") : ("UNIT_SPY","UNIT_SUBMARINE"),

			gc.getInfoTypeForString("VOTE_ABOL_OFFENSE") : ("UNIT_GERMAN_PANZER","UNIT_TANK","UNIT_GUNSHIP","UNIT_MODERN_ARMOR","UNIT_CAVALRY")
			}

...


def onBeginPlayerTurn(self, argsList):

		'Called at the beginning of a players turn'

		iGameTurn, iPlayer = argsList

		CvUtil.pyPrint('OnBeginPlayerTurn...')

		for iVote in range(gc.getNumVoteInfos()):
			
			CvUtil.pyPrint('Going through votes...')

			if gc.getGame().isVotePassed(iVote):
				
				CvUtil.pyPrint('A vote has passed...')
			
				if self.m_dAbol.has_key(iVote):
				
					CvUtil.pyPrint('An abolish vote has passed...')

			
					for iLoopPlayer in range(gc.getMAX_PLAYERS()):
				
						CvUtil.pyPrint('Going through players...')

						if (gc.getPlayer(iLoopPlayer).isAlive() and not gc.getPlayer(iLoopPlayer).isBarbarian()):

							CvUtil.pyPrint('Checking for players...')					
									
							for szAbolishedUnit in self.m_dAbol[iVote]:
						
								CvUtil.pyPrint('Going through abolished units in res...')					

								killList = PyPlayer(iLoopPlayer).getUnitsOfType(gc.getInfoTypeForString(szAbolishedUnit))

								CvUtil.pyPrint('Kill list created...')

								for pUnit in killList:

									CvUtil.pyPrint('Killing individual abolished units in the list...')
							
									pUnit.kill(true, iLoopPlayer)
 
Roamty said:
("UNIT_ICBM",),


A guess change to ("UNIT_ICBM"),

No, having the extra comma is right. If you just write ("UNIT_ICBM"), python will just think that the parenthesis surrounding the quotes are an expression, and thus will just put the string as the object inside, where you actually want a tuple (since later you will say "if something is in" that object, and it won't work correctly if the object is a string). With the comma, it knows that the items are to go into a tuple, even if it's just a tuple of one value.


@ JoshW:

I copied all that you put into my custom assets and it worked fine. All I had to do was change the "VOTE_ABOL_NUKES" to "VOTE_NO_NUKES" and when I voted for no nukes the nukes were all destroyed.
 
Top Bottom