Help LUA : infinit stack

Cat-du-fromage

Warlord
Joined
Nov 17, 2018
Messages
189
Hi,
Trying to rework a promotion from TCM's Austria-Hungary i had some problem when the bonus of +1 combat strenght stack every time i pick a promotion:

Zum Beispiel: Unit base combat strenght = 31,
Have Base 3 promotion
= Combat strenght = 34
Add 1 more promotion
= combat strenght = 38 WTF?????

In the base code, the strenght of the Unit was taken directly on [UNIT_TCM_GRENZER]

But now i try to use the ID to select the unit baseCombatStrength, since the promotion should not be lose on upgrade but it doesn't seem to work :undecide:

Code:
--------------------------------------------------------------
-- GE_Grenzer from TCM's Austria-Hungary
--------------------------------------------------------------
function GE_GetNumPromotions(unit)
        local numPromotions = 0
            if unit:IsHasPromotion(GameInfoTypes.PROMOTION_GRENZSCHUTZ) then
                for promotion in GameInfo.UnitPromotions() do
                        if unit:IsHasPromotion(promotion.ID) then
                            numPromotions = numPromotions + 1
                        end
                end
            end
        return numPromotions
end

local bonusPerPromotion = 1
local unitGrenzerID = GameInfoTypes["UNIT_TCM_GRENZER"]
local GEPromotion = GameInfoTypes["PROMOTION_GRENZSCHUTZ"]
local UnitGun = GameInfoTypes["UNITCOMBAT_GUN"]


function GE_Grenzer(playerID)
        local player = Players[playerID]
        if (player:GetCivilizationType() == civilisationID and player:IsEverAlive()) then
                for unit in player:Units() do
                        if unit:IsHasPromotion(GameInfoTypes.PROMOTION_GRENZSCHUTZ) then
                        local pUnit = unit:GetID() --Select Unit ID
                                local baseCombatStrength = pUnit:GetBaseCombatStrength() --Use Get base combat strenght from ID
                                if baseCombatStrength < (baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit)) then
                                    unit:SetBaseCombatStrength(baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit))
                                end
                              
                        end
                end
        end
end
    
if JFD_IsCivilisationActive(civilisationID) then
GameEvents.UnitPromoted.Add(GE_Grenzer)
GameEvents.UnitUpgraded.Add(GE_Grenzer)
        --GameEvents.PlayerDoTurn.Add(GE_Grenzer)
end

Note: the "pUnit:GetBaseCombatStrength()" doesn't work, it works only if i write "unit:GetBaseCombatStrength()"

Does someone see the error^^?
 
EDIT:
Code:
local bonusPerPromotion = 1
local unitGrenzerID = GameInfoTypes["UNIT_TCM_GRENZER"]
local unitGWIID = GameInfoTypes["UNIT_GREAT_WAR_INFANTRY"]
local unitInfantryID = GameInfoTypes["UNIT_INFANTRY"]
local unitMechInfantryID = GameInfoTypes["UNIT_MECHANIZED_INFANTRY"]
local GEPromotion = GameInfoTypes["PROMOTION_GRENZSCHUTZ"]
local UnitGun = GameInfoTypes["UNITCOMBAT_GUN"]


function GE_Grenzer(playerID, unitID)
        local player = Players[playerID]
        if (player:GetCivilizationType() == civilisationID and player:IsEverAlive()) then
                for unit in player:Units() do
                        if unit:GetUnitType() == unitGrenzerID then
                                local baseCombatStrength = GameInfo.Units["UNIT_TCM_GRENZER"].Combat
                                if baseCombatStrength < (baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit)) then
                                    unit:SetBaseCombatStrength(baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit))
                                    end
                            elseif unit:GetUnitType() == unitGWIID and unit:IsHasPromotion(GameInfoTypes.PROMOTION_GRENZSCHUTZ) then
                                    local baseCombatStrength = GameInfo.Units["UNIT_GREAT_WAR_INFANTRY"].Combat
                                    if baseCombatStrength < (baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit)) then
                                        unit:SetBaseCombatStrength(baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit))
                                        end
                                    elseif unit:GetUnitType() == unitInfantryID and unit:IsHasPromotion(GameInfoTypes.PROMOTION_GRENZSCHUTZ) then
                                    local baseCombatStrength = GameInfo.Units["UNIT_INFANTRY"].Combat
                                            if baseCombatStrength < (baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit)) then
                                                unit:SetBaseCombatStrength(baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit))
                                                end
                                                elseif unit:GetUnitType() == unitMechInfantryID and unit:IsHasPromotion(GameInfoTypes.PROMOTION_GRENZSCHUTZ) then
                                                    local baseCombatStrength = GameInfo.Units["UNIT_MECHANIZED_INFANTRY"].Combat
                                                        if baseCombatStrength < (baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit)) then
                                                        unit:SetBaseCombatStrength(baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit))
                                                        end
                             
                             
                        end
                end
            end
end

This methode works BUT it seems very very heavy in term of performance, is there anyway to reduce the amount of calcul/check?
 
EDIT:

This methode works BUT it seems very very heavy in term of performance, is there anyway to reduce the amount of calcul/check?

There are several things that can indeed be optimised. First off are Database Accesses (I.e. GameInfo.SOMETHING/GameInfoTypes.SOMETHING), which are relatively slow.
An easy change is to replace all instances of "GameInfoTypes.PROMOTION_GRENZSCHUTZ" (in your function) with "GEPromotion". This way, we only ask for the promotionID once (when the code is initialised). This is called caching.
Code:
--this:
unit:IsHasPromotion(GameInfoTypes.PROMOTION_GRENZSCHUTZ)
--is replaced by this:
unit:IsHasPromotion(GEPromotion)

-----------

The other times you access the DB is when you call "GameInfo.Units["UNIT_SOMETHING"].Combat".
We can cache this information as well when the code initialises as follows:
Code:
local tCombatStrengths = {}
for row in DB.Query("SELECT ID, Combat FROM Units WHERE Combat > 0") do
    tCombatStrengths[row.ID] = row.Combat;
end
The table now stores the (melee) combat strength of each unit (that has such a combat strength), using the ID of the unit as a key and the combat strength as a value.
I.e. tCombatStrengths[12] = the combat strength of the unit with an ID of 12. (NB: This ID is the value that is returned by unit:GetUnitType() and NOT the one returned by unit:GetID();)

Code:
--we can replace this:
local baseCombatStrength = GameInfo.Units["UNIT_SOMETHING"].Combat
--by this:
local baseCombatStrength = tCombatStrengths[GameInfoTypes.UNIT_SOMETHING]
------------

But by the way we set up the code, we can make it work for any unitType!
So we can then modify your function and remove all unittype-specific checks (E.g. for UNIT_MECHANIZED_INFANTRY), and change hardcoded values by something else.
Code:
--replace this:
local baseCombatStrength = tCombatStrengths[GameInfoTypes.UNIT_SOMETHING]
--by this:
local baseCombatStrength = tCombatStrengths[unit:GetUnitType()]

--and remove all unit:GetUnitType()-checks and duplicate code

This also means that the promotion will work properly for any arbitrary upgrade path of your UU

-----------

Lastly, we know which unit earned a promotion (or which one upgraded), so we do not need to loop over all units. In other words, remove the for-loop over all of the player's units.
Code:
--replace this:
for unit in player:Units() do
end
--by this:
unit = player:GetUnitByID(unitID)

-------------

The modified code will look as follows:
Spoiler :

Code:
local bonusPerPromotion = 1
local GEPromotion = GameInfoTypes["PROMOTION_GRENZSCHUTZ"] --cache the ID of PROMOTION_GRENZSCHUTZ

local tCombatStrengths = {}
for row in DB.Query("SELECT ID, Combat FROM Units WHERE Combat > 0 AND Cost > 0") do
    tCombatStrengths[row.ID] = row.Combat;
end

--function GE_GetNumPromotions is defined here somewhere

function GE_Grenzer(playerID, unitID)
    local player = Players[playerID]
    --you could remove the following if-statement so that the promotion also works if another player obtains the promotion/your UU (E.g. by City State-gift/when spawned in using Lua by another mod)
    if (player:GetCivilizationType() == civilisationID and player:IsEverAlive()) then
        unit = player:GetUnitByID(unitID) --we know which unit is upgraded, so we remove the loop over all units
        if unit:IsHasPromotion(GEPromotion) then --any unit that has the promotion should get a bonus (since the unit can be an upgrade from the UU)
            local baseCombatStrength = tCombatStrengths[unit:GetUnitType()] --obtain the cached combat strength of the unit
            if baseCombatStrength then --if the unit is not in the table, then it doesn't have a (melee) combat strength
                --unit has a melee combat strength; set it to the maximum of its base combat strength and the buff provided by the promotion
                unit:SetBaseCombatStrength(math.max(baseCombatStrength, baseCombatStrength + bonusPerPromotion*GE_GetNumPromotions(unit)));
            end
        end
    --note that all elseif's (for UNIT_MECHANIZED_INFNATRY and such) are removed; this code will work for any unit.
    end --if you choose to remove the first if-statement, also remove this end
end

if JFD_IsCivilisationActive(civilisationID) then
    GameEvents.UnitPromoted.Add(GE_Grenzer)
    GameEvents.UnitUpgraded.Add(GE_Grenzer)
end
 
Last edited:
OH Thanks! gonna try this

EDIT: Didn't know about "row", i will do some reasearch about that
 
Last edited:
Top Bottom