[BTS] Unit-player allegiance

lindsay40k

Emperor
Joined
Mar 13, 2009
Messages
1,696
Location
England
So, in Civ 5, most civilian units have an 'original owner' tag. It's not directly visible, but if you capture a civilian that used to belong to another player - most commonly, from barbarians - then you get the option to return it.

This, of course, is a limited concept to bring up when AI (including Barbarian) military units kill any civilian they capture.

Is there a way to bring this concept into a Civ IV mod and make it useable?

If we assume that nobody plays with a duplicate leader, one possibility is to create a unique 'promotion' for each leader. And, perhaps, additional ones for things like 'captive' and 'gifted' and 'loaned'. This could present hooks for diplomatic features to be attached to, for Workers acquired in conflict to have a lower build rate (or tendency to run off if not overseen), and so on. They'd only need to be checked for as and when, rather than adding a load of extra calculations to the start of turn.
 
It seems fairly simple to do if you mod the DLL. Just add an int telling the original playerID to the unit and add that info to savegames. It will be a bit more complex to make a menu to make the choice of donating the unit, but it's certainly doable. If the menu response is transmitted on the network (like most menu responses), then it will work in network games as well.
 
Hmm. I was hoping for a solution that is easily distributed to my friends as a self-contained mod... DLL editing raised the bar for testers installing - and, as I understand it, will also kill Mac compatibility.
 
If it has to be mac compatible, you likely need to use promotions, one for each civ. You then need python code in onUnitCreated to set the promotion and if you just conquer the unit, let it move to you, in which case the computer creates a unit for you (also onUnitCreated) and then kills the old one. This mean if a newly created unit has one of the promotions, open a window asking if you want to give it back. I can't remember offhand how to make popup windows in pure python, but I think it's doable. The reply should be transmitted on network, but it's possible that it will do that automatically. If not, then there is a user configurable network package, which can be made in python, which transmits ints. UnitID, playerID and newPlayerID are only 3 ints and you can use up to 5 (or have to use 5, but just adding 0 to unused works).
 
Another Mac compatible solution that is a bit more powerful is to use CvUnit::setScriptData(string). This allows you to store an arbitrary string of data in the unit instance with Python. The simplest use of this is to use onUnitCreated as in Nightinggale's suggestion, and then just store the player ID using this method as a string. And then later check against it when the unit is captured. If you want to be a bit more technical/versatile about it you can also create a Python class like "UnitData", give it whatever fields you need (such as iOriginalOwner) and then serialise and deserialise it to and from string with pickle. The only thing you should be aware of is that certain BUG functionalities use setScriptData, so if your mod is intended to be based on BUG you should check if you'd be overwriting each others data.
 
CvUnit::setScriptData(string)
I keep forgetting about that function because I never work on python only mods. Indeed when you need to store something in a non-DLL mod, store it here rather than hacks with promotions. Writing an interface makes it a clean solution, which is not only easier to read (less risk of bugs), it will make it easy to store more data in a future update because you only need to update the string read/write, not the places, which use the data.
 
It's also great for mixed mods that use Python code besides the DLL in case you want to persist anything from Python because it allows you to easily get that data into the save file without further hassle.

See here for an example of the interface Nightinggale mentioned. Some useful patterns that you should follow:
- create a singleton at the end to import in other Python modules
- work on the dict underlying the Python object instead of saving and loading the object itself (see update() method). This way, you can evolve the available variables stored in the object over time without breaking compatibility with older saves
- use onGameLoad() and save() event handlers to load and save your stored data (see here). I am using the BugData module here but you can replace it with setScriptData as well

Note that I am not storing unit based information, but game related information, which is then ultimately stored in CvGame::setScriptData(), but the same principles apply.
 
Last edited:
Back
Top Bottom