Need help with Python Code

Monaldinio

Prince
Joined
Jan 6, 2010
Messages
415
Location
Rostock
I would like to add a new wonder to my modmod, the AI-Pyramide (in German KI-Pyramide), maby you know it from Call to Power!

It removes all :mad: from the Cities

but

Each round there is a 5% chance that the Empire falls into anarchy for 4 rounds
and / or 20% of the cities split from the empire...

Error Screenshot

Spoiler :
attachment.php




PHP:
import random as rm    # Für randint, sample
import math            # Für ceil

from CvPythonExtensions import *

gc = CyGlobalContext()

# Schnittstelle zum EventManager
def handleAiWonder(pPlayer):
    # Wenn der Spieler nur eine Stadt hat, mache Anarchie
    if pPlayer.getNumCities() == 1:
        doAnarchie(pPlayer)
    # Abspaltung und Anarchie möglich
    else:
        doSeparation(pPlayer)

        doAnarchie(pPlayer)


class UnitData(object):
    def __init__(self, pUnit):
        self.unitType = pUnit.getUnitType()
        self.unitAIType = pUnit.getUnitAIType()
        self.pos = (pUnit.getX(), pUnit.getY())
        self.facingDirection = pUnit.getFacingDirection()
        
        self.promotions = [index for index in xrange(gc.getNumPromotionInfos())
                           if pUnit.isHasPromotion(index)]
        self.gameTurnCreated = pUnit.getGameTurnCreated()
        self.damage = pUnit.getDamage()
        self.level = pUnit.getLevel()
        self.experience = pUnit.getExperience()
        self.modifier = max(1, 100 + pUnit.getPlayer().getLevelExperienceModifier())
        self.name = pUnit.getNameNoDesc()
        self.leaderUnitType = pUnit.getLeaderUnitType()


    def toUnit(self, pNewPlayer):
        iX, iY = self.pos
        pUnit = pNewPlayer.initUnit(self.unitType, iX, iY,
                                    self.unitAIType,
                                    self.facingDirection)

        for index in self.promotions:
            pUnit.setHasPromotion(index, True)

        pUnit.setGameTurnCreated(self.gameTurnCreated)
        pUnit.setDamage(self.damage)

        pUnit.setLevel(self.level)

        # Erfahrung
        iNewModifier = max(1, 100 + pNewPlayer.getLevelExperienceModifier())
        pUnit.setExperience(max(0, (self.experience * self.modifier) / iNewModifier))

        pUnit.setName(self.name)
        pUnit.setLeaderUnitType(self.leaderUnitType)

        return pUnit
        

# Führt Anarchie durch, wenn dieser Fall eintritt
def doAnarchie(pPlayer):
    if rm.randint(1, 1) == 1: # 5% Change auf Anarchie für 5
                               # Runden.
        pPlayer.changeAnarchyTurns(max(0, 5 - pPlayer.getAnarchyTurns()))


# Führt die Abspaltung aus.
def doSeparation(pPlayer):
    if rm.randint(1, 1) == 1: # (5% Change auf Abspaltung
        # Es kommt zur Abspaltung, berechne Anteil der
        # abzuspaltenden Städte.
        # Soll immer aufgerundet werden? Also auf jeden Fall mindestens
        # eine Stadt sich abspalten?
        # Soll sich die Hauptstadt auch abspalten können?

        # Ermittel die Städte, die sich abspalten sollen
        possibleCities = choosePossibleSepCities(pPlayer)
        k = math.ceil(len(possibleCities) / 5.0) # Aufrunden
        sepCities = rm.sample(possibleCities, k)

        pNewPlayer = gc.getBARBARIAN_PLAYER()

        processSeparation(pPlayer, pNewPlayer, sepCities)


# Ermittelt alle Städte, die für eine Abspaltung in Frage kommen können.
def choosePossibleSepCities(pPlayer):
    def cityFilter(pCity):
        if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_KI_PYRAMIDE")):
            return False

        return True
    
    
    bReversed = False
    pCity, iterOut = pPlayer.firstCity(bReversed)

    cities = []
    while not iterOut is None:
	if cityFilter(pCity):
		cities.append(pCity.getID())

        pCity, iterOut = pPlayer.nextCity(iterOut, bReversed)

    return cities

# Spaltet die betreffenden Städte von der alten Civ ab.
def processSeparation(pPlayer, pNewPlayer, cities):
    bConquest = True
    bTrade = False
    bUpdatePlotGroups = True  # ?
    for iCityID in cities:
        pCity = pPlayer.getCity(iCityID)

        # Daten von überlaufenden Einheiten sichern.
        unitDatas = seperateUnits(pCity)

        # Stadt überlaufen lassen        
        pNewPlayer.acquireCity(pCity, bConquest, bTrade, bUpdatePlotGroups)

        # Neue Einheiten erzeugen
        for unitData in unitDatas:
            unitData.toUnit(pNewPlayer)



def seperateUnits(self, pCity):
    # Nur Einheiten des Spielers werden berücksichtigt.
    # Transportierte Einheiten werden nicht berücksichtigt.
    def unitFilter(self, pUnit):
        if pUnit.getOwner() != pCity.getOwner():
            return False
        if pUnit.isCargo():
            return False
        

        return True
    
    pPlot = pCity.plot()

    possibleUnitIDs = set((index for index in xrange(pPlot.getNumUnits())
                           if unitFilter(pPlot.getUnit(index))) )

    # Nur 50% der Einheiten sollen übertreten
    # Falls Transportschiffe dabei sind, so wird deren Ladung gelöscht werden.
    k = math.ceil(len(possibleUnitIDs) / 2.0) # Aufrunden
    sepUnitIDs = rm.sample(possibleUnitIDs, k)

    noSepUnitIDs = possibleUnitIDs.difference(sepUnitIDs)

    transportIDs = []
    for noSepUnitID in noSepUnitIDs:
        pLoopUnit = pPlot.getUnit(noSepUnitID)
        if pLoopUnit.hasCargo():
            transportIDs.append(noSepUnitID)  # Transporter werden zu letzt gelöscht.
            continue

        pLoopUnit.kill(False, pLoopUnit.getOwner())

    # Transporteinheiten löschen
    # Bemerkung: Falls auch transportierte Einheiten überlaufen sollen, so
    #            muss hier darauf aufgepasst werden, sie erst aus der Transporteinheit
    #            zu entfernen.
    for transportID in transportIDs:
        pLoopUnit = pPlot.getUnit(transportID)
        pLoopUnit.kill(False, pLoopUnit.getOwner())

    return (UnitData(pPlot.getUnit(index)) for index in sepUnitIDs)
 

Attachments

  • Civ4ScreenShot0511.JPG
    Civ4ScreenShot0511.JPG
    200.7 KB · Views: 194
You seem to be trying to call the cityFilter method on a NoneType object, i.e. the argument isn't a city as expected. Not sure how this could've happened. I usually use the PyPlayer.getCityList() method from the PyHelpers module to get a list of all cities, maybe you should try the same (I say this because it seems to hint at a larger issue with accessing player cities).

To get rid of the immediate issue, I suggest you include additional checks into the cityFilter local method to exclude cases where pCity isn't a city object, such as
Code:
if pCity is None:
        return False
 
Okay, here's how I would do it (changes/additions in red, removed parts in green). I personally consider it awkward to use the iterator instead of using PyHelpers to get the whole city list at once, so I added that part too.

Code:
import random as rm    # Für randint, sample
import math            # Für ceil
[COLOR="Red"]import PyHelpers[/COLOR]

from CvPythonExtensions import *

gc = CyGlobalContext()
[COLOR="Red"]PyPlayer = PyHelpers.PyPlayer[/COLOR]

# Schnittstelle zum EventManager
def handleAiWonder(pPlayer):
    # Wenn der Spieler nur eine Stadt hat, mache Anarchie
    if pPlayer.getNumCities() == 1:
        doAnarchie(pPlayer)
    # Abspaltung und Anarchie möglich
    else:
        doSeparation(pPlayer)

        doAnarchie(pPlayer)


class UnitData(object):
    def __init__(self, pUnit):
        self.unitType = pUnit.getUnitType()
        self.unitAIType = pUnit.getUnitAIType()
        self.pos = (pUnit.getX(), pUnit.getY())
        self.facingDirection = pUnit.getFacingDirection()
        
        self.promotions = [index for index in xrange(gc.getNumPromotionInfos())
                           if pUnit.isHasPromotion(index)]
        self.gameTurnCreated = pUnit.getGameTurnCreated()
        self.damage = pUnit.getDamage()
        self.level = pUnit.getLevel()
        self.experience = pUnit.getExperience()
        self.modifier = max(1, 100 + pUnit.getPlayer().getLevelExperienceModifier())
        self.name = pUnit.getNameNoDesc()
        self.leaderUnitType = pUnit.getLeaderUnitType()


    def toUnit(self, pNewPlayer):
        iX, iY = self.pos
        pUnit = pNewPlayer.initUnit(self.unitType, iX, iY,
                                    self.unitAIType,
                                    self.facingDirection)

        for index in self.promotions:
            pUnit.setHasPromotion(index, True)

        pUnit.setGameTurnCreated(self.gameTurnCreated)
        pUnit.setDamage(self.damage)

        pUnit.setLevel(self.level)

        # Erfahrung
        iNewModifier = max(1, 100 + pNewPlayer.getLevelExperienceModifier())
        pUnit.setExperience(max(0, (self.experience * self.modifier) / iNewModifier))

        pUnit.setName(self.name)
        pUnit.setLeaderUnitType(self.leaderUnitType)

        return pUnit
        

# Führt Anarchie durch, wenn dieser Fall eintritt
def doAnarchie(pPlayer):
    if rm.randint(1, 1) == 1: # 5% Change auf Anarchie für 5
                               # Runden.
        pPlayer.changeAnarchyTurns(max(0, 5 - pPlayer.getAnarchyTurns()))


# Führt die Abspaltung aus.
def doSeparation(pPlayer):
    if rm.randint(1, 1) == 1: # (5% Change auf Abspaltung
        # Es kommt zur Abspaltung, berechne Anteil der
        # abzuspaltenden Städte.
        # Soll immer aufgerundet werden? Also auf jeden Fall mindestens
        # eine Stadt sich abspalten?
        # Soll sich die Hauptstadt auch abspalten können?

        # Ermittel die Städte, die sich abspalten sollen
        possibleCities = choosePossibleSepCities(pPlayer)
        k = math.ceil(len(possibleCities) / 5.0) # Aufrunden
        sepCities = rm.sample(possibleCities, k)

        pNewPlayer = gc.getBARBARIAN_PLAYER()

        processSeparation(pPlayer, pNewPlayer, sepCities)


# Ermittelt alle Städte, die für eine Abspaltung in Frage kommen können.
def choosePossibleSepCities(pPlayer):
    def cityFilter(pCity):
        [COLOR="Red"]if pCity is None:
            return False[/COLOR]

        if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_KI_PYRAMIDE")):
            return False

        return True
    
    
    [COLOR="Green"]#bReversed = False
    #pCity, iterOut = pPlayer.firstCity(bReversed)

    #cities = []
    #while not iterOut is None:
    #if cityFilter(pCity):
        #cities.append(pCity.getID())

        #pCity, iterOut = pPlayer.nextCity(iterOut, bReversed)[/COLOR]

    [COLOR="Red"]cities = []
    for city in PyPlayer(pPlayer.getID()).getCityList():
        pCity = city.GetCy()

        if cityFilter(pCity):
            cities.append(pCity.getID())[/COLOR]

    return cities

# Spaltet die betreffenden Städte von der alten Civ ab.
def processSeparation(pPlayer, pNewPlayer, cities):
    bConquest = True
    bTrade = False
    bUpdatePlotGroups = True  # ?
    for iCityID in cities:
        pCity = pPlayer.getCity(iCityID)

        # Daten von überlaufenden Einheiten sichern.
        unitDatas = seperateUnits(pCity)

        # Stadt überlaufen lassen        
        pNewPlayer.acquireCity(pCity, bConquest, bTrade, bUpdatePlotGroups)

        # Neue Einheiten erzeugen
        for unitData in unitDatas:
            unitData.toUnit(pNewPlayer)



def seperateUnits(self, pCity):
    # Nur Einheiten des Spielers werden berücksichtigt.
    # Transportierte Einheiten werden nicht berücksichtigt.
    def unitFilter(self, pUnit):
        if pUnit.getOwner() != pCity.getOwner():
            return False
        if pUnit.isCargo():
            return False
        

        return True
    
    pPlot = pCity.plot()

    possibleUnitIDs = set((index for index in xrange(pPlot.getNumUnits())
                           if unitFilter(pPlot.getUnit(index))) )

    # Nur 50% der Einheiten sollen übertreten
    # Falls Transportschiffe dabei sind, so wird deren Ladung gelöscht werden.
    k = math.ceil(len(possibleUnitIDs) / 2.0) # Aufrunden
    sepUnitIDs = rm.sample(possibleUnitIDs, k)

    noSepUnitIDs = possibleUnitIDs.difference(sepUnitIDs)

    transportIDs = []
    for noSepUnitID in noSepUnitIDs:
        pLoopUnit = pPlot.getUnit(noSepUnitID)
        if pLoopUnit.hasCargo():
            transportIDs.append(noSepUnitID)  # Transporter werden zu letzt gelöscht.
            continue

        pLoopUnit.kill(False, pLoopUnit.getOwner())

    # Transporteinheiten löschen
    # Bemerkung: Falls auch transportierte Einheiten überlaufen sollen, so
    #            muss hier darauf aufgepasst werden, sie erst aus der Transporteinheit
    #            zu entfernen.
    for transportID in transportIDs:
        pLoopUnit = pPlot.getUnit(transportID)
        pLoopUnit.kill(False, pLoopUnit.getOwner())

    return (UnitData(pPlot.getUnit(index)) for index in sepUnitIDs)
 
random.sample(possibleCities, k) requires k to be an integer, but the floor() method returns a float. Pass int(k) instead of k as argument.
 
self.separateUnits(pCity) instead of separateUnits(pCity).
 
Back
Top Bottom