# Need CvPythonExtensions as a name in the global scope, in case Cy* classes are in script data.
import CvPythonExtensions
class ScriptDataCache(object):
'''
Allows custom data to be stored on Cv* objects that have a setScriptData() function.
The custom data is stored as a dictionary (a.k.a. map or hash table), each item being a key-value pair.
Usage:
dictionary = PyHelpers.ScriptData[<cyobj or UID>]
# use and modify the dictionary however you want
The key must be a Cy* object or the UID of a Cy* object.
The values to be stored must have a meaningful repr value, e.g. eval(repr(value)) doesn't raise an exception.
To store a Cy* object that has the method getID() or getUID(), get its UID:
uid = CvUtil.getCyUID(cyobj).
To retrieve the Cy* object from this UID:
cyobj = CvUtil.getCyObject(uid).
This function uses cvobj.getScriptData(), so the existing script data must be either a serialized dictionary or an empty string.
To store values whose repr value requires a name, that name must be present in PyHelper's global namespace. This can be done in another module as follows:
PyHelpers.<name> = <name>
For example, if an object has a repr value of 'Item(10)', in order for that value to be eval-ed successfully,
the module defining Item must have at the end:
PyHelpers.Item = Item
Note: cvojb.setScriptData() is called only right before the game is saved for efficiency reasons.
A side effect of this is that if this module is reloaded, all custom data since the game's last load is lost.
'''
def __init__(self):
self.__dct = None
def __realInit(self):
if self.__dct == None:
self.__dct = {}
import CvEventInterface
eventMgr = CvEventInterface.getEventManager()
def preSaveHandler(self, argsList):
for k in self.__dct:
cyobj = CvUtil.getCyObject(k)
cyobj.setScriptData(`self.__dct[k]`)
eventMgr.addEventHandler('OnPreSave', preSaveHandler)
def gameLoadHandler(self, argsList):
if ScriptData.__dct != None:
ScriptData.__dct.clear()
eventMgr.addEventHandler('OnLoad', gameLoadHandler)
eventMgr.addEventHandler('GameStart', gameLoadHandler)
def __getitem__(self, cyobj):
self.__realInit()
k = CvUtil.getCyUID(cyobj)
if k == cyobj:
cyobj = CvUtil.getCyObject(cyobj)
r = self.__dct.get(k)
if r == None:
sData = cyobj.getScriptData()
if len(sData) == 0:
r = {}
else:
r = eval(cyobj.getScriptData())
self.__dct[k] = r
return r
def __setitem__(self, cyobj, v):
self.__realInit()
k = CvUtil.getCyUID(cyobj)
if k == cyobj:
cyobj = CvUtil.getCyObject(cyobj)
self.__dct[k] = v
return v
def __delitem__(self, cyobj):
self.__realInit()
k = CvUtil.getCyUID(cyobj)
if k == cyobj:
cyobj = CvUtil.getCyObject(cyobj)
d = self.__dct.get(k)
if d == None:
self.__dct[k] = {}
else:
d.clear()
def __iter__(self):
self.__realInit()
return self.__dct.__iter__()
def __repr__(self):
return self.__dct.__repr__()
ScriptData = ScriptDataCache()
# I placed the following funcs in CvUtil.py
def getCyUID(cyobj):
'''
Gets and returns a unique ID for a Cy* object or any object that has a getUID() or getID() method.
CyGame, CyUnit, CyPlayer, CyPlot, CySelectionGroup, CyArea, CyTeam, and CyCity are supported.
If the getUID() method exists, it must return a tuple in the format: (type, data1 [, data2 [, ...] ])
If the getID() method exists, it must return an integer.
If an object has both getUID() and getID() methods, the getUID() method is used instead of the getID() method.
Returns the passed value if it doesn't have a unique ID.
'''
typ = type(cyobj)
if typ == CyGame:
return (typ.__name__,)
elif typ == CyUnit:
return (typ.__name__, cyobj.getOwner(), cyobj.getID())
elif typ == CyPlayer:
return (typ.__name__, cyobj.getID())
elif typ == CyPlot:
return (typ.__name__, cyobj.getX(), cyobj.getY())
elif typ == CySelectionGroup:
return (typ.__name__, cyobj.getOwner(), cyobj.getID())
elif typ == CyArea:
return (typ.__name__, cyobj.getID())
elif typ == CyTeam:
return (typ.__name__, cyobj.getID())
elif typ == CyCity:
return (typ.__name__, cyobj.getOwner(), cyobj.getID())
else:
f = getattr(cyobj, 'getUID', None)
if f != None:
return f()
f = getattr(cyobj, 'getID', None)
if f != None:
return (typ, f())
return cyobj
def getCyObject(uid):
'''
Gets and returns an object from a unique ID or a type that has the class method getObject(). See getCyUID().
If the getObject() class method exists, it must accept arguments *uid[1:].
Returns the passed value if it's not a unique ID.
'''
if type(uid) == tuple and len(uid) > 0:
typ = uid[0]
if typ == CyGame.__name__:
return gc.getGame()
elif typ == CyUnit.__name__:
return gc.getPlayer(uid[1]).getUnit(uid[2])
elif typ == CyPlayer.__name__:
return gc.getPlayer(uid[1])
elif typ == CyPlot.__name__:
return CyMap().plot(uid[1], uid[2])
elif typ == CySelectionGroup.__name__:
return gc.getPlayer(uid[1]).getSelectionGroup(uid[2])
elif typ == CyArea.__name__:
return CyMap().getArea(uid[1])
elif typ == CyTeam.__name__:
return gc.getTeam(uid[1])
elif typ == CyCity.__name__:
return gc.getPlayer(uid[1]).getCity(uid[2])
else:
f = getattr(typ, 'getObject', None)
if f != None:
return f(*uid[1:])
return uid