Creating a new unit in Python? How?

mrkingkong said:
Hey there.

Im attempting to get my second successful piece of code working, and have a question to which i hope there is a very simple and easy answer:

How do you create a new unit on a particular plot using Python?

Thanks for any help,
mrkingkong

Code to create a new Warrior on pPlot belonging to pPlayer:

Code:
	newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_WARRIOR'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
 
Thanks Kael!

However (and oh yes, you really should have been expecting this) my code project is quite an ambitious one, to say it is my second effort at Python and my first was a "You have started a new turn" message.

Therefore i would love it if you (or anyone else) could point out where the following code is going wrong, and help a noob on his way to Python brilliance (yeah right!):

Code:
	def onCombatResult(self, argsList):
		'Combat Result'
		pWinner,pLoser = argsList
		playerX = PyPlayer(pWinner.getOwner())
		unitX = PyInfo.UnitInfo(pWinner.getUnitType())
		playerY = PyPlayer(pLoser.getOwner())
		unitY = PyInfo.UnitInfo(pLoser.getUnitType())
		if (not self.__LOG_COMBAT):
			return
		if pLoser.getUnitType() == gc.getInfoTypeForString('UNIT_KNIGHT'):
                        CyInterface().addImmediateMessage("Your knight has survived the fall, and gets to his feet", "")
                        deadUnitX = pLoser.getX()
                        deadUnitY = pLoser.getY()
                        newUnit = PlayerY.initUnit(gc.getInfoTypeForString('UNIT_SWORDSMAN'), deadUnitX, deadUnitY, UnitAITypes.NO_UNITAI)
		if playerX and playerX and unitX and playerY:
			CvUtil.pyPrint('Player %d Civilization %s Unit %s has defeated Player %d Civilization %s Unit %s' 
				%(playerX.getID(), playerX.getCivilizationName(), unitX.getDescription(), 
				playerY.getID(), playerY.getCivilizationName(), unitY.getDescription()))

What im attempting to do:
Create a Swordsman whenever a Knight is killed in battle, as though the rider survived his horse's death. It should also display a message telling the user something corny, IE. "The Knight gets up from his fall". I do eventually plan to have this as a chance event (eg 25% of the time he will survive), but for now wanted to get it working so it would definately happen, to test it.

What actually happens:
Well, as far as i can tell, not a lot. The Knight just dies, the victory message is displayed and Civ life goes on, without any swordsmen rising valiantly or anything! :p

Help is much appreciated. Im learning (slowly), i promise! :crazyeye:
mrkingkong
 
mrkingkong said:
Thanks Kael!

However (and oh yes, you really should have been expecting this) my code project is quite an ambitious one, to say it is my second effort at Python and my first was a "You have started a new turn" message.

Therefore i would love it if you (or anyone else) could point out where the following code is going wrong, and help a noob on his way to Python brilliance (yeah right!):

Code:
	def onCombatResult(self, argsList):
		'Combat Result'
		pWinner,pLoser = argsList
		playerX = PyPlayer(pWinner.getOwner())
		unitX = PyInfo.UnitInfo(pWinner.getUnitType())
		playerY = PyPlayer(pLoser.getOwner())
		unitY = PyInfo.UnitInfo(pLoser.getUnitType())
		if (not self.__LOG_COMBAT):
			return
		if pLoser.getUnitType() == gc.getInfoTypeForString('UNIT_KNIGHT'):
                        CyInterface().addImmediateMessage("Your knight has survived the fall, and gets to his feet", "")
                        deadUnitX = pLoser.getX()
                        deadUnitY = pLoser.getY()
                        newUnit = PlayerY.initUnit(gc.getInfoTypeForString('UNIT_SWORDSMAN'), deadUnitX, deadUnitY, UnitAITypes.NO_UNITAI)
		if playerX and playerX and unitX and playerY:
			CvUtil.pyPrint('Player %d Civilization %s Unit %s has defeated Player %d Civilization %s Unit %s' 
				%(playerX.getID(), playerX.getCivilizationName(), unitX.getDescription(), 
				playerY.getID(), playerY.getCivilizationName(), unitY.getDescription()))

What im attempting to do:
Create a Swordsman whenever a Knight is killed in battle, as though the rider survived his horse's death. It should also display a message telling the user something corny, IE. "The Knight gets up from his fall". I do eventually plan to have this as a chance event (eg 25% of the time he will survive), but for now wanted to get it working so it would definately happen, to test it.

What actually happens:
Well, as far as i can tell, not a lot. The Knight just dies, the victory message is displayed and Civ life goes on, without any swordsmen rising valiantly or anything! :p

Help is much appreciated. Im learning (slowly), i promise! :crazyeye:
mrkingkong


Did you see anything in the python logs? To turn them on and use them, check out TGA's tutorial, specifically the debugging section, here:

http://forums.civfanatics.com/showthread.php?t=154130
 
Ive turned them on (using TGA's tutorial) but i dont really understand which log file i should be looking in and what i should be looking for in that file.

Do you think the code might be in the wrong place? Or is it just a problem with a line of code, or something that iv missed out that should be included? Because the added section of code (the second IF statement) doesnt appear to have any effect whatsoever, which would suggest to me that the code is being bypassed by the computer???
 
mrkingkong said:
Ive turned them on (using TGA's tutorial) but i dont really understand which log file i should be looking in and what i should be looking for in that file.

Do you think the code might be in the wrong place? Or is it just a problem with a line of code, or something that iv missed out that should be included? Because the added section of code (the second IF statement) doesnt appear to have any effect whatsoever, which would suggest to me that the code is being bypassed by the computer???

TheGreatApple said:
This should enable in-game python popups, and cause all errors and messages to be printed in your My Documents/My Games/Civilization 4/Logs directory. The three important logs to look for when debugging are PythonDbg, PythonErr and PythonErr2.

For all I know, they really act as two different files, the debug and error files. Unfortunately, the error files also show some debug statements, which is absurd. I typically look at the PythonErr2 log, since it's normally the smallest (gets cleared every time the python modules are reset in Civ4), however, sometimes the message doesn't get printed there, and I have to check PythonDbg.

What you're looking for is out-of-the-ordinary stuff. Especially things that show errors and line number, like:

Traceback (most recent call last):

File "CvEventInterface", line 27, in onEvent

File "CvCustomEventManager", line 79, in handleEvent

File "CvCustomEventManager", line 90, in _handleDefaultEvent

File "CvMercEventManager", line 173, in onBeginPlayerTurn

File "MercenaryUtils", line 1400, in placeMercenaries

File "MercenaryUtils", line 2558, in place

RuntimeError: unidentifiable C++ exception
ERR: Python function onEvent failed, module CvEventInterface

This is an error I was working with recently. A traceback is all the functions that the code is running. So, there was code running in CvEventInterface in the function onEvent. On line 27, the code called the function "handleEvent" in the module CvCustomEventManager. On line 79 of that function, it called, and so on...

What you want to look at is the last line. In this case, it's the "MercenaryUtils" module, line 2558, in the function called "place".

Finally, the second-to-last line shows the actual error message. In this case, c++ exception, which means there was some error in the function called by python (in this case, a bad object was being passed into the function being called at line 2558).

TGA's tutorial lists a couple of common error messages and what they mean. If you're still having trouble, post them here.

If you're not getting any error messages, you might want to try lacing your code with some debug statements.

What this basically means is that you put statements that print out to the log files some statement that you can recognize so that you can see exaclty what the code's doing. So, for example:

Code:
def onCombatResult(self, argsList):
		'Combat Result'
		pWinner,pLoser = argsList
		playerX = PyPlayer(pWinner.getOwner())
		unitX = PyInfo.UnitInfo(pWinner.getUnitType())
		playerY = PyPlayer(pLoser.getOwner())
		unitY = PyInfo.UnitInfo(pLoser.getUnitType())
		if (not self.__LOG_COMBAT):
			return
		[b]CvUtil.pyPrint("Checking if Knight")[/b]
		if pLoser.getUnitType() == gc.getInfoTypeForString('UNIT_KNIGHT'):
			[b]CvUtil.pyPrint("It is a Knight")[/b]
                        CyInterface().addImmediateMessage("Your knight has survived the fall, and gets to his feet", "")
                        deadUnitX = pLoser.getX()
			[b]CvUtil.pyPrint("deadUnitX: %d" % deadUnitX")[/b]
                        deadUnitY = pLoser.getY()
			[b]CvUtil.pyPrint("deadUnitY: %d" % deadUnitY")[/b]
                        newUnit = PlayerY.initUnit(gc.getInfoTypeForString('UNIT_SWORDSMAN'), deadUnitX, deadUnitY, UnitAITypes.NO_UNITAI)
		if playerX and playerX and unitX and playerY:
			CvUtil.pyPrint('Player %d Civilization %s Unit %s has defeated Player %d Civilization %s Unit %s' 
				%(playerX.getID(), playerX.getCivilizationName(), unitX.getDescription(), 
				playerY.getID(), playerY.getCivilizationName(), unitY.getDescription()))

Oh, and I just noticed something while doing this:

Code:
if (not self.__LOG_COMBAT):
			return

Because you probably never changed the __LOG_COMBAT to log all of this functions entries, this if statement is true (since the self.__LOG_COMBAT variable is false by default), and thus the statement is going to return before your code is called. You want this statement after your code, before the long CvUtil.pyPrint, because this statement is kind of coupled with the debug statement below it. This is true with all the event managers functions. So, it should be:

Code:
def onCombatResult(self, argsList):
		'Combat Result'
		pWinner,pLoser = argsList
		playerX = PyPlayer(pWinner.getOwner())
		unitX = PyInfo.UnitInfo(pWinner.getUnitType())
		playerY = PyPlayer(pLoser.getOwner())
		unitY = PyInfo.UnitInfo(pLoser.getUnitType())
		
		if pLoser.getUnitType() == gc.getInfoTypeForString('UNIT_KNIGHT'):
                        CyInterface().addImmediateMessage("Your knight has survived the fall, and gets to his feet", "")
                        deadUnitX = pLoser.getX()
                        deadUnitY = pLoser.getY()
                        newUnit = PlayerY.initUnit(gc.getInfoTypeForString('UNIT_SWORDSMAN'), deadUnitX, deadUnitY, UnitAITypes.NO_UNITAI)
		
		[b]if (not self.__LOG_COMBAT):
			return[/b]
		if playerX and playerX and unitX and playerY:
			CvUtil.pyPrint('Player %d Civilization %s Unit %s has defeated Player %d Civilization %s Unit %s' 
				%(playerX.getID(), playerX.getCivilizationName(), unitX.getDescription(), 
				playerY.getID(), playerY.getCivilizationName(), unitY.getDescription()))
 
Thanks Gerikes! Awesome :goodjob:

A detailed mini tutorial on Python, and then fixing the problem with my code as well! Thanks a lot :D

And on the plus side, i wasnt too far away from having winning code first time. Great stuff.

Civ modding starts to look all the more sweet!

mrkingkong
 
Back
Top Bottom