GameEvents:Cityconnection.add() and GameEvents:CityConnected.Add()

Cat-du-fromage

Warlord
Joined
Nov 17, 2018
Messages
183
Hi!

i recently found a post about those beautiful gameevents^^ (Cityconnections and CityConnected) BUT Impossible to make them works..
I'm using the vox populi DLL.

LINK ok the post: https://forums.civfanatics.com/threads/river-connections.522983/

Code:
--=====================================================
--UAConnectionCities
--=====================================================
function UAConnectionT(PlayerID,iCityX, iCityY, iToCityX, iToCityY, bDirect)
    local BuildingDummyForConnected = GameInfoTypes.BUILDING_DF_CONNECTED
    local player = Players[PlayerID]
if player:GetCivilizationType() == civilizationID then
    for city in player:Cities() do
    print("true ok2")
        if Player:IsCapitalConnectedToCity(city) and player:IsEverAlive() and not city:IsCapital() and ((not bDirect) or bDirect) then
        local pCity = Map.GetPlot(iCityX, iCityY):GetPlotCity()
        local pToCity = Map.GetPlot(iToCityX, iToCityY):GetPlotCity()
        city:SetNumRealBuilding(GameInfoTypes.BUILDING_DF_CONNECTED,1)
        print("true ok3")
        end
return true
    end
end
return false
end
--GameEvents.CityConnections.Add(UAConnectionT)
GameEvents.CityConnected.Add(UAConnectionT)
--GameEvents.PlayerDoTurn.Add(UAConnection)
Code:
--=============================================================================================
--KH_Viribus Unitis
--=============================================================================================
function ViribusUnitisT(PlayerID,iCityX, iCityY, iToCityX, iToCityY,bDirect)
    local player = Players[PlayerID]
    local baseCombatStrength
    local NBDummy = player:CountNumBuildings(GameInfoTypes.BUILDING_DF_CONNECTED)
    local NbConnect = NbCityConnected()

    if player:GetCivilizationType() == civilizationID and (not bDirect) then
        for unit in player:Units() do
            if unit:IsHasPromotion(GameInfoTypes.PROMOTION_VIRIBUS_UNITIS) and unit:GetUnitCombatType() == GameInfoTypes.UNITCOMBAT_MELEE or unit:GetUnitCombatType() == GameInfoTypes.UNITCOMBAT_GUN then
                local sUnit = unit:GetBaseCombatStrength()
                local unitType = unit:GetUnitType();
              
                local force = GameInfo.Units[unit:GetUnitType()].Combat;
                unit:SetBaseCombatStrength(force + (1*(NbCityConnected())))
                print("trueVU ok1")
                return true
                elseif unit:IsHasPromotion(GameInfoTypes.PROMOTION_VIRIBUS_UNITIS) and unit:GetUnitCombatType() == GameInfoTypes.UNITCOMBAT_SIEGE then
                        local rUnitType = unit:GetUnitType();
                        local rForce = GameInfo.Units[unit:GetUnitType()].RangedCombat;
                        unit:SetBaseRangedCombatStrength(rForce + (1*(NbCityConnected())))
                        print("trueVU ok2")
                        return true
                end
        end
    end
    return false
end
GameEvents.CityConnections.Add(ViribusUnitisT)
GameEvents.CityConnected.Add(ViribusUnitisT)
--GameEvents.PlayerDoTurn.Add(ViribusUnitis)

EDIT: i Know the code is not yet quite optimised^^' i tweaked it so much it became a mess
 
Last edited:
First off you will need to enable the events in the <CustomModOptions> (assuming that this is not done so already):
Code:
--This is an SQL-file that needs an OnModActivated->UpdateDatabase entry
UPDATE CustomModOptions
SET Value = 1
WHERE Name = "EVENTS_CITY_CONNECTIONS";

If this is not done, then your code will never run in the first place!

NB: I'm assuming you've also set up a dependency for Vox Populi (or the VMC/CP DLL) in your mod's properties, which ensures that that mod loads before your mod.


You also seem to be understanding these GameEvents wrong. GameEvents.CityConnections is similar to GameEvents.PlayerCanConstruct. Once at least one method that is hooked into the event returns true (or false, dependent on the function), it enables something else.
For the case of GameEvents.PlayerCanConstruct, it disallows a specific player to construct a specific building once any method hooked into it returns false.
For the case of GameEvents.CityConnections, it will enable the GameEvents.CityConnected hook once any method hooked into it returns true.

Similarly, if any method hooked into GameEvents.CityConnected returns true, then the game will add a 'City Connection' between the two cities that were passed as a parameter. This allows for custom city connections to be added, such as Air Routes or city connections via rivers. In other words, GameEvents.CityConnected does NOT fire when two cities become connected!

(The reason why two city connection-GameEvents are needed to do a single thing is to reduce computation time. GameEvents.CityConnected will fire many, many, many times!)
 
Last edited:
arf... as i feared, so there is no way to "fire" a function when a city connection is established?
 
arf... as i feared, so there is no way to "fire" a function when a city connection is established?
There is actually no need for one. All city connection logic is handled at the start of each turn. This is also the time when the cityConnected/cityconnection-broken notifications are send out.
So what you can do is find out whether a city is connected to a player's capital is to call pPlayer:IsCapitalConnectedToCity(pCity) in GameEvents.PlayerDoTurn(..).

Below is a very simple setup that fires a LuaEvent when a city connection is made or broken.
It stores the connection status of each city each turn, and if that status does not match the current status (E.g. last turn the city was connected, but now it isn't), then we know that the city got (dis)connected!
Spoiler :

I expect this to work for City States and Barbarians too, but I did not test such scenario specifically.
Note that the LuaEvent will also fire for the capital city of a player.

The LuaEvent works with the CityConnected-event added by VMC/CP. (tested with Whoward's "Global - Air Routes" mod that adds city connections between cities that have an airport).
The LuaEvent does NOT fire mid-turn when a city becomes blockaded/un-blockaded due to an enemy moving into or dying in range of cities with a harbour. (This could be fixed by relying on GameEvents.UnitSetXY(..) and GameEvents.UnitPrekill(..) but that introduced quite a bit of lag for such an edge case.)
Code:
--per player, stores whether each of their cities is connected to the capital
local tConnections = {};

function initCityConnectionTable()
    for iPlayer = 0, GameDefines.MAX_PLAYERS-1,1 do
        local pPlayer = Players[iPlayer];
        if pPlayer:IsEverAlive() then
            tConnections[iPlayer] = {};
        end
    end
end
initCityConnectionTable();


function findNewCityConnections(iPlayer)
    local pPlayer = Players[iPlayer];
    for pCity in pPlayer:Cities() do
        local iCity = pCity:GetID();
        if tConnections[iPlayer][iCity] == nil and pPlayer:IsCapitalConnectedToCity(pCity) then
            --new city connection established
            tConnections[iPlayer][iCity] = true;
            LuaEvents.CityConnectionChanged(iPlayer, iCity, true);
        elseif tConnections[iPlayer][iCity] == true and not pPlayer:IsCapitalConnectedToCity(pCity) then
            --city connection broken
            tConnections[iPlayer][iCity] = nil;
            LuaEvents.CityConnectionChanged(iPlayer, iCity, false);
        end
    end
end
GameEvents.PlayerDoTurn.Add(findNewCityConnections);

--==============================================================================

--Example of how to use the LuaEvent
function onCityConnectionChange(iPlayer, iCity, bEstablished)
    local pPlayer = Players[iPlayer];
    local pCity = pPlayer:GetCityByID(iCity);
    if bEstablished then
        print(""..pPlayer:GetName().." ("..iPlayer..") established a City Connection between their Capital City and "..pCity:GetName().." ("..iCity..")");
    else
        print(""..pPlayer:GetName().." ("..iPlayer..") broke a City Connection between their Capital City and "..pCity:GetName().." ("..iCity..")");
    end
end
LuaEvents.CityConnectionChanged.Add(onCityConnectionChange);
 
Last edited:
Very very interesting, i'm just wondering if this method is heavy in term of calculation?

Normally a try to avoid as much as possible the
"GameEvents.PlayerDoTurn.Add"

Had some bad experiences using to much of these^^
 
PlayerDoTurn is one of the "lighter" events - only firing once per player turn, unlike some of the other events that can fire many, many times during every players turn. The code given also only checks each city (of which there are typically few) than, say each unit or owned plot.
 
Top Bottom