Removeable City Center Buildings

DonCan94

Chieftain
Joined
Dec 21, 2020
Messages
26
First things first, I have zero experience at modding, so pls be gentle :)

As the title suggests, I tried to create a mod that allows me to remove city center buildings. I only want this because of visuals, I don't like how the walls in civ 6 look. My attempt was to take an existing similar mod, Removable Districts in this case, and then edit the files so it would do the same action but for a different thing. 3 files are SQL queries, which I'm pretty ok at, so these should work, but one is a LUA script, where I'm an absolute beginner. So I tried to simply replace the words 'District' with 'Building' but yeah, this obviously didn't work.
It would be really nice if someone could help me out with this, the LUA script also isn't long :) (19 lines without comments)
 
I actually have something like this;

-- print ("------------------------------");
-- print ("------------------------------");
-- print ("REMOVABLE CITY CENTER BUILDINGS LOADED");
-- print ("------------------------------");
-- print ("------------------------------");
local function OnProjectCompleted(playerID, cityID, projectIndex, buildingIndex, locX, locY, bCanceled)
-- print ("------------------------------");
local pPlayer = Players[playerID];
-- local pCity = pPlayer:GetCities():FindID( cityID );
local sProjectTypeCompleted = GameInfo.Projects[projectIndex].ProjectType;

-- print (Locale.Lookup(pCity:GetName()) .. " at " .. pCity:GetX() .. " " .. pCity:GetY() .." complered project " ..sProjectTypeCompleted);
if pPlayer:IsHuman() and string.find(sProjectTypeCompleted, "PROJECT_REMOVE_BUILDING_") then
local sCivType = PlayerConfigurations[playerID]:GetCivilizationTypeName();
local pCity = pPlayer:GetCities():FindID( cityID );
local pCityBuildings :table = pCity:GetBuildings();
local sBuildingType = GameInfo.Projects[projectIndex].PrereqBuilding;
local iBuildingID = GameInfo.Buildings[sBuildingType].Index;

for row in GameInfo.RD_civType_uniqueBuildingType() do
if row.CivType == sCivType and row.ReplacedBuildingType == sBuildingType then
-- print(row.UniqueBuildingType .." unique ");
iBuildingID = GameInfo.Buildings[row.UniqueBuildingType].Index;
break;
end
end
-- print(sBuildingType .." removed");
pCityBuildings:RemoveBuilding(iBuildingID);
-- CityManager.DestroyBuilding(pBuilding);
-- else
-- print("Not a building removal project");
end
end

Events.CityProjectCompleted.Add(OnProjectCompleted);


Is there a way how I can debug this code to identify possible syntax errors?
The 3 adjusted SQL querries for example all work fine, I tested them with DB Browser. I appended them to this post as text files if you wanna have a look.

And thanks for the reply :)
 

Attachments

  • RemovableCityCenterBuildings_Icons.txt
    176 bytes · Views: 31
  • RemovableCityCenterBuildings.txt
    1.3 KB · Views: 35
  • RemovableCityCenterBuildings_text.txt
    215 bytes · Views: 49
The definition for table "Projects":
Code:
CREATE TABLE "Projects" (
		"ProjectType" TEXT NOT NULL,
		"Name" TEXT NOT NULL,
		"ShortName" TEXT NOT NULL,
		"Description" TEXT,
		"PopupText" TEXT,
		"Cost" INTEGER NOT NULL,
		"CostProgressionModel" TEXT NOT NULL DEFAULT "NO_PROGRESSION_MODEL",
		"CostProgressionParam1" INTEGER NOT NULL DEFAULT 0,
		"PrereqTech" TEXT,
		"PrereqCivic" TEXT,
		"PrereqDistrict" TEXT,
		"RequiredBuilding" TEXT,
		"VisualBuildingType" TEXT,
		"SpaceRace" BOOLEAN NOT NULL CHECK (SpaceRace IN (0,1)) DEFAULT 0,
		"OuterDefenseRepair" BOOLEAN NOT NULL CHECK (OuterDefenseRepair IN (0,1)) DEFAULT 0,
		"MaxPlayerInstances" INTEGER,
		"AmenitiesWhileActive" INTEGER,
		"PrereqResource" TEXT,
		"AdvisorType" TEXT,
		"WMD" BOOLEAN NOT NULL CHECK (WMD IN (0,1)) DEFAULT 0,
		"UnlocksFromEffect" BOOLEAN NOT NULL CHECK (UnlocksFromEffect IN (0,1)) DEFAULT 0,
		PRIMARY KEY(ProjectType),
		FOREIGN KEY (PrereqTech) REFERENCES Technologies(TechnologyType) ON DELETE SET DEFAULT ON UPDATE SET DEFAULT,
		FOREIGN KEY (PrereqCivic) REFERENCES Civics(CivicType) ON DELETE SET DEFAULT ON UPDATE SET DEFAULT,
		FOREIGN KEY (PrereqDistrict) REFERENCES Districts(DistrictType) ON DELETE SET DEFAULT ON UPDATE SET DEFAULT,
		FOREIGN KEY (VisualBuildingType) REFERENCES Buildings(BuildingType) ON DELETE SET DEFAULT ON UPDATE SET DEFAULT,
		FOREIGN KEY (PrereqResource) REFERENCES Resources(ResourceType) ON DELETE SET DEFAULT ON UPDATE SET DEFAULT,
		FOREIGN KEY (ProjectType) REFERENCES Types(Type) ON DELETE CASCADE ON UPDATE CASCADE,
		FOREIGN KEY (RequiredBuilding) REFERENCES Buildings(BuildingType) ON DELETE CASCADE ON UPDATE CASCADE);
Note that the FOREIGN KEY constraint for column PrereqDistrict is a DistrictType from table Districts whereas you are supplying
Code:
BuildingType
FROM Buildings .....
Which ought to shove you back to the game's main menu because of an InvalidReference error.

Also note the final FOREIGN KEY and its related Column in the definition of table "Projects".

I would also change this line
Code:
FROM Buildings WHERE PrereqDistrict = 'DISTRICT_CITY_CENTER' AND Capital = 0;
to
Code:
FROM Buildings WHERE PrereqDistrict = 'DISTRICT_CITY_CENTER' AND Capital = 0 AND InternalOnly=0;
In order to give maximum built-in compatibility to other mods.
 
I would not think you would actually need this
Code:
CREATE TABLE "RCCB_civType_uniqueBuildingType" (
		"CivType" TEXT NOT NULL,
		"ReplacedBuildingType" TEXT NOT NULL,
		"UniqueBuildingType" TEXT NOT NULL);

INSERT INTO RCCB_civType_uniqueBuildingType ( CivType, ReplacedBuildingType, UniqueBuildingType )
 SELECT a.CivilizationType, c.ReplacesBuildingType, b.BuildingType FROM CivilizationTraits AS a, Buildings AS b, BuildingReplaces AS c WHERE a.TraitType = b.TraitType AND c.CivUniqueBuildingType = b.BuildingType AND b.PrereqDistrict = 'DISTRICT_CITY_CENTER' AND b.Capital = 0;
nor its related code in the lua script because even if it is a CivUniqueBuilding if it is a City Center building that is not a Palace (and in my suggested amendment an InternalOnly building) it will already be listed as a "Project" from your code here:
Code:
INSERT INTO Types ( Type, Kind )
 SELECT 'PROJECT_REMOVE_'||BuildingType, 'KIND_PROJECT' 
FROM Buildings WHERE PrereqDistrict = 'DISTRICT_CITY_CENTER' AND Capital = 0;


INSERT INTO Projects (
    ProjectType,
    Name,
    ShortName,
    Description,
    Cost,
    CostProgressionModel,
    CostProgressionParam1,
    PrereqDistrict )
SELECT 	
	'PROJECT_REMOVE_'||BuildingType,
	CASE WHEN instr(Name, 'LOC_') = 0 THEN 'Remove '||Name ELSE 'LOC_PROJECT_REMOVE'||substr(Name, 4) END,
	CASE WHEN instr(Name, 'LOC_') = 0 THEN 'Remove '||Name ELSE 'LOC_PROJECT_REMOVE'||substr(Name, 4) END,
    'Removes this building.',
    80,
    'COST_PROGRESSION_GAME_PROGRESS',
    200,
    BuildingType
FROM Buildings WHERE PrereqDistrict = 'DISTRICT_CITY_CENTER' AND Capital = 0;
Players cannot construct buildings they do not have the trait for when a trait is assigned to a building so it does not matter that there will be "extra" projects that a specific player may never be able to use because they can never create BUILDING_X. Your lua code can simply access the PrereqBuilding as you are doing here and eliminate said building:
Code:
local sBuildingType = GameInfo.Projects[projectIndex].PrereqBuilding;
		local iBuildingID = GameInfo.Buildings[sBuildingType].Index;

In your lua code you can easily cure any possible issues of the game conflating a unique version of a building with the standard one by a slight alteration here
Code:
local bDebug = true
function Debug(sMessage)
	if bDebug then print(sMessage) end
end
function OnProjectCompleted(playerID, cityID, projectIndex, buildingIndex, locX, locY, bCanceled)
	Debug("------------------------------")
	local pPlayer = Players[playerID];
	local pCity = pPlayer:GetCities():FindID( cityID );
	local sProjectTypeCompleted = GameInfo.Projects[projectIndex].ProjectType;

	Debug(Locale.Lookup(pCity:GetName()) .. " at X" .. pCity:GetX() .. ", Y" .. pCity:GetY() .. " completed project " .. sProjectTypeCompleted)
	if pPlayer:IsHuman() and string.find(sProjectTypeCompleted, "PROJECT_REMOVE_BUILDING_") then
		Debug("playerID " .. playerID .. " was judged as a Human and the completed project as a building removal project");
		local sCivType = PlayerConfigurations[playerID]:GetCivilizationTypeName();
		local pCityBuildings :table = pCity:GetBuildings();
		local sBuildingType = GameInfo.Projects[projectIndex].PrereqBuilding;
		Debug("sBuildingType was evaluated as " .. sBuildingType);
		local iBuildingID = GameInfo.Buildings[sBuildingType].Index;
		Debug("iBuildingID was evaluated as " .. iBuildingID);

		--for row in GameInfo.RD_civType_uniqueBuildingType() do
		--	if row.CivType == sCivType and row.ReplacedBuildingType == sBuildingType then
		--		-- print(row.UniqueBuildingType .." unique ");
		--		iBuildingID = GameInfo.Buildings[row.UniqueBuildingType].Index;
		--		break;
		--	end
		--end
		if pCityBuildings:HasBuilding(iBuildingID) then
			pCityBuildings:RemoveBuilding(iBuildingID);
			Debug(sBuildingType .. " removed");
		else
			Debug("The City was evaluated as not having building " .. sBuildingType);
		end
	else
		Debug("Not a building removal project");
	end
	Debug("------------------------------")
end
Events.CityProjectCompleted.Add(OnProjectCompleted);
print ("------------------------------");
print ("------------------------------");
print ("REMOVABLE CITY CENTER BUILDINGS LOADED");
print ("------------------------------");
print ("------------------------------");
The only effective way to debug lua code is to run it ingame with print statements sprinkled through the code to be able to see what happens at each logical step of the process, which I have added as an easily disabled part of the code.
 
First of all; Thanks for this detailed answer! :)

Note that the FOREIGN KEY constraint for column PrereqDistrict is a DistrictType from table Districts whereas you are supplying

This is a good point, it makes no sense to insert BuildingType into PrereqDistrict, so I replaced it with the RequiredBuilding column instead.

Which ought to shove you back to the game's main menu because of an InvalidReference error.

The thing is when I start a game with the mod activated, it always loads the game but without it. It's not in the mods list in the menu nor can I remove a built city centre building. I don't get thrown back into the main menu, so maybe I do something wrong in the first place?

The only effective way to debug lua code is to run it ingame with print statements sprinkled through the code to be able to see what happens at each logical step of the process, which I have added as an easily disabled part of the code.

I tried to run the game while I have the FireTuner open because there is something called LuaConsole, but I don't see output about my mod there.
 
Sounds like incorrect activation methods to me. But it may be some other simple mistake. Even if you do not have Firetuner (aka also called LiveTuner) enabled you should see print commands reported in the Lua.log file if you have print statements in the code and the script is actually being used.

Zip the mod as it is in the game's Mods folder at ~\Documents\My Games\Sid Meier's Civilization VI\Mods and attach to a post.
 
Ok, I discovered Project > Properties > In-Game Action, now my mod actually changes stuff in the game. Sorry, as I stated before, I'm completely new to modding.

I now see the projects to remove buildings, but I see all of them, even if the corresponding building isn't built yet. But at least they are greyed out and not selectable. (see screenshot rccb-sc01.png)

When I complete the project, I this error message in the Live Tuner:

RemovableCityCenterBuildings: ------------------------------
RemovableCityCenterBuildings: Uruk at X38, Y8 completed project PROJECT_REMOVE_BUILDING_MONUMENT
RemovableCityCenterBuildings: playerID 0 was judged as a Human and the completed project as a building removal project
Runtime Error: C:\Users\Username\Documents\My Games\Sid Meier's Civilization VI\Mods\RemovableCityCenterBuildings\RemovableCityCenterBuildings.lua:17: operator .. is not supported for string .. nil
stack traceback:
C:\Users\Username\Documents\My Games\Sid Meier's Civilization VI\Mods\RemovableCityCenterBuildings\RemovableCityCenterBuildings.lua:17: in function 'OnProjectCompleted'
[C]: in function 'func'
[C]: in function '(anonymous)'

Then building is still present in the city, both visually and also in the City Details, and the removal project can be started again.
Is there maybe a problem with the debug line, since it has double periods?
EDIT: no it isn't, its
local pCityBuildings :table = pCity:GetBuildings();

But thanks a lot, now I can debug the lua code and try some stuff out :)
 

Attachments

  • rccb-sc01.png
    675.7 KB · Views: 42
  • rccb-output01.png
    582.5 KB · Views: 42
Last edited:
looks like this line is returning a nil value
Code:
local sBuildingType = GameInfo.Projects[projectIndex].PrereqBuilding;
And you can't concatenate nil with a text string, which is what this line would be doing in such a case
Code:
Debug("sBuildingType was evaluated as " .. sBuildingType);

Double period
Code:
..
is one method to concatenate (string together) one text string into another but you can never string together boolean, table values, or nil values with a text string.

The fact that you are getting nil there would tend to be usable debugging information since "sBuildingType" is not getting any valid data it would seem. An inspection of the actual database after your SQL code executes would be in order as a next step.
Code:
C:\Users\UserName\Documents\My Games\Sid Meier's Civilization VI\Cache/DebugGameplay.sqlite
It can be opened with any SQL database viewer program. You can not "hurt" it. The old copy is discarded by the game as part of game startup and a new fresh copy is made as the game loads and then as all mods add their code. It is then locked before ever you get to click on Start Journey or Continue Journey. The database is constructed before any InGameActions lua script executes.
 
It works now!

The problem was PrereqBuilding, this doesn't seem to exist but RequiredBuilding does!

Thanks a lot!

I will playtest this a bit with other buildings, situations etc.
Also, the icons and texts are not quite there yet, as you saw in the screenshot. I want to format the texts and make the removal projects invisibile if the building is not built yet.
Do you know in which databases the tables IconDefinitions and LocalizedText are?
I probably need to provide these custom icons for the game, how do I do that? (after I created them of course)

But again, I can't stress it enough, thank you so much :)
 
Last edited:
The Icons and Text files are in the same Folder as the Code's file. "RemovableDistricts_Icons.sql" and "RemovableDistricts_text.sql" (if you're just copied the Removable Districts Mod, to re-use it for Buildings).

I've an older Version so the code may be different in your case, but similar:
Code:
INSERT INTO IconDefinitions    ( Name, Atlas, "Index" )    -- MUST BE DOUBLE QUOTE
SELECT 'ICON_PROJECT_REMOVE'||substr(Name, 5), Atlas, "Index" FROM IconDefinitions WHERE Name LIKE 'ICON_DISTRICT_%';

INSERT INTO LocalizedText ( Language, Tag, Text )
 SELECT 'en_US', 'LOC_PROJECT_REMOVE'||substr(Tag, 4), 'Remove '||Text
FROM LocalizedText WHERE Language = 'en_US' AND Tag LIKE 'LOC_DISTRICT%NAME';

rename this to:
Code:
INSERT INTO IconDefinitions    ( Name, Atlas, "Index" )    -- MUST BE DOUBLE QUOTE
SELECT 'ICON_PROJECT_REMOVE'||substr(Name, 5), Atlas, "Index" FROM IconDefinitions WHERE Name LIKE 'ICON_BUILDING_%';

INSERT INTO LocalizedText ( Language, Tag, Text )
 SELECT 'en_US', 'LOC_PROJECT_REMOVE'||substr(Tag, 4), 'Remove '||Text
FROM LocalizedText WHERE Language = 'en_US' AND Tag LIKE 'LOC_BUILDING%NAME';
 
Yep, as I said in the first post, I simply took the files from the Removable Districts mod and rewrote it for city center buildings.
I have the same lines as you suggested, visible in the txt files I attached in one of my replies.

But this doens't seem to work as you can see in a previewious screenshot 'rccb-sc01.png' :/
 
Try this for Text:
Code:
INSERT INTO LocalizedText ( Language, Tag, Text )
SELECT
'en_US' as Language,
'LOC_PROJECT_REMOVE'||substr(Tag, 4) as Tag,
'Remove '||Tag  as Text,
FROM LocalizedText WHERE Language = 'en_US' AND Tag LIKE 'LOC_BUILDING%NAME';

For Icons:
Code:
INSERT INTO IconDefinitions ( Name, Atlas, "Index" )
SELECT
'ICON_PROJECT_REMOVE'||substr(Name, 5) as Name,
Atlas,
'Index',
FROM IconDefinitions WHERE Name LIKE 'ICON_BUILDING_%';
 
Ok I figured out the source of the problem; You need to define the text and icon update in the Properties as such, and not as UpdateDatabase, which I did because these are in SQL. My bad.

Now I just need to figure out how to hide the removal projects if the buildings are not built jet and how to change the project icons.

But thanks for the replies :)
 
Now I just need to figure out how to hide the removal projects if the buildings are not built jet and how to change the project icons.
Use requirements. Do the Icons Show up as the original Buildings or the same as before?
 
So you want to make the Icons have a ban circle on them, or something like that?
I can help you with that if you want.
 
Yes, exactly. I already created them, pretty simple ones, see the appended file.

So you want to make the Icons have a ban circle on them, or something like that?
I can help you with that if you want.
That would be really nice :)
 

Attachments

  • Remove_Monument.png
    Remove_Monument.png
    99.6 KB · Views: 38
OK! I'll have it done by Tomorrow.
 
I think you missunderstood me, I already have all the symbols, I need help to implement them ^^'

But thanks anyway :)
 
Top Bottom