Investigating how to make a policy give a building

Machiavelli24

Mod creator
Joined
May 9, 2012
Messages
818
Since there are certain effects that can conveniently be done with buildings but not with social policies I've been trying to figure out a way to create a social policy that can give an arbitrary building.

I believe there would be three parts to this:

1. The building xml. NeverCapture and NukeImmune would need to be true, to avoid players without the social policy from getting the building through conquest or from a nuke eliminating the building.

2. A lua function that gave the building would need to be written. Presumably Player.HasPolicy can provide the check but I haven't found a way to simply give a city a building. Would city.doTask or city.pushOrder work? My concern is that would result in the player getting the effect at the end of their turn, not as soon as they adopted the policy. It would be awkward but a tolerable compromise.

3. The lua function needs to be called when the policy is adopted, a city is founded or a city is conquered. Would EventPoliciesDirty, SerialEventCityCreated and SerialEventCityCaptured cover what I need?

3b. More would have to be done if the policy was mutually exclusive with another branch, since that would require a way to remove all the buildings. However, I just want to see if I can get the simple case working first before worrying about that.
 
but I haven't found a way to simply give a city a building.

-- Add a granary
city:SetNumRealBuilding(GameInfoTypes["BUILDING_GRANARY"], 1)

-- Take it away
city:SetNumRealBuilding(GameInfoTypes["BUILDING_GRANARY"], 0)


(see the New World scenario script or C:\Program Files (x86)\Steam\SteamApps\common\sid meier's civilization v\Debug\Selected City.ltp)
 
city:SetNumRealBuilding(GameInfoTypes["BUILDING_GRANARY"], 0)

One note: if you remove a building that has a specialist slot using this function, and there was a specialist in it at the time, then things will break badly. So if you're going to remove a building with a slot, make sure you forcibly de-slot the specialist first. I believe the command is
Code:
city:DoTask(TaskTypes.TASK_REMOVE_SPECIALIST, iSpecialist, iBuilding, iPlayer)
(I might be a bit off; I'm at work, right now, but I use this mechanism quite a bit in my Mythology mod.) This dumps that specialist back into the "unemployed" pool. If the AI for that city is set to automatically allocate specialists, then it'll immediately put it somewhere else (another building, working a plot, etc.) but if you're on manual then you'll need to take care of it yourself the next turn.
If you sell a building using the normal interface, the game will already do this; you only need to do it yourself when using SetNumRealBuildings to remove a building in Lua.

Other than that, SetNumRealBuilding works extremely well. One thing to realize is that the 1/0 on the last argument isn't actually a boolean; if your building class has the <NoLimit> flag, you can use this function to place 10 copies of a building in a city. My own mods, especially the Mythology mod, use this mechanism HEAVILY to add all sorts of bonuses; for instance, I've got "Priest" specialists that add +1 Happiness, so each turn I track how many Priests are being used in each city and then just add N copies of a +1 Happiness building to that city.
 
SetNumRealBuilding is perfect, and it is idempotent, which will keep the logic simple. Also good to know about the specialist issue when removing the building.

Unfortunately EventPoliciesDirty doesn't seem to trigger when an AI player gains a social policy and I'm not seeing any other event that looks like it could be used to detect when a policy has been taken. It looks like I'll need to use PlayerDoTurn.

Since I want to avoid unnecessary calls to a function which calls SetNumRealBuilding for each city a player has, I have couple of question related to adding and removing event listeners.

1. If I add the same function to a particular event twice, will that function be run twice each time the event fires or only once?

2. If the answer to the above question is that it will be run twice, if I use GameEvents.PlayerDoTurn.remove(myFunc) will that remove both listeners or only one of them?

3. Is adding or removing listeners relatively expensive and something that should be avoided when possible?
 
1. If I add the same function to a particular event twice, will that function be run twice each time the event fires or only once?

Don't - there are better ways of doing this.

If you really want a function to run many times, add a counter that controls how many times it runs. This also avoids the (minimal) overhead of adding/removing listeners, you just increment/decrement the count.

If your functions are closures (eg, a different closure for each city), you'll still need to store the created function pointer somewhere so you can remove the correct one for the city, in this case just store a list of cities you need to run against (instead of the simple counter) and add to, remove from that list.
 
Thanks again whoward69, I've been able to get everything tested and working with those events. I was looking at the event lists on the wiki and missed those. Is there a better source of information? The wiki is somewhat sparse.

As for adding and removing listeners, that is no longer required thanks to those two policy events. I never intended to add a function twice as an awkward way to loop -- rather I was trying to figure out if I would need to avoid adding the listener a second time or if an inability for the same function to be listening more than once would take care of it for me.

I've also been attempting to move the functionality to xml to make it easier to work with. If I wanted to create a table that looks like:

Code:
<Table name="Policy_FreeBuildingClass">
   <Column name="PolicyType" type="text" reference="Policies(Type)"/>
   <Column name="PolicyBranchType" type="text" reference="PolicyBranchTypes(Type)"/>
   <Column name="BuildingClassType" type="text" reference="BuildingClasses(Type)"/>
   <Column name="IsRemovedWhenPolicyBlocked" type="boolean" default="true"/>
</Table>

So I think the SQLite I should use is:

Code:
CREATE TABLE Policy_FreeBuildingClass (
  PolicyType text REFERENCE Policies(Type),
  PolicyBranchType text REFERENCE PolicyBranchTypes(Type),
  BuildingClassType text REFERENCE BuildingClasses(Type),
  IsRemovedWhenPolicyBlocked boolean DEFAULT true
);

So in Modbuddy I'll need to create a sql file and put that in and have the file run on the OnModActivate action. Followed by an xml file with something like:

Code:
<Policy_FreeBuildingClass>
	<Row>
		<PolicyType>POLICY_OLIGARCHY</PolicyType>
		<PolicyBranchType>POLICY_BRANCH_TRADITION</PolicyBranchType>
		<BuildingClassType>BUILDINGCLASS_WORKSHOP</BuildingClassType>
	</Row>
</Policy_FreeBuildingClass>
 
Just merge the 1st and 3rd blocks of xml into a single file, no need to use SQL
 
Back
Top Bottom