How to intercept when a building/wonder has been constructed?

g02703

Chieftain
Joined
Apr 2, 2016
Messages
33
... either because it was paid for by energy/diplomacy, or completed by production after a few turns.

I was able to "intercept" when a unit is created. That's Events.SerialEventUnitCreated(playerId, unitId, ihexVec, iUnitType, iCultureType, civId, iPrimaryColor, iSecondaryColor, iUnitFlagIndex, iFogState). What is the Event or GameEvent for when a building or wonder has been completed (and its parameters)?

There's GameEvents.BuildFinished(...), but I believe that's for constructing roads, deforestation, and tile improvements.

There are also events that have the word "Can" in them, like PlayerCanConstruct and PlayerCanCreate, but the wording of those functions seem like they would be fired right before a user sees the list of what they can build in a city, and that I should return a boolean should I choose to handle these events.

I cannot figure this one out after looking at documentation, the .lua files in the game, lots of code in this forum, and from looking at a few people's sponsor mods' .lua files. So, I would really appreciate any lead on this.
 
Unless it has been changed over the last year (which I doubt) Events.SerialEventUnitCreated is imperfect. You want to use this.

A general warning, you want to be careful when using Events rather than GameEvents. Most Events are for UI or animation stuff and may not work as their name implies. For example, they may not fire for the AI or things the player can't see (due to fog of war).

As for the PlayerCanX/CityCanX events they can be used to add additional restrictions to buildings/units/etc. The functions you attach to these events will need to return true or false. If it returns false it prevents the building (or unit, etc) from being built. If it returns true than the building is allowed.
 
First of all, thank you everyone for the useful and helpful responses.

The BuildingProcessed has 3 parameters - the playerId, buildingTypeId (I believe, according to how it's used in the scripts I've seen), and cityId.

I know how to access the player and city instances. How do I access the building type by the id? I'm accustomed to accessing data by the upper-case-string keys, like GameInfo.Buildings["BUILDING_KEY"]. How does one search this table for a building whose ID is of a certain value (without looping through the entire table until the ID is equal to the id I want)?

I see a lot of if(buildingTypeId == GameInfo.Buildings["BUILDING_RELIC"]) (relic, for example), but that requires me to nit-pick the buildings that I want to do something about. I want is to find the building by ID, and once I get that building, I want to know at least its construction cost and whether it's a wonder (is the determination for wonder where its building class's MaxClobalInstances=1?) in order to determine what to reward the player.
 
Simple:

GameInfo.Buildings[BuildingID]
 
You did mention something I would also like to know the answer to. Is there any way via lua to identify all buildings that are wonders? I want to cycle through each wonder, and when its built have the player be granted 20% of its production cost as culture if they have a perk. I could just create a function for each wonder, but that's quite tedious. It'd be much easier if there were just a way to identify if a building is a wonder.
 
No direct way (that I know of), but you can indirectly do it by creating a table of all Wonders as shown in this thread: http://forums.civfanatics.com/showthread.php?t=546357

You'd then just have to make the game loop through that table and see whether one of these IDs matches the building that is being processed.
 
@Ryika - so I guess GameInfo.Buildings then will always contain a building whose ID = i? From other programming experience, that seems too good to be true.
 
Not exactly sure what you're asking there.

GameInfo.Buildings will by default return nil, because i is not defined.

If however you create a table (I'll skip the part where you use sql to skip building the lua table for this example):

Code:
local nationalWonders = {
  GameInfoTypes.BUILDING_NATIONAL_EPIC,
  GameInfoTypes.BUILDING_NATIONAL_COLLEGE,
  GameInfoTypes.BUILDING_NATIONAL_TREASURY,
  GameInfoTypes.BUILDING_HEROIC_EPIC,
  GameInfoTypes.BUILDING_CIRCUS_MAXIMUS,
  GameInfoTypes.BUILDING_GRAND_TEMPLE,
  GameInfoTypes.BUILDING_IRONWORKS,
  GameInfoTypes.BUILDING_HERMITAGE,
  GameInfoTypes.BUILDING_OXFORD_UNIVERSITY,
  GameInfoTypes.BUILDING_TOURIST_CENTER,
  GameInfoTypes.BUILDING_INTELLIGENCE_AGENCY
}

...and then fill i with a for-loop, for example by using...

Code:
for iWonderID = 1, #nationalWonders - 1 , 1 do -- edit: Made a mistake here, it's #nationalWonders, not #nationalWonders - 1 because we've started by 1 (instead of adding +1 to the for-loop afterwards
  local iWonder = nationalWonders[iWonderID]
  local iBuilding = GameInfo.Buildings[iWonder] 
  -- or just: nationalWonders[GameInfo.Buildings[i]]

  -- do stuff here
end

...then that code will automatically do everything you add to the for-loop for every building that you've added to the table.

Haven't actually tested the code so there may be syntax or logical errors in there.
 
Ah. So if I defined local i = 3, then can I guarantee that GameInfo.Buildings[3].ID = 3? Is the index of a building (or anything that has an ID field) in the array/table that it's in always equal to the ID field of that very building?

So I guess I need to create the table of known wonders (or wonders I choose that would award a player if they build it). So if (nationalWonders[GameInfo.Buildings[buildingTypeIdFromFunctionParameter]] ~= nil) then it means the building just built is on the manually-coded nationalWonders table; therefore, I can then reward the player.

I'll do some experimenting to see if it's possible to find out if a building is a wonder without having to manually build a national wonders table. Once I determine (I don't know how) that it's impossible or will take too long, I'll resort to the manual wonders table. The thing is, if I downloaded a mod that has new wonders, my mod wouldn't recognize it as a wonder, so the player wouldn't be awarded.
 
Ah. So if I defined local i = 3, then can I guarantee that GameInfo.Buildings[3].ID = 3? Is the index of a building (or anything that has an ID field) in the array/table that it's in always equal to the ID field of that very building?
Yes, what you're doing there is basically: "Here in the ID of a building, tell me which ID this building has." - it's the same thing. A more applicable example is if you have the ID but want the game to give you the type of the building.

GameInfo.Buildings[3].Type would return BUILDING_RELIC

However, hardcoding numbers like that is generally not advised as other mods can change the ordering of buildings (by deleting some for example), which would then cause huge compatibility issues. That's why a system as the one I outlined in the post above (indexing buildings by their Types, there's no reason why another mod would ever realistically change those) is a much better solution (not sure if the "best" solution, but at least it's the one that I'm using).

So I guess I need to create the table of known wonders (or wonders I choose that would award a player if they build it). So if (nationalWonders[GameInfo.Buildings[buildingTypeIdFromFunctionParameter]] ~= nil) then it means the building just built is on the manually-coded nationalWonders table; therefore, I can then reward the player.
Not 100% sure what you're trying to do here but from my understanding this would not work. As far as I can tell you're confusing the ordering-id used in the lua table with the building-id used in the xml table.

To use the table to find out whether a building is on the table you have to loop through the table itself. I guess an example is the easiest way to show what I mean by that:
Code:
-- Create a Table
local BuildingsTable = {
  GameInfoTypes.BUILDING_HEADQUARTERS,
  GameInfoTypes.BUILDING_RELIC
}

local BuildingInTable


function BuildingProcessedTestFunction(playerID, buildingID, cityID, buildingAdded)

	-- Loop through all Entries in the BuildingTable (# returns the number of Entries in a Table, so 2 in this example)
	for TableID = 1, #BuildingsTable, 1 do 

		-- Indexes the GameInfoType for the TableID (so GameInfoTypes.BUILDING_RELIC (which is 3) for TableID 2)
		local BuildingInTable = BuildingsTable[TableID] 

		-- Check if the ID of the Building in the Table matches the ID of our Building."
		if buildingID == BuildingInTable then
			print("Building with ID " .. buildingID .. " was found in Table on position " .. TableID .. ".")
			
			-- We found our Building so we can do stuff here.
		end
	end
end
GameEvents.BuildingProcessed.Add(BuildingProcessedTestFunction)

So if a Relic is finished the Lua would print this:
Building with ID 3 was found in Table on position 2.

I'll do some experimenting to see if it's possible to find out if a building is a wonder without having to manually build a national wonders table. Once I determine (I don't know how) that it's impossible or will take too long, I'll resort to the manual wonders table. The thing is, if I downloaded a mod that has new wonders, my mod wouldn't recognize it as a wonder, so the player wouldn't be awarded.
That's possible by following the instructions in the thread that I linked above. I just didn't include the SQL-thing in this thread because the lua itself is already complicated enough.
 
OK. Thank you. This will do for now. I will keep this thread open while developing.
 
If you have a building and you want to know if it is a wonder, there is a relatively easy way to do it. Get the buildingClass then look in the BuildingClasses table. If MaxGlobalInstances == 1 you know it is a World Wonder. If MaxPlayerInstances == 1 it is usually* a National Wonder.

*Headquarters, Culper Lodge and Spy Agency all use MaxPlayerInstances == 1 so you may need to explicitly exclude them.
 
Top Bottom