[GS] Possible to embody the strategic resource used by specific civilization?

Lenin1870

Chieftain
Joined
Mar 16, 2013
Messages
50
I strike a plan of another civilization. So, I have a question. Can I make an unique strategic resource and assign unique functions?

In my plan, the resource will have following functions:
Specific civilization receives 1% combat strength bonus per 1 quantity of unique strategic resource.
Specific civilization receives 10% reduction of military unit purchase cost per 20 quantities of unique strategic resource.
Specific civilization receives extra behavior per 50 quantities of unique strategic resource.
 
For creating a single-civ-only unique resource: Yes. Go check out "Super Civs VI" in the steam workshop, and you can probably dig into his stuff to figure out how he did it (there's a lot of civs packed in there, so I am not sure.)

I'm rather new to creating mods myself, so I'm very uncertain about the other functions of that resource. I am strongly suspecting you'd need to do work in Lua code if it is at all possible.
 
For creating a single-civ-only unique resource: Yes. Go check out "Super Civs VI" in the steam workshop, and you can probably dig into his stuff to figure out how he did it (there's a lot of civs packed in there, so I am not sure.)

I'm rather new to creating mods myself, so I'm very uncertain about the other functions of that resource. I am strongly suspecting you'd need to do work in Lua code if it is at all possible.
Thanks. I'll refer to what you said. But I think it might be better to embody the function as a unique yield than a unique strategic resource now. Is it complicated to make a new yield?
 
Hmm, unless there is something weird on the hardcode side, it looks like it's easy.

C:\Program Files (x86)\Steam\steamapps\common\Sid Meier's Civilization VI\Base\Assets\Gameplay\Data\Yields.xml

Code:
<GameInfo>
    <Kinds>
        <Row Kind="KIND_YIELD"/>
        <Row Kind="KIND_PSEUDOYIELD"/>
    </Kinds>
    <Types>
        <Row Type="YIELD_FOOD" Kind="KIND_YIELD"/>

and there are all sorts of pseudoyields, which might also be what you want? I'm not sure which would be best for your purpose.
 
Hmm, unless there is something weird on the hardcode side, it looks like it's easy.

C:\Program Files (x86)\Steam\steamapps\common\Sid Meier's Civilization VI\Base\Assets\Gameplay\Data\Yields.xml

Code:
<GameInfo>
    <Kinds>
        <Row Kind="KIND_YIELD"/>
        <Row Kind="KIND_PSEUDOYIELD"/>
    </Kinds>
    <Types>
        <Row Type="YIELD_FOOD" Kind="KIND_YIELD"/>

and there are all sorts of pseudoyields, which might also be what you want? I'm not sure which would be best for your purpose.
Maybe normal "KIND_YIELD" corresponds to my purpose. Now my plan is making unique yield had integer between 0 and 100. Only specific civilization can use it, and it is notated in top panel yield UI of Prussia, the specific civilization.
Then, I'll give mentioned abilities of first thread according as points of unique yield.

For example:
Prussia receives 1% combat strength bonus per 1 point of unique yield. (Max 100% combat strength bonus)
Prussia receives 10% reduction of military unit purchase cost per 20 points of unique yield. (Max -50% discount)
Prussia receives extra behavior per 50 points of unique yield. (Max 2 times of behavior)


A pathes of unique yield acquisition like following:
When Prussian military units destroy enemy military units, 1 unique yield point gains. When enemy military units destroy Prussian units, 1 unique yield point is lost.
When Prussia suffers to decrease loyalty, 1 unique yield point is lost per city decreased loyalty.
When Prussia completes Encampment Training, a kind of the district project, gains 5 unique yield points.
When Prussia encounters budget deficit, 1 unique yield point is lost every turn before Prussia turns into the budget profit-making.
 
While the Yield itself should work, attaching modifiers to the current value might be tricky, based on what I've been told.

Basically, anything that wants an integer as an input, it only wants an integer. You can't create a pointer with XML or SQL. Lua can read the integer for you, but I don't know if you can script it to constantly update the integer for you in a running game. If it can, I think you'd need to give the initial input of 'zero' when you call the effects. Looks like you want

EFFECT_ADJUST_PLAYER_STRENGTH_MODIFIER via MODIFIER_UNIT_ADJUST_COMBAT_STRENGTH
Argument is 'amount', and value is an integer, you might have to abandon % value, if this can work at all, but maybe you can feed it Unique Yield /10 (for a +10 max bonus) or UY/5 (for a +20 max bonus), etc.

EFFECT_ADJUST_ALL_UNITS_PURCHASE_COST via MODIFIER_PLAYER_CITIES_ADJUST_UNIT_PURCHASE_COST
This one is also an integer, so be careful with it or you'll have free units.

I have no idea on the extra behaviors, in part because I'm not sure what you mean by extra behaviors.
 
While the Yield itself should work, attaching modifiers to the current value might be tricky, based on what I've been told.

Basically, anything that wants an integer as an input, it only wants an integer. You can't create a pointer with XML or SQL. Lua can read the integer for you, but I don't know if you can script it to constantly update the integer for you in a running game. If it can, I think you'd need to give the initial input of 'zero' when you call the effects. Looks like you want

EFFECT_ADJUST_PLAYER_STRENGTH_MODIFIER via MODIFIER_UNIT_ADJUST_COMBAT_STRENGTH
Argument is 'amount', and value is an integer, you might have to abandon % value, if this can work at all, but maybe you can feed it Unique Yield /10 (for a +10 max bonus) or UY/5 (for a +20 max bonus), etc.

EFFECT_ADJUST_ALL_UNITS_PURCHASE_COST via MODIFIER_PLAYER_CITIES_ADJUST_UNIT_PURCHASE_COST
This one is also an integer, so be careful with it or you'll have free units.

I have no idea on the extra behaviors, in part because I'm not sure what you mean by extra behaviors.
'Extra behavior' means addtional attack chance if a unit remains movement points.
 
Oh, yeah, then that should be encoded into two separate traits/abilities, non-permanent, and have 1st trait apply if yield is equal to or greater than 50, and second one apply if yield is equal to or greater than 100.

That is, that is, if you could make it work at all. this one would be harder. The Lua Code would need to insert the Modifier and Modifier Arguments into the database on the fly, and I don't think it can do that. There's no way to assign a numerical value to turn it on or off that I know of.
 
Lua cannot alter anything in the Database.
I'd like to add that in some rare exotic cases, in which you want a very slowly (say twice in a whole game or every 36 turns) changing parameter in the Database, you can work around that limitation by stopping the game, manually editing Database-defining .xml/.sql files & reloading the game.

[I intent to use a Lua script to analyze continuously the (single) human player situation / ranking and generate (very seldom) a message to save the game and edit human player's base parameters like maintenance / loyality / amenities etc. directly OR use (shell-scripted) another file out of a set of files and reload the game and continue play.]

I'm aware that such an approach doesn't solve Lenin1870's task - as it is defined now.

.
 
Yes but that's not altering the database by lua code. That's directly editing the contents of the database by using database methods in SQL or XML files.

And I in fact find myself doing this sort of thing quite often when I want to try out something different than what I had originally coded. You can even create entirely new modifier setups, attach them to game objects like world wonders, and reload the game. In this sort of experimentation, though, you need to be sure the game object has not already been created. For example attaching a new modifier to an existing world wonder often does not work, whereas attaching a new modifier to a world wonder that has not been completed yet does because the game has as yet not enacted the effects of the wonder from tables such as BuildingModifiers.
 
  • Like
Reactions: cvb
I refered to JFD's Lua code and made Lua about Militarization, a new yield. But I faced an issue like that:

Runtime Error: C:\Users\Partiya\Documents\My Games\Sid Meier's Civilization VI\Mods\YukinoGermany\Core\Lua\JFD_YukinoGermany_TopPanelMilitarization.lua:25: function expected instead of nil
stack traceback:
C:\Users\Partiya\Documents\My Games\Sid Meier's Civilization VI\Mods\YukinoGermany\Core\Lua\JFD_YukinoGermany_TopPanelMilitarization.lua:25: in function 'YukinoGermany_UI_UpdateTopPanelMilitarization'
[C]: in function 'func'
[C]: in function '(anonymous)'

Lua Codes are following:

JFD_YukinoGermany_TopPanelMilitarization
Code:
include("JFD_YukinoGermany_Utils.lua");
include("YukinoGermany_GamePlay.lua");
include("TopPanel");


local Load
local Save

function YukinoGermany_InitSaveUtils()
    Save = ExposedMembers.YukinoGermany.Save
    Load = ExposedMembers.YukinoGermany.Load
end
YukinoGermany_InitSaveUtils()

function YukinoGermany_UI_InitTopPanelMilitarization()
    Controls.YieldMilitarization:ChangeParent(ContextPtr:LookUpControl("/InGame/TopPanel/YieldStack"))
end
Events.LoadScreenClose.Add(YukinoGermany_UI_InitTopPanelMilitarization);

function YukinoGermany_UI_UpdateTopPanelMilitarization()
    local numMilitarizationBalance = Player_GetMilitarizationBalance(GermanyID);
    local numMilitarizationBalanceMax = Player_GetMilitarizationBalanceMax(GermanyID);
    Controls.YieldMilitarization:SetHide(false)
    Controls.YieldMilitarizationBalance:SetText(numMilitarizationBalance .. "/" .. numMilitarizationBalanceMax)
    Controls.YieldMilitarizationPerTurn:SetText("+" .. tostring(numMilitarizationPerTurn))     
    Controls.YieldMilitarizationButtonStack:CalculateSize()
end
LuaEvents.JFD_UpdateTopPanelMilitarization.Add(YukinoGermany_UI_UpdateTopPanelMilitarization);

function Initialize()   
    Events.LoadGameViewStateDone.Add(        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.LocalPlayerChanged.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.CityLoyaltyChanged.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.UnitKilledInCombat.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.CityProductionCompleted.Add(        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.SystemUpdateUI.Add(                YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.TurnEnd.Add(                        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.Combat.Add(                        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.VisualStateRestored.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
end
Initialize();


JFD_YukinoGermany_Utils
Code:
include("JFD_YukinoGermany_SaveUtils.lua");   

-- CACHING UTILS
local Load
local Save
local GermanyID = GameInfo.Civilizations["CIVILIZATION_GERMANY"].Index;

--JFD_YukinoGermany_InitSaveUtils
function JFD_YukinoGermany_InitSaveUtils()
    Save = ExposedMembers.YukinoGermany.Save
    Load = ExposedMembers.YukinoGermany.Load
end
JFD_YukinoGermany_InitSaveUtils()

----------------------------------------------------------------------------
-- Militarization YIELD
----------------------------------------------------------------------------
--Player_GetMilitarizationBalance
function Player_GetMilitarizationBalance(GermanyID)
    local Germany = Players[GermanyID]
    return Load(GermanyID .. "_MILITARIZATION") or 0
end       
----------------------------------------------------------------------------
--Player_GetMilitarizationBalanceMax
function Player_GetMilitarizationBalanceMax(GermanyID)
    local Germany = Players[GermanyID]
    local numMax = 100
    return numMax
end     
----------------------------------------------------------------------------
--Player_ChangeMilitarizationBalance
function Player_ChangeMilitarizationBalance(GermanyID, numChange)
    local Germany = Players[GermanyID]
    local numMilitarizationBalance = Player_GetMilitarizationBalance(GermanyID)
    local numMilitarizationBalanceMax = Player_GetMilitarizationBalanceMax(GermanyID)
    local newMilitarizationBalance = math.min(numMilitarizationBalanceMax, (numMilitarizationBalance+numChange))
    Player_SetMilitarizationBalance(GermanyID, math.max(newMilitarizationBalance,0))
end                         
----------------------------------------------------------------------------
--Player_SetMilitarizationBalance
function Player_SetMilitarizationBalance(GermanyID, numSet)
    Save(GermanyID .. "_MILITARIZATION", numSet)
    LuaEvents.JFD_UpdateTopPanelMilitarization()
end


YukinoGermany_GamePlay
Code:
local numMilitarizationPerTurn = 0;
local ProjectEncampmentTrainingID = GameInfo.Projects["PROJECT_ENHANCE_DISTRICT_ENCAMPMENT"].Index;

function GetGermanyPlayer()
    if gGermanyPlayer == nil then   
        for playerNum = 0, GameDefines.MAX_CIV_PLAYERS - 1 do
            local player = Players[playerNum];           
            if player ~= nil and PlayerConfigurations:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
                gGermanyPlayer = player;
                break;
            end
        end
    end
    
    return gGermanyPlayer;
end

function GermanynUnitsLoseMilitarizationYield(iPlayer)
    local pPlayer = Players[iPlayer]; 
    local gGermanyPlayer = GetGermanyPlayer();
    local pPlayerUnits = pPlayer:GetUnits();
    local gGermanyPlayerUnits = gGermanyPlayer:GetUnits();
    if gGermanyPlayer:IsAlive and pPlayerUnits == gGermanyPlayerUnits then
        numMilitarizationPerTurn = numMilitarizationPerTurn - 1
    end
end
Events.UnitKilledInCombat.Add(GermanynUnitsLoseMilitarizationYield);

function GermanynUnitsGetMilitarizationYield(iPlayer)
    local pPlayer = Players[iPlayer]; 
    local gGermanyPlayer = GetGermanyPlayer();
    local pPlayerUnits = pPlayer:GetUnits();
    local gGermanyPlayerUnits = gGermanyPlayer:GetUnits();
    if gGermanyPlayer:IsAlive and pPlayerUnits != gGermanyPlayerUnits then
        numMilitarizationPerTurn = numMilitarizationPerTurn + 1
    end
end
Events.UnitKilledInCombat.Add(GermanynUnitsGetMilitarizationYield);

function GermanynLoyaltyLoseMilitarizationYield()
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerCitiesLoyalty = gGermanyPlayer:GetCities():ChangeLoyalty();
    if gGermanyPlayer:IsAlive and gGermanyPlayerCitiesLoyalty < 0 then
        numMilitarizationPerTurn = numMilitarizationPerTurn - 1
    end
end
Events.CityLoyaltyChanged.Add(GermanynLoyaltyLoseMilitarizationYield);

function GermanynEncampmentTrainingCompleteGetMilitarizationYield()
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerCitiesProduction = gGermanyPlayer:GetCities():GetBuildQueue();
    if gGermanyPlayer:IsAlive and gGermanyPlayerCitiesProduction == ProjectEncampmentTrainingID then
        numMilitarizationPerTurn = numMilitarizationPerTurn + 5
    end
end
Events.CityProductionCompleted.Add(GermanynEncampmentTrainingCompleteGetMilitarizationYield);

function GermanynDeficitLoseMilitarizationYield()
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerGoldYield = gGermanyPlayer:GetTreasury():GetGoldYield();
    if gGermanyPlayer:IsAlive and gGermanyPlayerGoldYield < 0 then
        numMilitarizationPerTurn = numMilitarizationPerTurn - 1
    end
end
Events.TurnEnd.Add(GermanynDeficitLoseMilitarizationYield);

function GermanynMilitarizationBonus(iPlayer)
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerUnits = gGermanyPlayer:GetUnits();
    local gGermanyPlayerUnitsCombat = gGermanyPlayerUnits:GetDamage();
    local gGermanyPlayerUnitsExperience = gGermanyPlayerUnits:GetExperience();
    local GermanynUnitsCombat = 0;
    local GermanynUnitsExperience = 0;
    if gGermanyPlayer:IsAlive and gGermanyPlayerUnits:IsCombatUnit() then
        for numMilitarizationPerTurn, Units in gGermanyPlayerUnits:Members() do
            GermanynUnitsCombat = gGermanyPlayerUnitsCombat * 0.01
            GermanynUnitsExperience = gGermanyPlayerExperience * 0.005
            Units:ChangeDamage(GermanynUnitsCombat)
            Units:ChangeExperience(GermanynUnitsExperience)
        end
    end
end
Events.TurnEnd.Add(GermanynMilitarizationBonus);

function GermanyOnCombat(combatResult)
    local attacker = combatResult[CombatResultParameters.ATTACKER]
    local defender = combatResult[CombatResultParameters.DEFENDER]
    GermanynKillBonus(attacker, defender)
end
Events.Combat.Add(GermanyOnCombat)

function GermanynKillBonus(attacker, defender)
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerUnits = gGermanyPlayer:GetUnits();
    local pPlayerAttacker = Players[attacker.ID.player];
    local pPlayerDefender = Players[defender.ID.player]; 
    local pPlayerAttackerUnit = pPlayerAttacker:GetUnits();
    local pPlayerDefenderUnit = pPlayerDefender:GetUnits();
    local GermanynEnemyUnitsKillRate = 0;
    if pPlayer.GetCivilizationTypeName() ~= "CIVILIZATION_GERMANY" then
        for i = 0, numMilitarizationPerTurn do
            GermanynEnemyUnitsKillRate = GermanynEnemyUnitsKillRate + 0.1
        end
        if Math.random() * 100 <= GermanynEnemyUnitsKillRate and gGermanyPlayerUnits == pPlayerAttackerUnit then
           UnitManager.Kill(pPlayerDefenderUnit)
        elseif Math.random() * 100 <= GermanynEnemyUnitsKillRate and gGermanyPlayerUnits == pPlayerDefenderUnit then
            UnitManager.Kill(pPlayerAttackerUnit)
        end
    end
end


I'm not sure about my issue. How do I fix it?
 
One problem I see is here:
Code:
local GermanyID = GameInfo.Civilizations["CIVILIZATION_GERMANY"].Index;

function Player_GetMilitarizationBalance(GermanyID)
    local Germany = Players[GermanyID]
         ………….
Variable "GermanyID" is being set to the row Index value for "CIVILIZATION_GERMANY" within database table "Civilizations"

You are then attempting to use this integer ID "Index" number as an ID # for lua table "Players". This is not how the lua table "Players" works. The ID #'s have no relationship whatever to the Database Index # from Database table "Civilizations".

In single player games, for example, Players[0] is always the human player, regardless of the Civilization or Leader the human is playing. It is not going to be "CIVILIZATION_AMERICA" (the first listed in table "Civilizations") unless the human player is using America as their civilization. If they are using "CIVILIZATION_INDIA" however, the Player ID # in lua table "Players" is still # 0.

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

Another is here
Code:
if pPlayer.GetCivilizationTypeName() ~= "CIVILIZATION_GERMANY" then
I don't see where you've ever defined the object "pPlayer" at that localization scope and the "GetCivilizationTypeName" method belongs to the PlayerConfigurations system rather than the Player system. Lastly, colon syntax is required, rather than dot syntax.

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

You also seem to be trying to jam GameplayScipt code into a User Interface context, which cannot be done because these two sides of the game's lua system do not share all the lua API methods. Certain stuff that is valid in UI Context will cause a GameplayScript to generate runtime errors, and vice versa.
 
Last edited:
One problem I see is here:
Code:
local GermanyID = GameInfo.Civilizations["CIVILIZATION_GERMANY"].Index;

function Player_GetMilitarizationBalance(GermanyID)
    local Germany = Players[GermanyID]
         ………….
Variable "GermanyID" is being set to the row Index value for "CIVILIZATION_GERMANY" within database table "Civilizations"

You are then attempting to use this integer ID "Index" number as an ID # for lua table "Players". This is not how the lua table "Players" works. The ID #'s have no relationship whatever to the Database Index # from Database table "Civilizations".

In single player games, for example, Players[0] is always the human player, regardless of the Civilization or Leader the human is playing. It is not going to be "CIVILIZATION_AMERICA" (the first listed in table "Civilizations") unless the human player is using America as their civilization. If they are using "CIVILIZATION_INDIA" however, the Player ID # in lua table "Players" is still # 0.

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

Another is here
Code:
if pPlayer.GetCivilizationTypeName() ~= "CIVILIZATION_GERMANY" then
I don't see where you've ever defined the object "pPlayer" at that localization scope and the "GetCivilizationTypeName" method belongs to the PlayerConfigurations system rather than the Player system. Lastly, colon syntax is required, rather than dot syntax.

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

You also seem to be trying to jam GameplayScipt code into a User Interface context, which cannot be done because these two sides of the game's lua system do not share all the lua API methods. Certain stuff that is valid in UI Context will cause a GameplayScript to generate runtime errors, and vice versa.

Thank you very much! I resolved the issue finally. But a new issue has arisen.
20200405063029_1.jpg

The militarization yield in the upper left is shown as +nil. What should I do not to print value if the yield is zero?

My fixed Lua is following:
Code:
local Load
local Save

function YukinoGermany_InitSaveUtils()
    Save = ExposedMembers.YukinoGermany.Save
    Load = ExposedMembers.YukinoGermany.Load
end
YukinoGermany_InitSaveUtils()

function YukinoGermany_UI_InitTopPanelMilitarization()
    local playerID = Game:GetLocalPlayer()
    local pPlayerConfig = PlayerConfigurations[playerID]

    if pPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        Controls.YieldMilitarization:ChangeParent(ContextPtr:LookUpControl("/InGame/TopPanel/YieldStack"))
    end
end
Events.LoadScreenClose.Add(YukinoGermany_UI_InitTopPanelMilitarization);

function YukinoGermany_UI_UpdateTopPanelMilitarization()
    local playerID = Game:GetLocalPlayer()
    local pPlayerConfig = PlayerConfigurations[playerID]

    if pPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        local numMilitarizationBalance = Player_GetMilitarizationBalance(playerID);
        local numMilitarizationBalanceMax = Player_GetMilitarizationBalanceMax(playerID);
        Controls.YieldMilitarization:SetHide(false)
        Controls.YieldMilitarizationBalance:SetText(numMilitarizationBalance .. "/" .. numMilitarizationBalanceMax)
        Controls.YieldMilitarizationPerTurn:SetText("+" .. tostring(numMilitarizationPerTurn))     
        Controls.YieldMilitarizationButtonStack:CalculateSize()
    end
end
LuaEvents.JFD_UpdateTopPanelMilitarization.Add(YukinoGermany_UI_UpdateTopPanelMilitarization);

function Initialize()   
    Events.LoadGameViewStateDone.Add(        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.LocalPlayerChanged.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.CityLoyaltyChanged.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.UnitKilledInCombat.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.CityProductionCompleted.Add(        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.SystemUpdateUI.Add(                YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.TurnEnd.Add(                        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.Combat.Add(                        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.VisualStateRestored.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
end
Initialize();
 
numMilitarizationBalance is returning Nil because variable numMilitarizationPerTurn is not defined and is therefore nil. You are running into the inherent issues with attempting to use GameplayScript effects in a User Interface context via attempts to communicate data between the two sides of the game's lua engine since Firaxis made this far more difficult to achieve when they released Gathering Storm.

You can set a PlayerObject "property" in a gameplay script and the value in that property can be read out (and will be the same value) in both GameplayScript and User Interface contexts. But only GameplayScripts can write new values to the PlayerObject "property".

In a gameplay script here is an example where I am recording whether or not a modifierID has been attached to a PlayerObject "property":
Code:
function AddModifierToPlayer(iPlayer, sModifierID, bDebugPrint)
	local function Dprint(sMessage)
		if bDebugPrint then print(sMessage) end
	end
	Dprint("function AddModifierToPlayer was executed with argument values of iPlayer = " .. tostring(iPlayer) .. ", sModifierID = " .. tostring(sModifierID) .. ", bDebugPrint = " .. tostring(bDebugPrint))
	local pPlayer = Players[iPlayer]
	if (pPlayer ~= nil) then
		Dprint("AddModifierToPlayer(" .. tostring(iPlayer) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. "): Proceeding with evaluating whether the player already has the modifier.")
		local PlayerProperty = pPlayer:GetProperty(sModifierID)
		Dprint("pPlayer:GetProperty(" .. sModifierID .. ") == " .. tostring(PlayerProperty))
		if (PlayerProperty == nil) then
			Dprint("Modifier " .. sModifierID .. " was seen as NOT being already attached to the player")
			pPlayer:AttachModifierByID(sModifierID)		
			pPlayer:SetProperty(sModifierID, 1)
			Dprint("Modifier " .. sModifierID .. " attached to the player")
		else
			Dprint("Modifier " .. sModifierID .. " was seen as being already attached to the player")
		end
	else
		Dprint("AddModifierToPlayer(" .. tostring(iPlayer) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. ") failed because the iPlayer value is not usable")
	end
end
The important bit for you is this line here where the attachment is recorded onto the Player Object "pPlayer":
Code:
pPlayer:SetProperty(sModifierID, 1)
It is recording the PlayerObject "Name" as the text string held within variable "sModifierID"

When I want to extract the value of this same property attached to the same player object I am doing it on this line
Code:
local PlayerProperty = pPlayer:GetProperty(sModifierID)
And then in my case I am checking whether the value received is nil or not
Code:
if (PlayerProperty == nil) then
In my case whenever the value extracted for that PlayerProperty text-string is "nil" it means my code has never previously assigned that ModifierId to that player, and I can proceed.

But you can just as easily calculate the current value of the numMilitarizationPerTurn variable in a Gameplay script and then set that integer numerical value into a custom PlayerProperty such as
Code:
pPlayer:SetProperty("MilitarizationPerTurn", 15)
And then In your User Interface file you can access the current value with
Code:
local numMilitarizationPerTurn = Players[Game:GetLocalPlayer()]:GetProperty("MilitarizationPerTurn")
if (numMilitarizationPerTurn ~= nil) then
	--do stuff here
end
One player object can be assigned multiple different PlayerProperties each with their own text-string identifier. The nice thing about this is that the game saves all this into the SaveGame file inherently. No other action is needed.
 
numMilitarizationBalance is returning Nil because variable numMilitarizationPerTurn is not defined and is therefore nil. You are running into the inherent issues with attempting to use GameplayScript effects in a User Interface context via attempts to communicate data between the two sides of the game's lua engine since Firaxis made this far more difficult to achieve when they released Gathering Storm.

You can set a PlayerObject "property" in a gameplay script and the value in that property can be read out (and will be the same value) in both GameplayScript and User Interface contexts. But only GameplayScripts can write new values to the PlayerObject "property".

In a gameplay script here is an example where I am recording whether or not a modifierID has been attached to a PlayerObject "property":
Code:
function AddModifierToPlayer(iPlayer, sModifierID, bDebugPrint)
    local function Dprint(sMessage)
        if bDebugPrint then print(sMessage) end
    end
    Dprint("function AddModifierToPlayer was executed with argument values of iPlayer = " .. tostring(iPlayer) .. ", sModifierID = " .. tostring(sModifierID) .. ", bDebugPrint = " .. tostring(bDebugPrint))
    local pPlayer = Players[iPlayer]
    if (pPlayer ~= nil) then
        Dprint("AddModifierToPlayer(" .. tostring(iPlayer) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. "): Proceeding with evaluating whether the player already has the modifier.")
        local PlayerProperty = pPlayer:GetProperty(sModifierID)
        Dprint("pPlayer:GetProperty(" .. sModifierID .. ") == " .. tostring(PlayerProperty))
        if (PlayerProperty == nil) then
            Dprint("Modifier " .. sModifierID .. " was seen as NOT being already attached to the player")
            pPlayer:AttachModifierByID(sModifierID)     
            pPlayer:SetProperty(sModifierID, 1)
            Dprint("Modifier " .. sModifierID .. " attached to the player")
        else
            Dprint("Modifier " .. sModifierID .. " was seen as being already attached to the player")
        end
    else
        Dprint("AddModifierToPlayer(" .. tostring(iPlayer) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. ") failed because the iPlayer value is not usable")
    end
end
The important bit for you is this line here where the attachment is recorded onto the Player Object "pPlayer":
Code:
pPlayer:SetProperty(sModifierID, 1)
It is recording the PlayerObject "Name" as the text string held within variable "sModifierID"

When I want to extract the value of this same property attached to the same player object I am doing it on this line
Code:
local PlayerProperty = pPlayer:GetProperty(sModifierID)
And then in my case I am checking whether the value received is nil or not
Code:
if (PlayerProperty == nil) then
In my case whenever the value extracted for that PlayerProperty text-string is "nil" it means my code has never previously assigned that ModifierId to that player, and I can proceed.

But you can just as easily calculate the current value of the numMilitarizationPerTurn variable in a Gameplay script and then set that integer numerical value into a custom PlayerProperty such as
Code:
pPlayer:SetProperty("MilitarizationPerTurn", 15)
And then In your User Interface file you can access the current value with
Code:
local numMilitarizationPerTurn = Players[Game:GetLocalPlayer()]:GetProperty("MilitarizationPerTurn")
if (numMilitarizationPerTurn ~= nil) then
    --do stuff here
end
One player object can be assigned multiple different PlayerProperties each with their own text-string identifier. The nice thing about this is that the game saves all this into the SaveGame file inherently. No other action is needed.

I tried to apply your example, but I don't sure to do it exactly.
"Runtime Error: C:\Users\Partiya\Documents\My Games\Sid Meier's Civilization VI\Mods\YukinoGermany\Core\Lua\JFD_YukinoGermany_TopPanelMilitarization.lua:31: function expected instead of nil"
This error occurs when play games. Now I don't know where the problem is. 31st line of JFD_YukinoGermany_TopPanelMilitarization corresponds to the following: "local numMilitarizationPerTurn = Players[playerID]:GetProperty("MilitarizationPerTurn")
I just insert the sentence above and add below things for each function like
gGermanyPlayer:SetProperty("MilitarizationPerTurn", -1).




My entire code is:


JFD_YukinoGermany_TopPanelMilitarization.lua

Code:
include("JFD_YukinoGermany_Utils.lua");
include("YukinoGermany_GamePlay.lua");


local Load
local Save

function YukinoGermany_InitSaveUtils()
    Save = ExposedMembers.YukinoGermany.Save
    Load = ExposedMembers.YukinoGermany.Load
end
YukinoGermany_InitSaveUtils();

function YukinoGermany_UI_InitTopPanelMilitarization()
    local playerID = Game:GetLocalPlayer()
    local pPlayerConfig = PlayerConfigurations[playerID]

    if pPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        Controls.YieldMilitarization:ChangeParent(ContextPtr:LookUpControl("/InGame/TopPanel/YieldStack"))
    end
end
Events.LoadScreenClose.Add(YukinoGermany_UI_InitTopPanelMilitarization);

function YukinoGermany_UI_UpdateTopPanelMilitarization()
    local playerID = Game:GetLocalPlayer()
    local pPlayerConfig = PlayerConfigurations[playerID]
    local numMilitarizationPerTurn = Players[playerID]:GetProperty("MilitarizationPerTurn")

    if pPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        local numMilitarizationBalance = Player_GetMilitarizationBalance(playerID);
        local numMilitarizationBalanceMax = Player_GetMilitarizationBalanceMax(playerID);
        Controls.YieldMilitarization:SetHide(false)
        Controls.YieldMilitarizationBalance:SetText(numMilitarizationBalance .. "/" .. numMilitarizationBalanceMax)
        if (numMilitarizationPerTurn ~= nil) then
            Controls.YieldMilitarizationPerTurn:SetText("+" .. tostring(numMilitarizationPerTurn)) 
        end
        Controls.YieldMilitarizationButtonStack:CalculateSize()
    end
end
LuaEvents.JFD_UpdateTopPanelMilitarization.Add(YukinoGermany_UI_UpdateTopPanelMilitarization);

function Initialize() 
    Events.LoadGameViewStateDone.Add(        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.LocalPlayerChanged.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.CityLoyaltyChanged.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.UnitKilledInCombat.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.CityProductionCompleted.Add(        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.SystemUpdateUI.Add(                YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.TurnEnd.Add(                        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.Combat.Add(                        YukinoGermany_UI_UpdateTopPanelMilitarization );
    Events.VisualStateRestored.Add(            YukinoGermany_UI_UpdateTopPanelMilitarization );
end
Initialize();


YukinoGermany_GamePlay.lua

Code:
local MilitarizationPerTurn = 0;
local ProjectEncampmentTrainingID = GameInfo.Projects["PROJECT_ENHANCE_DISTRICT_ENCAMPMENT"].Index;

function GetGermanyPlayer()
    if gGermanyPlayer == nil then 
        for playerNum = 0, Game:GetLocalPlayer() do
            local player = Players[playerNum]; 
            local playerConfig = PlayerConfigurations[playerNum]         
            if player ~= nil and playerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
                gGermanyPlayer = player;
                break;
            end
        end
    end
  
    return gGermanyPlayer;
end

function GermanynUnitsLoseMilitarizationYield(iPlayer)
    local pPlayer = Players[iPlayer];
    local gGermanyPlayer = GetGermanyPlayer();
    local pPlayerUnits = pPlayer:GetUnits();
    local gGermanyPlayerUnits = gGermanyPlayer:GetUnits();
    if gGermanyPlayer:IsAlive() and pPlayerUnits == gGermanyPlayerUnits then
        MilitarizationPerTurn = MilitarizationPerTurn - 1
        gGermanyPlayer:SetProperty("MilitarizationPerTurn", -1)
    end
end
Events.UnitKilledInCombat.Add(GermanynUnitsLoseMilitarizationYield);

function GermanynUnitsGetMilitarizationYield(iPlayer)
    local pPlayer = Players[iPlayer];
    local gGermanyPlayer = GetGermanyPlayer();
    local pPlayerUnits = pPlayer:GetUnits();
    local gGermanyPlayerUnits = gGermanyPlayer:GetUnits();
    if gGermanyPlayer:IsAlive() and pPlayerUnits ~= gGermanyPlayerUnits then
        MilitarizationPerTurn = MilitarizationPerTurn + 1
        gGermanyPlayer:SetProperty("MilitarizationPerTurn", 1)
    end
end
Events.UnitKilledInCombat.Add(GermanynUnitsGetMilitarizationYield);

function GermanynLoyaltyLoseMilitarizationYield()
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerCitiesLoyalty = gGermanyPlayer:GetCities():ChangeLoyalty();
    if gGermanyPlayer:IsAlive() and gGermanyPlayerCitiesLoyalty < 0 then
        MilitarizationPerTurn = MilitarizationPerTurn - 1
        gGermanyPlayer:SetProperty("MilitarizationPerTurn", -1)
    end
end
Events.CityLoyaltyChanged.Add(GermanynLoyaltyLoseMilitarizationYield);

function GermanynEncampmentTrainingCompleteGetMilitarizationYield()
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerCitiesProduction = gGermanyPlayer:GetCities():GetBuildQueue();
    if gGermanyPlayer:IsAlive() and gGermanyPlayerCitiesProduction == ProjectEncampmentTrainingID then
        MilitarizationPerTurn = MilitarizationPerTurn + 5
        gGermanyPlayer:SetProperty("MilitarizationPerTurn", 5)
    end
end
Events.CityProductionCompleted.Add(GermanynEncampmentTrainingCompleteGetMilitarizationYield);

function GermanynDeficitLoseMilitarizationYield()
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerGoldYield = gGermanyPlayer:GetTreasury():GetGoldYield();
    if gGermanyPlayer:IsAlive() and gGermanyPlayerGoldYield < 0 then
        MilitarizationPerTurn = MilitarizationPerTurn - 1
        gGermanyPlayer:SetProperty("MilitarizationPerTurn", -1)
    end
end
Events.TurnEnd.Add(GermanynDeficitLoseMilitarizationYield);

function GermanynMilitarizationBonus(iPlayer)
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerUnits = gGermanyPlayer:GetUnits();
    local gGermanyPlayerUnitsCombat = gGermanyPlayerUnits:GetDamage();
    local gGermanyPlayerUnitsExperience = gGermanyPlayerUnits:GetExperience();
    local GermanynUnitsCombat = 0;
    local GermanynUnitsExperience = 0;
    if gGermanyPlayer:IsAlive() and gGermanyPlayerUnits:IsCombatUnit() then
        for MilitarizationPerTurn, Units in gGermanyPlayerUnits:Members() do
            GermanynUnitsCombat = gGermanyPlayerUnitsCombat * 0.01
            GermanynUnitsExperience = gGermanyPlayerExperience * 0.005
            Units:ChangeDamage(GermanynUnitsCombat)
            Units:ChangeExperience(GermanynUnitsExperience)
        end
    end
end
Events.TurnEnd.Add(GermanynMilitarizationBonus);

function GermanyOnCombat(combatResult)
    local attacker = combatResult[CombatResultParameters.ATTACKER]
    local defender = combatResult[CombatResultParameters.DEFENDER]
    GermanynKillBonus(attacker, defender)
end
Events.Combat.Add(GermanyOnCombat)

function GermanynKillBonus(attacker, defender)
    local gGermanyPlayer = GetGermanyPlayer();
    local gGermanyPlayerUnits = gGermanyPlayer:GetUnits();
    local pPlayerAttacker = Players[attacker.ID.player];
    local pPlayerConfigAttacker = PlayerConfigurations[attacker.ID.player];
    local pPlayerDefender = Players[defender.ID.player];
    local pPlayerConfigDefender = PlayerConfigurations[defender.ID.player];   
    local pPlayerAttackerUnit = pPlayerAttacker:GetUnits();
    local pPlayerDefenderUnit = pPlayerDefender:GetUnits();
    local GermanynEnemyUnitsKillRate = 0;
    for i = 0, MilitarizationPerTurn do
       GermanynEnemyUnitsKillRate = GermanynEnemyUnitsKillRate + 0.1
    end
    if Math.random() * 100 <= GermanynEnemyUnitsKillRate and gGermanyPlayerUnits == pPlayerAttackerUnit and pPlayerConfigAttacker:GetCivilizationTypeName() == "CIVILIZATION_GERMANY"  then
        UnitManager.Kill(pPlayerDefenderUnit)
    elseif Math.random() * 100 <= GermanynEnemyUnitsKillRate and gGermanyPlayerUnits == pPlayerDefenderUnit and pPlayerConfigDefender:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
         UnitManager.Kill(pPlayerAttackerUnit)
    end
end
 
Last edited:
This is no
Code:
include("YukinoGermany_GamePlay.lua");

You just need to set up "YukinoGermany_GamePlay.lua" as a file in an InGameActions AddGameplayScript action.

And it is
Code:
Game.GetLocalPlayer()
I did not notice before that you were attempting
Code:
Game:GetLocalPlayer()
which will always be a nil function since "Game" methods are considered "static" and therefore use "dot" rather than "semi-colon" syntax.
 
This is no
Code:
include("YukinoGermany_GamePlay.lua");

You just need to set up "YukinoGermany_GamePlay.lua" as a file in an InGameActions AddGameplayScript action.

And it is
Code:
Game.GetLocalPlayer()
I did not notice before that you were attempting
Code:
Game:GetLocalPlayer()
which will always be a nil function since "Game" methods are considered "static" and therefore use "dot" rather than "semi-colon" syntax.


The error has gone but new one comes now. I would appreciate if you gave checking.

Runtime Error: C:\Users\Partiya\Documents\My Games\Sid Meier's Civilization VI\Mods\YukinoGermany\Core\Lua\JFD_YukinoGermany_TopPanelMilitarization.lua:21: attempt to index a nil value
stack traceback:
C:\Users\Partiya\Documents\My Games\Sid Meier's Civilization VI\Mods\YukinoGermany\Core\Lua\JFD_YukinoGermany_TopPanelMilitarization.lua:21: in function 'YukinoGermany_UI_InitTopPanelMilitarization'
[C]: in function 'func'
[C]: in function '(anonymous)'

The 21st line:
Controls.YieldMilitarization:ChangeParent(ContextPtr:LookUpControl("/InGame/TopPanel/YieldStack"))
 
Eventually, I met the same issue. In fact, I made a mark of militarization when player brought mouse cursor at militarization bar. I thought it caused error, so I deleted it. But an original issue has come.


local numMilitarizationPerTurn = Players[playerID]:GetProperty("MilitarizationPerTurn")

Runtime Error: C:\Users\Partiya\Documents\My Games\Sid Meier's Civilization VI\Mods\YukinoGermany\Core\Lua\JFD_YukinoGermany_TopPanelMilitarization.lua:29: function expected instead of nil
stack traceback:
C:\Users\Partiya\Documents\My Games\Sid Meier's Civilization VI\Mods\YukinoGermany\Core\Lua\JFD_YukinoGermany_TopPanelMilitarization.lua:29: in function 'YukinoGermany_UI_UpdateTopPanelMilitarization'
[C]: in function 'func'
[C]: in function '(anonymous)'
 
Last edited:
I resolve all issues written Lua.log. But my fixed Lua involves freezing game. I didn't see any issues in Lua.log. Issued Lua script is YukinoGermany_GamePlay.lua:

Code:
-- ===============================================================================================
-- STATE / CACHE from BlackDeathScenario
-- Custom data are kept in cache and set into game as a property on the Game object from script thread
-- Set your custom data = pObject:SetProperty(sPropertyName, value)
-- Get your custom data = pObject:GetProperty(sPropertyName)
-- Object can be Game, pCity, pPlayer, pUnit, pPlot
-- Value are variable, field, structure
-- Name of Value (sPropertyName) are stored in g_PropertyKeys
-- Handle among script thread and UI thread guarantee function in this file
-- This file must be insert on start every file using "include" command (from UI and script)
-- When save, load game system do it with yours custom data
-- ===============================================================================================

g_PropertyKeys = {
    MilitarizationPerTurn    = "MilitarizationPerTurn",        -- your custom data
};

-- Every time SetObjectState is called, the script-side cache is updated to prevent unnecessary
-- calls down to gamecore.
local USE_CACHE : boolean = true;
local g_ObjectStateCache = {};

-- ===========================================================================
function SetObjectState(pObject : object, sPropertyName : string, value)
    if (sPropertyName == nil) then
        return nil;
    end

    if (pObject == nil) then
        print("StateUtils SetObjectState: ERROR: object is nil!");
        return nil;
    end

    if (USE_CACHE == true) then
        if (g_ObjectStateCache[pObject] == nil) then
            g_ObjectStateCache[pObject] = {};
        end

        -- update cache
        g_ObjectStateCache[pObject][sPropertyName] = value;
    end
   
    -- when you call this function from UI thread you must update data on script thread
    if UI ~= nil then
        -- We are on the UI side of things.  We must sent a command to change the game state
        local kParameters:table = {};
        kParameters.propertyName = sPropertyName;
        kParameters.value = value;
        kParameters.objectID = pObject:GetComponentID();
        -- Send this GameEvent when processing the operation
        kParameters.OnStart = "OnPlayerCommandSetObjectState";

        UI.RequestPlayerOperation(Game.GetLocalPlayer(), PlayerOperations.EXECUTE_SCRIPT, kParameters);
    else
    -- when you call this function from script thread you send data into gamecore
        if (pObject.SetProperty ~= nil) then
            pObject:SetProperty(sPropertyName, value);
        end
    end
end

-- ====================================================================================
-- This function is called from UI thread using function "UI.RequestPlayerOperation" as event
function OnPlayerCommandSetObjectStateHandler(ePlayer : number, params : table)

    local pObject = Game.GetObjectFromComponentID(params.objectID);

    if pObject ~= nil then
        SetObjectState(pObject, params.propertyName, params.value);
    end
       
end

-- This file gets includes on both the UI and GameCore side, we only want to handle the event on the Game Core side.
if UI == nil then
    GameEvents.OnPlayerCommandSetObjectState.Add( OnPlayerCommandSetObjectStateHandler );
end

-- ===================================================================================
function GetObjectState(pObject : object, sPropertyName : string, bCacheCheckOnly : boolean)
    if (sPropertyName == nil) then
        return nil;
    end

    if (pObject == nil) then
        print("StateUtils GetObjectState: ERROR: object is nil!");
        return nil;
    end

    bCacheCheckOnly = bCacheCheckOnly or false;

    if (USE_CACHE == true) then
        if (g_ObjectStateCache[pObject] == nil) then
            g_ObjectStateCache[pObject] = {};
        end

        if (g_ObjectStateCache[pObject][sPropertyName] ~= nil) then
            return g_ObjectStateCache[pObject][sPropertyName];
        else
            if (bCacheCheckOnly) then
                return nil;
            else
                return RefreshObjectState(pObject, sPropertyName);
            end
        end
    else
        return pObject:GetProperty(sPropertyName);
    end
end

-- ===========================================================================
-- Forces a call to gamecore and cache update.
function RefreshObjectState(pObject : object, sPropertyName : string)
    if (sPropertyName == nil) then
        return nil;
    end

    if (pObject.GetProperty == nil) then
        return nil;
    end  

    local propResult = pObject:GetProperty(sPropertyName);

    if (g_ObjectStateCache[pObject] == nil) then
        g_ObjectStateCache[pObject] = {};
    end

    g_ObjectStateCache[pObject][sPropertyName] = propResult;
    return propResult;
end
-- ===================================================================================

local MilitarizationPerTurn = 0;
local ProjectEncampmentTrainingID = GameInfo.Projects["PROJECT_ENHANCE_DISTRICT_ENCAMPMENT"].Index;

function pPlayerUnitsGetOrLoseMilitarizationYield(iKilledPlayerID, iKilledUnitID, iPlayerID, iUnitID)
    local pKilledPlayer = Players[iKilledPlayerID];
    local pKilledPlayerConfig = PlayerConfigurations[iKilledPlayerID];
    local pKilledUnit = pPlayer:GetUnits():FindID(iKilledUnitID);
    local pPlayer = Players[iPlayerID];
    local pPlayerConfig = PlayerConfigurations[iPlayerID];
    local pUnit = pPlayer:GetUnits():FindID(iUnitID);
    if pPlayer:IsAlive() and pPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" and pKilledPlayer:IsAlive() and pKilledPlayerConfig:GetCivilizationTypeName() ~= "CIVILIZATION_GERMANY" then
        if pUnit ~= pKilledUnit and UnitManager.Kill(pKilledUnit) then
            MilitarizationPerTurn = MilitarizationPerTurn + 1
            SetObjectState(pPlayer, g_PropertyKeys.MilitarizationPerTurn, 1)
        end
    elseif pPlayer:IsAlive() and pPlayerConfig:GetCivilizationTypeName() ~= "CIVILIZATION_GERMANY" and pKilledPlayer:IsAlive() and pKilledPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        if pUnit ~= pKilledUnit and UnitManager.Kill(pKilledUnit) then
            MilitarizationPerTurn = MilitarizationPerTurn - 1
            SetObjectState(pPlayer, g_PropertyKeys.MilitarizationPerTurn, -1)
        end
    end
end
Events.UnitKilledInCombat.Add(pPlayerUnitsGetOrLoseMilitarizationYield);

function pPlayerLoyaltyLoseMilitarizationYield(iPlayerID, iCityID)
    local pPlayer = Players[iPlayerID];
    local pPlayerConfig = PlayerConfigurations[iPlayerID];
    local pCity = pPlayer:GetCities():FindID(iCityID);
    if pPlayer:IsAlive() and pPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        if pCity:ChangeLoyalty(-1) then
            MilitarizationPerTurn = MilitarizationPerTurn - 1
            SetObjectState(pPlayer, g_PropertyKeys.MilitarizationPerTurn, -1)
        end
    end
end
Events.CityLoyaltyChanged.Add(pPlayerLoyaltyLoseMilitarizationYield);

function pPlayerEncampmentTrainingCompleteGetMilitarizationYield(iPlayerID, iCityID, iProjectID, iBuildingIndex, iX, iY, bCancelled)
    local pPlayer = Players[iPlayerID];
    local pPlayerConfig = PlayerConfigurations[iPlayerID];
    local pCity = pPlayer:GetCities():FindID(iCityID);
    if pPlayer:IsAlive() and pPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        if iProjectID == ProjectEncampmentTrainingID and bCancelled == false and pCity == pPlayer:GetCities():Members() then
            MilitarizationPerTurn = MilitarizationPerTurn + 5
            SetObjectState(pPlayer, g_PropertyKeys.MilitarizationPerTurn, 5)
        end
    end
end
Events.CityProjectCompleted.Add(pPlayerEncampmentTrainingCompleteGetMilitarizationYield);

function pPlayerDeficitLoseMilitarizationYield(iPlayerID, iGoldPerTurn, iCurrentTreasury)
    local pPlayer = Players[iPlayerID];
    local pPlayerConfig = PlayerConfigurations[iPlayerID];
    if pPlayer:IsAlive() and pPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" and iGoldPerTurn < 0 then
        MilitarizationPerTurn = MilitarizationPerTurn - 1
        SetObjectState(pPlayer, g_PropertyKeys.MilitarizationPerTurn, -1)
    end
end
Events.TreasuryChanged.Add(pPlayerDeficitLoseMilitarizationYield);

function pPlayerOnCombat(m_CombatResults)
    local attacker = m_CombatResults[CombatResultParameters.ATTACKER]
    local defender = m_CombatResults[CombatResultParameters.DEFENDER]
    pPlayerMilitarizationBonus(attacker, defender)
    pPlayerKillBonus(attacker, defender)
end
Events.Combat.Add(pPlayerOnCombat);

function pPlayerMilitarizationBonus(attacker, defender)
    local attackerID = attacker[CombatResultParameters.ID];
    local pAttackingPlayer = Players[attackerID.player];
    local pAttackingPlayerConfig = PlayerConfigurations[attackerID.player];
    local pAttackingPlayerUnits = pAttackingPlayer:GetUnits();
    local defenderID = defender[CombatResultParameters.ID];
    local pDefendingPlayer = Players[defenderID.player];
    local pDefendingPlayerConfig = PlayerConfigurations[defenderID.player];
    local pDefendingPlayerUnits = pDefendingPlayer:GetUnits();
    local iAttackerCombatStrength    = attacker[CombatResultParameters.COMBAT_STRENGTH];
    local iDefenderCombatStrength    = defender[CombatResultParameters.COMBAT_STRENGTH];
    local iAttackerBonus            = attacker[CombatResultParameters.STRENGTH_MODIFIER];
    local iDefenderBonus            = defender[CombatResultParameters.STRENGTH_MODIFIER];
    local iPlayerUnitExperienceBonus = 1;

    if pAttackingPlayer:IsAlive() and pAttackingPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        for i, pAttackingPlayerUnit in pAttackingPlayerUnits:Members() do
            for j = 0, MilitarizationPerTurn do
                iAttackerBonus = iAttackerBonus + iAttackerCombatStrength * 0.01
                pAttackingPlayerUnit:GetExperience():ChangeExperience(iPlayerUnitExperienceBonus)
            end
        end
    elseif pDefendingPlayer:IsAlive() and pDefendingPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        for i, pDefendingPlayerUnit in pDefendingPlayerUnits:Members() do
            for j = 0, MilitarizationPerTurn do
                iDefenderBonus = iDefenderBonus + iDefenderCombatStrength * 0.01
                pDefendingPlayerUnit:GetExperience():ChangeExperience(iPlayerUnitExperienceBonus)
            end
        end
    end
end

function pPlayerKillBonus(attacker, defender)
    local attackerID = attacker[CombatResultParameters.ID];
    local pAttackingPlayer = Players[attackerID.player];
    local pAttackingPlayerConfig = PlayerConfigurations[attackerID.player];
    local pAttackingPlayerUnits = pAttackingPlayer:GetUnits();
    local defenderID = defender[CombatResultParameters.ID];
    local pDefendingPlayer = Players[defenderID.player];
    local pDefendingPlayerConfig = PlayerConfigurations[defenderID.player];
    local pDefendingPlayerUnits = pDefendingPlayer:GetUnits();
    local pPlayerEnemyUnitsKillRate = 0;

    for i = 0, MilitarizationPerTurn do
       pPlayerEnemyUnitsKillRate = pPlayerEnemyUnitsKillRate + 0.1
    end
    if pAttackingPlayer:IsAlive() and pAttackingPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        local pkAttacker = UnitManager.GetUnit(attackerID.player, attackerID.id);
        if Automation.GetRandomNumber(100) <= pPlayerEnemyUnitsKillRate then
            for i, pDefendingPlayerUnit in pDefendingPlayerUnits:Members() do
                pkAttacker.Kill(pDefendingPlayerUnit)
            end
        end
    elseif pDefendingPlayer:IsAlive() and pDefendingPlayerConfig:GetCivilizationTypeName() == "CIVILIZATION_GERMANY" then
        local pkDefender = UnitManager.GetUnit(defenderID.player, defenderID.id);
        if Automation.GetRandomNumber(100) <= pPlayerEnemyUnitsKillRate then
            for i, pAttackingPlayerUnit in pAttackingPlayerUnits:Members() do
                 pkDefender.Kill(pAttackingPlayerUnit)
            end
        end
    end
end
 
Top Bottom