C++/Lua Request Thread

Looking at this code, I can't help but feel it's terribly and blasphemously incorrect, but I'm not experienced enough with Lua to know for sure:
Can I get some input?

Most of your variable names have correct syntax but many modders have adopted Hungarian Notation.
Basically, since a variable can have any type (E.g. integer, string, table, etc.), it can make code more readable to explicitly mention this type in the variable name.
For instance, a variable named pUnit will contain a pointer to a unit (i.e. you can use methods such as :GetName() or :GetUnitType() on such a variable).
sUnit will contain a string related to a unit (for instance a name)
iUnit will contain an integer related to a unit (for instance an ID)
tUnit will contain a table related to a unit, etc.
Therefore, following Hungarian Notation, your variable can rename the following variables:
Code:
iCivilisationID = GameInfoTypes["CIVILIZATION_SERTAO"]
iUAPromotion = GameInfoTypes["PROMOTION_SERTAO_RELIGION_PROMOTION"]
bHasCreatedReligion = pPlayer:HasCreatedReligion()
iPlayerReligion = pPlayer:GetReligionCreatedByPlayer()
iCityReligion = pCity:GetReligiousMajority()
pUnit = pPlayer:GetUnitByID(UnitID)
pPlot = pUnit:GetPlot()
iCity = pPlot:GetCityPurchaseID()

What is a problem is that you try to define several variables outside of the methods:
Code:
local HasCreatedReligion = player:HasCreatedReligion()
local PlayerReligion = player:GetReligionCreatedByPlayer()
local CityReligion = City:GetReligiousMajority()
local Unit = player:GetUnitByID(UnitID)
local Plot = Unit:GetPlot()
local City = Plot:GetCityPurchaseID()
However, at this point, it is unknown to the game what the value for say, player, actually is (as this has not been defined)

If we move some of these variables of your original function, we obtain the following.
At this point, the game knows which values (most of) these variables have
Code:
function CombatBonus(iPlayer, iUnit)
    local pPlayer = Players[iPlayer]
    if pPlayer:GetCivilizationType() == iCivilisationID then
        if pPlayer:HasCreatedReligion() then
            -- Note that this will not work as pCity is not defined!
            if pPlayer:GetReligionCreatedByPlayer() == pCity:GetReligiousMajority() then
                -- Note that pUnit is not defined!
                pUnit:SetHasPromotion(UAPromotion, true)
            else
                pUnit:SetHasPromotion(UAPromotion, false)
            end
        end
    end
end


Your base logic seems good, though you're attempting to look at a city that has not been used anywhere before.
Hence, we need a way to obtain a city, which we can do based on the plot the unit is currently standing on:
Code:
-- This assumes we know pUnit
local pPlot = pUnit:GetPlot()
local iCity = pPlot:GetCityPurchaseID() -- I do not know for sure what :GetCityPurchaseID(..) will return if standing on neutral lands (I assume it's either nil or -1)
if iCity and iCity > 0 then
    local pCity = pPlayer:GetCityByID(iCity)
    -- Do logic with pCity here
end

The same goes for a unit, which we can obtain by:
Code:
-- This assumes we have pPlayer and iUnit
local pUnit = pPlayer:GetUnitByID(iUnit)

Combining the things gives us the following (along with a small trick to avoid code repetition):
Code:
iCivilisationID = GameInfoTypes["CIVILIZATION_SERTAO"]
iUAPromotion = GameInfoTypes["PROMOTION_SERTAO_RELIGION_PROMOTION"]

function CombatBonus(iPlayer, iUnit)
    local pPlayer = Players[iPlayer]
    local pUnit = pPlayer:GetUnitByID(iUnit)
    local bGrantPromotion = false -- Indicates whether the unit will gain/lose the promotion
   
    if pPlayer:GetCivilizationType() == iCivilisationID then
        if pPlayer:HasCreatedReligion() then
            local pPlot = pUnit:GetPlot()
            local iCity = pPlot:GetCityPurchaseID() -- I do not know for sure what :GetCityPurchaseID(..) will return if standing on neutral lands (I assume it's either nil or -1)
            if iCity and iCity > 0 then
                local pCity = pPlayer:GetCityByID(iCity)
                if pCity and pPlayer:GetReligionCreatedByPlayer() == pCity:GetReligiousMajority() then
                    -- A small print-statement that is useful for testing purposes.
                    -- Probably a good idea to comment out or remove upon release, as it'll get printed MANY times
                    print("Adding promotion since "..pCity:GetName().." and "..pPlayer:GetName().." share their religion")
                    bGrantPromotion = true
                end
            end
        end
        pUnit:SetHasPromotion(iUAPromotion, bGrantPromotion)
    end
end

if IsCivActive(civilisationID) then
    GameEvents.UnitSetXY.Add(CombatBonus)
end
 
Thank you so much!
Now the only thing I still have to do is pedia and diplomacy text, and I'll have finished my first mod!
 
Hi again!
I'm making another civ and I'm trying to make an Unique Unit with the following ability:
"When starting a turn while standing on friendly territory, gain +1 Movement and ignore enemy Zone of Control."
By looking at some threads here and at the Lua of some other mods, I made this:

Code:
local iDummyPromotion = GameInfoTypes["PROMOTION_QB_FRIENDLY_TURF_DUMMY"]     -- has no attributes and is given to the UU for free
local iTruePromotion = GameInfoTypes["PROMOTION_QB_FRIENDLY_TURF_ACTIVATED"]  -- contains the actual attributes
--
function FriendlyTurfBonus(iPlayer)
    local pPlayer = Players[iPlayer]
   
    if iPlayer < iMaxCivs then
       for pUnit in pPlayer:Units() do
            if pUnit:IsHasPromotion(iDummyPromotion) then
               if pUnit:GetPlot():GetOwner() == iPlayer then
                 pUnit:SetHasPromotion(iTruePromotion, true)
               else
                 pUnit:SetHasPromotion(iTruePromotion, false)
               end        
            end
        end  
    end
end
--
GameEvents.PlayerDoTurn.Add(FriendlyTurfBonus)

Would this be correct?

edit: I tested it and it didn't work lmao, then I fixed it
might as well post my fixed code here

Code:
function FriendlyTurfBonus(iPlayer)
    local pPlayer = Players[iPlayer]
    
    if pPlayer:IsAlive() then
       for pUnit in pPlayer:Units() do
            if pUnit:IsHasPromotion(iDummyPromotion) then
               if pUnit:GetPlot():GetOwner() == iPlayer then
                 pUnit:SetHasPromotion(iTruePromotion, true)
                 pUnit:ChangeMoves(1 * GameDefines.MOVE_DENOMINATOR) -- wtf why does pUnit:SetMoves(pUnit:GetMoves() + 1) work differently Lua is so weird
               else
                 pUnit:SetHasPromotion(iTruePromotion, false)
               end         
            end
        end   
    end
end
--
GameEvents.PlayerDoTurn.Add(FriendlyTurfBonus)
 
Last edited:
(I hope I write this in correct thread...)
I'm quite new to Lua modding (I have some experience with XML files and in programming in general, though I never write anything in Lua) but there is a think I start to wonder about lua scripts... how (well WHEN would be better word) are they executed and how to make the game run such script. I want to make mountain workable - even without carrying on using mod for that, so more like alltime game modification. (Because honestly mountain aren't useless in real life, can be source of various resources etc... so why they should be useless in civ?)

I found two mods, which add special hidden feature to all mounaints(FEATURE_MOUNTAIN_YIELD), which enable to reffer tomountain in XML files, to easily and flexibly set its yield. The script (which was part of this mod) look like this:
Code:
Events.SequenceGameInitComplete.Add(function()
    for i = 0, Map:GetNumPlots() - 1, 1 do
        local fPlot = Map.GetPlotByIndex( i );
        if fPlot:IsMountain() then
            if fPlot:GetFeatureType() == -1  then
                fPlot:SetFeatureType(GameInfoTypes.FEATURE_MOUNTAIN_YIELD,-1);
            end
        end
    end
end)
and in the second it was:
Code:
function Mountaineering(iPlayer)
    print("Assigning ski slopes");
    -- loop through all plots
    for iPlot = 0, Map.GetNumPlots() - 1 do
        local plot = Map.GetPlotByIndex(iPlot);
        if plot:IsMountain() and plot:GetFeatureType() == -1 then
            plot:SetFeatureType(GameInfoTypes.FEATURE_SKI);
        end
    end
end
Events.SequenceGameInitComplete.Add( Mountaineering );
But I noticed (in the first case at least) that it's file wasn't mentioned in modinfo file, and also that simply put script file (WorkableMountain.lua) into proper Lua folder (I mean [gamefolder]\Assets\DLC\Expansion2\Gameplay\Lua) won't be enough so I considered to add that part to some of the scripts there (FeatureGenerator.lua seems the most suitable)... but just my knowing of the game and general-programming knowledge isn't enough to say where exactly I should put it. Or maybe just the part between "(funtction()" and "end)"... I'm also not sure whit one would be better, though difference seems minimal(but there could be some details of Lua programming that are unknown to me)

EDIT:
After some test I come up with the following solution(not exactly working yet...):
In the file "FeatureGenerator.lua" inside function "function FeatureGenerator:AddFeaturesAtPlot(iX, iY)"
At the end I added followind code:
Code:
    if (plot:GetFeatureType() == FeatureTypes.NO_FEATURE) and plot:IsMountain()  then
        plot:SetFeatureType(GameInfoTypes.FEATURE_MOUNTAIN_YIELD);
        print("Assigning mountain feature(once)");
    end
I know it would kinda trash my log file with many instance of that communicate (1 for every mountain which isn't already a nature wonder).
Then in "CIV5Features_Expansion2.xml" I put the following entries:
Code:
<Features>
      <Row>
            <Type>FEATURE_MOUNTAIN_YIELD</Type>
            <Description>Góry</Description>
            <Civilopedia>Mountains will provide +3 [ICON_production]</Civilopedia>
            <ArtDefineTag>NULL</ArtDefineTag>
            <Movement>2</Movement>
            <Impassable>true</Impassable>
            <PortraitIndex>8</PortraitIndex>
            <IconAtlas>NW_ATLAS</IconAtlas>
        </Row>
    </Features>
<Feature_YieldChanges>
        <Row>
            <FeatureType>FEATURE_MOUNTAIN_YIELD</FeatureType>
            <YieldType>YIELD_PRODUCTION</YieldType>
            <Yield>3</Yield>
        </Row>
</Feature_YieldChanges>
...but in game (though i get this lot of "Assigning mountain feature(once)" in Lua file, and there seemengly isn't any error (excluding missing refference, but considering I just hardcoded the description I can simply skip this warning)) the mountain still have no yields, are not workable.
What have I missing here?
 
Last edited:
Can change flevors in lua?
For Example, if Golden age MAJOR_CIV_APPROACH_WAR 5 >>> 10 etc.
 
Was wondering if there is a way to create a new mission and have its effects defined in lua. I'd love to hear how to do it as I've been searching through posts and lua references trying to figure it all out.

I've noticed that some missions in the CIV5Missions.xml file are linked to EntityEventTypes. It seems to me that the entity events just link the action to its animations through the Civ5EntityEvents.xml file. I guess the xml files just link the mission to its art and animation.

I have found no reference to entity events in the lua reference, but have found several references to missions, such as Unit:pushMission or Unit:CanStartMission. I'm just lost as to how to add new missions into the game.

For reference, my hope is to revamp air combat a bit by adding new air missions and disabling the old versions. Is this possible with lua or is this something I'd have to dive into the dll to accomplish?

Many Thanks!
 
Would it be easy to give all the techs of previous eras to all civs when a civ enters a new era. For example Morroco enters the industrial era, so all other civs get all techs up until the industrial era.
 
Top Bottom