Can a building have multiple MutuallyExclusiveGroup tags?

Galgus

Emperor
Joined
Aug 22, 2012
Messages
1,705
Can I give a building, say, mutually exclusive group 843 and group 844 to exempt it from two groups of buildings?

I'm wanting to create a case where building A can be used with buildings B, C, and D while all of them are incompatible with buildings E, F, and G.

Can I do this by using mutually exclusive group tags like this?

Tag 1 - A, E

Tag 2 - A, F

Tag 3 - A, G

...and so on with every building I don't want A to be built with, and then again with B, C, and D.

If this is possible I can make Affinity building sets that exclude all buildings of other affinities to force more specialization.

If there is a more convenient way to do this, I'd also be happy to hear it.
 
No, as with all entries in a table a tag can only exist once.

Personal opinion: I find that sounds way too complicated anyway. Why not just add similar Buildings of different Affinities to a group to force Players to choose one? That would already force them to choose out of three options instead of being able to get all of them, without having to remember or look up tons and tons of dependencies.
 
That is the fallback plan, though national wonders in the mix complicates it a bit.

I'm mostly using national wonders to have access to playerperks through the building system.

Can a national wonder be made to require multiple types of buildings in all cities?
 
Yes, that should be possible using the <Building_PrereqBuildingClasses>-Table for both building classes and a <NumBuildingNeeded> of -1.
 
You should be able exclude from multiple sources if you use the <Building_LockedBuildingClasses> table, instead of a MutuallyExclusiveGroup tag. Each entry in the table will prevent the BuildingType being constructed if one BuildingClassType is present in the city, but you can define as many entries as you want for the same building.


I managed mutually exclusive national wonders in Affinity As Yields by having every national wonder grant a free building in all cities, then using Building_LockedBuildingClasses to prevent each wonder being built if that building is present.
 
Use a CityCanConstruct check to implement the restriction.

Do what ever logic you want via lua, if you return false the building will be blocked.
 
I've never modded with lua, and I have some questions over the example.

With this:

Code:
GameEvents.CityCanConstruct.Add(function(iPlayer, iCity, iBuildingType)

Would the iPlayer part be unnecessary since, if I understand it correctly, it is used to restrict something to a faction?

Would this be basically how to code a building restriction?

Code:
GameEvents.CityCanConstruct.Add(function(iCity, iBuildingType)

if (iBuildingType == GameInfo.Buildings["***the building being restricted***"].ID) then

   if (iBuildingType == GameInfo.Buildings["***the buildings it cannot coexist with***"].ID) then

   return false;

   end

I don't understand how it checks if a building exists in the city to see whether or not it can construct it.

Do you know of a reference for restricting a building if a different building exists in the city with lua?
 
That lua just goes through the list of all Buildings that are available to the player every time you open the construction menu, to add additional restrictions you'll need additional checks. The logic behind your example is correct, but some errors exist:

Code:
-- Prevents Relics from being constructed
GameEvents.CityCanConstruct.Add(function([COLOR="red"]iPlayer, iCity,[/COLOR] iBuildingType)
	if (iBuildingType == GameInfo.Buildings["BUILDING_RELIC"].ID) then
		return false
	end
[COLOR="red"]
	return true
end)[/COLOR]

Explanations:

GameEvents.CityCanConstruct.Add(function(iPlayer, iCity, iBuildingType)
-> A function always fills its arguments linearly, the names you put in are just the names you want to use in the function, but it does not change what is put into that name. So in your example, iCity would contain the playertype and iBuildingType would contain the city.

return true
-> In my testings this function will block all buildings if nothing at all is returned. Which is weird, but it seems you need to return true if none of the return falses took place (which is good practice anyway).

end)
-> Is just the rest of the code that you didn't include/need to make the example work by itself.


An example on how to block buildings when other buildings exist:
Code:
-- Prevents Relic in Capital (By checking if Headquarters exist in the City)
GameEvents.CityCanConstruct.Add(function(iPlayer, iCity, iBuildingType)
	if (iBuildingType == GameInfo.Buildings["BUILDING_RELIC"].ID) then

		-- Need to convert iCity to a usable format
		local pCity = Players[iPlayer]:GetCityByID(iCity)

		if (pCity:GetNumBuilding(GameInfo.Buildings["BUILDING_HEADQUARTERS"].ID) > 0) then
			return false
		end
	end
	
	return true
end)
 
I assume that blocking multiple buildings would just involve repeating the headquarters bit before the first end?

Code:
-- Prevents Relic in Capital (By checking if Headquarters exist in the City)
GameEvents.CityCanConstruct.Add(function(iPlayer, iCity, iBuildingType)
	if (iBuildingType == GameInfo.Buildings["BUILDING_RELIC"].ID) then

		-- Need to convert iCity to a usable format
		local pCity = Players[iPlayer]:GetCityByID(iCity)

		if (pCity:GetNumBuilding(GameInfo.Buildings["BUILDING_HEADQUARTERS"].ID) > 0) then
			return false
                if (pCity:GetNumBuilding(GameInfo.Buildings["**second building**"].ID) > 0) then
			return false
                if (pCity:GetNumBuilding(GameInfo.Buildings["**third building**"].ID) > 0) then
			return false
		end
	end
	
	return true
end)

It makes a little more sense to me now, but I don't know if it would want another line of spacing and more end statements in its format.
 
Empty lines are just for readability, use or skip additional lines whereever you want. You're missing some ends though:
Code:
		if (pCity:GetNumBuilding(GameInfo.Buildings["BUILDING_HEADQUARTERS"].ID) > 0) then
			return false
		end
		
		if (pCity:GetNumBuilding(GameInfo.Buildings["**second building**"].ID) > 0) then
			return false
		end
		
		if (pCity:GetNumBuilding(GameInfo.Buildings["**third building**"].ID) > 0) then
			return false
		end

A different version (that makes no difference in this case because the 'return false' exits the function anyway) would of course be to use elseif:

Code:
		if (pCity:GetNumBuilding(GameInfo.Buildings["BUILDING_HEADQUARTERS"].ID) > 0) then
			return false		
		elseif (pCity:GetNumBuilding(GameInfo.Buildings["**second building**"].ID) > 0) then
			return false
		elseif (pCity:GetNumBuilding(GameInfo.Buildings["**third building**"].ID) > 0) then
			return false
		end

The first example would try to check single building separately (if the 'return false' didn't end the function), the second one would check the first one first and if it's not true check the first elseif, if that one's also not true check the second one etc.

If you're sure you want to use exactly the same code for all three of them (which is likely to be the case here) you can of course also use the or-operator:

Code:
		if (pCity:GetNumBuilding(GameInfo.Buildings["BUILDING_HEADQUARTERS"].ID) > 0) or (pCity:GetNumBuilding(GameInfo.Buildings["**second building**"].ID) > 0) or pCity:GetNumBuilding(GameInfo.Buildings["**third building**"].ID) > 0) then
			return false
		end
If one of the three is true (aka if one of these buildings exists) it will return false (aka the building can't be constructed).

Overall I'd suggest reading or watching some basic Lua-Tutorials first, Lua-code becomes really easy to read and understand once you have the very basics.

Disclaimer: I didn't actually test the code in this post.
 
There might be somewhere in the range of 35 buildings that each affinity building would be incompatible with, so I'll probably go with the first option since it may be easier to check.

Thanks everyone for the help, I should have what I need to make the proper restrictions.
__________________________

As one final question, is it possible to use the affinity and hybrid affinity symbols in building names?

I feel like that would help readability.
 
There might be somewhere in the range of 35 buildings that each affinity building would be incompatible with
Okaaaaaaaay. Well, don't quite know what you're planning, but I'd suggest getting familiar with tables to see if it makes sense to use them to "simulate" multiple mutually exclusive groups for some automation, because that sound like otherwise the code would become massive.

As one final question, is it possible to use the affinity and hybrid affinity symbols in building names?
Yes, just use the text-icons: [ICON_PURITY] ; [ICON_HARMONY] ; [ICON_SUPREMACY] ; [ICON_SUPREMACY_PURITY] ; [ICON_PURITY_HARMONY] ; [HARMONY_SUPREMACY]
 
Planning exclusive building lists for each affinity and hybrid affinity...I may need to look into tables, but can deal with tedious code so long as I understand it.
 
Well, if you really want to make it so that every building belongs to only one group at a time that is meant to block out all other groups, then I'd do it completely different and somewhat mimic what HandyVac did in his AffinityAsYields-Mod:

- add a new column to the buildings-table via SQL
- add new entries to that column via XML (<- That's where you can easily change things around later)
- add lua-code that checks whether the city already has buildings that belong to another group and block them if true
(With some changes in the Lua the restrictions to "only one group!" can of course be changed)

That way you will have like... 95% less overall code and 99% less headaches if you want to change things around later.

I created a working prototype, feel free to download the attachment for easy testing.

Or just look at the code here:
Spoiler :

An SQL-File:
Code:
-- Add a new entry to the Building Table that we can later check via Lua.
-- The entries will be filled in by the code in Buildings.xml!

ALTER TABLE Buildings ADD AffinityAffiliation text;

An XML-File:
Code:
<GameData>
	<Buildings>
		<!-- Here we'll fill in the new AffinityAffiliation-Entry that has been
			 created in UpdateBuildingTable.sql Note that I'm just using the 
			 "AFFINITY_PURITY"/"AFFINITY_SUPREMACY" for consistency, you could 
			 as well add new Entries named "THIS_IS_A_BUILDING" and it would
			 create it's own "simulated" mutually exclusive group that blocks
			 out all the other AffinityAffiliation-Entries. The Code in
			 AffinityAffiliationTestScript.lua will look at these table entries. 
			 
			 This is the ONLY thing that needs to be changed around if we want 
			 to change the availability of buildings later, lua will do the rest
			 automatically! 
			 
			 For this prototype I've added entries to 3 buildings, all of them
			 have been made available on turn 1 by Debug.xml. We can test the
			 system by for example constructing a Gene Garden and we'll see that
			 the Feedsite Hub will vanish, while the Skycrane can still be 
			 constructed! -->

		<Update>
			<Where Type="BUILDING_GENE_GARDEN"/>
			<Set AffinityAffiliation="AFFINITY_PURITY"/>
		</Update>
		<Update>
			<Where Type="BUILDING_SKYCRANE"/>
			<Set AffinityAffiliation="AFFINITY_PURITY"/>
		</Update>
		<Update>
			<Where Type="BUILDING_FEEDSITE_HUB"/>
			<Set AffinityAffiliation="AFFINITY_SUPREMACY"/>
		</Update>
		
		<!-- etc. - add your entries here~ -->
		
	</Buildings>
</GameData>

A Lua-File:
Code:
-- This little function checks the groups of the Buildings that can be
-- built and blocks all Buildings that are in a different group than
-- any of the existing ones. 

GameEvents.CityCanConstruct.Add(function(iPlayer, iCity, iBuildingType)

	-- Need to convert iCity to a usable format
	local pCity = Players[iPlayer]:GetCityByID(iCity)

	-- If the Building has an Entry to the AffinityAffiliation Table then...
	if GameInfo.Buildings[iBuildingType].AffinityAffiliation ~= nil then
		-- ...go through ALL buildings that exist...
		for pBuilding in GameInfo.Buildings() do
			-- ...and check if it exists in the city.
			if pCity:IsHasBuilding(pBuilding.ID) then 
				-- If it exists, then check if it has an AffinityAffiliation entry.
				if GameInfo.Buildings[pBuilding.ID].AffinityAffiliation ~= nil then
					-- If it has one, then check if it's different from the building that is being checked!
					if GameInfo.Buildings[iBuildingType].AffinityAffiliation ~= GameInfo.Buildings[pBuilding.ID].AffinityAffiliation then
						-- If it is different finally return false!
						return false
					end
				end
			end
		end
	end
	
	-- Return true if the code above has not returned false (aka if 
	-- the building has no AffinityAffiliation-entry or no building 
	-- of a different group exists).
	return true
end)

And another XML-File I created just for Debugging:
Code:
<GameData>
	<Buildings>
		<!-- This file is just for debug; to be able to easily test if the system works by having 2
			 Affinity Buildings that can be built instantly. It has nothing to do with the actual system! -->

		<Update>
			<Where Type="BUILDING_GENE_GARDEN"/>
			<Set Cost="1"
				 PrereqTech="TECH_HABITATION"/>
		</Update>
		<Update>
			<Where Type="BUILDING_FEEDSITE_HUB"/>
			<Set Cost="1"
				 PrereqTech="TECH_HABITATION"/>
		</Update>
		<Update>
			<Where Type="BUILDING_SKYCRANE"/>
			<Set Cost="1"
				 PrereqTech="TECH_HABITATION"/>
		</Update>
	</Buildings>

	<Building_AffinityPrereqs>
		<Delete>
			<BuildingType>BUILDING_FEEDSITE_HUB</BuildingType>
			<AffinityType>AFFINITY_TYPE_SUPREMACY</AffinityType>
			<Level>2</Level>
		</Delete>
		<Delete>
			<BuildingType>BUILDING_GENE_GARDEN</BuildingType>
			<AffinityType>AFFINITY_TYPE_PURITY</AffinityType>
			<Level>2</Level>
		</Delete>
		<Delete>
			<BuildingType>BUILDING_SKYCRANE</BuildingType>
			<AffinityType>AFFINITY_TYPE_PURITY</AffinityType>
			<Level>10</Level>
		</Delete>
	</Building_AffinityPrereqs>
</GameData>

 

Attachments

I'll look into the folder when I'm able.

Is the full affinity affiliation code in the sql file, or should I look for it in the affinity as yields mod?

Anyway, this is a far more convenient method.
 
No, that's all the coded needed up there.
 
I get it now, I was overcomplicating the affinity affiliation text in my head.

Thank you for all of your help on this.

That could kind of work as a modder's tool mod for anyone trying something similar, though it is somewhat narrow.
 
In trying to use the code alongside a (RT) Modder Resource - Modular Building Quests resource, and everything seemed to work other than the exclusivity code with the test code.

I moved the debug code into the buildings folder for the test.

The Lua was put in Content with the Custom type while the SQL and XML files are in Actions with Update Database.

The logs did not seem to have useful information.

Do you have any idea what went wrong?

I think I entered it correctly, but I could be wrong.

The site did not want to upload the logs for some reason, but I can send them if you think they would be useful.
 
Back
Top Bottom