Unique Victory Conditions

Well, here goes:

The players (and teams) in your mod are ordered in a specific order. Since your mod has spawning Civs there will also additional players (and teams?) popping up in mid-game. Each player is indexed (enumerated) with a integer identifier - the player's PlayerType value. So the first one is player 0, the second one player 1 and so on.

Now, these values are only numbers - not actual players. Each player is instead something called a CvPlayer object in the SDK. In Python terms these are known as CyPlayer objects - instances of the CyPlayer class. The PlayerType/ID/index/whatever is merely a reference to such an object.

So, you basically can do nothing useful with a integer value. Adding and subtracting to it wont affect any player in the game, at least. In order to do something with a player you need to get its CyPlayer instance - and invoke some method on it.

Lets propose that you wanna check whether or not the first player is alive. So we already know that the first player is identified by the integer value 0. Next we need to get the CyPlayer object corresponding to that value, and the way to do it (without CivPlayer) is to use the CyGlobalContext.getPlayer() method. Since a CyGlobalContext object is usually assigned to the constant gc, the code could look something like this:
Code:
gc.getPlayer(0)
And we invoke the CyPlayer.isAlive() method on it:
Code:
gc.getPlayer(0).isAlive()
This method invocation will return a True/False value (a boolean) depending on if the first player is alive or not. This in turn can be used in conditional statements or as an argument in some function.

The same applies to teams, in principle. But fetching both TeamType values and CyTeam objects from PlayerTypes/IDs is actually pretty messy. And this was the motif for creating CivPlayer in the first place.

The various different ways to fetch the same CyPlayer instance with CivPlayer would be (proposing that the first players is Rome):
Code:
Civ("Rome").get(CyPlayer)
pointer("Rome", CyPlayer)
instance(0).get(CyPlayer)
CivPlayer.CyPlayer("Rome")
CivPlayer(0).get(CyPlayer)
So you get some options - what you end up using is basically a matter of taste/convenience.

The downside of all this goodness is that you need to understand what a CivPlayer instance is. :p And thus I still need to document the module! :cry:
 
So "Cy" is used when referring to the Normal Civ Python (hence, Cy!) and "Py" is used when referring to the PyHelpers Module!
I think I get it...
Read my previous post (the long one), but your observation is also true.

It could be noted that Cy instances are mere references to the "real" Cv objects (in the SDK) because the game isn't written in Python, after all. What a Py instance is, is that its a wrapper for a Cy instance - sorta like CivPlayer wraps up all the various player/team values for easy access. (It even includes a PyPlayer instance! :eek:)
 
No, I definitely don't have the time to be "releasing" anything, so I'm just gonna attach both CivPlayer and its companion module DataStorage to this post, as pre-release. Put both files in your mod's Python folder. Add these lines in these places in CvEventManager.py:
Code:
	def onLoadGame(self, argsList):
[B]
		import CivPlayer
		CivPlayer.setup()[/B]

	def onGameStart(self, argsList):
		'Called at the start of the game'
[B]
		import CivPlayer
		CivPlayer.setup()[/B]
Doing this avoids the issue of the XML portion of your mod being initialized only after the CvEventManager.py files is initialized. So it makes the Event Manager import and setup CivPlayer each time the game is launched instead, which then makes it work as intended. Note that you need to do this also when new players are added to the game, because otherwise CivPlayer will be outdated.

Now, what exactly is a CivPlayer instance? Well, a CivPlayer instance is of course an instance of the CivPlayer class, which is almost like a data structure of sorts. The important thing to understand is that there is one CivPlayer instance initialized for each player, and that it holds the following values:
  1. PlayerType (aka player ID or enumerated player index)
  2. CyPlayer instance
  3. TeamType (aka team ID or enumerated team index)
  4. CyTeam instance
  5. PyPlayer instance
The actual API (interface) for CivPlayer simply offers up a variety of ways to fetch these values from the CivPlayer instance, and also various ways to fetch said CivPlayer instance.

If you know the PlayerType (here as the ePlayer variable) you can fetch the corresponding CivPlayer instance with:
Code:
instance(ePlayer)
If you wanna use the (short) name of the Civilization of that player instead:
Code:
Civ("Libya")
The most basic way to fetch one of the associated player values from a CivPlayer instance is to use the get() method. It takes a integer value as the argument, but I've assigned the integers involved to these names for convenience:
  • 0 playerID
  • 1 CyPlayer
  • 2 teamID
  • 3 CyTeam
  • 4 PyPlayer
So if you already have a CivPlayer instance (here assigned to the variable pCivPlayer) and need the CyTeam, then you can use the name CyTeam instead of the integer value 3:
Code:
pCivPlayer.get(CyTeam)
Although this is exactly the same thing:
Code:
pCivPlayer.get(3)
I just think that the name is easier to memorize.

So, in conclusion, you firstly need a CivPlayer instance and then you fetch the value with get(). But there is shortcut, namely the pointer() function:
Code:
pointer("Austria", teamID)

There are other things also, like support for custom stored values and some wrappers for useful methods. But I simply have to save that to the proper documentation. Its all documented in the code however, but without any examples of usage.

And finally, in order to utilize CivPlayer you need to put this line at the top of that module:
Code:
from CivPlayer import *
 

Attachments

Thank you!
I get it now, and this looks better than identifing every civ, team etc. :D
I might need help later :) (but I'll test this first...)
 
Make sure you enable Python exceptions in the .ini file. You might also consider enabling Python pop-ups - so that you become aware of any errors. The messages get posted in the logs in the \Logs\ folder.
 
Asoka won!
I think I wrote some code wrong...
EDIT:Wait, there is no code to make Rome win...
 

Attachments

  • Civ4ScreenShot0035.JPG
    Civ4ScreenShot0035.JPG
    68 KB · Views: 52
  • UniqueVictory.zip
    UniqueVictory.zip
    437 bytes · Views: 36
  • CvEventManager.zip
    CvEventManager.zip
    10 KB · Views: 33
I don't think you wanna define a new victory type for each Civ, but rather just make one dummy victory type for the unique victories. Just a suggestion.

Tell me when you're done fiddling with the Python and I can take a look at it.
 
I updated the CivPlayer module - new version attached in the post above. A previous change had disabled players script data, but this version should be operational. (Credit to j_mie6 for testing and running into the exception that prompted me to notice this.) This only affects the performance of the companion DataStorage module however, so if you're not even doing any custom values (and player specific data to be exact) you wouldn't even notice it.
 
Back
Top Bottom