Celtic Mod v2.0 Release

Ok, here's your code with the indentation fixed. You could also lose the bits marked red, since you've already defined iEngland, iRussia and pRussia at the beginning. (You could do the same with pEngland if you like.)
Spoiler :
Code:
                #Minor secessions to England
                pEngland = gc.getPlayer(con.iEngland)
                if (pEngland.isAlive() and not pEngland.isHuman()):
                        if (iGameTurn == con.i1450AD):

                                utils.flipCity(lCork, True, True, [COLOR="Red"]con.[/COLOR]iEngland, lMinors)
                                utils.flipCity(lGalway, True, True, [COLOR="Red"]con.[/COLOR]iEngland, lMinors)
                                utils.flipCity(lDublin, True, True, [COLOR="Red"]con.[/COLOR]iEngland, lMinors) #Teamhair
                                utils.flipCity(con.tCapitals[iCeltia], True, True, [COLOR="Red"]con.[/COLOR]iEngland, lMinors) #Tara

                                utils.createGarrisons(lCork, iEngland, 2)
                                utils.createGarrisons(lGalway, iEngland, 2)
                                utils.createGarrisons(lDublin, iEngland, 2)
                                utils.createGarrisons(con.tCapitals[iCeltia], iEngland, 2)

        
                #secessions to Russia
[COLOR="Red"]                pRussia = gc.getPlayer(con.iRussia)[/COLOR]
                if (pRussia.isAlive() and not pRussia.isHuman()):
                        if (iGameTurn == con.i1730AD):

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

                                utils.flipCity(lMinsk, True, True, [COLOR="Red"]con.[/COLOR]iRussia, lOwner)
                                utils.flipCity(lKiev, True, True, [COLOR="Red"]con.[/COLOR]iRussia, lOwner)
                                utils.flipCity(lPeter, True, True, [COLOR="Red"]con.[/COLOR]iRussia, lOwner)
                                utils.flipCity(lVilnius, True, True, [COLOR="Red"]con.[/COLOR]iRussia, lOwner)
                                utils.flipCity(lOdessa, True, True, [COLOR="Red"]con.[/COLOR]iRussia, lOwner)

                                utils.createGarrisons(lMinsk, iRussia, 2)
                                utils.createGarrisons(lKiev, iRussia, 2)
                                utils.createGarrisons(lPeter, iRussia, 2)
                                utils.createGarrisons(lVilnius, iRussia, 2)
                                utils.createGarrisons(lOdessa, iRussia, 2)
About indentation, every level has 8 more blank spaces than the previous, so you should count those to make sure. I put blank lines between the blocks of code also, to try and make things clearer. Once you get why its formated in this way I'd say your pretty much ready to try your hand at some Python yourself.

I'll try the code out myself once I get home, to make sure the units are created and whatnot.

Also, I'll get to work on creating a custom function for city flipping, since the method above is very cumbersome and nothing I'd wanna use myself...
 
ah, that worked...excellent!

I was putting in the definition of creategarrison but not actually telling the game to do it.

I'm going to toy with it for a week or so, but I think I'm done...final release is forthcoming...
 
Further on indentation, just so you're able to follow my trail of though.

Note how a new level of additional 8 blank spaces is added to every line that ends with a colon. So everything that is typed into that new level - and to any further level - "belongs" to the line with the colon. Most of the time its an "if" statement or a "for (...) in" loop or a definition line (starts with "def").

Once the business conducted at those "new" levels is done the code reverts back to the original level and continues from there. There is nothing random, arbitrary or mystical with indentation in Python - its rather logical.

An example (I've marked every new level with a "->"):
Code:
                pRussia = gc.getPlayer(con.iRussia)
                if (pRussia.isAlive() and not pRussia.isHuman()):
                     [COLOR="Red"]->[/COLOR] 
                        if (iGameTurn == con.i1730AD):
                             [COLOR="Red"]->[/COLOR] 
                                iHuman = utils.getHumanID()
                                numCivs = len(lOwner)
                                for num in range (numCivs):
                                     [COLOR="Red"]->[/COLOR] 
                                        iCiv = lOwner[num]
                                     [COLOR="Red"]<-[/COLOR] if (iCiv == iHuman):
                                            [COLOR="Red"]->[/COLOR]
                                               lOwner.remove(iCiv)
                                            [COLOR="Red"]<-[/COLOR] break

                                utils.flipCity(lMinsk, True, True, con.iRussia, lOwner)
Note how the "if" statements get nested into each other, so that the levels keep adding up. The loop ("for num in range (numCivs):" or "loop the variable this many times") is broken once it has run its course - or with the "break" command if it produces a hit in the form of a human player. This is why the indentation returns to the original level - and commences with the actual city flip.

A good rule of thumb :goodjob: is to always add a new level of indentation when a line ends with a colon. The trick is to know where to come back to the original level - and to figure out what level that is. But it should be pretty logical - you just need to think of the code as a script that the game engine is reading. It starts at the first line and follows on through to the end. So you need to go through the code line by line and interpret what instructions the computer is being fed.

One good thing to know is that the "break" command ends a loop entirely - and thus the indentation returns the same level as the line initializing the loop. And also that a "continue" command skips the present loop and start the next one. (If its the last loop it will of course break the loop.)

I'm not sure if your taking any of this in, but I feel pretty committed to force feeding you this stuff so that you will be able sort out all this Python business yourself going forward. If I managed to work all this out by myself you should also be able to.

edit: You did it! Congrats! :king:
 
The appropriate function to reveal/hide a piece of terrain is setRevealed;
I think I'm actually gonna create a custom function that lets you do these things by giving it the ID of the Civ who's map you wanna copy (and the target Civ, of course). It should be easy enough.

There seems to be a lot of setRevealed() functions in the API though, but guess I'd have to use the one listed under CyPlot:
185. VOID setRevealed (TeamType eTeam, BOOL bNewValue, BOOL bTerrainOnly, TeamType eFromTeam)
void (int /*TeamTypes*/ eTeam, bool bNewValue, bool bTerrainOnly, int /*TeamTypes*/ eFromTeam)
Does anyone know how that last argument (eFrom) is used? I guess you give it the index number of a team (with CyPlayer.getTeam()) but what does it do?

There is one under CyMap also, I wonder if that one reveals the whole map then? :confused: Under CyMap there's also setRevealedPlots() which seems to be able to reveal many plots on one single command - but how is it determined what plots? (What is a CyMap instance, anyway? Is it the entire map as defined by the scenario file or is it the map revealed to one Civ? :confused:)
Of course, it also doesn't make much sense when America spawns several techs ahead of anybody else.
This is another good point. I think I'll just mod my own game so that it looks for what Civs are present in the American Core Area at spawn, and then grants all the Techs and Maps of those Civs. But no Techs beyond that - or maybe just Liberalism/Nationalism or something...? :confused:

So there's another function I could do. (Copy Techs of another Civ.)

edit: I'd need some method of looking whether or not a Civ has revealed a tile or not. I'm guessing the function to use would be CyPlot.getRevealedTeam() then? Or maybe CyPlot.isRevealed()?
 
"so that it looks for what Civs are present in the American Core Area at spawn, and then grants all the Techs and Maps of those Civs."

this should be done for all spawns throughout the game. if you'd done it, i'd include it!
 
this should be done for all spawns throughout the game. if you'd done it, i'd include it!
No, I don't believe so. The Americans are special because they're actually a European colony or several colonies. Thus they should know what they knew before they declared independence.

In my own Russia scenario the Russians start out as Kievan Rus and are thus varangians (vikings). I'll implement this function so that the Russians get maps of Scandinavia and beyond, and that the Vikings get maps of the Novgorod area (the new starting location - before Kiev flips).

It would also be cool to have an American spawn date that depends on the stability of the colonies themselves, as well as whether or not Nationalism and Liberalism are discovered. I'm not sure if it would work so well, though...
 
that does seem rather complex, and you're right, the franks and the goths did not know what the romans knew even though they repopulated the same area...
 
What about Portugal then? Should they know what Spain knows? The Netherlands? It seems rather silly that these two Civs are more advanced than their neighbors, and have no knowledge of the world around them.

Changes like this are bound to effect game balance, though. It could be another case of AI only features...

Too bad I don't have the time or the energy to do any Python tonight, but I'll try to figure this thing out during the weekend. I how have several new functions to work on... :king:
 
I think it would be a balance issue...and the techs they start with are an attempt to predict where the world is at that time (or where it was historically). I like the idea of giving civs typical starting lists, but there should be a function that shares the map with civs in its normal area and eliminates any techs not found in the tech lists of civs there at the time of spawning.
 
Ok, good stuff, taking notes. :goodjob:

Take a look at the list lNeighbours (and possibly lOlderNeighbours) in Consts.py - what about a function that grants a Civ all Techs already known by two or more neighbors - and/or a map of all tiles already known by two or more of them? :king:

When used for a spawning Civ it wouldn't make the newcomer the most advanced Civ in the neighborhood, but also not the least advanced one.
 
Did you ever test this properly? Because I was just improvising as I was typing you a responce. :mischief:
Code:
                                iHuman = utils.getHumanID()
                                numCivs = len(lOwner)
                                for num in range (numCivs):
                                        iCiv = lOwner[num]
                                        if (iCiv == iHuman):
                                               lOwner.remove(iCiv)
                                               break
It dawned on me that there's a much simpler way of doing what I was trying to achieve with the code above:
Code:
                                iHuman = utils.getHumanID()
                                if (iHuman in lOwner):
                                        lOwner.remove(iHuman)
Since the variable iHuman is a integer value from 0 to 30, and since all the variables in the list are also integer values, its possible to simply check with the command "in" whether or not this value is already in the list. And if so, remove this value from the list. Plain and simple.

To put it in a simpler form we could just replace all the variables with the numerical values and type in the list without using the list variable. If we assume that the human player is Germany (Civ #17) - this is what the computer is actually seeing:
Code:
if (17 in [17, 11, 19, 25, 7, 4, 16, 15, 14]):
        [17, 11, 19, 25, 7, 4, 16, 15, 14].remove(17)
It would be great if you could actually test this for me! :goodjob:
 
On the balance: I've noticed that tech nearly always runs slow in my games; thus this plan would effectively nerf most late-spawning civs. Perhaps a slight global reduction in tech costs, to make research easier?

Also, I had this experience several times with the Americans:
Spawn several techs ahead.
Build initial cities, flip a few, get one in a congress. Conquer a canal city in Central America.
Economy crashes due to 150% inflation. All workers, and most of the army, disband.

That convinced me to drastically cut inflation, to 1/3 of its former value. (Not included in my bugfix patch)

OK, what would this change look like:
Neighbors with earlier spawn date (not the same as "older neighbors" list):
Egypt, Babylon, China, India: None. Starting techs, map should be unchanged.
Greece: None. Unchanged.
Persia: Babylon, India. Sharing fits well; guarantee techs for starting units as well.
Carthage: Egypt, Greece. Sharing sounds good; Carthage should know something about the Mediterranean. Guarantee techs up to Sailing, HBR, Archery as well.
Rome: Egypt, Babylon, Greece, Carthage. Should guarantee Iron Working.
Ethiopia: Egypt. Starting state should be unchanged (they need to be able to research Theology immediately for the UHV).
Maya: None. Unchanged.
Vikings: None. Unchanged.
Japan: China. Starting techs should be unchanged.
Arabia: Egypt, Babylon, Persia, Ethiopia. Lots of good shared stuff, should guarantee Guilds, HBR, and prereqs. On 600 AD start, give them unchanged techs and a bunch of map info.
Khmer: India, China, Japan. Should guarantee techs for elephants.
Spain: Carthage, Rome. Guarantee IW, Feudalism, what else? They're going to hurt if Carthage is already collapsed.
France: Rome, Vikings, Spain. A lot like Spain, but the Vikings are a very predictable tech list.
England: Rome, Vikings, Spain, France. OK, they'll know everything the French do.
Germany: Rome, Vikings, France, England. More of the same.
Russia: Persia, Vikings, Germany. That should get them some western map knowledge.
Netherlands: Vikings, France, England, Germany.
Mali: Egypt, Carthage, Ethiopia. That's a way to get screwed. Should guarantee a bunch of stuff, maybe add Arabia as a neighbor.
Portugal: Carthage, Rome, Spain, France. Guarantee Guilds; they need to be close to Optics to have a decent shot at anything.
Inca: Spain. Unchanged.
Mongolia: India, China, Japan, Russia, Persia. Guarantee techs for starting units.
Aztecs: Spain. Unchanged.
Turkey: Babylon, Greece, Persia, Russia, Mongolia. Add Arabia. Guarantee Gunpowder.
America: Japan, Spain, France, England, Russia, Inca, Aztecs. Wrong list entirely. Build a new list of any Old World civ with a city in North or Central America, plus their default neighbors. Also guarantee Democracy and prereqs. They can get their advanced military units, but they only get techs to replace them if some Old World civs know.

There are a lot of places where the 600 AD start would have to be treated differently if implementing this idea.
 
baldyr...at least for my mod, but perhaps for the original as well...couldn't map tiles simply be revealed in the WB save, and contact established with select civs there as well?
 
OK, what would this change look like:

(---)

There are a lot of places where the 600 AD start would have to be treated differently if implementing this idea.
You seem to have an idea so I say run with it. I'll try to figure out how to make the functions to use with it - you focus on the design aspects, ok? :goodjob:

I'd use such a modmod myself for sure, so I'm looking forward to your design. :king: If done right it could be released as something like "Dynamic Starts Modmod" and I think it would get some downloads also. By the way, we could make it a custom option whether to use dynamic starts - or perhaps only use them for the AI Civs. It shouldn't be too hard to do, I think... :p
 
baldyr...at least for my mod, but perhaps for the original as well...couldn't map tiles simply be revealed in the WB save, and contact established with select civs there as well?
I believe I've already tried all that for my own scenario, and since it didn't work I started to poke around with Python instead...

But you should probably try it! It would help me out for sure if you figure out how to get it working. (I ended up giving the Russians a Workboat on the first turn instead, to reveal some coastal west of Novgorod. I've been meaning to find a better way of revealing tiles though.)
 
You seem to have an idea so I say run with it. I'll try to figure out how to make the functions to use with it - you focus on the design aspects, ok?
I'm not ideal as a speculative modder; I'm working on Mac, so I'm stuck with not quite the latest version and with no DLL changes. Also, I can't clone the folder and have two working copies at once. Still, this could probably be done Python-only, confined to the RiseAndFall file (aside from the option)...
All right, I'll do it.
 
Still, this could probably be done Python-only, confined to the RiseAndFall file (aside from the option)...
All right, I'll do it.
Yeah, I was also thinking Python only. :king:

I've started working on functions you could use. This is what I have so far:
Spoiler :
Code:
### dynamic starts within RiseAndFall.py ###

##        def getAreaCivs(self, TL, BR, lExceptions, bMajorOnly, bAIOnly):

##        def makeTechList(self, iCiv, bTraded):

##        def copyTechs(self, iCiv, tTechList, iNumHits, bHuman):

##        def copyWorldMap(self, iReceiver, lProviders, iNumHits, bHuman):
custom.getAreaCivs() returns a list of all the Civs with cities present inside an predefined area. You can use any of the Core/Normal/Broader Areas or any other area, and it also handles exceptions (used to define Core/Normal area).

custom.makeTechList() returns a list containing all the Techs owned by iCiv. I'll look into making it possible to except traded Techs (as Tech brokering isn't allowed in RFC) with bTraded.

custom.copyTechs() takes a tuple list containing several merged Tech lists (above function) as an argument with a target value to determine what Techs iReceiver should get.

custom.copyWorldMap() takes a list of Civs and a target value to determine which map plots should be revealed to iReceiver.


Other functions I'm working on at the moment include:
Spoiler :
Code:
        def foundCity(self, lCity, name, iPopulation, bForce, iCityCulture, iCulture, iUnitType, iNumUnits, iReligion, lBuildings):

        def flipCity(self, tCityPlot, iNewOwner, bOnlyAI, iOldOwner, bAIOnly, bMinorsOnly, bConquest, bKillUnits, iNumGarrison, bConscript):

        def createGarrison(self, iCiv, bHuman, tCityPlot, iNumUnits, bConscript):

        def setBuildings(self, tCityPlot, lBuildings):

        def setGlobalBuilding(self, iCiv, iBuilding):
Any other suggestions are also welcome! :king:
 
Ok, here's the long advertised custom function for city flipping then. I urge antaine to use this instead of the default utils.flipCity() function, as it isn't designed to be used for scenario making or the like. There is no extra code (except the turn trigger) required and no list of possible owners need to be defined!

In short, my custom.flipCity() function allows to control what Civ can be the original owner, and whether or not the old owner or the new owner can be the human player, or if the original owner must be a minor Civ (useful for flipping independent cities only). Also, spawning defending units is built into the custom.flipCity() function and it uses an enhanced custom.createGarrison() function which allows for more unit types - and Unique Units - to be created. (Number of available unit types has gone up from 6 to 14, making it suitable for use in all ages and with all Civs.)

Here's the breakdown for all the arguments or settings:
Spoiler :
custom.flipCity(tCityPlot, iNewOwner, bOnlyAI, iOldOwner, bAIOnly, bMinorsOnly, bConquest, bFlipUnits, iNumGarrison, bConscript)
  • tCityPlot: This a tuple and the city coordinates must be entered within brackets ([...]). It could also be a tuple variable or a list variable (the lCity lists can be used conveniently enough).
  • iNewOwner: This is a integer value or variable for the index number of the receiving Civ.
  • bOnlyAI: This is a boolean ("True" or "False") that determines if the receiving Civ can be the human player. The "True" setting makes it AI only and the "False" setting also enables the human player.
  • iOldOwner: This is another integer value or variable corresponding to the original owner. It can be set to "-1" as default for allowing any Civ to be the original owner.
  • bAIOnly: This is another boolean determining whether or not the owner can be the human player. ("False" = human player enabled.)
  • bMinorsOnly: This boolean can be used to restrict the owner to minor Civs only ("True").
  • bConquest: This boolean determines whether or not the city will be conquered or traded. (This should also affect UHVs!) If set to "True" the city will go into resistance and lose population points (if over size 2). Any units belonging to the previous owner are killed off and there will be a state of war between the receiving Civ and the previous owner.
  • bFlipUnits: This boolean determines whether or not also units inside the city will flip. This option is only available if the previous boolean is set to "False".
  • iNumGarrison: This integer value determines whether or not and how many units will be automatically spawned in the flipped city. Note that no units will spawn if the previous boolean is enabled ("True").
  • bConscript: This boolean determines if the units spawned are to be the default defensive unit of the era (from Warrior all the way to Mech Infantry) for the city's new owner ("False") - or if the units are the unit type that would be available for drafting with the Nationhood Civic ("True"). Pre Gunpowder these are actually melee units, and if the prerequisite strategic resources aren't available in the city this means that Warrior units will be spawned instead.
In order to use this (and other custom functions) the attached Custom.py file must be present in the \Assets\Python\ folder. Also, the file itself must be imported into the .py files where it is used (like Barbs.py) with this line (under "from CvPythonExtensions import *"):
Code:
import Custom
If the custom. prefix is to work this line must be entered (preferable under "# globals"):
Code:
custom = Custom.Custom()
The entire code we made earlier for flipping cities for Russia and creating the garrisons can be replaced by this:
Code:
                #secessions to Russia
                if (iGameTurn == con.i1730AD):
                        custom.flipCity(lMinsk, iRussia, True, -1, True, False, True, False, 2, False)
                        custom.flipCity(lKiev, iRussia, True, -1, True, False, True, False, 2, False)
                        custom.flipCity(lPeter, iRussia, True, -1, True, False, True, False, 2, False)
                        custom.flipCity(lVilnius, iRussia, True, -1, True, False, True, False, 2, False)
                        custom.flipCity(lOdessa, iRussia, True, -1, True, False, True, False, 2, False)
It does everything that the previous code did and it does it better, as there are fewer built in glitches and limitations. There's also more options available (see the documentation above) and it will also be very easy to go forward with additional city flips.

Note that the code above makes Russia go to war with whatever Civ that controls any of these cities! This can be avoided by changing the bConquest boolean to "False" - then the units in the city will also be moved out instead of killed off. (It could break the balance of power if the previous owner had a huge stack of units inside the city when it is "conquered"! Those units would simply vanish into thin air, opening the way for further Russian territorial gains...)
 

Attachments

  • Custom.zip
    2.9 KB · Views: 95
great...I'm testing it out today. as for the listing capability...can it be used to simply flip all nonhuman holdings in the nonhuman Russian core territory? I've found that by 1720, germany has completely swamped russia (both west and east of moscow), all the way to the urals...so flipping those five cities doesn't really help all that much...
 
great...I'm testing it out today. as for the listing capability...can it be used to simply flip all nonhuman holdings in the nonhuman Russian core territory? I've found that by 1720, germany has completely swamped russia (both west and east of moscow), all the way to the urals...so flipping those five cities doesn't really help all that much...
So, we're back at the "second spawn" discussion again, then? :rolleyes: This is pretty much what I've been saying all along so I'm agreeing with you whole-heartedly on this subject.

I haven't made that one yet but It's on the agenda. I was gonna go forward with the stuff for the dynamic starting conditions stuff we discussed earlier, but I guess it can wait...

In the meanwhile, here's some other stuff I have been spending the last day doing:
http://forums.civfanatics.com/showpost.php?p=8917744&postcount=66

You should find some of it very useful for making historical scenarios and such!

Also, the custom.foundCity() function is still available (with added functionality) if you change your mind...
http://forums.civfanatics.com/showpost.php?p=8897296&postcount=22
 
Top Bottom