The Game value is a constant so it should really be defined at the top of the module (right after the imports) - because you can't predict where you'd wanna be able to use it in the future. Having constants at the beginning means that they will be available for everything below.
But, the isDate() function could be defined like this also - and then you don't even need the Game constant:
All the constant really adds to the module is convenience of writing the code - you don't have to spell the class constructor out every time you need to access a method belonging to the class. But it does nothing. You would be able to change every instance of gc to CyGlobalContext() but now you probably realize what I'm getting at here...
Yeah, I could explain the CivPlayer module/class now if you're done with your event and can spare the attention... But as I said; you don't need it for your own scripting. But it will help you understand the other modules I've done for you, as I'm relying on it quite heavily...
Oh, I forgot to mention that you need to import the CivPlayer module in order to access the instance() function. So using the first sample function i posted would work just as well - but you wouldn't have to import anything.
which module needs to import CivPlayer?
Should I import my event into the main game now of wait until I have your code and work on new events in the mean time?
Ok, this is the problem: The CivIV Python API is divided into classes and in a scenario mod like yours the Player ID and the Team ID mostly match up - but they don't have to. In any case you will be needing CyPlayer and CyTeam instances to invoke methods on. And these methods are regularly requesting Player IDs and Team IDs as parameters.
What this leads to is a constant invocation of these methods:
As you can see, these functions are referencing each-other and they all take a PlayerType (player ID) as the argument. So its quite convenient.
I, on the other hand, decided to go somewhat overboard with this.
The CivPlayer module basically contains i class called CivPlayer. Each instance of this class holds the PlayerType, the CyPlayer instance , the TeamType and the CyTeam instance associated to that player. I also added a PyPlayer instance just for the hell of it, making it redundant to even import the PyHelpers module to access PyPlayer methods.
The main method for use with a CivPlayer instance is CivPlayer.get() and it basically returns one of these values. I set it up so that you can use the name of the value as the argument (even if integers work the same):
Another thing that the CivPlayer module does is it creates ready-to-use CivPlayer instances for all the players at initialization. These can be accessed with the functions Civ() and instance() - the first one takes the name of the player's Civilization (string) as the argument and the second one takes the player's ID number (the PlayerType). There is also a pointer() function for accessing player values directly:
Code:
pTeam = pointer("Rome", CyTeam)
Whether you wanna use the helper function I posted above - or import and use the CivPlayer module - is basically up to you.
Any module that is calling any of the functions defined in the module, of course. Right now I guess it would be the the CustomFunctions module which is using a instance() function call.
not really sure on post 526, my understanding is not quite there yet... If a practical implication of this code could be added etc it may help me to understand
File "CvEventInterface", line 23, in onEvent
file "CvEventManager", line 187, in handleEvent
file "CvEventManager", line 332 in onGameStart
file "CvEventManger", line 1118, in initiateCustomPython
file "string", line 52, in load_module
file "ModEvents" line 11, in ?
NameError: name 'eBarbarians' is not defined
EDIT: found the problem! I used eBarbarians instead of eBarbarian! Ah... my first bit of fault finding!
well... at least I found it!
EDIT2: ! the Utils you posted on here has eAxeman defined but mine doesn't so it caused an exception, I will define it for my own Utils so this should work now...
EDIT3: another exception ! But this time I think something is wrong with your code?
File "CvEventInterface", line 23, in onEvent
file "CvEventManager", line 187, in handleEvent
file "CvEventManager", line 376 in onBeginGameTurn
file "ModEvents", line 17, in eventSpartacus
file "CustomFunctions", line 22, in spawnUnits
file "CivPlayer" line 45, in instance
IndexError: list index out of range
????
I included my current code in this spoiler!
Spoiler:
Code:
from Utils import *
from CustomFunctions import *
##Spartacus rebellion##
iYear = -3800
spartacusHeader = "Spartacus"
spartacusMessage = "Spartacus has invaded southern Italy as revenge for his cruel mistreatment.\n\n A barbarian army will spawn in sourthern Italy"
#unit definitions: coordinates, player, number to be spawned, unit type (defined by Utils)#
tSpartacus = (27, 15)
eSpartacus = eBarbarian
iSpartacusUnitNumber = 3
eSpartacusUnit = eAxeman
def eventSpartacus():
if isDate(iYear):
showPopup(spartacusHeader, spartacusMessage)
spawnUnits(eSpartacus, eSpartacusUnit, tSpartacus, iSpartacusUnitNumber)
and
Code:
##custom functions for use in modding etc##
##use if required/wanted!##
from Popup import PyPopup
from CvPythonExtensions import *
from PyHelpers import *
from CivPlayer import *
def save():
print "Save"
def showPopup(popupHeader, popupMessage):
modPopup = PyPopup()
modPopup.setHeaderString(popupHeader)
modPopup.setBodyString(popupMessage)
modPopup.launch()
def isDate(iDate):
return CyGame().getTurnYear(CyGame().getGameTurn() +1) > iDate and CyGame().getGameTurnYear() <= iDate <= iDate
def spawnUnits(ePlayer, eUnitType, tCoords, iNum):
iX, iY = tCoords
instance(ePlayer).get(PyPlayer).initUnit(eUnitType, iX, iY, iNum)
not really sure on post 526, my understanding is not quite there yet... If a practical implication of this code could be added etc it may help me to understand
EDIT2: ! the Utils you posted on here has eAxeman defined but mine doesn't so it caused an exception, I will define it for my own Utils so this should work now...
EDIT3: another exception ! But this time I think something is wrong with your code?
File "CvEventInterface", line 23, in onEvent
file "CvEventManager", line 187, in handleEvent
file "CvEventManager", line 376 in onBeginGameTurn
file "ModEvents", line 17, in eventSpartacus
file "CustomFunctions", line 22, in spawnUnits
file "CivPlayer" line 45, in instance
Yeah, I now realize that you can't use the Barbarians as the ePlayer with the spawnUnits() function. This is because the function is using CivPlayer.instance() to fetch a CivPlayer instance for that player - but there is none. (Barbarians are not included in the pre-set players for reasons logical.) There is a couple of things we could do to rectify the situation, and I suggest we change the function definition to this to accommodate all players (including barbarians):
So instead of fetching a pre-existing CivPlayer instance we make a new on-the-fly each time we spawn units. This is done by calling the CivPlayer class constructor.
Note that when using CivPlayer you don't even need to import PyHelpers - at least not for accessing PyPlayer methods.
oh no.... I forgot.... In my mod I didn't remove units or change around unique units (from previous problems) and so, I forgot to tell you that some units are not exactly what you think they are... for example axemen in the xml are actuly spearmen in game (which is why my event just spawned spears instead of axes) here is the full (I think... I might have forgot some) list of unit swaps
axeman (in game)= macemen (in xml)
spearmen (in game)= axemen (in xml)
advanced spearman (in game)= spearman (in xml)
punic war elephant (in game)= khmer war elephant (in xml)
so make sure you change your definitions to the appropriate thingys (but leave names for example eAxeman = getIndex("Unit", "Maceman"))
Ok, lets take it from the top. Each player has a index number from zero and up, as you well know. In a scenario like yours these are actually defined in the WBS. So these are integers as far the CivIV Python is concerned and referred to in the API is PlayerTypes. Another commonly used name is Player ID and these enumerated players are usually assigned to the variable iPlayer or ePlayer (depending on who is doing the scripting - very confusing stuff).
So, the SDK is assigning each player a CyPlayer object - an instance of the CyPlayer class. These are really not created by any Python constructor but rather inherited from the C++ code in the SDK where they are known as CvPlayer objects.
You will frequently need to get the CyPlayer of a PlayerType/ID - and vice versa. So lets look at the API. In the CyGlobalContext class there is this method:
So it returns a CyPlayer instance and takes a integer value as the parameter. Example of use:
Code:
pPlayer = gc.getPlayer(0)
Since we now have a CyPlayer instance (assigned to the pPlayer name - p is for "pointer", by the way) we can use it to get the PlayerType/ID of that instance. In the CyPlayer class there is this:
The method takes no parameter and returns a integer value:
Code:
ePlayer = pPlayer.getID()
This would return the integer value 0 (zero) in this example.
Now, basically the same goes for teams - TeamTypes and CyTeam instances. But firstly we can use this CyPlayer class method to get the Team ID belonging to our player:
As the API entry clearly states - the method returns a integer value and that would be the TeamType/ID. Now that we have that can we go looking for a method for fetching the CyTeam instance belonging to that enumerated team. In CyGlobalContext we find:
To me it looks terrible and I hate writing this over and over again. So in order to eliminate the need to do it I could either use the helper functions I posted in the earlier post, or I can use the CivPlayer class for this.
Take another look at the earlier post and see if you can't make sense of it now. If not I'll explain further.
so where would the code on the end of the first explanation be used?
if I wanted to define greece I would say pTeam = pointer("Greece", CyTeam)
and hellenic rebels (who I am dealing with for the next event) would be ("Hellenic Rebels", Cyteam0)
to use all of this where and what would need to be defined?
do you use the displayed name of the civ (as you would see it in game)?
may need a little more explaining (oh how evil I am )
on a new note: for my new event I am going to use an elif statement... basically if a certain coordinate is controlled by ANYBODY (but the rebels themselves) then the event spawns a large army BUT if the coordinate is controlled by the rebels or empty and is 4 plots away from another city, a city (called X for now) is spawned with a small army to guard it. Anything else and the large army spawns (regarding if neither can happen).
The only problem I forsee is with the minimal distance from the city and the preliminary code for this would probs be
Code:
[COLOR="Red"]##Macedonia Rebellion##[/COLOR]
iYear = -3800 [COLOR="red"]#Btw this is my standard year keeper[/COLOR]
macedoniaHeader = [COLOR="Green"]"Macedonian Rebellion"[/COLOR]
macedoniaMessage = [COLOR="green"]"Leaders of a small rebel group hope to reform the mighty kingdom of Macedon!n\n\ Either a large army of barbarians will spawn in the north of Greece or a city and a small army will appear of rebels!"[/COLOR]
[COLOR="red"]#unit definitions: coordinates, player, number to be spawned, unit type (defined by Utils)#[/COLOR]
tMacedonia = (?, ?) [COLOR="red"]#don't know them yet![/COLOR]
eMacedoniaCity = eHellenicRebels[COLOR="red"] #will probs be defined with your thingy when I know how to use it![/COLOR]
eMacedoniaArmy = eBarbarian
iMacedoniaCityUnitNumber1 = 4
iMacedoniaCityUnitNumber2 = 2
iMacedoniaArmyUnitNumber = 8
eMacedoniaUnit1 = eAdvancedSpearman
eMacedoniaUnit2 = eArcher
[COLOR="Orange"]def[/COLOR] [COLOR="Blue"]eventMacedonia[/COLOR]():
[COLOR="orange"]if[/COLOR] isDate(iYear):
showPopup(macedoniaHeader, macedoniaMessage)
[COLOR="orange"]if[/COLOR]: [COLOR="red"]#english:if tMacedonia is == -1(no civ?) and tMacedonia = minimum spawn distance?: Basically if tMacedonia is not owned by anybody and is not within city min distance:[/COLOR]
[COLOR="red"]#make city called X[/COLOR]
spawnUnits(eMacedoniaCity, eMacedoniaUnit2, tMacedonia, iMacedoniaCityUnitNumber1)
spawnUnits(eMacedoniaCity, eMacedoniaUnit1, tMacedonia, iMacedoniaCityUnitNumber2)
[COLOR="orange"]elif[/COLOR]: [COLOR="red"]#english:if tMacedonia is == 11(hellenic rebels?) and tMacedonia = minimum spawn distance?: Basically if tMacedonia is owned by Hellenic Rebels and is not within city min distance:[/COLOR]
[COLOR="red"]#make city called X[/COLOR]
spawnUnits(eMacedoniaCity, eMacedoniaUnit2, tMacedonia, iMacedoniaCityUnitNumber1)
spawnUnits(eMacedoniaCity, eMacedoniaUnit1, tMacedonia, iMacedoniaCityUnitNumber2)
[COLOR="orange"]else[/COLOR]:
spawnUnits(eMacedoniaArmy, eMacedoniaUnit1, tMacedonia, iMacedoinaArmyUnitNumber)
confusing isn't it? as you can see by the comments there are certain things I don't know but the ONE thing I am sure of is the else statement ! hope you can help fill in the blanks a little...
so where would the code on the end of the first explanation be used?
if I wanted to define greece I would say pTeam = pointer("Greece", CyTeam)
and hellenic rebels (who I am dealing with for the next event) would be ("Hellenic Rebels", Cyteam0)
to use all of this where and what would need to be defined?
do you use the displayed name of the civ (as you would see it in game)?
may need a little more explaining (oh how evil I am )
I realize using CivPlayer might not be the easy route to take - and you'd have to include the module in any project that relies on it. So learning the basic way to do things might be valuable in itself.
I'm gonna refrain from explaining how it works and focus on how to use CivPlayer. Your example with assigning the CyTeam instance of the Greek Civ to the name pTeam is correct use of the pointer() function. But I don't quite understand where you got the name "Cyteam0" for the rebel example.
The Civ() and pointer() functions use the short description names of the civilizations. I guess you defined these in the XML.
To use CivPlayer to get various player values is achieved by simply importing CivPlayer:
These are just some of the ways you can get various values with CivPlayer without ever using the any of the method I listed with API entries in the previous post. I designed it so that I would have lots of options to play around with - you don't have to use all of them if you don't want to.
on a new note: for my new event I am going to use an elif statement... basically if a certain coordinate is controlled by ANYBODY (but the rebels themselves) then the event spawns a large army BUT if the coordinate is controlled by the rebels or empty and is 4 plots away from another city, a city (called X for now) is spawned with a small army to guard it. Anything else and the large army spawns (regarding if neither can happen).
Ok, you seem to have grasped the concept of conditional statements and how to use if/elif/else. Just make sure to put the colon at the end of the lines once you complete the statement.
What you need is probably some more helper functions - for checking the validity of plots in regard to ownership, for checking if there are any cities blocking the area, and for spawning cities. This are my suggestions:
PHP:
Map = CyMap()
def isPlotOwner(tCoords, ePlayer):
"""
Returns True if ePlayer is the owner of the plot at tCoords.
Otherwise returns False.
"""
pPlot = getPlot(tCoords)
eOwner = pPlot.getOwner()
return eOwner == ePlayer
def checkCityProximity(tCoords, iDistance=4):
"""
Returns False if any city is within iDistance from the plot at
tCoords. Otherwise returns True.
"""
pPlot = getPlot(tCoords)
iX = pPlot.getX()
iY = pPlot.getY()
for iCurrentX in range(iX - iDistance, iX + iDistance + 1):
for iCurrentY in range(iY - iDistance, iY + iDistance + 1):
pCurrentPlot = Map.plot(iCurrentX, iCurrentY)
if pCurrentPlot.isCity():
return False
return True
def spawnCity(tCoords, ePlayer, name=None, iSize=2):
"""
Creates a new CyCity instance at tCoords belonging to ePlayer,
of iSize and with the optional name. (Returns None.)
"""
iX, iY = tCoords
pPlayer = pPlayer = gc.getPlayer(ePlayer) # alt instance(ePlayer).get(CyPlayer)
pCity = pPlayer.initCity(iX, iY)
pCity.setPopulation(iSize)
if name != None:
pCity.setName(name)
def getPlot(tCoords):
"""
Returns the CyPlot instance of the plot at tCoords.
"""
iX, iY = tCoords
return Map.plot(iX, iY)
Before simply adding these to CustomFunctions and continuing with scripting your event, you should probably make a point of understanding everything that is going on here. (Otherwise I just made you a disservice.) Refer to the API and any textbook/tutorial you have access to when in doubt. Ask if anything is unclear still, because it can all make sense. And you will be so much better off.
Now that you have the building blocks - and you understand how the work - can you figure out how to make the conditional statements work?
sorry I dont know about swedish keyboards but british ones have shift + 0 = ) so I must have not deleted it
the only conditional statements I don't really understand are the "for", "is" and "in"(actually is that even a conditional?) and I mostly understand what you have done in those functions!
def isPlotNotOwned(tCoords):
"""
Returns True if Plot is not owned by anybody (made by J_mie6)
if plot is owned returns False
"""
pPlot = getPlot(tCoords)
eOwner = pPlot.getOwner()
return eOwner != eHellenicRebels or eRome or eGreece or eGaul or eBrittania or eCarthage or eEgypt or eGermania or ePictia or eItalianRebels or eNubianRebels or eNumidianRebels or eGallicRebels or eBrythonRebels or eGermanicRebels or ePictishRebels or eIberia or eGreekColonies
you didn't give me a if plot is NOT owned by anybody function so I made my own (albeit very messy)! But I am sure there is a better way to do this... btw all of the "e"s are defined in Utils!
Code:
##Macedonia Rebellion##
iYear = -3800
macedoniaHeader = "Macedonian Rebellion"
macedoniaMessage = "Leaders of a small rebel group hope to reform the mighty kingdom of Macedon!n\n\ Either a large army of barbarians will spawn in the north of Greece or a city and a small army will appear of rebels!"
#unit definitions: coordinates, player, number to be spawned, unit type (defined by Utils)#
tMacedonia = (0, 0)
eMacedoniaCity = eHellenicRebels
eMacedoniaArmy = eBarbarian
iMacedoniaCityUnitNumber1 = 4
iMacedoniaCityUnitNumber2 = 2
iMacedoniaArmyUnitNumber = 8
eMacedoniaUnit1 = eAdvancedSpearman
eMacedoniaUnit2 = eArcher
eCityName = "Macedon"
iCitySize = 2
iCityDistance = 4
def eventMacedonia():
if isDate(iYear):
showPopup(macedoniaHeader, macedoniaMessage)
if isPlotNotOwned(tMacedonia) == True and checkCityProximity(tMacedonia, iCityDistance) == True:
spawnCity(tMacedonia, eHellenicRebels, eCityName, iCitySize)
spawnUnits(eMacedoniaCity, eMacedoniaUnit2, tMacedonia, iMacedoniaCityUnitNumber1)
spawnUnits(eMacedoniaCity, eMacedoniaUnit1, tMacedonia, iMacedoniaCityUnitNumber2)
elif isPlotOwner(tMacedonia, eHellenicRebels) == True and checkCityProximity(tMacedonia, iCityDistance) == True:
spawnCity(tMacedonia, eHellenicRebels, eCityName, iCitySize)
spawnUnits(eMacedoniaCity, eMacedoniaUnit2, tMacedonia, iMacedoniaCityUnitNumber1)
spawnUnits(eMacedoniaCity, eMacedoniaUnit1, tMacedonia, iMacedoniaCityUnitNumber2)
else:
spawnUnits(eMacedoniaArmy, eMacedoniaUnit1, tMacedonia, iMacedoniaArmyUnitNumber)
and this is the finished (hopefully) event for it. Does the function and the code work fine you think?
the only conditional statements I don't really understand are the "for", "is" and "in"(actually is that even a conditional?) and I mostly understand what you have done in those functions!
The isPlotOwner() function returns a True value if ePlayer is the plot owner. If you wanna use the return value as its opposite then you simply add the command not.
This is how you would check if a plot belongs to anybody but ePlayer:
Code:
if not isPlotOwner(ePlayer):
This is now you would check if a plot belongs to nobody :
Code:
if isPlotOwner(-1):
(It might be that CyPlot.getOwner() returns None if there is no owner, I don't remember.)
But you could also check whether a plot belongs to anyone (not no owner):
Code:
if not isPlotOwner(-1):
The methods in the API that are marked BOOL also return True/False, and they actually follow the same naming convention. Like CyPlot.isCity(), which will return True if there is a city on the plot, and false if there is not. So you use not to check if there is no city present:
##Macedonia Rebellion##
iYear = -3800
macedoniaHeader = "Macedonian Rebellion"
macedoniaMessage = "Leaders of a small rebel group hope to reform the mighty kingdom of Macedon!n\n\ Either a large army of barbarians will spawn in the north of Greece or a city and a small army will appear of rebels!"
#unit definitions: coordinates, player, number to be spawned, unit type (defined by Utils)#
tMacedonia = (0, 0)
eMacedoniaCity = eHellenicRebels
eMacedoniaArmy = eBarbarian
iMacedoniaCityUnitNumber1 = 4
iMacedoniaCityUnitNumber2 = 2
iMacedoniaArmyUnitNumber = 8
eMacedoniaUnit1 = eAdvancedSpearman
eMacedoniaUnit2 = eArcher
eCityName = "Macedon"
iCitySize = 2
iCityDistance = 4
def eventMacedonia():
if isDate(iYear):
showPopup(macedoniaHeader, macedoniaMessage)
if isPlotOwner(tMacedonia, -1) == True and checkCityProximity(tMacedonia, iCityDistance) == True:
spawnCity(tMacedonia, eHellenicRebels, eCityName, iCitySize)
spawnUnits(eMacedoniaCity, eMacedoniaUnit2, tMacedonia, iMacedoniaCityUnitNumber1)
spawnUnits(eMacedoniaCity, eMacedoniaUnit1, tMacedonia, iMacedoniaCityUnitNumber2)
elif isPlotOwner(tMacedonia, eHellenicRebels) == True and checkCityProximity(tMacedonia, iCityDistance) == True:
spawnCity(tMacedonia, eHellenicRebels, eCityName, iCitySize)
spawnUnits(eMacedoniaCity, eMacedoniaUnit2, tMacedonia, iMacedoniaCityUnitNumber1)
spawnUnits(eMacedoniaCity, eMacedoniaUnit1, tMacedonia, iMacedoniaCityUnitNumber2)
else:
spawnUnits(eMacedoniaArmy, eMacedoniaUnit1, tMacedonia, iMacedoniaArmyUnitNumber)
would be better? Or can I use isPlotNotOwned anyway or will it not work? will this code work?
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.