Retain data between turns, calls, etc?

krille

CivDOS Fanatic
Joined
Sep 5, 2005
Messages
337
Hello.

I have made a mod (MyMod.py) that is called on by some CvCustomEventManager.py everytime certain events occurs. This works fine. The problem is, I want to retain certain data between function calls.

if my python file looks something like this:
Code:
somevar
anothervar

def MyOnEvent():
    local_var = somevar + anothervar
    anothervar = local_var

The next time MyOnEvent() is called by CvCustomEventManager somevar is 0 / uninitialized. I want to retain the values of somevar and anothervar (not local_var though) between game turns, function calls and what else. If I also wanted it to be saved and loaded on save_game and load_game I'd use SdToolKit / ScriptData, but in this particular case that's not what I want.

For example, I know that the BUG mod team is somehow able to retain data between function calls / python calls. Without using ScriptData. So it should be doable, but how?
 
Yay!

Finally, got it to work! (Using a class.)
 
Could you share this information with everyone? :)
 
Ok, I'll try to sum it up.

Note: This all refers to the BUG mod's CvCustomEventManager.py file. But it should be generally applicable on most CustomEventManagers.

Anyway, in the __init__ function of the class CvCustomEventManager, we would normally put:
self.addEventHandler("EndPlayerTurn", RetainData.onEndPlayerTurn)

But to make the data retainable, we put this instead:
RetainData.RetainData(self)

Now in order to make this work, we must turn RetainData into a class, or at least create the class RetainData in RetainData.py. The class could look something look like this:


Code:
from CvPythonExtensions import *
import CvUtil
import PyHelpers

mess = CyInterface().addImmediateMessage
gc = CyGlobalContext()

class RetainData:
    def __init__(self, eventManager):
        self.iIndex = 0
        self.iList = []
        self.evMgr = eventManager
        self.evMgr.addEventHandler("EndPlayerTurn", self.onEndPlayerTurn)


    def onEndPlayerTurn(self, argsList):
        iGameTurn, iPlayer = argsList
        pPlayer = gc.getPlayer(iPlayer) # the CyPlayer object
        if pPlayer.isHuman() == False:
            return 0
        self.iIndex += 1
        self.iList.append(iGameTurn)
        mess(
            "RetainData, index: " +
            repr(self.iIndex) +
            ", list: " +
            repr(self.iList), "")

On every turn of the human player, iIndex will be incremented by 1 and the current game turn will be added to the list iList. Then a message will be outputted stating the current value of iIndex as well as printing out the entire list of game turns passed since RetainData was initialized.

Hope this is easy enough.
 
Cool, I don't understand everything but I get the gist. So your int variable corresponds to each turn (year) of the game?

Thanks for the clarification. Some of the terms you use are still a bit baffling for me (like what an "eventHandler" is), but I ordered a Python book to learn the proper terminology and start "from scratch," as opposed to the "bastard knowledge" I've gained looking at other people's code. :)
 
Forgot to include CvCustomEventManager.py from the BUG project: :mischief:

Code:
## Copyright (c) 2005-2006, Gillmer J. Derge.

## This file is part of Civilization IV Alerts mod.
##
## Civilization IV Alerts mod is free software; you can redistribute
## it and/or modify it under the terms of the GNU General Public
## License as published by the Free Software Foundation; either
## version 2 of the License, or (at your option) any later version.
##
## Civilization IV Alerts mod is distributed in the hope that it will
## be useful, but WITHOUT ANY WARRANTY; without even the implied
## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
## See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Civilization IV Alerts mod; if not, write to the Free
## Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
## 02110-1301 USA
    
__version__ = "$Revision: 1.3 $"
# $Source: /usr/local/cvsroot/Civ4lerts/src/main/python/CvCustomEventManager.py,v $


import CvEventManager
import ReminderEventManager
import autologEventManager
import Civ4lerts
import MoreCiv4lerts
import UnitNameEventManager

import BugOptionsEventManager


##-------------------------------------------------------------------

class CvCustomEventManager(CvEventManager.CvEventManager, object):

    """Extends the standard event manager by adding support for multiple
    handlers for each event.
    
    Methods exist for both adding and removing event handlers.  A set method 
    also exists to override the default handlers.  Clients should not depend 
    on event handlers being called in a particular order.
    
    This approach works best with mods that have implemented the design
    pattern suggested on Apolyton by dsplaisted.
    
    http://apolyton.net/forums/showthread.php?threadid=142916
    
    The example given in the 8th post in the thread would be handled by adding
    the following lines to the CvCustomEventManager constructor.  The RealFort,
    TechConquest, and CulturalDecay classes can remain unmodified.
    
        self.addEventHandler("unitMove", rf.onUnitMove)
        self.addEventHandler("improvementBuilt", rf.onImprovementBuilt)
        self.addEventHandler("techAcquired", rf.onTechAcquired)
        self.addEventHandler("cityAcquired", tc.onCityAcquired)
        self.addEventHandler("EndGameTurn", cd.onEndGameTurn)
        
    Note that the naming conventions for the event type strings vary from event
    to event.  Some use initial capitalization, some do not; some eliminate the
    "on..." prefix used in the event handler function name, some do not.  Look
    at the unmodified CvEventManager.py source code to determine the correct
    name for a particular event.
    
    Take care with event handlers that also extend CvEventManager.  Since
    this event manager handles invocation of the base class handler function,
    additional handlers should not also call the base class function themselves.

    """

    def __init__(self, *args, **kwargs):
        super(CvCustomEventManager, self).__init__(*args, **kwargs)
        # map the initial EventHandlerMap values into the new data structure
        for eventType, eventHandler in self.EventHandlerMap.iteritems():
            self.setEventHandler(eventType, eventHandler)
        # --> INSERT EVENT HANDLER INITIALIZATION HERE <--

        autologEventManager.autologEventManager(self)
        Civ4lerts.Civ4lerts(self)
        MoreCiv4lerts.MoreCiv4lerts(self)
        ReminderEventManager.ReminderEventManager(self)
        UnitNameEventManager.UnitNameEventManager(self)
        
        BugOptionsEventManager.BugOptionsEventManager(self)

    def addEventHandler(self, eventType, eventHandler):
        """Adds a handler for the given event type.
        
        A list of supported event types can be found in the initialization 
        of EventHandlerMap in the CvEventManager class.

        """
        self.EventHandlerMap[eventType].append(eventHandler)

    def removeEventHandler(self, eventType, eventHandler):
        """Removes a handler for the given event type.
        
        A list of supported event types can be found in the initialization 
        of EventHandlerMap in the CvEventManager class.  It is an error if 
        the given handler is not found in the list of installed handlers.

        """
        self.EventHandlerMap[eventType].remove(eventHandler)
    
    def setEventHandler(self, eventType, eventHandler):
        """Removes all previously installed event handlers for the given 
        event type and installs a new handler .
        
        A list of supported event types can be found in the initialization 
        of EventHandlerMap in the CvEventManager class.  This method is 
        primarily useful for overriding, rather than extending, the default 
        event handler functionality.

        """
        self.EventHandlerMap[eventType] = [eventHandler]

    def handleEvent(self, argsList):
        """Handles events by calling all installed handlers."""
        self.origArgsList = argsList
        flagsIndex = len(argsList) - 6
        self.bDbg, self.bMultiPlayer, self.bAlt, self.bCtrl, self.bShift, self.bAllowCheats = argsList[flagsIndex:]
        eventType = argsList[0]
        return {
            "kbdEvent": self._handleConsumableEvent,
            "mouseEvent": self._handleConsumableEvent,
            "OnSave": self._handleOnSaveEvent,
            "OnLoad": self._handleOnLoadEvent
        }.get(eventType, self._handleDefaultEvent)(eventType, argsList[1:])

    def _handleDefaultEvent(self, eventType, argsList):
        if self.EventHandlerMap.has_key(eventType):
            for eventHandler in self.EventHandlerMap[eventType]:
                # the last 6 arguments are for internal use by handleEvent
                eventHandler(argsList[:len(argsList) - 6])

    def _handleConsumableEvent(self, eventType, argsList):
        """Handles events that can be consumed by the handlers, such as
        keyboard or mouse events.
        
        If a handler returns non-zero, processing is terminated, and no 
        subsequent handlers are invoked.

        """
        if self.EventHandlerMap.has_key(eventType):
            for eventHandler in self.EventHandlerMap[eventType]:
                # the last 6 arguments are for internal use by handleEvent
                result = eventHandler(argsList[:len(argsList) - 6])
                if (result > 0):
                    return result
        return 0

    # TODO: this probably needs to be more complex
    def _handleOnSaveEvent(self, eventType, argsList):
        """Handles OnSave events by concatenating the results obtained
        from each handler to form an overall consolidated save string.

        """
        result = ""
        if self.EventHandlerMap.has_key(eventType):
            for eventHandler in self.EventHandlerMap[eventType]:
                # the last 6 arguments are for internal use by handleEvent
                result = result + eventHandler(argsList[:len(argsList) - 6])
        return result

    # TODO: this probably needs to be more complex
    def _handleOnLoadEvent(self, eventType, argsList):
        """Handles OnLoad events."""
        return self._handleDefaultEvent(eventType, argsList)

As you can see, addEventHandler is a function of the CvCustomEventManager used to add event handlers. addEventHandler("some event", MyFunction) means when "some event" occurs MyFunction will be called.
 
Shqype, do you care to share which book you've ordered? I'm contemplating ordering a book myself and would welcome a recommendation. Of course, if you get it and it's not what you are hoping for, that would be helpful to know too... :)

Cheers,
ripple01
 
Shqype, do you care to share which book you've ordered? I'm contemplating ordering a book myself and would welcome a recommendation. Of course, if you get it and it's not what you are hoping for, that would be helpful to know too... :)

Cheers,
ripple01

Sure, no problem.

Learning Python: 3rd Edition is the book that I ordered. I haven't received it yet, but it looks really good, not only for beginners, but experienced programmers that are new to the Python language.

I'll let you know how it is once I get a chance to work through it.
 
Back
Top Bottom