Question about lua debugging, constructing a building immediately when a district is completed

Odofakyodo

Chieftain
Joined
Aug 3, 2021
Messages
10
Hello!

In the mod I'm working on I'm having a trouble accomplishing something that seems like it should be fairly straightforward to do, but I'm having some issues with it. In the mod, I have created a new district, let's call it Foo. I have also created a new building, let's call it Bar. I want to set it up so that when a Foo district has finished construction then a Bar building is automatically created in it (sort of like what happens when Babylon constructs its first Campus and then a Library is automatically created). I have added Foo and Bar in the game's databases and I am able to successfully construct both a Foo and a Bar in the game through "normal" gameplay.

The problem I'm running into is that I cannot get a Bar to be automatically built through gameplay scripts. I have added a new gameplay script - MyScript.lua - to the In-Game actions and it appears to be getting executed. However, the script appears to be halting execution in the middle of my function and I do not know why. Here is the entire script file:

Code:
-- ===========================================================================
function OnDistrictConstructed(iPlayer, eDistrictType, iX, iY)
    
    print("OnDistrictConstructed: BEGIN");
    print("OnDistrictConstructed:   eDistrictType = " .. eDistrictType);
    print("OnDistrictConstructed:   (".. iX .. "," .. iY .. ")");

    -- If this is a Foo District, then immediately construct the Bar Building.
    if (eDistrictType == GameInfo.Districts["DISTRICT_FOO"].Index) then
        print("OnDistrictConstructed:   District is a Foo!");
        local pPlot = Map.GetPlot(iX, iY);
        print("OnDistrictConstructed:   Plot index is " .. pPlot:GetIndex());
        local pCity = Cities.GetCityInPlot(iX, iY);
        print("OnDistrictConstructed:   City is " .. pCity:GetName());
        pCity:CreateBuilding(GameInfo.Buildings["BUILDING_BAR"].Index, 100, pPlot:GetIndex());
    end

    print("OnDistrictConstructed: END");
end

GameEvents.OnDistrictConstructed.Add(OnDistrictConstructed);

When I construct a Foo district in the game, the output in Lua.log ends with

Code:
MyScript: OnDistrictConstructed: BEGIN
MyScript: OnDistrictConstructed:   eDistrictType = 36
MyScript: OnDistrictConstructed:   (12,15)
MyScript: OnDistrictConstructed:   District is a Foo!
MyScript: OnDistrictConstructed:   Plot index is 672

Due to the fact that the city name is not printed out, the function seems to fail on the line

Code:
local pCity = Cities.GetCityInPlot(iX, iY);

However, it is strange to me that there are no error messages. I've scoured other lua scripts and there are quite a few in both the base game and other mods that use "Cities.GetCityInPlot()", so I'm not sure what is wrong here. I'm a game programmer by trade and recently gotten into modding Civ VI. I have many many years experience with C++, but Lua is new to me, so I don't know if there's something wrong I'm doing with the language, or if there's something missing from my mod setup. I'm hoping someone can offer some insight, or at least give me some tips on debugging the lua script.

Thanks!
 

Zegangani

King
Joined
Oct 9, 2020
Messages
819
I think that's because iX and iY return the position of the recently constructed District, not of the City Center. So with:
Code:
pCity = Cities.GetCityInPlot(iX, iY);
You're tryingng to get the City in that plot, where there is no city but a regular district. (Though no clue why there is no errors in the log)

What you should try instead is getting the owner City of the plot. I'm not on my PC rn, so can't give you the method/object used for that, but you can easily find it in the modding companion or the game's files.
 

Odofakyodo

Chieftain
Joined
Aug 3, 2021
Messages
10
Hi Zegangani, thanks for the reply! I was thinking that GetCityInPlot() would retrieve the city from any plot in that city, but I ended up trying GetPlotPurchaseCity() and that seems to work. At least, now I can get the city and its name is printing out.

I also realized that the code I had before for building the city was incorrect, and I am currently trying the method suggest in this post. The code now looks like this:
Code:
-- ===========================================================================
function OnDistrictConstructed(iPlayer, eDistrictType, iX, iY)
    
    print("OnDistrictConstructed: BEGIN");
    print("OnDistrictConstructed:   eDistrictType = " .. eDistrictType);
    print("OnDistrictConstructed:   (".. iX .. "," .. iY .. ")");

    -- If this is a Foo District, then immediately construct the Bar Building.
    if (eDistrictType == GameInfo.Districts["DISTRICT_FOO"].Index) then
        print("OnDistrictConstructed:   District is a Foo!");
        local pPlot = Map.GetPlot(iX, iY);
        print("OnDistrictConstructed:   Plot index is " .. pPlot:GetIndex());
        local pCity = Cities.GetCityInPlot(iX, iY);
        print("OnDistrictConstructed:   City is " .. pCity:GetName());
        local pBuildQueue = city:GetBuildQueue();
        print("OnDistrictConstructed:   After GetBuildQueue()");
        pBuildQueue:CreateIncompleteBuilding(GameInfo.Buildings["BUILDING_BAR"].Index, pPlot:GetIndex(), 100);
    end

    print("OnDistrictConstructed: END");
end

GameEvents.OnDistrictConstructed.Add(OnDistrictConstructed);
However, this is still not successful. I see the city name getting printed to the Lua.log (and in FireTuner console), but strangely the "After GetBuildQueue()" line and the "END" line do not print. This suggests that something is wrong with the following line:
Code:
local pBuildQueue = city:GetBuildQueue();
I see that GetBuildQueue() appears to be mostly used in UI scripts. Tutorial.lua looks to be a gameplay script and seems to be the only exception I can find. I also noticed that GetCityInPlot() seems to be exclusively used by UI scripts. I'm wondering if I'm using functions in the wrong context? Is there a reliable way to know that? What happens if you do that? My current hypothesis is that calling a function in the wrong context results in the execution just silently failing. And I am also starting to wonder if it's even possible to create a building in the gameplay context since all of the examples I can find (CreateIncompleteBuilding(), CreateBuilding(), and CityManager.RequestOperation()) are in UI Scripts. I may try switching to the UI context and see if I find more success.
 

Odofakyodo

Chieftain
Joined
Aug 3, 2021
Messages
10
Ha! Shortly after my last post I tried copying a different building construction example (from WorldBuilderPlacement.lua) and it seemed to work. Whew!

Code:
local bStatus : boolean;
local sStatus : table;
bStatus, sStatus = WorldBuilder.CityManager():CreateBuilding(pCity, GameInfo.Buildings["BUILDING_BAR"].Index, 100, pPlot:GetIndex());

I'm still not sure why the build queue method did not work, but I suspect the context was the problem.
 
Top Bottom