Making a moving city.

Jarlaxe Baenre

Emperor
Joined
Feb 17, 2010
Messages
1,959
Location
Calgary, Alberta, Canada
Is there a way with python to make a moving city? (Similar to the Camp in Warlords Mongols mod, but can build buildings and choose what units to build)

I saw a screenshot of it in the Rise of the Homos development thread, but he never released that moving city into the jungles of civfanatics.
 
It would probably involve raising and rebuilding the city every time its moved. And how would one move it in the first place?

If you make a your "camp" into a unit you can move it around. Give that unit a custom Camp action that makes it found a city on the current tile - without using up the unit. Then, whenever the unit moves, the city is razed but before it is all the "settings" or attributes of the city are saved and stored. Next time the Camp action is used for the unit the new city will automatically get the same attributes - name, population, buildings, settled GP, build progress.

But I do think this is really a C++ job - doing something like this in Python would only be limiting and result in a collection of compromises. You could probably create an entirely new breed of cities with the SDK; moving cities, belonging to the CvCamp class. :D
 
It could be... I don't know exactly how I would go about doing it though. Its a pretty big undertaking, in any case.
 
It could probably be done pretty easily in Python using the "camp" unit that Baldyr suggests. Just do a check in CvEventManager.onUnitMove for the unit type. If it's a camp unit delete the old city and create the new city. The city data could be stored in the unit as script data. You would also have to do a check if the camp unit is to close to an existing city - if so don't build the new city - wait until the unit moves? I just thought of another possible problem when the unit moves. If it is capable of moving multiple plots in one turn it may cause graphics and/or game speed problems as the city switched plots multiple times in one turn. You would probably want to limit the number of units of this type that are on the map at the same time.
 
The idea was actually to have a unit action for the Camp unit. So the city wouldn't rebuild itself any time the unit stopped, but you'd have to found it every time. And any time the unit moves the city is razed.
 
It is possible to do in python, but relatively complicated. The way that I would think about it is a special settler unit. The city would have a building which can generate this settler unit; generating the settler destroys this building so that only one special settler can exist per city. The special settler has an action button which essentially destroys the city and converts it into data stored on the unit scriptdata. This data is complex; it includes a list of the buildings, the settled great people, perhaps the production queue if you want, etc.

When the settler is "carrying" the city it can move normally and do whatever it wants; the city does not exist. A special settler which is carrying a city does not need a special "found city" button, it already has one. But when a special settler settles, the data which is stored on its scriptdata is used to add all of the buildings, settled great people, etc to the city.

The hard part is converting all of the information about a city basically into one string. Once you have that then adding all the information from the string into the new city should be easy.
 
The hard part is converting all of the information about a city basically into one string. Once you have that then adding all the information from the string into the new city should be easy.
I don't think that would be any real problem. Just brake it into segments:
  1. Find the methods for fetching all the data from the city.
  2. Assign values to a data structure - like a list or dictionary.
  3. Pickle the data structure and store it as scriptData.
I actually think that #1 is the hardest part. Remember that you also need to find all the methods for setting those values in the new city.
 
It turned out to be even simpler than I originally though:
Code:
tData = iPopulation, name, settledGP, eProduction, iProgress #add whatever values/variables you like
pUnit.setScriptData(pickle.dumps(tData))
I left out step 1, of course, because I don't actually know values need to be fetched from the city.

And to retrieve the data:
Code:
tData = pickle.loads(pUnit.getScriptData())
iPopulation, name, settledGP, eProduction, iProgress = tData
The tData data structure is a tuple type, by the way.
 
Find the methods for fetching all the data from the city.

I think the hard part is thinking of all the data which needs to be stored. Once you do that, formatting as a string or pickle, writing the code to create it, and writing the code to extract it is all relatively easy. Users will file bugs like, "My city used to have such-and-such before it moved, but now that is gone".
 
Yeah, but thats part of the actual mod design work. (Not all data needs to be saved, like the city cold always be size 1 and never grow. Or production queues could be ignored. Its basically up the mod designer. And I'm not sure all data even can be retrieved - with Python.)

I'm not doing any of the design on this since its not my project - I only posted what was needed in terms of code. In case someone though storing the data was the big issue.

I would probably have most difficulties with making the Camp (special Settler) unit myself, but thats because I'm only focusing on Python with my own modding. Of course I would get the job done, eventually. Probably after reading up on XML and modding in general...

...and you are of course absolutely correct about the bug report prediction... The Camp unit/city should ideally not be a city or a unit to begin with. Meaning it should probably be done in C++.
 
Unfortunately that is the complicated data to fetch. You basically need to loop through all building types - including wonders - and check them with CyCity.isHasBuilding(). Every building type that registered as True should be appended to a list. Then you store that list and once the new city is founded you loop through the list and set the buildings one by one with CyCity.setNumRealBuilding().
 
That is not complicated, it is one loop. Three lines to build the data, three lines to recreate the city from the data.

What about settled great people?
 
Other things to consider:

  • :culture:
  • :gp: points per type
  • in revolt? how much longer?
  • defense and damage from bombardment
  • trade routes
  • religions
  • corporations
  • :mad: from whipping, defying resolutions, etc.
  • :mad: and :yuck: from espionage missions
  • citizen assignments and governor settings
 
I realize that the designer for this mod can't use a custom DLL, but I still think this is a SDK thing and should not be done with Python. Because regular cities aren't suited for being used as mobile cities. It simply won't be good enough to play.
 
I think we have gone way beyond the level of detail the OP can evaluate. But, the technique I have described about building a long string to put onto the unit scriptdata has practically zero runtime, and the runtime it has, occurs when the user pushes a button. So there will be no lag.
 
Interesting point about the runtime... Would also cPickle cause considerable lag for something like this?

Perhaps you should put your string setup into a ready-to-use application instead, so that people new to Python don't get turned off by the OOP. (Something like sdToolkit but more accessible and without pickling.) They would use a properly documented API instead, without having to even look at your code. :D (This actually sounds like a nice project. If I only had the time I'd love to explore it myself.)
 
Pickling something once, even a complex object, takes practically zero runtime. Computations which happen one time when the user pushes a button are usually un-noticeable. Some other mods apparently pickle and unpickle objects upon every access, dozens or hundreds of times per turn. This is not the right way to go about it, and can cause considerable lag.
 
Yeah, you should only pickle onPreSave and unpickle onPreLoad (or whatever). The data needs to be stored somewhere in the meanwhile though.

I'm seeing the upside of your approach - storing the actual data in string type directly as scriptData.
 
Back
Top Bottom