DireAussie
Chieftain
- Joined
- Nov 12, 2005
- Messages
- 60
How does it work?
Certain classes in civ 4 expose a setScriptData() function and a getScriptData() function that you can use to put data into that will automatically be saved and loaded by civ 4 when someone saves/loads a game.
The classes with these functions are:
CyCity
CyGame
CyPlayer
CyPlot
CyUnit
How do I do it?
The first thing you should do is make a new IO class in its own module (.py file). In my example I show below I created a file called RandomEventsIO.py:
The code is explained below:
PythonExtensions are the exposed c++ functions that you can call in python. This gives you access to CyGame, CyPlot objects and functions etc
pickle is a special python module that allows you to write stuff out to files and read stuff in from files without having to worry about the data type.
This gets the CyGame instance from the globalcontext object. I will be reading/writing my script data out to this CyGame object.
This function is to be called only when starting a new game. In your CvEventManager file you have a function called:
def onGameStart(self, argsList):
you can put a call to this function in here (see later)
This creates a "dictionary of values". All is basically is is an array with three elements but instead of doing scriptDictCyGame[0], scriptDictCyGame[1] and scriptDictCyGame[2] to get the values I can do scriptDictCyGame['olympicCounter'], scriptDictCyGame['locustSwarmOccupyingPlotX'] and scriptDictCyGame['locustSwarmOccupyingPlotY'] respectively to get the values.
The values of these variables should be what you want them to be on initialisation.
This writes the whole scriptDictCyGame array out to the CyGame class' script data. Since this is a new game we need to do this at the very start because at the start of a new game there's nothing at all saved in the script data.
Using these variables
Now that you have set these variables up, you need to make functions so you change the variables in your python code. I'll explain one of them below:
This gets the "olympicCounter" variable from the python code. Note how we first have to load the entire scriptDictCyGame array using pickle, then we can get the single value from the array.
This sets the "olympicCounter" variable to a new value. Note how you have to first load the entire array out, then change the one value, then save the entire array back in.
OK, I have created this IO class. Now what?
You'll need to create an instance of this new IO object. A starting point in doing this is in the CvEventManager.py module.
You'll need to add:
to the top
in the def __init__(self): function you need to add (anywhere inside it):
This will create an object (instance) of the RandomEventsIO class when the EventClass object is created (which is as soon as civ 4 loads I think).
Now you want your IO object to set itself up when the user starts a new game. To do that, find the
def onGameStart(self, argsList):
function, and add this to the bottom:
self.randomEventsIO.setupScriptData()
This will initialise the values and put them inside the CyGame script data.
OK, its ready to use. How do I use/save/load my variables?
At ALL times you must use the get and set functions for the relavent variable. You may need to pass the IO object off to other modules through the argument list of your functions. Other than that you dont have to worry about saving and loading as Civ 4 saves all the script data when the user saves the game and it loads the data back in when the user loads a game.
What should I use, CyCity, CyGame, CyPlayer, CyPlot, CyUnit?
That depends on what you need to save. If you want to save a piece of data about all units, for example, "number of kills", then you'll need to use the saveScriptData/loadScriptData provided by CyUnit, and you'll need to loop through every unit to do it.
Certain classes in civ 4 expose a setScriptData() function and a getScriptData() function that you can use to put data into that will automatically be saved and loaded by civ 4 when someone saves/loads a game.
The classes with these functions are:
CyCity
CyGame
CyPlayer
CyPlot
CyUnit
How do I do it?
The first thing you should do is make a new IO class in its own module (.py file). In my example I show below I created a file called RandomEventsIO.py:
Code:
#import utility functions
from CvPythonExtensions import *
import pickle
# globals
gc = CyGlobalContext()
CyGameInstance = gc.getGame()
class RandomEventsIO:
def setupScriptData( self ):
"Initialise the global script data dictionary for usage. setupScriptData should ALWAYS set the variables for a NEW GAME"
#this needs to hold all variables to be saved between games
scriptDictCyGame = { 'olympicCounter': 1,
'locustSwarmOccupyingPlotX': -1,
'locustSwarmOccupyingPlotY': -1
}
CyGameInstance.setScriptData( pickle.dumps(scriptDictCyGame) )
def getOlympicCounter( self ):
"Returns olympicCounter"
scriptDictCyGame = pickle.loads( CyGameInstance.getScriptData() )
return scriptDictCyGame['olympicCounter']
def setOlympicCounter( self, newValue ):
"Sets olympicCounter."
scriptDictCyGame = pickle.loads( CyGameInstance.getScriptData() )
scriptDictCyGame['olympicCounter'] = newValue
CyGameInstance.setScriptData( pickle.dumps(scriptDictCyGame) )
def getLocustSwarmOccupyingPlot(self):
"get the plot the locust swarm is occupying"
scriptDictCyGame = pickle.loads( CyGameInstance.getScriptData() )
return scriptDictCyGame['locustSwarmOccupyingPlotX'], scriptDictCyGame['locustSwarmOccupyingPlotY']
def setLocustSwarmOccupyingPlot(self, x, y):
"sets the plot the locust swarm is occupying"
scriptDictCyGame = pickle.loads( CyGameInstance.getScriptData() )
scriptDictCyGame['locustSwarmOccupyingPlotX'] = x
scriptDictCyGame['locustSwarmOccupyingPlotY'] = y
CyGameInstance.setScriptData( pickle.dumps(scriptDictCyGame) )
The code is explained below:
Code:
from CvPythonExtensions import *
import pickle
PythonExtensions are the exposed c++ functions that you can call in python. This gives you access to CyGame, CyPlot objects and functions etc
pickle is a special python module that allows you to write stuff out to files and read stuff in from files without having to worry about the data type.
Code:
CyGameInstance = gc.getGame()
This gets the CyGame instance from the globalcontext object. I will be reading/writing my script data out to this CyGame object.
Code:
def setupScriptData( self ):
This function is to be called only when starting a new game. In your CvEventManager file you have a function called:
def onGameStart(self, argsList):
you can put a call to this function in here (see later)
Code:
scriptDictCyGame =
{
'olympicCounter': 1,
'locustSwarmOccupyingPlotX': -1,
'locustSwarmOccupyingPlotY': -1
}
This creates a "dictionary of values". All is basically is is an array with three elements but instead of doing scriptDictCyGame[0], scriptDictCyGame[1] and scriptDictCyGame[2] to get the values I can do scriptDictCyGame['olympicCounter'], scriptDictCyGame['locustSwarmOccupyingPlotX'] and scriptDictCyGame['locustSwarmOccupyingPlotY'] respectively to get the values.
The values of these variables should be what you want them to be on initialisation.
Code:
CyGameInstance.setScriptData( pickle.dumps(scriptDictCyGame) )
This writes the whole scriptDictCyGame array out to the CyGame class' script data. Since this is a new game we need to do this at the very start because at the start of a new game there's nothing at all saved in the script data.
Using these variables
Now that you have set these variables up, you need to make functions so you change the variables in your python code. I'll explain one of them below:
Code:
def getOlympicCounter( self ):
"Returns olympicCounter"
scriptDictCyGame = pickle.loads( CyGameInstance.getScriptData() )
return scriptDictCyGame['olympicCounter']
This gets the "olympicCounter" variable from the python code. Note how we first have to load the entire scriptDictCyGame array using pickle, then we can get the single value from the array.
Code:
def setOlympicCounter( self, newValue ):
"Sets olympicCounter."
scriptDictCyGame = pickle.loads( CyGameInstance.getScriptData() )
scriptDictCyGame['olympicCounter'] = newValue
CyGameInstance.setScriptData( pickle.dumps(scriptDictCyGame) )
This sets the "olympicCounter" variable to a new value. Note how you have to first load the entire array out, then change the one value, then save the entire array back in.
OK, I have created this IO class. Now what?
You'll need to create an instance of this new IO object. A starting point in doing this is in the CvEventManager.py module.
You'll need to add:
Code:
import RandomEventsIO
to the top
in the def __init__(self): function you need to add (anywhere inside it):
Code:
self.randomEventsIO = RandomEventsIO.RandomEventsIO()
This will create an object (instance) of the RandomEventsIO class when the EventClass object is created (which is as soon as civ 4 loads I think).
Now you want your IO object to set itself up when the user starts a new game. To do that, find the
def onGameStart(self, argsList):
function, and add this to the bottom:
self.randomEventsIO.setupScriptData()
This will initialise the values and put them inside the CyGame script data.
OK, its ready to use. How do I use/save/load my variables?
At ALL times you must use the get and set functions for the relavent variable. You may need to pass the IO object off to other modules through the argument list of your functions. Other than that you dont have to worry about saving and loading as Civ 4 saves all the script data when the user saves the game and it loads the data back in when the user loads a game.
What should I use, CyCity, CyGame, CyPlayer, CyPlot, CyUnit?
That depends on what you need to save. If you want to save a piece of data about all units, for example, "number of kills", then you'll need to use the saveScriptData/loadScriptData provided by CyUnit, and you'll need to loop through every unit to do it.