Requesting following features

yes if Asaf could post the code here and see what you can do with it!

If I could have a Mini Project(something nice and simple) that would be fun!
 
You could probably try your hand at some event scripting. I'm not talking about "random events", but rather custom Python code that fires at some pre-set juncture in the scenario. Like on a certain game turn, or whatever. All you pretty much need for something like this is covered in the tutorial, so I suggest you read trough it.

Learning programming by looking at some sample code and figuring out how to copy it is a terrible way of learning, by the way. It will take forever to get anywhere with it and you end up not fully understanding many of the most basic things. If you instead commit yourself to reading up on the subject before trying to make your own code, you will be able to learn pretty much everything useful for modding - in only a fraction of the time. I know this from my own experience.

I started out with trying to script scenario events for a RFC mod-mod. After a few months I was able to do most of the things I could imagine, so I though I knew how to write Python. I was wrong. :rolleyes: When I first picked up a textbook I not only realized that I would have saved hundreds of work hours by simply reading through the first few chapters - probably just one nights worth of studying, but once I had read the 75% of it (a few weeks later) I was already making my own scenario scripting application (based on Object-Oriented Programming) with its own API and everything!

Anyway. You could try adding a new function definition to the CustomFeatures module (part of the Python I've submitted for your mod).

In the body of the function definition you add the code for event you wanna trigger (= the script). Firstly you need some condition to be met (evaluated by an if statement) and then you need the effect. The most basic condition is probably a game turn (integer value) and for the effect you could have a unit spawn. So basically a historical event of some sort or another.

The function name is then added into the CvEventManager module in the proper place. Basically every game event has its own function definition, like onBeginGameTurn(). Note however that the CustomFeatures module is referred to as simply Custom in the Event Manager used in your mod. So the function call has to include this name or it won't be recognized. Like: Custom.myHistoricalEvent()

If this is too much for you to chew then you can just read the tutorial, because its all explained in there.
 
at the moment I am reading a lesson online (Lessons) and I will get around to trying out making an event when I fisnihed learning from this! btw part of my learning class was to set up my own little database! put this into a text doc and save as database.py and run it in IDLE python GUI and stuff (only square, rectangle and triangle are available at the moment)

Spoiler :

Code:
loop = 1 
choice = 0

class Shape:
    def __init__(self,x,y):
        self.x = x
        self.y = y
    description = "This shape has not been described yet"
    author = "Nobody has claimed to make this shape yet"
    def area(self):
        return self.x * self.y
    def perimeter(self):
        return 2 * self.x + 2 * self.y
    def describe(self,text):
        self.description = text
    def authorName(self,text):
        self.author = text
    def scaleSize(self,scale):
        self.x = self.x * scale
        self.y = self.y * scale

class Triangle(Shape):
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def area(self):
        return (self.x * self.y) / 2
    def perimeter(self):
        return (self.x ** 2 + self.y ** 2)**.5 + self.x + self.y
    def hypotenuse(self):
        return (self.x ** 2 + self.y ** 2)**.5
    def describe(self,text):
        self.description = text
    def authorName(self,text):
        self.author = text
    def scaleSize(self,scale):
        self.x = self.x * scale
        self.y = self.y * scale

rectangle = Shape(100,45)
square = Shape(10,10)
triangle = Triangle(10,30)

while loop == 1:
    choice = input("please type in the name of your shape: ")
    if choice == rectangle:
        print "Area:", rectangle.area()
        print "Perimeter:", rectangle.perimeter()
        rectangle.describe("A wide rectangle, more than twice\
            as wide as it is tall")
        rectangle.scaleSize(input("To a scale of: "))
        print "Scaled Area:", rectangle.area()
        yes = 1
        no = 2
        quit = input("do you want to quit? yes/no: ")
        if quit == yes:
            loop = 0
            print "thank you for using shape database.py"
        if quit == no:
            print "ok!"    
    if choice == square:
        print "Area:", square.area()
        print "Perimeter:", square.perimeter()
        square.describe("A simple square!")
        square.scaleSize(input("To a scale of: "))
        print "Scaled Area:", square.area()
        yes = 1
        no = 2
        quit = input("do you want to quit? yes/no: ")
        if quit == yes:
            loop = 0
            print "thank you for using shape database.py"
        if quit == no:
            print "ok!"
    if choice == triangle:
        print "Area:", triangle.area()
        print "Perimeter:", triangle.perimeter()
        print "Hypotenuse:", triangle.hypotenuse ()
        triangle.describe("A right angled triangle!")
        triangle.scaleSize(input("To a scale of: "))
        print "Scaled Area:", triangle.area()
        yes = 1
        no = 2
        quit = input("do you want to quit? yes/no: ")
        if quit == yes:
            loop = 0
            print "thank you for using shape database.py"
        if quit == no:
            print "ok!"

(I know I could probably use special functions defined at the top for quit but I haven't looked into that yet (I skipped it!)) Did this all from memory! :D

so yeah will try and make some sort of event following your instructions!

how is the code coming along over there?
 
Ok, its good to see you actually learning. :goodjob: But the stuff you posted is Object-Oriented Programming (the advanced variety) and not even necessary for CivIV modding. So you could get away with less. Much less... You can basically do anything without ever using classes - it only affects what the application interface will be. And using classes tends to more complicated than not using classes, especially early on.

But now I see how you were able to read some of my code. This is actually what the entire module looks right now (since you requested a status-report). Hopefully its starting to make all sorts of sense to you by now.
Spoiler :
Code:
### Rebellion mod component written for Jamie's Rome Mod, by Baldyr

from ModSettings import *

# constants

iNumTechs = len(tRebelUnitTechs)
iRandomSeed = getRandNum(iNumMajorPlayers, "seed")
tRebellionClasses = (
    "CulturalRebellion",
    "Resistance",
    "DomesticRebellion",
    "MinorRevolt"
    )

# main functions

def process(iGameTurn):
    print "process()"
    checkRebellion()
    for pCurrentRebellion in getRebellions():
        print pCurrentRebellion
        if pCurrentRebellion.checkTermination(iGameTurn):
            removeRebellion(pCurrentRebellion)
            pCurrentRebellion.setDeactivate()
            pCurrentRebellion.addTerminationMessage()
        else:
            pCurrentRebellion.process(iGameTurn)

def checkRebellion():
    print "checkRebellion()"
    #pCivPlayer = instance((iGameTurn + iRandomSeed) % iNumMajorPlayers)
    pCivPlayer = Civ("Rome")
    if not pCivPlayer.isAlive(): return
    lCities = pCivPlayer.get(PyPlayer).getCityList()
    lCities.reverse()
    lDefectingCities = list()
    iNumCities = len(lCities)
    bCivilWarValid = not CivilWar.isCivilWarActive() and iNumCities >= iRequiredNumCivilWarCities
    for pCity in (city.GetCy() for city in lCities):
        print pCity.getName()
        if isRebellion(pCity) or pCity.getPopulation() < iRequiredRebellionCitySize: continue
        if checkClasses(pCity, pCivPlayer): return
        if bCivilWarValid and CivilWar.checkUnhappiness(pCity):
            lDefectingCities.append(pCity)
    if ( bCivilWarValid
         and CivilWar.checkConditions(len(lDefectingCities), iNumCities) ):
        CivilWar.initCivilWar(pCivPlayer, lDefectingCities)

def checkClasses(pCity, pCivPlayer):
    print "checkClasses()"
    for rebellionClass in tRebellionClasses:
        print rebellionClass
        if eval(rebellionClass + ".checkConditions(pCivPlayer, pCity)"):
            appendRebellion(eval(rebellionClass + "(pCivPlayer, pCity)"))
            return True

# data storage

def setup():
    setGlobalData("lCurrentRebellions", list())

def getRebellions():
    return getGlobalData("lCurrentRebellions")

def appendRebellion(pCurrentRebellion):
    lCurrentRebellions = getRebellions()
    lCurrentRebellions.append(pCurrentRebellion)
    setGlobalData("lCurrentRebellions", lCurrentRebellions)

def removeRebellion(pCurrentRebellion):
    lCurrentRebellions = getRebellions()
    lCurrentRebellions.remove(pCurrentRebellion)
    setGlobalData("lCurrentRebellions", lCurrentRebellions)


class Rebellion:

    # class methods for external access

    @classmethod
    def checkConditions(cls, pCivPlayer, pCity):
        print "Rebellion.checkConditions()"
        return pCity.isDisorder()

    # initialization

    def __init__(self, pCivPlayer, pCity):
        self.iRebellionTurn = Game.getGameTurn()
        self.plotID = getPlotID(pCity.plot())
        self.eCityOwner = pCivPlayer.get(playerID)
        self.setRebelPlayer(pCity)
        self.setUnitType(pCity)
        self.setNumUnits(pCity)
        self.setRebelUnitSettings(pCity)
        self.setMessages(pCity)
        self.setActivate()
        self.fire()
        print self

    def setRebelPlayer(self, pCity):
        self.eRebelPlayer = eBarbarian

    def setUnitType(self, pCity):
        self.eUnitType = getTechUnitType(self.getCityOwner().get(CyTeam))

    def setNumUnits(self, pCity):
        iNatives = pCity.getCulturePercentAnger() / 100
        iPopulation = pCity.getPopulation()
        self.iNumUnits = max(1, min(iNatives, iPopulation))

    def setRebelUnitSettings(self, pCity):
        self.iUnitAI = int(eAttackCity)
        self.ePromotion = None
        self.unitFlag = "rebel"

    def setMessages(self, pCity):
        self.rebellionMessage = defaultRebellionMessage
        self.terminationMessage = defaultTerminationMessage
        self.unitSpawnMessage = defaultUnitSpawnMessage

    # class instance interface

    def isRebellionTurn(self):
        return Game.getGameTurn() == self.iRebellionTurn

    def getRebellionTurn(self):
        return self.iRebellionTurn

    def getCityOwner(self):
        return instance(self.eCityOwner)

    def getRebelCiv(self):
        if self.eRebelPlayer == eBarbarian:
            return pBarbarianCiv
        else:
            return instance(self.eRebelPlayer)

    def getRebelPlayer(self):
        return self.eCityOwner + iNumMajorPlayers

    def getUnitType(self):
        return self.eUnitType

    def getPlotID(self):
        return self.plotID

    def getCyPlot(self):
        return Map.plotByIndex(self.getPlotID())

    def getPlotCoords(self):
        return getCoords(self.getCyPlot())

    def getCyCity(self):
        return self.getCyPlot().getPlotCity()

    def getCityName(self):
        return self.getCyCity().getName()

    def getNumUnits(self):
        return self.iNumUnits

    def getUnitAI(self):
        return UnitAITypes(self.iUnitAI)

    def isPromotion(self):
        return self.ePromotion != None

    def getPromotion(self):
        return self.ePromotion

    def isUnitFlag(self):
        return self.unitFlag != ""

    def getUnitFlag(self):
        return self.unitFlag
    
    def setPromotion(self, pUnit):
        if self.isPromotion():
            pUnit.setHasPromotion(self.getPromotion())

    def setUnitFlag(self, pUnit):
        if self.isUnitFlag():
            pUnit.setScriptData(self.getUnitFlag())

    def setActivate(self):
        self.getCyCity().setScriptData("rebellion")

    def setDeactivate(self):
        self.getCyCity().setScriptData("")

    def isActive(self):
        return self.getCyCity().getScriptData() == "rebellion"

    # shared customizable functionality

    def fire(self):
        self.spawnRebelUnits()
        self.addRebellionMessage()
        
    def process(self, iGameTurn):
        if isChance(iRebelReinforcementPercentage):
            self.spawnRebelUnits(iNumRebelReinforcements)

    def checkTermination(self, iGameTurn):
        print "Rebellion.checkTermination()"
        if self.getCyCity().isDisorder(): return False
        unitFlag = self.getUnitFlag()
        for pUnit in self.getRebelCiv().get(PyPlayer).getUnitList():
            if pUnit.getScriptData() == unitFlag:
                return False
        return True

    def killRebelUnits(self):
        flag = self.getUnitFlag()
        for pUnit in self.getRebelCiv().get(PyPlayer).getUnitList():
            if pUnit.getScriptData() == flag:
                pUnit.kill(True, self.getCityOwner().get(playerID))

    def addMessage(self, message, tColor):
        if not message: return
        if cityKnownByHuman(self.getCyCity()):
            addMessage(message, (self.getCityName(),), tColor)

    def addTerminationMessage(self, bRed=False, bWhite=True):
        bGreen = self.getCityOwner() == pHumanCiv
        self.addMessage(self.terminationMessage, (bGreen, bRed, bWhite))

    def addRebellionMessage(self, bGreen=False, bWhite=True):
        bRed = self.getCityOwner() == pHumanCiv
        self.addMessage(self.rebellionMessage, (bGreen, bRed, bWhite))

    def addUnitSpawnMessage(self, bGreen=False):
        bRed = self.getCityOwner() == pHumanCiv
        self.addMessage(self.unitSpawnMessage, (bGreen, bRed))

    def spawnRebelUnits(self, iNumUnits=0):
        print "Rebellion.spawnRebelUnits()"
        if not iNumUnits:
            iNumUnits = self.getNumUnits()
        eUnitType, eUnitAI = self.getUnitType(), self.getUnitAI()
        lPlots = self.getPlotList()
        if len(lPlots) == 0: return
        while iNumUnits:
            iX, iY = getRandomCoords(lPlots)
            print (iX, iY, eUnitType, eUnitAI)
            pUnit = self.getRebelCiv().get(CyPlayer).initUnit(eUnitType, iX, iY, UnitAITypes.UNITAI_ATTACK_CITY, DirectionTypes.NO_DIRECTION)
            self.setPromotion(pUnit)
            self.setUnitFlag(pUnit)
            iNumUnits -= 1
        if not self.isRebellionTurn():
            self.addUnitSpawnMessage()

    def getPlotList(self):
        lPlots = list()
        iX, iY = self.getPlotCoords()
        for eDirection in range(DirectionTypes.NUM_DIRECTION_TYPES):
            pPlot = plotDirection(iX, iY, DirectionTypes(eDirection))
            if isSpawnValid(pPlot):
                lPlots.append(pPlot)
        return lPlots

    def updateRebelTechs(self):
        rebelCiv = self.getRebelCiv().get(PyPlayer)
        for eTech in self.getCityOwner().get(PyPlayer).getResearchedTechList():
            if rebelCiv.hasResearchedTech(eTech): continue
            rebelCiv.setHasTech(eTech)


class CulturalRebellion(Rebellion):

    @classmethod
    def checkConditions(cls, pCivPlayer, pCity):
        return ( pCity.isDisorder()
                 and not pCity.isOccupation()
                 and not pCity.isNeverLost()
                 and isChance(iCulturalRebellionPercentage) )

    def __init__(self, pCivPlayer, pCity):
        Rebellion.__init__(self, pCivPlayer, pCity)

    def fire(self):
        self.setAtWar()
        self.spawnRebelUnits()
        self.addRebellionMessage()

    def setRebelPlayer(self, pCity):
        self.eRebelPlayer = getHighestCityCulturalRival(pCity)
        if self.eRebelPlayer == -1:
            self.eRebelPlayer = self.getRebelPlayer()
            self.updateRebelTechs()

    def setAtWar(self):
        setAtWar(self.getRebelCiv(), self.getCityOwner())


class Resistance(Rebellion):

    @classmethod
    def checkConditions(cls, pCivPlayer, pCity):
        return ( pCity.isDisorder()
                 and pCity.isOccupation()
                 and isChance(iResistancePercent) )

    def __init__(self, pCivPlayer, pCity):
        Rebellion.__init__(self, pCivPlayer, pCity)

    def setRebelPlayer(self, pCity):
        self.eRebelPlayer = pCity.getPreviousOwner()

    def setUnitType(self, pCity):
        self.eUnitType = getTechUnitType(self.getRebelCiv().get(CyTeam))

    def process(self, iGameTurn):
        if ( iGameTurn - self.getRebellionTurn() >= iResistanceDomesticTurns
             and isChance(iResistanceDomesticPercentage) ):
            self.killRebelUnits()
            removeRebellion(self)
            appendRebellion(DomesticRebellion(self.getCityOwner(), self.getCyCity()))
        else:
            Rebellion.process(self, iGameTurn)


class DomesticRebellion(Rebellion):

    @classmethod
    def checkConditions(cls, pCivPlayer, pCity):
        return ( pCity.isDisorder()
                 and isChance(iDomesticRebellionPercentage - isCivilized(pCivPlayer) * 5) )
    
    def __init__(self, pCivPlayer, pCity):
        Rebellion.__init__(self, pCivPlayer, pCity)

    def fire(self):
        self.spawnRebelUnits()
        self.addRebellionMessage()
        self.setCapital()
        self.changeCapitalHappinessPenalty(-iCapitalHappinessPenalty)

    def setRebelPlayer(self, pCity):
        self.eRebelPlayer = self.getRebelPlayer()
        self.updateRebelTechs()

    def setNumUnits(self, pCity):
        iUnhappyLevel = pCity.unhappyLevel(0)
        iPopulation = pCity.getPopulation()
        iDistance = getDistanceToCapital(pCity)
        self.iNumUnits = iPopulation / 2 * iUnhappyLevel + iDistance

    def checkTermination(self, iGameTurn):
        if Rebellion.checkTermination(self, iGameTurn):
            rebellion.changeCapitalHappinessPenalty(iCapitalHappinessPenalty)
            return True

    def changeCapitalHappinessPenalty(self, value):
        self.getCapital().changeExtraHappiness(value)

    def setCapital(self):
        self.iCapital = self.getCityOwner().get(CyPlayer).getCapitalCity().getID()

    def getCapital(self):
        return self.getCityOwner().get(CyPlayer).getCity(self.iCapital)
    

class MinorRevolt(Rebellion):

    @classmethod
    def checkConditions(cls, pCivPlayer, pCity):
        return ( pCity.angryPopulation()
                 and isChance(iMinorRevoltPercentage) )

    def __init__(self, pCivPlayer, pCity):
        Rebellion.__init__(self, pCivPlayer, pCity)

    def setRebelPlayer(self, pCity):
        self.eRebelPlayer = self.getRebelPlayer()
        self.updateRebelTechs()

    def setNumUnits(self, pCity):
        iUnhappyLevel = pCity.unhappyLevel(0)
        iAngryPopulation = pCity.angryPopulation()
        iPopulation = pCity.getPopulation()
        self.iNumUnits = max(1, min(iUnhappyLevel - iAngryPopulation, iPopulation))


class CivilWar(Rebellion):

    lCivilWarCities = list()

    @classmethod
    def isCivilWarActive(cls):
        return len(cls.lCivilWarCities) > 0

    @classmethod
    def resetCivilWar(cls):
        cls.lCivilWarCities = list()

    @classmethod
    def appendCivilWar(cls, pCivilWar):
        cls.lCivilWarCities.append(pCivilWar)
        appendRebellion(pCivilWar)
        pCivilWar.setActivate()

    @classmethod
    def removeCivilWar(cls, pCivilWar):
##        cls.lCivilWarCities.remove(pCivilWar)
        removeRebellion(pCivilWar)
        pCivilWar.setDeactivate()

    @classmethod        
    def checkUnhappiness(cls, pCity):
        iAngryCitizens = pCity.angryPopulation()
        iWarWeariness = pCity.getWarWearinessPercentAnger() / 100
        return iAngryCitizens - iWarWeariness >= iRequiredNumCivilWarUnhappiness
        
    @classmethod
    def checkConditions(cls, iNumDefectingCities, iNumTotalCities):
        return ( iNumDefectingCities >= iNumTotalCities * iCivilWarUnhappyCitiesPercentage / 100.0
                 and isChance(iCivilWarPercentage) )

    @classmethod
    def initCivilWar(cls, pCivPlayer, lDefectingCities):
        tLargestCity = None, 0, None
        for pCity in lDefectingCities:
            pCivilWar = CivilWar(pCivPlayer, pCity)
            iPopulation = pCity.getPopulation() 
            if iPopulation > tLargestCity[1]:
                tLargestCity = pCity, iPopulation, pCivilWar
        pRebelHQ = tLargestCity[2]
        setCapital(pRebelHQ.getCyCity())
        pRebelHQ.spawnRebelUnits()
        pRebelHQ.terminationMessage = civilWarTerminationMessage
        pRebelHQ.addRebellionMessage()

    def __init__(self, pCivPlayer, pCity):
        Rebellion.__init__(self, pCivPlayer, pCity)
        CivilWar.appendCivilWar(self)
        self.fire()

    def fire(self):
        self.flipCity()

    def setRebelPlayer(self, pCity):
        self.eRebelPlayer = self.getRebelPlayer()
        self.updateRebelTechs()

    def setNumUnits(self, pCity):
        self.iNumUnits = iCivilWarRebelArmySize

    def setMessages(self, pCity):
        self.rebellionMessage = civilWarMessage
        self.terminationMessage = ""
        self.unitSpawnMessage = civilWarUnitSpawnMessage

    def addMessage(self, message, tColor):
        addMessage(message, (self.getCityOwner().getName(),), tColor)

    def addTerminationMessage(self, bRed=False, bWhite=True):
        bGreen = self.getCityOwner() == pHumanCiv
        self.addMessage(self.terminationMessage, (bGreen, bRed, bWhite))

    def addRebellionMessage(self, bGreen=False, bWhite=True):
        bRed = self.getCityOwner() == pHumanCiv
        self.addMessage(self.rebellionMessage, (bGreen, bRed, bWhite))

    def addUnitSpawnMessage(self, bGreen=False):
        bRed = self.getCityOwner() == pHumanCiv
        Rebellion.addMessage(self, self.unitSpawnMessage, (bGreen, bRed))

    def flipCity(self):
        pCity = self.getCyCity()
        iX, iY = self.getPlotCoords()
        pRebelCiv = self.getRebelCiv()
        lCityUnits = getCityUnits(pCity)
        pRebelCiv.get(CyPlayer).acquireCity(pCity, True, False)
        for pUnit in lCityUnits:
            pRebelUnit = pRebelCiv.get(PyPlayer).initUnit(pUnit.getUnitType(), iX, iY)
            pRebelUnit.convert(pUnit)
            self.setUnitFlag(pRebelUnit)

    def checkTermination(self, iGameTurn):
        if not self.getRebelCiv().isAlive():
            self.terminateCivilWar()
            return True

    def terminateCivilWar(self):
        for pCivilWar in self.lCivilWarCities:
            if pCivilWar == self: continue
            self.removeCivilWar(pCivilWar)
        CivilWar.resetCivilWar()

    def process(self, iGameTurn):
        pass


class SlaveRevolt(Rebellion):
    
    @classmethod
    def checkConditions(cls, pCity):
        iSlaveryAnger = pCity.getHurryAngerModifier()
        return isChance(iSlaveRevoltDenominator + iSlaveRevoltDenominator * iSlaveryAnger)

    def __init__(self, ePlayer, pCity):
        Rebellion.__init__(self, instance(ePlayer), pCity)
        appendRebellion(self)

    def fire(self):
        self.spawnRebelUnits()
        self.changeCitySize(-getNumSlaveCitizens(self.getNumUnits()))
        self.addRebellionMessage()
        
    def setRebelPlayer(self, pCity):
        self.eRebelPlayer = eSlaves
        
    def setNumUnits(self, pCity):
        self.iNumUnits = pCity.getPopulation()

    def setRebelUnitSettings(self, pCity):
        self.iUnitAI = int(eAttackCity)
        self.ePromotion = None
        self.unitFlag = pCity.getCityName()

    def setMessages(self, pCity):
        self.rebellionMessage = slaveRevoltMessage
        self.terminationMessage = slaveRevoltTerminationMessage
        self.unitSpawnMessage = defaultUnitSpawnMessage

    def addTerminationMessage(self, bRed=False, bWhite=True):
        bGreen = self.getCityOwner() == pHumanCiv
        addMessage(self.terminationMessage, (), (bGreen, bRed, bWhite))

    def checkTermination(self, iGameTurn):
        if self.getCityOwner().get(CyPlayer).getCivics(eLabor) != eSlavery:
            self.changeCitySize(self.getNumSlaveCitizens(self.countRebelUnits())
            self.killRebelUnits()
            self.revertSlaveCities()
            return True
        return Rebellion.checkTermination(self, iGameTurn)

    def revertSlaveCities(self):
        pPlayer = self.getCityOwner().get(CyPlayer)
        for pCity in (city.GetCy() for city in self.getRebelCiv().get(PyPlayer).getCityList()):
            pPlayer.acquireCity(pCity, False, False)

    def process(self, iGameTurn):
        pass

    def countRebelUnits(self):
        flag = self.getUnitFlag()
        iNumUnits = 0
        for scriptData in (unit.CyGet().getScriptData() for unit in self.getRebelCiv().get(PyPlayer).getUnitList()):
            if scriptData == flag:
                iNumUnits += 1
        return iNumUnits

    def getNumSlaveCitizens(self, iNumUnits):
        return self.countRebelUnits() / iNumSlaveUnitsPerCitizen

    def changeCitySize(self, value):
        self.getCyCity().changePopulation(value)


# helper functions

def getPlotID(pPlot):
    return Map.plotNum(pPlot.getX(), pPlot.getY())

def getCyPlot(iPlot):
    return Map.plotByIndex(iPlot)

def getPlotUnitStrength(pCity):
    pPlot = pCity.plot()
    iNumUnits = pPlot.getNumUnits()
    iUnit = iStrength = 0
    while iUnit < iNumUnits:
        pUnit = pPlot.getUnit(iUnit)
        iStrength += pUnit.baseCombatStr()
        iUnit += 1
    return iStrength

def getNumStrenghtUnits(eUnitType, iStrength):
    return max(1, iStrength / gc.getUnitInfo(eUnitType).getCombat())

def isSpawnValid(pPlot):
    return not ( pPlot.isWater()
                 or pPlot.isPeak()
                 or pPlot.isCity() )

def getRandomCoords(lPlots):
    iRandNum = Game.getSorenRandNum(len(lPlots), "random plot")
    pPlot = lPlots[iRandNum]
    return getCoords(pPlot)

def cityKnownByHuman(pCity):
    return pCity.isRevealed(pHumanCiv.get(teamID), True) # change to False?

def getTechUnitType(pTeam):
    for i in xrange(iNumTechs - 1, -1, -1):
        eTechType = tRebelUnitTechs[i]
        if pTeam.isHasTech(eTechType):
            return tAdvancedRebelUnits[i]
    return eDefaultUnit

def getHighestCityCulturalRival(pCity):
    eOwner = pCity.getOwner()
    eCulturalOwner = pCity.findHighestCulture()
    if eCulturalOwner != eOwner:
        return eCulturalOwner
    else:
        tCurrentLeader = -1, 0
        for ePlayer in CivPlayer.getPlayers(index=playerID):
            if ePlayer == eOwner or ePlayer == eCulturalOwner: continue
            iCulture = pCity.getCulture(ePlayer)
            if iCulture > tCurrentLeader[1]:
                tCurrentLeader = ePlayer, iCulture
        return tCurrentLeader[0]

def isRebellion(pCity):
        return pCity.getScriptData() == "rebellion"

def isCivilized(pCivPlayer):
    return pCivPlayer in lCivilized

def getDistanceToCapital(pCity, pPlayer):
    iCityX, iCityY = getCoords(pCity)
    iCapitalX, iCapitalY = getCoords(pPlayer.getCapitalCity())
    return min(abs(iCityX - iCapitalX), abs(iCityY - iCapitalY))

def getCityUnits(pCity):
    eOwner = pCity.getOwner()
    lUnits = list()
    for pUnit in (pCity.getUnit(iUnit) for iUnit in range(pCity.getNumUnits())):
        if pUnit.getOwner() != eOwner: continue
        lUnits.append(pUnit)
    return lUnits

def setCapital(pCity):
    pCity.setNumRealBuilding(ePalace, 1)
    
def setAtWar(pCivPlayer1, pCivPlayer2):
    pTeam1 = pCivPlayer1.get(CyTeam)
    team2ID = pCivPlayer2.get(teamID)
    if not pTeam1.isAtWar(team2ID):
        pTeam1.declareWar(team2ID, True, -1)
I'm using an Object-Oriented approach on this particular module only because it allows me to re-use code in a convenient fashion for the different sub-classes. Note that the previous Rebels module had no class what-so-ever and worked just the same.

Know however that learning how to write Python is only half of Python-modding. The other part is knowing your way around CvEventManager and the CivIV Python API. This is where my tutorial comes in. Once you feel comfortable with Python you will be able follow along in the tutorial without any problems. Especially if you have a grasp of Object-Oriented Programming, since the API is built around this concept. :goodjob: (But you only need to use the pre-defined classes - not make your own. You can, of course, but you should have a reason for doing so.)

Thinking about it, you could actually make your own module. Something like a ScenarioEvents module with scripted historical events. The way I've setup your Event Manager is that you put all import statements into this function at the end (scroll to right to see all):
Spoiler :
Code:
def initiateCustomPython():
	if gc.getGame().isFinalInitialized():
		global Powers, Rebellion, Custom, CC[COLOR="Red"], Events[/COLOR]
		import Powers, Rebellion, CustomFeatures as Custom, CatapultConstruction as CC[COLOR="Red"], ScenarioEvents as Events[/COLOR]
		if not Rebellion.getGlobalScriptDict():
			Rebellion.setup()
		CC.load()
		from CvGameUtils import CvGameUtils
		CvGameUtils.powers = Powers
		Custom.gameTurn()
Then you'll be able to make function calls with Events.myCustomEvent().

Hell, we could go nuts and just make a ScenarioEvent class as a template for each historical event. So each event would have some game turn (or game year) as the condition, some map tile as the map target, some number and type of unit as the optional unit spawn, an in-game text message with the option of color coding and sound-effect, and so on. Then every event is an instance of the ScenarioEvent class. :goodjob: (Later there could be sub-classes of more specialized types of historical events, like city spawn events, war declaration events, or what have you.)

But you should prototype the basic script for a sample event without using a class before writing up an entire module (much like I did with the new Rebellion module). Good luck! (I you really wrote all that code you posted you don't need any luck. :lol:)
 
yes if Asaf could post the code here and see what you can do with it!

There's actually very little Python code required. Just download the mod and look for the 'ethnic buildings' comment in the python file.
Look at my post in the mod thread for some more details (in my sig).
 
It is tiny... But the question is wether it needs merging....

in CvMainInterface
Code:
		## Ethnic Buildings (Asaf) - Start ##
		pHeadSelectedCity = CyInterface().getHeadSelectedCity()
		if pHeadSelectedCity != None:
			print 'Found head selected city! ID =', pHeadSelectedCity.getID(), 'Owner =', pHeadSelectedCity.getOwner()
			CyGame().setBuildingModelCity(pHeadSelectedCity.getID(), pHeadSelectedCity.getOwner())
		## Ethnic Buildings (Asaf) - End ##

Spoiler :

automatic stop has incurred in my project... I don't know whether I need to do this in a specific python file or make my own and what files to import... at the moment all I would need is to define the barbs and iYear and the co-ordinates and of course the unit to spawn... maybee I just need some jump-leads. I can do python things like make calculators (which I have done!) and stuff but as soon as I saw that I would not be working with the things I have learnt and there would be new things which I have no clue how to use or define (at the moment...). :rolleyes: So at the moment I just need something to get me out of the "Oh No..." chair and into the "Yes I Can.py" chair!
 
Read my tutorial already! :D

The tutorial you've been reading is actually incomplete. Only the Python portion ever got made, while the CivIV modding portion never did. And it seems that the lessons were written prior to CivIV launch, so it includes thing that really didn't end up being useful for modding, like file I/O.
 
ok I will, the thing is eventuly I get tempted to copy and I don't know how far to let myself go lol.

anyway will the code I posted need merging? Or can I crack straight on with making my ethnic buildings?
 
ok I will, the thing is eventuly I get tempted to copy and I don't know how far to let myself go lol.
I'm not following. :p I was referring to you needing some hints on how to script a historical event. This would be covered in my tutorial, but in the form of the original Rebels module. (Not really a historical event as such, but its a working piece of CivIV Python. It does include conditional statements and spawns units, so its basically the same thing.)

Regarding modules and where to put code is really arbitrary. Personally I don't like to add entire scripts directly into the CvEventManager module, even if this probably would be he easier way. So you could just start by adding code to the onBeginGameTurn() method. Once you've declared success we could move that same code into a new module and import it. That would be a valuable lesson for the future. But you're of course allowed to do whatever you want with the Event Manager in your mod. My stuff will only be single line statements and function calls however. :D (And if you start editing CvEventManager I will stop adding anything to it. Then you get to do all of it. I'll tell you where to put stuff, so no worries.)

Regarding the Python Asaf wrote, I haven't looked into it myself yet. You could probably just use the module he has supplied as-is - or copy-paste the relevant code into your own version of that module.
 
what do you mean you will stop adding code to the event manager/ what does everything mean?:D in any case I was hoping to use a custom file like HistoricalEvents.py and make my stuff in there... If I did what would I need to import?:)

If asafs code conflicts with yours how will it be noticable? Should there be anythings to look out for?:think:

:c5happy:(I had to use this symbol lol, I just love how shiny it is :D)
 
what do you mean you will stop adding code to the event manager?
Ok, an actual example: This is the commanderring() function definition from CustomFeatures:
Spoiler :
Code:
def commandeering(pWinner, pLoser, winnerInfo, loserInfo):
        eWinner, eLoser = pWinner.getOwner(), pLoser.getOwner()
        if eWinner == eBarbarian:
                return
        elif eLoser == eBarbarian:
                pLoserCiv = pBarbarianCiv
        else:
                pLoserCiv = instance(eLoser)
        pWinningCiv = instance(eWinner)
        if ( winnerInfo.getUnitClassType() == eCommandeerer
             and loserInfo.getUnitCombatType() == eNaval
             and loserInfo.getUnitClassType() != ePrivateer
             and isChance(iCommandeeringProbability) ):
                pCaptured = pWinningCiv.get(PyPlayer).initUnit(loserInfo.getID(), pWinner.getX(), pWinner.getY())
                pCaptured.finishMoves()
                pCaptured.setDamage(iCaptureDamage, pWinningCiv.get(playerID))
                tStrings = pLoserCiv.getName(), loserInfo.getDescription()
                tColor = pHumanCiv == pWinningCiv, pHumanCiv == pLoserCiv
                addMessage(captureMessage, tStrings, tColor, getCoords(pWinner))
This is how I'm calling the function in CvEventManager:
Spoiler :
Code:
	def onCombatResult(self, argsList):
		'Combat Result'
		pWinner,pLoser = argsList
		
		playerX = PyPlayer(pWinner.getOwner())
		unitX = PyInfo.UnitInfo(pWinner.getUnitType())
		playerY = PyPlayer(pLoser.getOwner())
		unitY = PyInfo.UnitInfo(pLoser.getUnitType())

		[B]Custom.commandeering(pWinner, pLoser, unitX, unitY)[/B]
This is the original approach you were using (but with my take on the code):
Spoiler :
Code:
        def onCombatResult(self, argsList):
            'Combat Result'
            pWinner,pLoser = argsList
            winnerInfo = PyInfo.UnitInfo(pWinner.getUnitType())
            loserInfo = PyInfo.UnitInfo(pLoser.getUnitType())
            [B]eWinner, eLoser = pWinner.getOwner(), pLoser.getOwner()
            if eWinner == eBarbarian:
                return
            elif eLoser == eBarbarian:
                pLoserCiv = pBarbarianCiv
            else:
                pLoserCiv = instance(eLoser)
                pWinningCiv = instance(eWinner)
            if ( winnerInfo.getUnitClassType() == eCommandeerer
                 and loserInfo.getUnitCombatType() == eNaval
                 and loserInfo.getUnitClassType() != ePrivateer
                 and isChance(iCommandeeringProbability) ):
                pCaptured = pWinningCiv.get(PyPlayer).initUnit(loserInfo.getID(), pWinner.getX(), pWinner.getY())
                pCaptured.finishMoves()
                pCaptured.setDamage(iCaptureDamage, pWinningCiv.get(playerID))
                tStrings = pLoserCiv.getName(), loserInfo.getDescription()
                tColor = pHumanCiv == pWinningCiv, pHumanCiv == pLoserCiv
                addMessage(captureMessage, tStrings, tColor, getCoords(pWinner))[/B]
The code of course works the same. Its my preference to have the script defined as a function - preferably in another module. I'm also trying to keep your mod as clear as possible, so cluttering down the CvEventManager with thousands of lines of code isn't my objective.

in any case I was hoping to use a custom file like HistoricalEvents.py and make my stuff in there... If I did what would I need to import?
You import the module, of course. Like:
Code:
import HistoricalEvents
See my previous post on more details on how I've set up importing of my own modules in CvEventManager.

If asafs code conflicts with yours how will it be noticable? Should there be anythings to look out for?
I was under the impression that Asaf had edited CvMainInterface - not CvEventManager or CvGameUtils (the module I've added to).
 
well then that's great! where would I need to import the module to and what features would I need to import into MY module
 
well then that's great! where would I need to import the module to and what features would I need to import into MY module
It all depends on the mod. With your mod I've done all sorts of custom things, like the CivPlayer (and soon StoredData) module that handles different values commonly used to reference Civs/players and their teams, and the Utils module that collects helper functions and constants. The constants for the different mod features can be found in ModSettings - for your mod designing convenience. (I didn't know you were gonna start programming, so I tried to make things as accessible for you as possible.)

You import everything that you wanna use, basically. The only thing you need to import is CvPythonExtensions - thats the CivIV Python API. But there are lots of other modules - both part of CivIV and inherit to Python as such - that can be imported for various tasks. CvUtil and PyHelpers are two useful modules written by Firaxis while Pickle (and cPickle) is a commonly used module that is part of Python. But you only need to import those if you know what you wanna do with them.

Most of the time other modules are imported at the top of any given module, which makes sense. But there is one problem with this in a CivIV context. Because Python is loaded before XML on initialization, this also means that you can't access any of the settings defined with XML - on initialization. This means that all Python method invocations that are dependent on anything found in the XML entires need to be part of functions/methods or they won't return anything (or a default -1 value).

The root of the problem is that CvEventManager is loaded by the game on initialization - and if that module is importing your module on initialization - then your module won't recognize any XML stuff either. (Unless those lines of code are part of the body of some function - then they won't be fired until the function is called.)

So doing this in CvEventManager basically doesn't work:
Code:
from CvPythonExtensions import *
gc = CyGlobalContext()
eValue = gc.getInfoTypeForString("XML_VALUE")
But if the assignment statement is part of a function - and that function isn't run on initialization - then it would work:
Code:
def setValue()
    eValue = gc.getInfoTypeForString("XML_VALUE")
Now the eValue variable will indeed point to the value defined by the specified XML tag, but the problem is instead that the variable is only available within that function. So you can't access eValue. For this to work you need to use the global command:
Code:
def setValue()
    global eValue
    eValue = gc.getInfoTypeForString("XML_VALUE")
Now eValue can be accessed globally. But this is still very awkward for defining tons of constants, like I've been doing with your mod. (I'm assigning all values part of the game to constants, as a rule. This is of course not anything you have to do, but I find it makes things easier to develop and maintain.) So I figured I put all those assignment statements in another module and not import that module on initialization. So if we (as an exercise) create a module called Values and put this code in:
Code:
from CvPythonExtensions import *
gc = CyGlobalContext()
eValue = gc.getInfoTypeForString("XML_VALUE")
Then we don't import the module to CvEventManager on initialization, by putting the import statement - together with a global statement - into a function:
Code:
def setValues()
    global Values
    from Values import *
Now eValue will both point to the value we intend it to - and the constant is globally available in the entire CvEventManager module. But we of course need to call the setValues() function before this is true. So we need to know when its safe to do this - namely once the game is done initializing - because then all XML entries would have been parsed and the settings stored in the SDK.

If this doesn't make any sense at this point - don't worry about it. I've already found a solution for us - see the other post from the other day. It shows you exactly how to go about importing your own module(s). So don't bother with understanding everything related to importing modules into the Event Manager as it will only keep you from completing the task at hand.
 
I will get on to it in time then. (I understand most of what you said)

:rudolf::xmastree::xmas::c5happy:
 
I suggest you try your hand at this:

1. Add a script to the onBeginGameTurn() method in CvEventManager that spawns a unit on a specific game turn.
Spoiler :
hint: Note that CvEventManager imports the CvUtil module. This contains a helper function called spawnUnit(). The arguments are the UnitTypes value, a CyPlot instance and a CyPlayer instance. You call on the method like this:
Code:
CvUtil.spawnUnit(eUnitType, pPlot, pPlayer)
The variable names are of course arbitrary (examples).

Using this way of spawning a unit should be much easier than to figure out how CyPlayer.initUnit() works. You don't actually need to understand this at this point, as there are other options available. Like the PyPlayer.initUnit() method (in PyHelpers) that is much more useful!

2. Once you achieve the actual functionality you can move the script into a new module and define it as a function (taking the relevant data as arguments).
3. Import your module into CvEventManager.
4. In the place of the script you put a function call to the code in your module in the onBeginGameTurn() method in the Event Manager.

Good luck!
 
About import statements.

There are several ways to import a module. Firstly we use a simple import statement:
Code:
import MyModule
Now we can access everything (all names) in MyModule by calling it:
Code:
MyModule.variable
MyModule.function()
We can assign the module to a another name (other than its own):
Code:
import MyModule as Module
Now we can use the name Module to access MyModule:
Code:
Module.variable
Module.function()
Its also possible to only import some specific name() from a module:
Code:
from MyModule import name, function
Now we can access name(s) without calling the module:
Code:
name
function()
Note however that you can't have two names (variables/classes/functions) with the same name in any module, so there will be a conflict (one replaces the other) if both modules have defined the same name.

And finally we can simply import everything (all names) from a module and have access to them directly:
Code:
from MyModule import *
All names are now available without calling the module name:
Code:
name
function()
This last part is the reason why you can access the CivIV Python API directly, without using the name CvPythonExtensions.
 
ok well I should be able to cope with these...

btw, how are we going to handle the Roman Civil War (byzantium)?
Are we going to use an event, like The J did?
 
I was actually pondering this myself. You might as well forget about figuring out how the Event Manager works for now and tap into the random events interface instead. I guess we could figure it out together, then. :)

It makes sense to use an events setup already in place instead of making our own, not? The reason I've avoided modding random events myself is probably that it involves XML work. For which I have no stomach. :p

So with this said, I think that you should still work on your own module - and import it into the CvRandomEventsInterface (or whatever) module. If you import all the names you define directly into the main random events module by using the from and asterisk form it should work the same. Just a kind suggestion - it would be no extra work at all and you wouldn't have to bother with those thousands of lines of code already in the module.
Code:
from ScenarioEvents import *

In order to answer your actual question - I suggest you make a few minor historical/random/reoccurring events with Python elements (like unit and city spawns) first. Then we can tackle the actual Byzantium event together. :king: You might just be able to do this yourself, if you take this in strides.
 
I don't think I could do it myself but the working together seems promising... remember I can work my head around the XML (most of the time)
 
Ok, take a look at the XML for the random events. There are some Python related tags in there somewhere. These relate to points in the processing of random events where you can interrupt the game and run your own Python code. The returned boolean value of a condition check will for instance let you determine - with some Python expression - whether or not the event should trigger. At another juncture you're able to add your own effects - like units spawns.

What you put in the XML is probably the function names. Check out some tutorial for creating random events and it should probably make some sense to you with the Python knowledge you already have. If you can't figure something out I can have a look and see if I can't decipher what is supposed to go in.
 
Back
Top Bottom