Celtic Mod v2.0 Release

Thank you so much, I implemented the flip code. It flipped the cities, but they were lost again to barbarians, so I'm going to have to use the defensive unit code as well (I'll test that later today).

As for Oslo and Stockholm, I'm having them spawn at their founding dates (according to wikipedia)...that's enough to keep the english from going there (I also had to found two in northern scandinavia...i'm going to have to do copenhangen as wel...sigh...silly english).

I've decided to solve my russia problem like rhye did for the romans and i did for the celts...simply spawn 3-5 indy cities on the border at intervals so they're there before the Germans get there...

One other thing occurred to me, as I was on the Irish weekend, singing the rebel songs. I'd like to script two unit spawns, but I'm not sure how to allow them to spawn within english borders.

If the English are in control of Ireland by these times (they should be):

I'd like to spawn five Celtic pikemen (even though Celtia is tecnhically dead) at 49,55 in 1798 (United Irishmen rising in Wexford) and seven Celtic riflemen at 49,56 in 1916 (the Easter Rising). I'm not sure how to get the units to spawn within english cultural borders and to ensure that war exists between them (I imagine if I spawn them, Celtia exists, but war will then move them to a free tile outside the cultural borders...I figure these were the two big rebellions (but there was one every 50 years or so for centuries) that had slim chances of success. if they could capture a city Celtia would be back on the map, no?

In reality they failed rather definitively, but I figure those would be historically accurate times for the possible re-emergence of a dead celtia.

In any event, today I'll test the defensive unit code and script the russian cities. I don't think there's anything else for me to do after that, pending your answer about the Irish rebellions.
 
I'm not so sure about this turning into a historical scenario of sorts, but you're the designer so its up to you. Because nothing is really impossible to do.

The functions set up by Rhye for spawning units and cities aren't at all suited for anything like this, as they rather mimic the way barbarians spawn in the core game. But there's nothing forcing you to use Rhye's functions, so you could either make your own or find another one to use instead. But I'll just solve this one for you.

If you look at the function used in Barbs.py for spawning units, it looks like this:
Code:
[COLOR="DarkRed"]        def spawnUnits(self, iCiv, tTopLeft, tBottomRight, iUnitType, iNumUnits, iTurn, iPeriod, iRest, function, iForceAttack):[/COLOR]
                if (iTurn % iPeriod == iRest):
                        dummy, plotList = [COLOR="DarkRed"]utils.squareSearch( tTopLeft, tBottomRight, function, [] )[/COLOR]
                        if (len(plotList)):
                                rndNum = gc.getGame().getSorenRandNum(len(plotList), 'Spawn units')
                                result = plotList[rndNum]
                                if (result):
[COLOR="DarkRed"]                                        self.makeUnit(iUnitType, iCiv, result, iNumUnits, iForceAttack)[/COLOR]
What the function spawnUnits() actually does is that it selects a random tile within the coordinates (basically an "area" like core or the normal one) fed into the function. To determine whether or not there is a permitted tile available the spawnUnits() uses another function called utils.squareSearch(). This is why your Celtic rebels would fail to materialize, as anything with cultural borders would be of limits.

But if a tile is actually found it calls upon another function called makeUnit() to spawn the actual unit:
Code:
[COLOR="DarkRed"]        def makeUnit(self, iUnit, iPlayer, tCoords, iNum, iForceAttack):[/COLOR]
                'Makes iNum units for player iPlayer of the type iUnit at tCoords.'
                for i in range(iNum):
                        player = gc.getPlayer(iPlayer)
                        if (iForceAttack == 0):
 [COLOR="DarkRed"]                               player.initUnit(iUnit, tCoords[0], tCoords[1], UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)[/COLOR]
                        elif (iForceAttack == 1):
[COLOR="DarkRed"]                                player.initUnit(iUnit, tCoords[0], tCoords[1], UnitAITypes.UNITAI_ATTACK, DirectionTypes.DIRECTION_SOUTH)            [/COLOR]                      
                        elif (iForceAttack == 2):
[COLOR="DarkRed"]                                player.initUnit(iUnit, tCoords[0], tCoords[1], UnitAITypes.UNITAI_ATTACK_SEA, DirectionTypes.DIRECTION_SOUTH)[/COLOR]
What the "if/elif" statements do is assign different Unit AI Types to the unit, but thats really nothing you'd need to think about. (Defensive units mostly will set themselves to defend and offensive units will revert to attack.) The unit itself is actually created with the function(s) called CyPlayer.initUnit().

So, for you to achieve any imaginable Unit spawn you could just use the makeUnit() function instead of spawnUnit(). Or, if you want to, you could skip even this and use the function CyPlayer.initUnit() directly. Two examples:
Code:
self.makeUnit(con.iRifleman, iCeltia, [49, 56], 7, 0)
...or (more directly):
Code:
pCeltia.initUnit(con.iRifleman, 49, 56, UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
The second option would only produce one unit though, so you'd have to know how to loop it seven times (or you could just copy-paste the function seven times on consecutive lines, but then you wouldn't be able to show your code to anyone :p). So most of the times there is a point in using one of the functions set up by Rhye. :king:
 
Yeah, you also need some way of knowing if England controls Ireland, and the easiest way to determine this could be to check if the target tile (for the unit spawn) is owned by the English. You could try this code:
Code:
if (gc.getMap().plot([COLOR="DarkRed"]iX[/COLOR], [COLOR="DarkRed"]iY[/COLOR]).getOwner() == con.iEngland):
You'd have know where to put this line and what amount of indentation to use though. You replace the variables iX and iY with the actual coordinates, of course. (Or you could actually use variables, so that you don't have to type the same values over and over again.)
Then you need to check whether Celtia is alive, because you wouldn't wanna give free unit to an active Celtic player, right?
Code:
if (not pCeltia.isAlive()):
edit: Oh, you do remember how to make a Civ declare war on another, right? In case you have forgotten:
Code:
teamCeltia.declareWar(con.iEngland, False, -1)
Also, if you don't wanna keep typing con.iEngland you could just define the variable iEngland = con.iEngland at the beginning of Barbs.py.
 
As for Oslo and Stockholm, I'm having them spawn at their founding dates (according to wikipedia)...that's enough to keep the english from going there (I also had to found two in northern scandinavia...i'm going to have to do copenhangen as wel...sigh...silly english).
My point would be that Birka would be located on the same tile as Stockholm and Roskilde would become Copenhagen. And Oslo was named Christiania way back when.

The dates would then be AD 790 for Birka/Stockholm, AD 990 for Roskilde/Copenhagen and AD 1000 for Christiania/Olso.
 
heh...I have Oslo spawn as Christiania already, lol...I may not know as much scandinavian history as Irish, but I have some odds and ends... ;-)

I think you're right about the historical mod thing. It would really be a change that is out of place. I'm going to tinker with the city flipping and try to strengthen Russia a bit in the early years and beyond that, consider the next version a final release.

Then I'll start looking at the multiplayer version and see if the same stuff (celts, uhv's, etc) can be added in a similar fashion (I don't know how similar the multiplayer version is to the single player version).
 
two final questions...which impassable terrain is supposed to change when the vikings pop up? I don't see anything change for pre-astronomy passage.

Second, is there any way to reveal a large block of map to the Americans on spawn? It really doesn't make sense that the US doesn't know of Europe or the old world in the 1700s...
 
(49, 62) becomes coast instead of ocean when the Vikings learn Compass. It's in onTechAcquired in CvRFCEventHandler.py, as the following block:
Code:
		if (argsList[0] == con.iCompass):
			if (iPlayer == con.iVikings):
				gc.getMap().plot(49, 62).setTerrainType(con.iCoast, True, True)

The appropriate function to reveal/hide a piece of terrain is setRevealed; here's a line I used for my patch to hide stuff the debug catapult reveals:
Code:
gc.getMap().plot(0, 0).setRevealed(iOwner, False, True, -1)
(That first "False" should be "True" if you want to reveal instead of hiding)

That needs to be called for each plot hidden/revealed, so you'd want to set up some loops.
Of course, it also doesn't make much sense when America spawns several techs ahead of anybody else.
 
also...how does this look? I wanted to spawn several cities for the russians, but they are spaced such that they may not automatically flip, and may indeed wind up getting snatched by the Germans or Greeks. I figured I'd do a flip-script similar to the english one for the russians, but it's not just the minors this time. I referenced iGermany and iGreece...will this script work to flip those cities as long as the owner is one of those three? Also, how do I get it to flip the units inside with the cities for these scripts rather than kill them?

#secessions to Russia
pRussia = gc.getPlayer(con.iRussia)
if (pRussia.isAlive() and not pRussia.isHuman()):
if (iGameTurn == con.i1730AD):
utils.flipCity(lMinsk, True, True, con.iRussia, lMinors)
utils.flipCity(lKiev, True, True, con.iRussia, lMinors)
utils.flipCity(lPeter, True, True, con.iRussia, lMinors)
utils.flipCity(lVilnius, True, True, con.iRussia, lMinors)
utils.flipCity(lOdessa, True, True, con.iRussia, lMinors)

#secessions to Russia
pRussia = gc.getPlayer(con.iRussia)
if (pRussia.isAlive() and not pRussia.isHuman() and not pGermany.isHuman()):
if (iGameTurn == con.i1730AD):
utils.flipCity(lMinsk, True, True, con.iRussia, iGermany)
utils.flipCity(lKiev, True, True, con.iRussia, iGermany)
utils.flipCity(lPeter, True, True, con.iRussia, iGermany)
utils.flipCity(lVilnius, True, True, con.iRussia, iGermany)
utils.flipCity(lOdessa, True, True, con.iRussia, iGermany)

#secessions to Russia
pRussia = gc.getPlayer(con.iRussia)
if (pRussia.isAlive() and not pRussia.isHuman() and not pGreece.isHuman()):
if (iGameTurn == con.i1730AD):
utils.flipCity(lMinsk, True, True, con.iRussia, lGreece)
utils.flipCity(lKiev, True, True, con.iRussia, lGreece)
utils.flipCity(lPeter, True, True, con.iRussia, lGreece)
utils.flipCity(lVilnius, True, True, con.iRussia, lGreece)
utils.flipCity(lOdessa, True, True, con.iRussia, lGreece)
 
I actually don't need to flip the existing units if I could get the defensive unit code to work. Pasting what you posted didn't spawn the units (or return an error), but I'm not sure if I'm supposed to be plugging in coordinates or names there anywhere. Looking at it, it appears that it is code to make the defensive spawn happen whenever england or france flips a city (unless I'm reading it wrong, which I probably am)
 
oh, so rather than list them separately, I've created a group, lCentrals comprised of lMinors, Germany and Greece...however, I'm not sure how to execute the script only if the civ in question is minor, since it's possible for each of the 5 cities to be owned by a different civ, one of which might be human (germans or greeks)
 
It could be problematic to use the flipCity function for other than cities that you've already spawned yourself, because you can't predict where they are built. I'd suggest another method entirely - something along the lines of a spawn zone. Perhaps its possible to use the default one since you'd wanna flip cities inside the Russians Core Area anyways, right? I'll have a look after work. Otherwise I might just make another custom function for additional spawn areas for Civs already spawned.

Looking at your code you could do without repeating some line of code. This bit could only be used once:
Code:
                [COLOR="DarkOliveGreen"]pRussia = gc.getPlayer(con.iRussia)[/COLOR]
                if (pRussia.isAlive() and not pRussia.isHuman() and not pGermany.isHuman()):
Also, the first line could be defined at the start of the file (if you like), together with iRussia = con.iRussia.

But you don't need to repeat anything (as long as everything happens on the same game turn) if you put all the Civs concerned into a list - just line lMinors. So, you could add this line of code along the other lists defined in Barbs.py:
Code:
lMajors = [iGermany, iGreece]
You could just add other possible culprits also, like iNetherlands or iVikings, if you like. Then you use this list in the flipCity() function, like:
Code:
                                utils.flipCity(lMinsk, True, True, con.iRussia, [COLOR="Red"]lMajors[/COLOR])
The other settings or arguments are, as defined on the "def flipCity(...)" line in RFCUtils.py, 1. whether or not the city should be aquired by force (True or False) and 2. whether or not you wanna kill all the units inside the city (True or False). I don't believe the default flipCity() function can be used to also flip units inside the city though, but one could always code that oneself...

This is how you should spawn the garrisons, by the way:
Code:
createGarrisons(lMinsk, iRussia, 2):
This gives the Russian Civ 2 default defensive units on the location defined in lMinsk, as an example.

How to make Civs share world maps I wouldn't know right now, but you are absolute right on the issue, so I actually think I'll take a closer look at it. It smells like a custom function to me... :mischief:
 
however, I'm not sure how to execute the script only if the civ in question is minor, since it's possible for each of the 5 cities to be owned by a different civ, one of which might be human (germans or greeks)
So, what you pretty much need is a function that lets you flip cities in a pre-defined area that are not controlled by the human player - for a Civ not controlled by a human player.

To use the flipCity() function really seems suboptimal. So I'll just make something along the lines of cusom.flipArea() for you instead.

But if you're not going the historical scenario route, why flip any cities to the Russians? :crazyeye:
 
I want to flip them because I get the same result every single time...a german russia with a Moscow city-state...there's basically no Russia in the game then. I just want to make sure that they get into the Belarus/Ukraine area so that they might actually get to do something. I only spawned the cities mentioned above, so I don't mind flipping just them, as long as they are not human controlled.
 
hm...I'm not sure about the syntax, but couldn't I simply call for the flip of those five specific cities as long as russia and the city owner are both are not human? perhaps something like


#secessions to Russia
pRussia = gc.getPlayer(con.iRussia)
if (pRussia.isAlive() and not pRussia.isHuman() and not iOwner.isHuman()):
if (iGameTurn == con.i1730AD):
utils.flipCity(lMinsk, True, True, con.iRussia, iOwner)
utils.flipCity(lKiev, True, True, con.iRussia, iOwner)
utils.flipCity(lPeter, True, True, con.iRussia, iOwner)
utils.flipCity(lVilnius, True, True, con.iRussia, iOwner)
utils.flipCity(lOdessa, True, True, con.iRussia, iOwner)


I don't know if that will make sense to the computer, or how I should define iOwner to mean a non-human owner of the cities in question...
 
hm...I'm not sure about the syntax, but couldn't I simply call for the flip of those five specific cities as long as russia and the city owner are both are not human? perhaps something like


#secessions to Russia
pRussia = gc.getPlayer(con.iRussia)
if (pRussia.isAlive() and not pRussia.isHuman() and not iOwner.isHuman()):
if (iGameTurn == con.i1730AD):
utils.flipCity(lMinsk, True, True, con.iRussia, iOwner)
utils.flipCity(lKiev, True, True, con.iRussia, iOwner)
utils.flipCity(lPeter, True, True, con.iRussia, iOwner)
utils.flipCity(lVilnius, True, True, con.iRussia, iOwner)
utils.flipCity(lOdessa, True, True, con.iRussia, iOwner)


I don't know if that will make sense to the computer, or how I should define iOwner to mean a non-human owner of the cities in question...
First of all, you seem to have grasped the indentation business now. That's good! :goodjob:

The utils.flipCity() function is used for secession and for congresses, so it's not exactly what you need. If you you can hold on awhile I will make a custom function with settings (arguments) for pretty much anything one could need for making "historical" scenarios, like restricting the flip to AIs only. But in the meanwhile we could bring some order to your code.

Ok, here goes. I guess that you want the iOwner variable to hold the index number of all the Civs that could possibly be owners of the cities in question? The name you've chosen for this variable indicates to me that its an integer value, hence the name iOwner. What you need is a list (or all tuple) variable instead. So that would be called lOwner if we're gonna keep to the way Python is usually done. This list or variable could be defined something along the lines of:
Code:
lOwner = [iGermany, iVikings, iNetherlands, iTurkey]
(Or whatever.)

So, before you call on the utils.flipCity() function you wanna check if the integer values (iGermany, iViking and so on) inside the list variable (lOwner) correspond to the human player. A good start would be to identify the human player, then. First order of business could be to set up a variable (of the integer kind) that holds the index number of the human player, by using the utils.getHumanID() function supplied by Rhye:
Code:
iHuman = utils.getHumanID()
Now you check if any of the integer values in your list variable corresponds to this value. You do this by looping the contents of the list and comparing each of the values to "iHuman". And if you find the human ID in the list, you remove that entry from the list before going forward and calling on the flipCity() function.
Code:
numCivs = len(lOwner)
for num in range (numCivs):
        iCiv = lOwner[num]
        if (iCiv == iHuman):
                lOwner.remove(iCiv)
                break
What this code does is that it creates as many loops as there are entries in lOwner, checking the contents of the list one by one.

First we retrieve the number of entries with the command "len" and assign this value to the variable "numCivs". Then you loop the variable "num" as many times as the value of "numCivs". "num" will thus have the value corresponding to the active loop, so on the first loop it will be 0 (zero), on the second it will be 1, and then 2, and so on. So if the list has has 5 entries, then num will get the values 0-4 in that order.

Next the variable "iCiv" will be set to the "num" number entry in the list. So on the first loop "iCiv" will be entry #0 (the first one), on the next loop #1, and the third entry #2, and so on until the loop cycle is completed.

Then we use an "if" statement to see whether "iCiv" is a the human player, by comparing it to "iHuman" (see above). And if so, we take that entry out from the list with the command "remove" - and we quit the entire loop cycle with the command "break" as we've already found the human player. (No need to run any more checks.)

Note that this code will not be sufficient for you multiplayer version, since the lOwner list will actually get shortened by removing the human ID. Because, there could also be another human lurking in there, and the following entry would get a free pass. Also, the last loop wouldn't have any value to compare since one entry would be missing.

But when you get around to these issues I'll have a better function ready you could use. :king:

Also, the solution above would still be insufficient if say Rome has survived and occupies those parts of Russia. You'd have to enter pretty much all the Civs into the lOwner list to avoid those instances... It doesn't seem all too practical, now does it? :rolleyes:
 
...you could also use the PyPlayer.isHuman() function to check whether or not an entry in lOwners is the human player. Since its a PyPlayer function it requires you to fetch a "player instance" first. (That would be the actual Civ and not its index number. Obviously there would be a difference.)

You've already done this with CyGlobalContext.getPlayer() (commonly abbreviated to gc.getPlayer() as it states "gc = CyGlobalContext" at the start of Barbs.py). So the code above could also work with this setup:
Code:
numCivs = len(lOwner)
for num in range (numCivs):
        iCiv = lOwner[num]
        pCiv = gc.getPlayer(iCiv)
        if (pCiv.isHuman() == True):
                lOwner.remove(iCiv)
                break
It could be noted that you've used an abbreviated version of the "if" statement before, because the " == True" part doesn't really add anything other than a bit of clarity to the code.

By the way, I've concisely tried to make my code above as clear as possible, but it could also be condensed so that it takes up fewer lines:
Code:
for n in range (len(lOwner)):
        if (gc.getPlayer(lOwner[n]).isHuman()):
                lOwner.remove(lOwner[n])
                break
Kinda blows your mind, eh? :eek: I got rid of the variables "numCivs", "iCiv" and "pCiv" entirely and I abbreviated "num" to simply "n". Since these variables aren't constants (those would be the ones you recognize from Consts.py) they are mostly in the code for clarity anyway. (Also with a longer code it would be less to actually type.) It really does get blurred up though, not? :crazyeye:
 
so at the top in the definitions I'd put


iHuman = utils.getHumanID()
lOwner = [iGermany, iVikings, iNetherlands, iTurkey, iRome, iGreece, iEngland, iFrance, iSpain]





and be sure to define each "i" civ as well.

Then, down farther, I'd put




numCivs = len(lOwner)
for num in range (numCivs):
iCiv = lOwner[num]
if (iCiv == iHuman):
lOwner.remove(iCiv)
break

#secessions to Russia
pRussia = gc.getPlayer(con.iRussia)
if (pRussia.isAlive() and not pRussia.isHuman() and not lOwner.isHuman()):
if (iGameTurn == con.i1730AD):
utils.flipCity(lMinsk, True, True, con.iRussia, lOwner)
utils.flipCity(lKiev, True, True, con.iRussia, lOwner)
utils.flipCity(lPeter, True, True, con.iRussia, lOwner)
utils.flipCity(lVilnius, True, True, con.iRussia, lOwner)
utils.flipCity(lOdessa, True, True, con.iRussia, lOwner)





is that correct? I also need to get that code working for spawning defensive units or flipping of existing units for both the Russians and the English ones we did before (they don't spawn, and so free range barbarians (those outside of cities at the flip) just take the flipped cities back on the following turn)...
 
Ok, lets see. Its all about the logic, since we're trying to give orders to a computer chip... :p

You can't define anything but constants at the beginning of the file, because they will not be updated again once the file has loaded (during initializing). So you need to move out "iHuman = utils.getHumanID()" since that one might not be accurate otherwise. (If no human player is chosen before the game starts. Or what if the human player switches Civs in-game?) We'll just put that under "def checkGameTurn(...)", so that it get defined only when its actually used (see below).

So you trigger your "flip event" with the "if" statement about "iGameTurn" being this or that value, and you lose the code that does nothing (marked red):
Code:
                pRussia = gc.getPlayer(con.iRussia)
                if (pRussia.isAlive() and not pRussia.isHuman()[COLOR="Red"] and not lOwner.isHuman()[/COLOR]):
                        if (iGameTurn == con.i1730AD):
Because you can only feed the PyPlayer.isHuman() function a player instance, not a list variable like lOwner.

Next you check for the human player and remove that Civ's index number with this bit of code I dreamed up before (the loop is color marked so that you keep that separate in regard to the rest of the indentation):
Code:
iHuman = utils.getHumanID()
numCivs = len(lOwner)
[COLOR="Purple"]for num in range (numCivs):
        iCiv = lOwner[num]
        if (iCiv == iHuman):
                lOwner.remove(iCiv)
                break[/COLOR]
What you get is the lOwner list without the human player (if there ever was one), so you're good to continue to the actual flip. (Without risking to flip any cities belonging to the human player.)
Code:
                                utils.flipCity(lMinsk, True, True, con.iRussia, lOwner)
                                utils.flipCity(lKiev, True, True, con.iRussia, lOwner)
                                utils.flipCity(lPeter, True, True, con.iRussia, lOwner)
                                utils.flipCity(lVilnius, True, True, con.iRussia, lOwner)
                                utils.flipCity(lOdessa, True, True, con.iRussia, lOwner)
This is pretty much it, but you need to get all that indentation to match up exactly according to the rules of logic. :crazyeye: (By the way, if you put iRussia = con.iRussia at the beginning of the file you don't need to call on Consts.py with the con. prefix every time you use the Russian index number. But thats optional, I was just thinking that you might have defined the other Civs already.)

Regarding the utils.createGarrisons() function (oh, I guess I forgot about the utils. prefix earlier, because the function is in RFCUtils.py and it is defined as "utils = RFCUtils.RFCUtils()" at the beginning of the file) you should do one for each city.

Lets see if you get it right, otherwise you could just post your code for proofreading. But I'm pretty confident you can manage by now! :goodjob:
 
alright, this is what I have for proofreading...but the defensive units still won't spawn in flipped cities
 
Back
Top Bottom