Using LUA to replace/prevent additional settlers?

Symphony

Chieftain
Joined
Jul 18, 2018
Messages
3
Hi there, I'm currently trying to find a way to either replace (or delete) all settlers that spawn beyond the initial one (Collective Rule, starting in a later era, captured, being gifted one etc.) or prevent founding more than the capital for my custom civilization. I managed to find an example snippet of Lua (posted below) that works to some extent - when you try to move with a settler it'll replace them with a worker instead - however the problem is that you can softlock the game if you move your last remaining settler as it will replace that too. Also if you could somehow get control of a settler enough tiles away from your capital you'd be able to found another city as long as you didn't move the unit.

Unfortunately I really don't understand Lua, and despite many hours of trawling around I just can't work out how to do this. Is there anyone who could help?

Code:
function ReplaceSettler(iPlayer, iUnitID, iX, iY)
    local pPlayer = Players[iPlayer];
    if pPlayer:GetCivilizationType() == GameInfo.Civilizations.CIVILIZATION_PRIPRICH.ID then
        local pUnit = pPlayer:GetUnitByID(iUnitID);

        if (pUnit == nil or pUnit:IsDelayedDeath()) then
            return false;
        end

        if pUnit:GetUnitType() == GameInfoTypes["UNIT_SETTLER"] then
            newUnit = pPlayer:InitUnit(GameInfoTypes["UNIT_WORKER"], iX, iY);
            newUnit:FinishMoves();
            pUnit:Kill(true, -1);
        end
    end
end

GameEvents.UnitSetXY.Add(ReplaceSettler);
 
You can resolve the issue of losing your initial settler by inserting the line
Code:
if not pPlayer:GetCapitalCity() then return end
Within the block where you checked if pUnit is a settler. This line will end the function if the player has no capital (and thereby no cities) before it has a chance to replace the settler with a worker.

Keep in mind that this can be exploited if you start with extra settlers and move them all to where you want them before founding your first city.

You can also prevent the player from receiving settlers via collective rule, etc. in the first place by setting the NoAnnexing column to 'true' in the traits table (you'd think that would be handled by the Trait_NoTrain table, which I did, but I guess not). Not sure if you wanted your civ to be able to annex cities or not.
 
You can resolve the issue of losing your initial settler by inserting the line
Code:
if not pPlayer:GetCapitalCity() then return end
Within the block where you checked if pUnit is a settler. This line will end the function if the player has no capital (and thereby no cities) before it has a chance to replace the settler with a worker.

Keep in mind that this can be exploited if you start with extra settlers and move them all to where you want them before founding your first city.

You can also prevent the player from receiving settlers via collective rule, etc. in the first place by setting the NoAnnexing column to 'true' in the traits table (you'd think that would be handled by the Trait_NoTrain table, which I did, but I guess not). Not sure if you wanted your civ to be able to annex cities or not.

Yeah, unfortunately NoAnnexing didn't really suit my needs due to being so hard coded, I basically wanted to have Austria's ability but also be unable to earn or use settlers, forcing a balancing act between expansion and retaining city state allies due to various bonuses from that.

Thanks for that bit of code, it has solved the softlock problem which was the immediate concern, although it is a shame that then lets you exploit later era starts. Is there any way to maybe use that code for checking if you have a capital to either disable the Found City action, or immediately delete any of your remaining settlers that are still in play before they can take an action?
 
I was considering another function that would run when the player founded their first city and delete any extra settlers belonging to that player, but I decided an easier way to fix the exploit is to change WHEN ReplaceSettler itself runs. I've rewritten the function such that it now runs the instant a unit is created instead of whenever a unit moves. This allows it to catch any settlers the moment they're created and replace them with workers, including those added at the start of the game via later start era.
Code:
local pFirstSettler = nil
function ReplaceSettler(iPlayer, iUnit)
    local pPlayer = Players[iPlayer]
    --End the function ASAP if this isn't our civ
    if not pPlayer:GetCivilizationType() == GameInfo.Civilizations.CIVILIZATION_PRIPRICH.ID then return end
    --Does the player have any cities? If so, they've had a settler!
    if pPlayer:GetCapitalCity() then
        pFirstSettler = true
    end
    local pUnit = pPlayer:GetUnitByID(iUnit)
    --Just a sidenote: if you're going to check if pUnit (or any object) == nil, you CANNOT invoke it on the same line as that will cause a runtime error if it is nil!
    --if pUnit == nil then return end
    --if pUnit:IsDelayedDeath() then return end --I'll be honest, not too keen on what this does, but leaving it just in case!
    if pUnit:GetUnitType() == GameInfoTypes["UNIT_SETTLER"] then
        --If pFirstSettler is anything but nil, AND it does not match pUnit...
        if pFirstSettler and pFirstSettler ~= pUnit then
            --Then this is an extra settler. Delete it!
            newUnit = pPlayer:InitUnit(GameInfoTypes["UNIT_WORKER"], pUnit:GetX(), pUnit:GetY())
            newUnit:FinishMoves()
            pUnit:Kill(true, -1)
        else
            --Otherwise, consider this our first settler
            pFirstSettler = pUnit
        end
    end
end
Events.SerialEventUnitCreated.Add(ReplaceSettler)
I did some basic testing and it seems to work. Just replace your current function with this one and let me know if you run into any issues.
 
Wonderful, I've tested everything I can think of that might cause a problem and it works perfectly. Thanks for your help.
 
Top Bottom