Help needed interpreting onCombatResult arguments

Xyth

History Rewritten
Joined
Jul 14, 2004
Messages
4,106
Location
Aotearoa
I've been reading lots of Python tutorials and getting more familiar with the Civ4 API but sometimes I come across something that I can't work out. This section is one such case:

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 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 do PyPlayer and PyInfo do and why are they used here? They're not listed in the Python API. What's the difference between PyPlayer(pWinner.getOwner()) and gc.getPlayer(pWinner.getOwner()) for example?

Why does pLoser.getUnitCombatType() not work here? After a painful amount of trial and error I got unitY.info.getUnitCombatType() to work instead but I'd really like to understand why.
 
The reason PyInfo and PyPlayer classes aren't included in the API is because they're not part of CvPythonExceptions, but rather part of the PyHelpers module. You're not obligated to use the variables defined within the method - the PyInfo and PyPlayer instances are just something that the programmers at Firaxis added to the Event Manager module, mostly for their own usage. Since everything in PyHelpers is merely wrappers of some of the stuff found in the main API, you can do without it.

If you're into learning, make a point of figuring out the PyHelpers module. There are some good lessons in there, and once you figure out how the module operates you might find yourself actually finding good use for it. But until you know how it works, I'd suggest you give it a pass. (Because, its only helpful once you know how to use it. And its written by programmers, for programmers.)
 
And to answer your actual questions...
What's the difference between PyPlayer(pWinner.getOwner()) and gc.getPlayer(pWinner.getOwner()) for example?
If we establish that pWinner.getOwner() gives you the PlayerType (expressed as an integer value) of the winning player, then CyGlobalContext.getPlayer() gives you the CyPlayer instance of that player, right? What is a PyPlayer instance for that same player, then? Well, its an instance of a class called PyPlayer, that has an attribute called player. That attribute is a CyPlayer instance. Makes sense? If not, then you don't need to bother with it.

Why does pLoser.getUnitCombatType() not work here? After a painful amount of trial and error I got unitY.info.getUnitCombatType() to work instead but I'd really like to understand why.
There is no reason why the suggested method invocation wouldn't work, since there is a CyUnit.getUnitCombatType() method in the API. :dunno:

There is, however, also a CvUnitInfo.getUnitCombatType() method, and that is what's happening in the other example. Because PyInfo.unitInfo() holds a attribute called info which holds a CvUnitInfo instance.

As I said, only once you understand how PyHelpers operates does it make any sense actually using these wrappers for the API methods. At the beginner stage you're better off doing things the proper way, not resorting to elaborate shortcuts.
 
PyPlayer and PyInfo are defined in PyHelpers.py. They are not part of the Python API.

The classes in there are wrappers that make some stuff easier. I rarely use them since they don't actually make most stuff easier (for example, it's just as easy to call CyPlayer.getName() as is it to call PyPlayer.getName(), which itself calls CyPlayer.getName(), and saves you the time and memory used to create the PyPlayer type object), but they do include things like functions that return lists of a players cities or units done properly (using the iterator instead of looping over ID values).

Using PyPlayer(x) creates a PyPlayer class object which has the methods defined for it in PyHelpers available to it. Using gc.getPlayer(x) creates a CyPlayer class object which has all the functions listed in the API available to it. The functions available via the PyPlayer is actually a superset of those for CvPlayer since it includes the means to call everything available to a CyPlayer class object (since it actually has a CyPlayer object as part of its data, set up when the PyPlayer object is initialized).

They are not really needed here. I'd even say that using them here is just a waste of CPU time and memory.

Using pLoser.getUnitCombatType() should work.

I'll also point out that the expression used in the second "if" statement is clearly wrong. It checks the "playerX" object twice and does not check the unitY object (it is checking to make sure that these things actually exist before using them). All in all, this particular piece of code is very sloppy and a bad example (or a good example of what you should not be doing). The extra stuff that the PyPlayer object has is never used, so it should just use a CyPlayer, and likewise for the PyInfo.
 
I'll also point out that the expression used in the second "if" statement is clearly wrong. It checks the "playerX" object twice and does not check the unitY object (it is checking to make sure that these things actually exist before using them). All in all, this particular piece of code is very sloppy and a bad example (or a good example of what you should not be doing). The extra stuff that the PyPlayer object has is never used, so it should just use a CyPlayer, and likewise for the PyInfo.
Yeah, I've noticed that error too... There is some strange stuff in there.
 
Thanks for the explanations guys. It makes some more sense now.

I guess my question now is that in the code I'm adding to this routine I want to find, among other things, the Combat strength of a unit. It seems that while getUnitClassType() and such are available in both CyUnit and CvUnitInfo, the one I need, getCombat(), only seems to be present in CvUnitInfo. How do I call the stuff from CvUnitInfo (or anything in CvInfoBase) if what is have is something like pWinner or pPlayer?

And slightly off topic, is there a way of finding (base) combat odds or experience/GG points earned from combat via Python?


EDIT: Just discovered CyUnit.baseCombatStr() and CyUnit.currentCombatStr(), not sure how I missed those before. I could still use some help understanding how to use CvInfoBase though.
 
what you want is probably
PHP:
myUnitInfo = gc.getUnitInfo(gc.getInfoTypeForString(attacker.getUnitType()))

So if you have an unit instance, you have to get its type first, then you can get via the type the assigned unitInfo.

Edit:
For CvInfoBase in general...hhmm...have you read anything about inheritance yet?
Is easier to understand if you know it, but isn't necessary.
Because it seems that all the other XYInfo just inherit some basic functions from CvInfoBase (which is probably just a virtual class, but i haven't looked into the SDK).
 
what you want is probably
PHP:
myUnitInfo = gc.getUnitInfo(gc.getInfoTypeForString(attacker.getUnitType()))
I'm pretty sure that you can/need to skip CyGlobalContext.getInfoTypeForString() from this expression, because that takes a string parameter while CyUnit.getUnitType() returns an integer.

But other than that; its actually one of the major Eureka moments of Python modding to figure out how to get access to the info classes. This is actually something that PyHelpers is trying to make more accessible, but I doubt anyone actually uses it. :p
 
I'm pretty sure that you can/need to skip CyGlobalContext.getInfoTypeForString() from this expression, because that takes a string parameter while CyUnit.getUnitType() returns an integer.

:blush: oops, right.
 
Back
Top Bottom