• We are currently performing site maintenance, parts of civfanatics are currently offline, but will come back online in the coming days. For more updates please see here.

Trade Route Bug - Disappearing Trade Units and Self-Generating Trade Routes

Chrisy15

Flower, Beautiful
Joined
Jul 9, 2015
Messages
2,136
Using the method Unit.SetMoves on a Trade Unit is BAD.

Finally managed to solve a bug which I'd occasionally observed while playing, but which I'd struggled to find any discussion about online. It's caused a very specific action in a Lua script, and is easy to just not notice while playing, so I thought it'd be worth making an informational post about so that modders can avoid creating the problem.

The issue is caused by the method Unit.SetMoves being used on a Trade Unit to grant said Unit movement points.

Using SetMoves on a Trade Unit in a foreign Major Civilization's City, or which are not on a City tile, appears to have no consequence.
Using SetMoves on a Trade Unit in a Minor Civilization's City causes the Unit to be deleted but causes the Trade Route itself to continue; this allows a Civilization to construct more Trade Routes than they are meant to as these Unit-less Trade Routes do not contribute towards your Trade Route capacity.
Using SetMoves on a Trade Unit in one of its owner's Cities causes the Unit to move two tiles towards a target City, but if possible the Trade Unit will create a new Trade Route by itself - self-determining a new target City - rather than following the Trade Route which it is on. This again does not delete the previous Trade Route.

The solution to this problem is to take conscious effort to avoid Unit.SetMoves being invoked on Trade Units. Player.Units returns Trade Units as part of its iteration; Plot.GetNumUnits and Plot.GetUnit don't give you Trade Units, but Plot.GetLayerUnit does. Unit.IsTrade returns true for Trade Units, and false for everything else.
These problems can occur on GameEvents.PlayerDoTurn, but will apply whenever SetMoves is invoked on a Trade Unit in a City. Unit.ChangeMoves might not cause these problems, nor does passing a value of 0 into Unit.SetMoves, but the same precautions may as well be employed anyway.

So if you're writing code which uses SetMoves take care with what Units you're invoking the method on, and if you observe one of these issues in your game then this is what you'll want to trawl through your mod list looking for.

Enterprising minds might try and make something useful of the self-generated trade route interaction for some kind of effect, but anyone who does so can test the consequences themselves.
 
Last edited:
In a fun new update to this story, it has recently been discovered that the Trade Route Bug can also be caused by enemy Citadels!

In spite of Caravans and Cargo Ships being unable to partake in combat, they do in fact take attrition damage from Citadels controlled by Players at war with their owners. When one of these units die to attrition, the trade route does not die with it - in a similar manner to the error which occurs when a trade unit is granted movement while inside a City-State.

Unlike the discovery which incited this thread, this isn't an issue that modders or players have agency over. I suppose this offers a definitive direction that Lua scripts should not allow trade units to suffer damage through Unit.SetDamage, lest this issue be caused under more common circumstances. As to preventing citadels from killing trade units, I will ask @whoward69 and The Community Patch Team to consider exempting trade units from the IsNearEnemyCitadel check in DoUnitReset - or even outright preventing trade units from taking damage or dying outside of pillaging, given that their individual deaths are clearly not handled well by the engine - in their DLL mods.

The point of this post was to explain another way in which the Trade Route Bug can occur; the discovery that the bug can be caused by something other than an easily identifiable and correctable scripting error is frustrating, and I'm afraid to say that players who encounter this problem will have little recourse to solve it. The best I could offer would be running something such as
Code:
GameEvents.PlayerDoTurn.Add(function(playerID) local pPlayer = Players[playerID] for pUnit in pPlayer:Units() do if pUnit:IsTrade() then pUnit:SetDamage(0) end end end)
in FireTuner to heal any and all damage trade units take, before they can die at all.
 
Back
Top Bottom