Requesting following features

also, map restrictions are only really for one box, the flip code regards one box so thats 4 coords in total, not enough for a data structure :p (Well unless you think differently?)
Ok, I was assuming that you needed to define several boxes in order to define the area(s) you needed to control, but one single box is of course easy to achieve.

What are your thoughts on why the Byzantium -> constaninople code doesn't work?
I never did understand what the problem was, exactly...

(a question about that, if egypt founds Damasci will it be named the eygptian variant? I guess not as it is on the CityAcquiredAndKept callback Should be easy to hard code that in somewhere though!)
I'm counting on you to test these things throughly, but the way I set it up the special cases have priority over the names in the data-structure. Look again at the code and the documentation I supplied.

Spoiler :
nameCity() is calling renameCity() as its first order of business. The function actually exits with a return statement if a special case has happened, but I never actually got to test that.
 
I will test it!

Basically I did some tests with the ERE taking Byzantium off the barbs and then rome taking it and then the ERE taking it back and during those tests The ERE failed to change the name to Constantinople don't know if the roman change even works because when they take it the name is still the same. The weird thing is: It works with Damasci, if the Egyptians take that it becomes Damascus. That's the problem... I checked all the coords were correct and they are, and it has no reason it shouldn't work.. :confused:
 
The problem is most likely that this line isn't registering the ERE:
Code:
                if ePlayer == CivPlayer.playerID("Byzantium"):
Because there is no CivPlayer instance for the ERE before they actually spawn in mid-game, if I remember correctly. That is only added once that happens. So you basically can't simply test this by activating the player by adding units with the WB.

My best guess, anyway. Another likely issue would be the identifying string "Byzantium" above. If you're using something else in the XML tag ("CIVILIZATION_SOMETHING") it simply won't register, and I went by what XML files I could find on my computer.
 
I rthink it is byzantium I guess I will play a long test game for this :p
 
hmmm, Looking for a way to flip cities I came across this in Rebellion.py

pRebelCiv.get(CyPlayer).acquireCity(pCity, False, False)

However on searching for the acquireCity function in the api I couldn't find it...
1) where is it :p
2) what is needed to make it work?
 
actually I worked it out :p

Code:
##ERE Reinforce##
iNumCities = 4
iReinforceDate = 0 #Change!
tERETop = (0, 0) #Change!
tEREBottom = (0, 0) #Change!
reinforceMessage = "TXT_KEY_MODEVENTS_REINFORCE_MESSAGE"
pByzantium = instance(eByzantium)
iNumUnits = 3
def eventReinforce():
    if getNumPlayerCities(eByzantium) < iNumCities and isDate(iReinforceDate) and pByzantium.isAlive():
        for pCity in findAreaCities(tEREBottom, tERETop):
            pByzantium.get(CyPlayer).acquireCity(pCity, False, False)
            spawnCityGarrison(pByzantium, pCity, iNumUnits)
I moved the spawnCityGarrison() funtion from CustomFeatures to Helpers and added the 3rd parameter and of course changed it's use in all of it's uses to stop exceptions

edit: just made this function to make it easier :p
Code:
def flipCity(ePlayer, pCity):
        instance(ePlayer).get(CyPlayer).acquireCity(pCity, False, False)
 
This is from CyPlayer class:
VOID acquireCity (CyCity pCity, BOOL bConquest, BOOL bTrade)
void (CyCity* pCity, bool bConquest, bool bTrade)
So you need to invoke the method on a CyPlayer intance. The way I've fetched these objects in your mod can be seen on the line you were referencing yourself:
Code:
get(CyPlayer)
This method is part of the CivPlayer class and is invoked on a CivPlayer instance. One of those can be fetched with a large number of ways, but one I often end up using is:
Code:
instance(ePlayer)
So flipping pCity to the Roman player would then be:
Code:
instance(eRome).get(CyPlayer).acquireCity(pCity, True, False)
The True and False parameters indicate here that the city has been captured by conquest - not trade.

You could actually try to read all the documentation in the CivPlayer module, because that is a modding tool I developed and there is no reason why you shouldn't be using it also.
 
If all your events have this sort of thing:
Code:
iNumCities = 4
iReinforceDate = 0 #Change!
tERETop = (0, 0) #Change!
tEREBottom = (0, 0) #Change!
lERECities = list()
reinforceMessage = "TXT_KEY_MODEVENTS_REINFORCE_MESSAGE"
pByzantium = instance(eByzantium)
iNumUnits = 3
Then it could make sense to put all data into a data-structure.

edit: Nice going! This is proof that you actually understand what you're doing. :king:
 
so what is acquireCity(pCity, False, False)

if True, False is conquored and False, True is trade then what on earth does False, False mean?

You know if I was feeling adventurous I could make all this events into classes :p What sort of data structure

a note on classes: I find it great that with classes attributes are more easly obtained for example in my chess program (does do much right now :p) my check moves function that decides if a move is valid (it really is a beautiful program :p) uses classes to make things easier for example

Spoiler :

Code:
def checkMove(sTile, eTile, ePiece):
    sX, sY = getCoords(sTile)
    eX, eY = getCoords(eTile)
    eMoves = ePiece.Moves
    xMove = eX - sX
    yMove = eY - sY
    if ePiece.Name == "King":
        if xMove == eMoves["x"] or xMove == -1 * eMoves["x"] :
            if yMove == eMoves["y"] or yMove == -1 * eMoves["y"]:
                if hasBlack(sTile) and not hasBlack(eTile):
                    return True
                elif hasWhite(sTile) and not hasWhite(eTile):
                    return True
                else:
                    return False
            else:
                return False
        else:
            return False
    elif ePiece.Name == "Queen":
        if hasBlack(sTile) and not hasBlack(eTile):
            return True
        elif hasWhite(sTile) and not hasWhite(eTile):
            return True
        else:
            return False
    elif ePiece.Name == "Knight":
        pass
    elif ePiece.Name == "Rook":
        pass
    elif ePiece.Name == "Bishop":
        pass
    elif ePiece.Name == "Pawn":
        if not xMove == 1:
            if yMove == eMoves["y"] and xMove == eMoves["x"]:
                if hasBlack(sTile) and not hasBlack(eTile):
                    if hasBlack(sTile) and not hasWhite(eTile):
                        return True
                    else:
                        return False
                elif hasWhite(sTile) and not hasWhite(eTile):
                    if hasWhite(sTile) and not hasBlack(eTile):
                        return True
                    else:
                        return False
                else:
                    return False
            else:
                return False
        elif xMove == 1:
            if yMove == eMoves["y"]:
                if hasBlack(sTile) and hasWhite(eTile):
                    return True
                elif hasWhite(sTile) and hasBlack(eTile):
                    return True
                else:
                    return False
        else:
            return False

as you can see it makes it easier to use ePiece.name than to overcomplicate things and use something else or use tons of variables (as variables have to be called as KingName or QueenName it is far more efficient to use ePiece.Name)

hers the whole program if you are interested:
Spoiler :

Code:
##Chess##
##Classes, Functions, Constants##
#Classes
class Piece:
    def __init__(self):
        self.Name = ""
        self.sName = ""
        self.Value = 0
        self.Moves = {"y" : 0, "x" : 0, "xy" : 0, "-yx" : 0}
        self.Number = 0

class King(Piece):
    def __init__(self):
        self.Name = "King"
        self.sName = "Ki"
        self.Value = 100
        self.Moves = {"y" : 1, "x" : 1, "xy" : 1, "-yx" : 1}
        self.Number = 1

class Queen(Piece):
    def __init__(self):
        self.Name = "Queen"
        self.sName = "Q"
        self.Value = 20
        self.Moves = {"y" : 2, "x"  :2, "xy" : 2, "-yx" : 2}
        self.Number = 1

class Rook(Piece):
    def __init__(self):
        self.Name = "Rook"
        self.sName = "R"
        self.Value = 10
        self.Moves = {"y" : 2, "x" : 2, "xy" : 0, "-yx" : 0}
        self.Number = 2

class Bishop(Piece):
    def __init__(self):
        self.Name = "Bishop"
        self.sName = "B"
        self.Value = 10
        self.Moves = {"y" : 0, "x" : 0, "xy" : 2, "-yx" : 2}
        self.Number = 2

class Knight(Piece):
    def __init__(self):
        self.Name = "Knight"
        self.sName = "Kn"
        self.Value = 8
        self.Moves = {"kX" : {"x" : 3, "y" : 1}, "kY" : {"x" : 1, "y" : 3}}
        self.Number = 2

class Pawn(Piece):
    def __init__(self):
        self.Name = "Pawn"
        self.sName = "P"
        self.Value = 1
        self.Moves = {"y" : 1, "x" : 0, "xy" : 0, "-yx" : 0}
        self.Number = 8

class Board:
    def __init__(self):
        self.lenX = 8
        self.lenY = 8
        self.iTiles = self.lenX * self.lenY

#Functions
def setBoard():
    gameY = [range(1, (Board().lenX + 1)), \
                   range(1, (Board().lenX + 1)), \
                   range(1, (Board().lenX + 1)), \
                   range(1, (Board().lenX + 1)), \
                   range(1, (Board().lenX + 1)), \
                   range(1, (Board().lenX + 1)), \
                   range(1, (Board().lenX + 1)), \
                   range(1, (Board().lenX + 1))\
                 ]
    n = 0
    iNumPawn = 0
    iNumKing = 0
    iNumQueen = 0
    iNumRook = 0
    iNumBishop = 0
    iNumKnight = 0
    while n < Board().lenX:
        (gameY[1])[n] = [{Pawn().sName: "b"}, 0]
        (gameY[6])[n] = [{Pawn().sName: "w"}, 0]
        iNumPawn += 2
        n += 1
    n = 0
    while n < Board().lenY:
        if n == 0:
            (gameY[n])[0] = [{Rook().sName : "b"}, 0]
            (gameY[n])[1] = [{Knight().sName : "b"}, 0]
            (gameY[n])[2] = [{Bishop().sName : "b"}, 0]
            (gameY[n])[3] = [{Queen().sName : "b"}, 0]
            (gameY[n])[4] = [{King().sName : "b"}, 0]
            (gameY[n])[5] = [{Bishop().sName : "b"}, 0]
            (gameY[n])[6] = [{Knight().sName : "b"}, 0]
            (gameY[n])[7] = [{Rook().sName : "b"}, 0]
        else:
            (gameY[n])[0] = [{Rook().sName : "w"}, 0]
            (gameY[n])[1] = [{Knight().sName : "w"}, 0]
            (gameY[n])[2] = [{Bishop().sName : "w"}, 0]
            (gameY[n])[3] = [{Queen().sName : "w"}, 0]
            (gameY[n])[4] = [{King().sName : "w"}, 0]
            (gameY[n])[5] = [{Bishop().sName : "w"}, 0]
            (gameY[n])[6] = [{Knight().sName : "w"}, 0]
            (gameY[n])[7] = [{Rook().sName : "w"}, 0]
        iNumKing += 1
        iNumQueen += 1
        iNumRook += 2
        iNumBishop += 2
        iNumKnight += 2
        n += 7
    n = 2
    m = 0
    while n < Board().lenY - 2:
        while m < Board().lenX:
            (gameY[n])[m] = [{"0" : "0"}, 0]
            m += 1
        m = 0
        n += 1
    x = 0
    y = 0
    while y < Board().lenY:
        while x < Board().lenX:
            (gameY[y])[x][1] = (x + 1, y + 1)
            x += 1
        x = 0
        y += 1
    gameX = 0
    for iX in gameY:
        gameX += 1
    if iNumPawn == Pawn().Number * 2 and iNumKnight ==Knight().Number * 2 and iNumRook == Rook().Number * 2 and iNumBishop == Bishop().Number * 2 and iNumQueen == Queen().Number * 2 and iNumKing == King().Number * 2:
        if len(gameY) * gameX == Board().iTiles:
            return gameY

def displayGame(gameBoard):
    n = 9
    gameBoard.reverse()
    for y in gameBoard:
        n += -1
        print n, str(y[0][0])+",", str(y[1][0])+",", str(y[2][0])+",", str(y[3][0])+",", str(y[4][0])+",", str(y[5][0])+",", str(y[6][0])+",", y[7][0]
    print "        1          2            3            4            5           6           7            8"
    gameBoard.reverse()
    
def displayMessage(wPiece, lPiece, wPlayer, lPlayer, check = False, checkmate = False, stalemate = None):
    if check:
        print lPlayer, "is in check!"
    elif checkmate:
        print lPlayer, "is in check mate!"
        if loser == "Computer":
            print wPlayer, "has won the game!"
        else:
            print "Computer has won the game!"
    elif stalemate:
        print "A stalemate has occured, game ends!"
    else:   
        print wPlayer + "'s", wPiece, "has taken", lPlayer + "'s", lPiece + "!"

def checkMove(sTile, eTile, ePiece):
    sX, sY = getCoords(sTile)
    eX, eY = getCoords(eTile)
    eMoves = ePiece.Moves
    xMove = eX - sX
    yMove = eY - sY
    if ePiece.Name == "King":
        if xMove == eMoves["x"] or xMove == -1 * eMoves["x"] :
            if yMove == eMoves["y"] or yMove == -1 * eMoves["y"]:
                if hasBlack(sTile) and not hasBlack(eTile):
                    return True
                elif hasWhite(sTile) and not hasWhite(eTile):
                    return True
                else:
                    return False
            else:
                return False
        else:
            return False
    elif ePiece.Name == "Queen":
        if hasBlack(sTile) and not hasBlack(eTile):
            return True
        elif hasWhite(sTile) and not hasWhite(eTile):
            return True
        else:
            return False
    elif ePiece.Name == "Knight":
        pass
    elif ePiece.Name == "Rook":
        pass
    elif ePiece.Name == "Bishop":
        pass
    elif ePiece.Name == "Pawn":
        if not xMove == 1:
            if yMove == eMoves["y"] and xMove == eMoves["x"]:
                if hasBlack(sTile) and not hasBlack(eTile):
                    if hasBlack(sTile) and not hasWhite(eTile):
                        return True
                    else:
                        return False
                elif hasWhite(sTile) and not hasWhite(eTile):
                    if hasWhite(sTile) and not hasBlack(eTile):
                        return True
                    else:
                        return False
                else:
                    return False
            else:
                return False
        elif xMove == 1:
            if yMove == eMoves["y"]:
                if hasBlack(sTile) and hasWhite(eTile):
                    return True
                elif hasWhite(sTile) and hasBlack(eTile):
                    return True
                else:
                    return False
        else:
            return False
                

def getCoords(Tile):
    Tile = Tile[1]
    return Tile

def splitMoves(sTile, eTile):
    sX, sY = getCoords(sTile)
    eX, eY = getCoords(eTile)
    xMove = eX - sX
    yMove = eY - sY
    n = 0
    if xMove == yMove:
        pass
    Split = {"Moves":n}
    return Split

def hasBlack(Tile):
    if Tile[0][Tile[0].keys()[0]] == "b":
        return True
    else:
        return False

def hasWhite(Tile):
    if Tile[0][Tile[0].keys()[0]] == "w":
        return True
    else:
        return False
        
#Constants
gameBoard = setBoard()
Computer = "Computer"
Player = input("Player name (within ''): ")

##Game programming##
displayGame(gameBoard)
The horrible thing was I was unable to make a function to convert what I call the GameBoard instance ([y][x]) into coords so had to manually do it :rolleyes:


edit: changed the function :p
Code:
def flipCity(ePlayer, pCity, bConquored = False, bTrade = False):
        instance(ePlayer).get(CyPlayer).acquireCity(pCity, bConquored, bTrade)
 
within my code would pCity.isCapital() work (I'm pretty sure it would as it fits in the acquireCity which also requires a CyCity)
 
so what is acquireCity(pCity, False, False)

if True, False is conquored and False, True is trade then what on earth does False, False mean?
I guess it simply bypasses any special circumstances brought on by conquering cities (like decreasing city size, spawning free units for barbarians) and traded cities. But you could just experiment with it yourself. :D

You know if I was feeling adventurous I could make all this events into classes :p What sort of data structure
That is what I believe I originally suggested, but didn't have the time to setup for you.

If all events have the same sort of data, like coordinates, target players and whatnot, you could simply create a data structure for those values:
Code:
eventDataDict = {
#date, tCoords1, tCoords2, lCities, message, eTarget, iNumUnits
"My_Event": [ 45, (45, 46), (56, 75), lMyEventCities, myEventMessage, eByzantium, 5 ]
...
}
Then you could access the data for any event by using its string name as a key:
Code:
lSettings = eventDataDict["My_Event"]
The settings could then be assigned to actual local variables by using tuple assignment:
Code:
iDate, tCoords1, tCoords2, lCityList, message, ePlayer, iNumUnits = lSettings
Or for short:
Code:
iDate, tCoords1, tCoords2, lCityList, message, ePlayer, iNumUnits = eventDataDict["My_Event"]
Now, not all events uses the same values, so you could use dictionaries within dictionaries... :p

a note on classes: I find it great that with classes attributes are more easly obtained for example in my chess program (does do much right now :p) my check moves function that decides if a move is valid (it really is a beautiful program :p) uses classes to make things easier for example
Yeah, you're on to something here. :D I actually used this sort of Object-Oriented Programming with the CatapultConstruction module. And I would use it for the scenario events also, if I were you.

Regarding your chess program; you could put all those pieces into a data-structure also... There is seldom any need to use these elaborate if-trees that you have in your program.

The horrible thing was I was unable to make a function to convert what I call the GameBoard instance ([y][x]) into coords so had to manually do it :rolleyes:
What seems to be the problem, you mean?
 
within my code would pCity.isCapital() work (I'm pretty sure it would as it fits in the acquireCity which also requires a CyCity)
What? You can of course invoke any method belonging to the CyCity class on any CyCity instance.
 
well it is impossible AFAIK to take a list reference of say gameBoard[3][4] and turn it into coords of (4, 3) as if you were to do this: (gameBoard[4], gameBoard[3]) it would fail miserably...

I see what you mean so the code could be expressed as
Code:
##ERE Reinforce##
Data = {"Reinforce" : [4, 0, (0, 0), (0, 0), "TXT_KEY_MODEVENTS_REINFORCE_MESSAGE", instance(eByzantium), 3]} #this would of course be at the top :p
def eventReinforce():
    if getNumPlayerCities(eByzantium) < Data["Reinforce"][0] and isDate(Data["Reinforce"][1]) and Data["Reinforce"][5].isAlive():
        for pCity in findAreaCities(Data["Reinforce"][3], Data["Reinforce"][2]):
            if not pCity.isCapital():
                flipCity(eByzantium, pCity)
                spawnCityGarrison(Data["Reinforce"][5], pCity, Data["Reinforce"][6])
                addMessage(Data["Reinforce"][4], (), eWhite) #Blue?
A little hard to read though :p

I was just checking :p, where can I find the list of useable message colours again?

edit: oh, I see :p you of course extract the data from the Dictionary and assign to values :p not use the raw Data["Reinforce"][x] stuff :p
What is the benefit of doing it like this?
 
well it is impossible AFAIK to take a list reference of say gameBoard[3][4] and turn it into coords of (4, 3) as if you were to do this: (gameBoard[4], gameBoard[3]) it would fail miserably...
I'm still not quite sure what you're trying to achieve, but if you create a GameBoard class you could also have a GameBoardTile class, where each instance is a board tile. Each GameBoardTile instance would then have a pair of board coordinates as attributes, as well as a black/white (probably boolean) attribute, and a enumerated index value. These instances could be created dynamically at the start of the program, by using a script that generates these instances and stores them in a data-structure of some sort.

Then you could have a getCoords() method belonging to the GameBoardTile class, which returns the coordinate attribute of that instance. Problem solved?

I see what you mean so the code could be expressed as
No, the point would be to have all the data for all events in one single data-structure. At the top/bottom of the module, or even i a module of its own. Sorta like we did with the historical city sites, remember?

I was just checking :p, where can I find the list of useable message colours again?
In the API.
 
I realised what you meant with the data after I posted see the edit :lol:

At some point I will have to try your solution to my problem :p at the moment the problem is fixed by this (I needed it fixed to make the checkMoves work):
Code:
    while y < Board().lenY:
        while x < Board().lenX:
            (gameY[y])[x][1] = (x + 1, y + 1)
            x += 1
        x = 0
        y += 1

Thanks for the colours!
 
I adapted your function from your tutorial to make this :p:

Code:
def getCityRadiusTiles(pCity, bNaval = False):
        lPlots = list()
        if type(pCity) == type((0, 0)):
                pCity = getPlotCity(pCity)
        iCityX = pCity.getX()
        iCityY = pCity.getY()
        for iPlotX in range(iCityX - 1, iCityX + 2):
                for iPlotY in range(iCityY - 1, iCityY + 2):
                        pPlot = getPlot((iPlotX, iPlotY))
                        if not bNaval:
                                if isSpawnValid(pPlot) == False or pPlot.isCity(): continue
                                lPlots.append(pPlot)
                        else:
                                if not pPlot.isWater(): continue
                                lPlots.append(pPlot)
        return lPlots
and I made this for convenience :p:
Code:
def getPlotCity(tCoords):
       return getPlot(tCoords).getPlotCity()
The clever thing about the first function is that it can be supplied with a city or coords making it a lot more flexible (one of my better ideas :p)
using these for the second event (I am on a roll today :p especially with understanding and finding things in the API (second function all made form the API :D))

edit: ah forgot about that Naval bit (that swaps from returning land tiles to returning Sea tiles :p) there done!
 
edit: oh, I see :p you of course extract the data from the Dictionary and assign to values :p not use the raw Data["Reinforce"][x] stuff :p
What is the benefit of doing it like this?
Well, its mostly an issue of taste and design. I like to have the data in one place and the code in another. And I want my code to be as compact as possible. I can see the benefit with doing it the other way also, so you could go with whatever you like.

I adapted your function from your tutorial to make this :p:
After I wrote that tutorial I've learned that there is a even better way to iterate city tiles. From HistoricalCities.py:
Code:
        for iIndex in xrange(19):
                tAlternateSite = getCoords(pCity.getCityIndexPlot(iIndex))
The CyCity.getCityIndexPlot() method returns CyPlot instances corresponding to the map tiles inside the city's radius. All you need to do is to supply it a integer value 0-19. :king:

edit: The benefit of using this method is of course that basically the same looping of X and Y coordinates are done in C++ instead of Python, which is much, much faster.
 
easily replaced within my own function :p

In my code I use this:
Code:
spawnUnits(eRome, getIndex("Unit", "Quintrireme"), getRandomCoords(getCityRadiusTiles(tByzantium, True)), Round((x * iNumUnits)/3, True))
to spawn ships in a sea tile around byzantium... However I was rather annoyed that python has no round up function (the amount of ships is the minimum needed to transport the spawned troops so it needs to round the number up even if it should round down) so I resorted to my own Round(x, bUp) function :p:
Code:
def Round(x, bUp):
        """
        Rounds either up or down
        """
        if type(x) == type(1):
                x = float(x)
        iRoundBase = x - round(x)
        if iRoundBase < 0.5 and bUp:
                return int(round(x) + 1)
        elif iRoundBase >= 0.5 and not bUp:
                return int(round(x) - 1)
        else:
                return int(round(x))
much better :p it allows you to force a number to be rounded up no matter what (or always down if you wanted with bUp = False)
 
The way I've done rounding to the nearest integer is to add 0.5 to float values before converting to integer. Its a really slim solution to a - you're right - annoying problem.
 
there I fixed my function :p

Code:
def getCityRadiusTiles(pCity, bNaval = False):
        lPlots = list()
        if type(pCity) == type((0, 0)):
                pCity = getPlotCity(pCity)
        for iIndex in xrange(19):
                pPlot = getPlot(getCoords(pCity.getCityIndexPlot(iIndex)))
                if not bNaval:
                        if isSpawnValid(pPlot) == False or pPlot.isCity(): continue
                        lPlots.append(pPlot)
                else:
                        if not pPlot.isWater(): continue
                        lPlots.append(pPlot)
        return lPlots
that good?

btw feel free to use any of the functions you see today :p all are useful in some way :D
 
Back
Top Bottom