C++/Lua Request Thread

not like im expecting any answer but also does anyone have an example of a 3d array in the C++ dll? i'd want to create like a table with 5 columns, instead of 4 but i can't reverse engineer without an existing example
 
N dimensional arrays in C++ are just a vector of pointers to (N-1) dimensional arrays - recurse until you have a vector of pointers to objects. But I don't see why you need a 3-d array. A table with 5 columns is still a 2-D array - it just has one more column.

See my DLL tutorials - they cover adding a column to an existing table, and adding both primary and secondary tables
 
i was attempting to do something like a table with policytype, improvement type, resourcetype yieldtype yield for example

edit: To clarify, i know how to execute sql queries from c++ and how to work with arrays etc and everything, but to learn i need a example, as im terrible with creating new solutions from myself
 
Last edited:
See the three DLL tutorials I mentioned, as they'll walk through most (if not all) of what you need to know
 
Hi--to help my friends and others practice without other people, I'm trying to make an AI that can simulate playing against a human better.

The thing holding me up right now is I'd like to apply AIPerEraModifier to ResearchPercent. So, for the AI, each tech costs 100% in the Ancient Era, 86% in the Classical Era, 72% in Medieval, 58% Renaissance, etc.

I've been told it's very possible to do this, and someone gave an example of how to do it here (but for something else, not ResearchPercent). Any idea where I could start learning how to do this?
 
Looks like I've figured a few things out. I searched all the files in Assets/DLC for anything containing AIPerEraModifier and ResearchCost and found a few things of note. I think I found where the cost of each tech is calculated, though it's not clear to me how it identifies whether you're a human or not, so I can't quite tell what fiddling with it does.

The relevant bits seem to be this for an example of AIPerEraModifier:

Code:
int CvPlayer::getProductionNeeded(BuildingTypes eBuilding) const
{
    int iProductionNeeded;

    CvBuildingEntry* pkBuildingInfo = GC.getBuildingInfo(eBuilding);
    if(pkBuildingInfo == NULL)
    {
        //This should never happen.
        return 1;
    }

    iProductionNeeded = pkBuildingInfo->GetProductionCost();

    if (pkBuildingInfo->GetNumCityCostMod() > 0 && getNumCities() > 0)
    {
        iProductionNeeded += (pkBuildingInfo->GetNumCityCostMod() * getNumCities());
    }

    if (isMinorCiv())
    {
        iProductionNeeded *= GC.getMINOR_CIV_PRODUCTION_PERCENT();
        iProductionNeeded /= 100;
    }

    iProductionNeeded *= GC.getBUILDING_PRODUCTION_PERCENT();
    iProductionNeeded /= 100;

    iProductionNeeded *= GC.getGame().getGameSpeedInfo().getConstructPercent();
    iProductionNeeded /= 100;

    iProductionNeeded *= GC.getGame().getStartEraInfo().getConstructPercent();
    iProductionNeeded /= 100;

and this calculation for the cost of each tech:

Code:
/// Accessor: what is the cost of researching this tech (taking all modifiers into account)
int CvTeamTechs::GetResearchCost (TechTypes eTech) const
{
    CvAssertMsg(eTech != NO_TECH, "Tech is not assigned a valid value");
    CvTechEntry* pkTechInfo = GC.getTechInfo(eTech);
    if(pkTechInfo == NULL)
    {
        return 0;
    }

    int iCost = pkTechInfo->GetResearchCost();

    CvHandicapInfo* pkHandicapInfo = GC.getHandicapInfo(m_pTeam->getHandicapType());
    if(pkHandicapInfo)
    {
        iCost *= pkHandicapInfo->getResearchPercent();
        iCost /= 100;
    }

    iCost *= GC.getMap().getWorldInfo().getResearchPercent();
    iCost /= 100;

    iCost *= GC.getGame().getGameSpeedInfo().getResearchPercent();
    iCost /= 100;

    iCost *= GC.getGame().getStartEraInfo().getResearchPercent();
    iCost /= 100;

    iCost *= std::max(0, ((GC.getTECH_COST_EXTRA_TEAM_MEMBER_MODIFIER() * (m_pTeam->getNumMembers() - 1)) + 100));
    iCost /= 100;

    return std::max(1, iCost);
}

So it looks like I'm close. Just need to figure out how to modify this in the right way, then read up on building the appropriate files, and then test it.
 
though it's not clear to me how it identifies whether you're a human or not
Via the CvHandicapInfo data - this is the difficulty the player has selected. If your code needs to differentiate human/AI players, use the isHuman() method on the CvPlayer object
 
Hey all! I'm working on my first mod and was wondering if anyone wouldn't mind looking over some of my lua and giving me some constructive feedback. This is my first experience with lua, so I'm sure that I'm missing some things or making rookie mistakes. Appreciate it!

The mod essentially allows for one of three unrest events to occur under the correct conditions (revolts, rebellions, and revolutions). Revolts cause a turn of resistance in a city, rebellions spawn barbarian units around a city, much like in vanilla civ, and revolutions give the city to a City State that is reserved at the beginning of the game, thanks to some help from Gedemon's code. I tried to leave plenty of comments in the lua, but if you have questions feel free to ask..

Here's the code:
Code:
function UnrestEventOccurs()
                for i = 0, pCity:GetNumCityPlots() - 1, 1 do
                        local pPlot = pCity:GetCityIndexPlot (i)
                        local PlotOccupationNum = pPlot:GetNumUnits()
                        if pPlot:IsMountain(0) and pPlot: IsCity(0) and pPlot:IsImpassible(false) and pPlot:IsWater(0) and PlotOccupationNum < 1 then --is plot not a mountain/city/impassible/water/occupied?
                            table.insert(openPlots, pPlot) --(Thanks Gedemon!)
                        end
                end
                Shuffle(openPlots) --(Thanks Gedemon!)
                if RevoltOccurs() = true and pCity:GetResistanceTurns() == 0 then --makes sure city is not already resisting, don't want to add more resistance to already resisting city
                    pCity:ChangeResistanceTurns(1) --gives city 1 turn of resistance
                end

                if RebellionOccurs() = true then
                    local openPlots = {}
                    
                    for n, pPlot in pairs(openPlots) do --spawn units (between 1 to 5 depending upon city size)
                        for i= NumRebel, 0, -1 do
                            BARBARIAN:InitUnit(SelectedUnitType, pPlot:GetX(), p:GetY(), UNITAI_ATTACK)
                        end

                if RevolutionOccurs() = true then
                    Controls.RevolutionPopup:SetHide(false) --triggers revolution popup
                    RevCS:AcquireCity:(pCity, false, true)
                    if city:GetResistanceTurns() > 0 then --prevents new city from having resistance for City State (Thanks Gedemon)
                        city:ChangeResistanceTurns(-city:GetResistanceTurns())
                        end

                    if not player:IsHuman() then --prevents City State from Razing or Puppeting (Thanks Gedemon)
                        city:SetPuppet(false)
                            if city:IsRazing() then
                            city:DoTask(TaskTypes.TASK_UNRAZE, -1, -1, -1)
                            end
                    end

                    if city:IsOccupied() then --prevents new city being "occupied" by City State (Thanks Gedemon)
                        city:SetOccupied(false)
                    end

                    for CivilWar = true do
                        pPlayerTeam:Declarewar(RevCS) --war between revolution and player
                        pPlayerTeam:CanChangeWarPeace(RevCS) = false --disallow peace
                        for n, pPlot in pairs(openPlots) do --spawn units (between 2 to 6 depending upon city size)
                            for i= NumRebel + 1, 0, -1 do
                                RevCS:InitUnit(SelectedUnitType, pPlot:GetX(), p:GetY(), UNITAI_DEFENSE)
                            end
                        end
                        CivilWar = false
                    end           
                                
                    for Independence = true do   
                        pPlayer:ForcePeace(RevCS) --forces the player to be at peace with the new City State
                        pPlayer:DoMinorLiberationByMajor(RevCS) --New City State will be happy with player for being granted independence rather than civil war
                        RevCS:WasResurrectedThisTurnBy(pPlayer)   
                        for n, pPlot in pairs(openPlots) do --spawn units (between 0 to 4 depending upon city size)
                            for i= NumRebel - 1, 0, -1 do
                                RevCS:InitUnit(SelectedUnitType, pPlot:GetX(), p:GetY(), UNITAI_DEFENSE)
                            end
                        end
                        Independence = false
                    end
 
Hey all! I'm working on my first mod and was wondering if anyone wouldn't mind looking over some of my lua and giving me some constructive feedback. This is my first experience with lua, so I'm sure that I'm missing some things or making rookie mistakes. Appreciate it!

Code:
pPlot:IsMountain(0)
pPlot: IsCity(0)
These methods do not take any parameters, so you can remove the 0 there and just open and close the brackets immediately. Moreover, you have a space between 'pPlot:' and 'IsCity()'

Code:
pPlot:IsImpassible(false)
This uses wrong syntax, as IsImpassble(..) does not take any parameters. Since you want to check if a plot is impassible, you should use either of the following:
Code:
pPlot:IsImpassable() == false
not pPlot:IsImpassable()

Code:
local PlotOccupationNum = pPlot:GetNumUnits()
PlotOccupationNum is 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 be named iPlotOccupationNum since your variable stores an integer.

[COCE]
if RevoltOccurs() = true and ...
[/CODE]
You should use two equal signs here, or drop '= true' all together. I.e. use either of the following:
Code:
if RevoltOccurs() == true and ...
if RevoltOccurs() and ...
The same goes for other places in the code. Basically, if you're comparing (i.e. comparing the contents of a variable to something else) you'll need two equal signs, and if you're assigning (i.e. setting a variable) you'll need only a single equal sign

Code:
for n, pPlot in pairs(openPlots) do
While it's not wrong, when not using a variable we usually just name it _. So something like:
Code:
for _, pPlot in pairs(openPlots) do

Code:
for CivilWar = true do
This is not a loop, you'll want to replace the "for...do" by "if..then"

The variable "RevCS" does not seem to be defined anywhere (and does not follow Hungarian Notation). Other undefined variables are "BARBARIAN", "SelectedUnitType", "pPlayerTeam", "NumRebel", and "UNITAI_ATTACK"

Code:
pPlayerTeam:Declarewar(RevCS)
This has two mistakes. First off, the method is named "DeclareWar(..)" (with a capital letter!). Secondly, since I assume RevCS is a pointer, this won't work as the method requires the TeamID according to the cheatsheets/documentation. The result of your current code is that the player team will always declare ware to team 0 (which will be the human player in single player games)

Code:
pPlayerTeam:CanChangeWarPeace(RevCS) = false --disallow peace
This suffers the same issue as earlier: RevCS is a pointer but the method requires a TeamID
Moreover, this method is a getter method; you only get a return value but cannot set the value it will return. I'm not sure what the method returns anyway.

The same you-pass-a-pointer-as-an-argument-but-you-need-to-pass-an-ID occurs in the following lines:
Code:
pPlayer:ForcePeace(RevCS)
pPlayer:DoMinorLiberationByMajor(RevCS)
RevCS:WasResurrectedThisTurnBy(pPlayer)
For 99% of the methods you'll ever use you'll need to pass IDs rather than pointers as arguments.

Code:
for Independence = true do   
    --[..] snipped code
    Independence = false
 end
The variable "Independence" is never set to true in any of your code.

Lastly, you seem to be missing many 'end's. Every loop and if-statement needs an 'end'
 
@Troller0001 Thank you so much! That was immensely helpful! I knew I was making mistakes, but I didn’t realize just how much of it I was messing up.

I’ve been mostly using Bane's guide here: https://forums.civfanatics.com/threads/lua-basic-modding-guide-for-civ5.533853/ or the reference wiki here: http://modiki.civfanatics.com/index.php?title=Lua_and_UI_Reference_(Civ5). Are there other resources I could use? What cheatsheet/documentation is available for beginners like me.

I will definitely start being better about using Hungarian notation. I just recently read about it in another thread and your explanation helps a lot.

Again I really appreciate you taking some time to look this over! Thank you!
 
What cheatsheet/documentation is available for beginners like me.
I've been using Whoward's Cheatsheets (though I've also added the Lua methods introduced in VMC manually)
The actual files are somewhere here on CFC but I always seem to lose the actual thread, so I'm just gonna attach them in this post.

While these do not actually provide a description of what the methods do, they do provide what parameters and return values they have. Moreover, the names are usually self-explanatory, but if in doubt you can always google on the method name (tip: include site:civfanatics.com at the end of your search term to only search on CFC) or search directly in the base game (Lua) resources (using a tool such as Agent Ransack).

Note that the ModWiki can be very useful at times, even though it may be outdated
 

Attachments

  • Lua Methods - incl VMC v87.xlsx
    149.7 KB · Views: 184
  • Game events.xlsx
    15.5 KB · Views: 173
  • Various Mod Components_V80_Events.xlsx
    16.3 KB · Views: 169
I'm currently interested in trying to add some new variables to City-State interactions, particularly when meeting them for the first time; however from looking through the base code, the idea on how to implement this feature is a bit over my head (I can work .Lua okay, but translating C++ from the .DLL to .Lua is currently beyond me).

Basically what I'm hoping for is that when a player meets a City-State for the first time, the player receives an additional bonus based on whether they're the first player to meet the City-State or not, but also based on it's Trait.

A rundown of what I had in mind:
Cultured City-States - provide 20 Culture to the first player that meets them (10 to everyone else).
Maritime City-States - provide a +1 Population increase in the nearest City of the first player that meets them (ala the Goody Hut; also everyone else would get nothing or perhaps a slight one-time Food boost in their nearest City, maybe 25%).
Mercantile City-States - provide twice as much Gold when meeting them (60 Gold for first player, 30 for everyone else).
Militaristic City-States - provide +10 Experience Points to the closest Combat Unit owned by the first player to meet them (+5 XP for the closest unit for everyone else).

I'm hoping that these effects would bring the other 4 types of City-States in line with the Religious City-States, which provide a Faith yield when first meeting them.

If anyone can rig something like this up for me, that'd be great!
 
Hi! I'm new to modding and am making my first civ.
I'm trying to add an UB for my civ that allows any city it's built on to purchase military units with Faith.
Also, part of my civ's UA is giving +35% Combat Strength (through a promotion) to units standing on a tile that belongs to a city that follows the civ's religion.

I'm relatively inexperienced with Lua (the coding that makes an other portion of my civ's UA work was taken and adapted from another mod's code) , so I don't even know is this is doable. Can I get some assistance?
 
I'm currently interested in trying to add some new variables to City-State interactions, particularly when meeting them for the first time; however from looking through the base code, the idea on how to implement this feature is a bit over my head (I can work .Lua okay, but translating C++ from the .DLL to .Lua is currently beyond me).

Basically what I'm hoping for is that when a player meets a City-State for the first time, the player receives an additional bonus based on whether they're the first player to meet the City-State or not, but also based on it's Trait.

A rundown of what I had in mind:
Cultured City-States - provide 20 Culture to the first player that meets them (10 to everyone else).
Maritime City-States - provide a +1 Population increase in the nearest City of the first player that meets them (ala the Goody Hut; also everyone else would get nothing or perhaps a slight one-time Food boost in their nearest City, maybe 25%).
Mercantile City-States - provide twice as much Gold when meeting them (60 Gold for first player, 30 for everyone else).
Militaristic City-States - provide +10 Experience Points to the closest Combat Unit owned by the first player to meet them (+5 XP for the closest unit for everyone else).

I'm hoping that these effects would bring the other 4 types of City-States in line with the Religious City-States, which provide a Faith yield when first meeting them.

If anyone can rig something like this up for me, that'd be great!

You do not actually need to touch the DLL at all in order to do something like this; all of it can be done using Lua.
We have a method that runs when one team meets another team (which also works for city-states):
Code:
-- Runs when one team meets another
function onTeamMeet(iTeamThatWasMet, iTeamThatDiscovered)
   -- do stuff here
end
GameEvents.TeamMeet.Add(onTeamMeet)

We can then find out a minor civ's trait by using:
Code:
pMinorPlayer:GetMinorCivTrait()

Granting culture, gold, population, food, or exp can be done using:
Code:
pPlayer:ChangeGold(..)
pPlayer:ChangeJONSCulture(..)
pCity:ChangeFood(..) -- can always be the capital city or the nearest city can be found by looping over all of a player's cities
pCity:ChangePopulation(..) -- can always be the capital city or the nearest city can be found by looping over all of a player's cities
pUnit:ChangeExperience(..) -- finding the nearest unit can be done by looping over all of a player's units

To ensure that each minor civ only gives its big bonus a single time you could use a variable to indicate such a thing. However, unless you're using TableSaverLoader or SaveUtils, this won't work when saving and reloading games.
Alternatively you can give a minor civ a dummy building/dummy policy after they have granted their first time bonus, and only grant the first time bonus if such a building/policy is not owned by the minor civ.

If you want to modify the default gold gained from meeting city states then you can modify the following entries in the <Defines>-table:
Code:
MINOR_CIV_CONTACT_GOLD_FIRST
MINOR_CIV_CONTACT_GOLD_OTHER

Note: The Relgious City State's Faith boost is hardcoded into the DLL. There's no way of changing it around (unless you manually remove it with Lua after meeting them).
 
Ahhh! Excellent! I knew this was possible with the .Lua, I just couldn't picture how to do it in my head. This will be an excellent step towards realizing this. Thank you!
 
Hi! I'm new to modding and am making my first civ.
[..]
Also, part of my civ's UA is giving +35% Combat Strength (through a promotion) to units standing on a tile that belongs to a city that follows the civ's religion.
[..]

You can grant/remove a promotion that always gives 35% combat strength based on whether the unit is standing on such a tile.

To obtain the religion a player has founded (if any), to find the majority religion of a city, and to get the plot of a unit and city of a plot you can use:
Code:
local bHasCreatedReligino = pPlayer:HasCreatedReligion() -- should be called before pPlayer:GetReligionCreatedByPlayer()
local eReligion = pPlayer:GetReligionCreatedByPlayer() -- note that this will return 0 for a "pantheon religion" (unsure what the behaviour would be if the player has not created a religion)
local eReligion = pCity:GetReligiousMajority() -- note that this will return 0 for a "pantheon religion" (and I assume -1 if there is no majority religion)
local pPlot = pUnit:GetPlot()
local iCity = pPlot:GetCityPurchaseID()

You can then either use one of the following hooks:
Code:
GameEvents.PlayerDoTurn(..) -- loop over all units, and give/remove the promotion (less laggy option, but only applies at the start of a player's turn)
GameEvents.UnitSetXY(..) -- give/remove the promotion for a single unit (laggy option, as this fires for every unit that moves, but does apply instantly)


P.s. LeeS already replied to the other question you asked in a different thread.
 
Thanks! Now I just need to learn how to code in Lua so that I can actually use the stuff you've given me, hehe...
 
Last edited:
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:
Code:
local civilisationID = GameInfoTypes["CIVILIZATION_SERTAO"]
local UAPromotion = GameInfoTypes["PROMOTION_SERTAO_RELIGION_PROMOTION"]
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()
--
function IsCivActive(civilisationID)
    for iSlot = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local slotStatus = PreGame.GetSlotStatus(iSlot)
        if (slotStatus == SlotStatus["SS_TAKEN"] or slotStatus == SlotStatus["SS_COMPUTER"]) then
            if PreGame.GetCivilization(iSlot) == civilisationID then
                return true
            end
        end
    end

    return false
end
--
function CombatBonus(PlayerID, UnitID)
    local player = Players[PlayerID]
    if player:GetCivilizationType() == civilisationID then
        if HasCreatedReligion then
            if PlayerReligion == CityReligion then
                Unit:SetHasPromotion(UAPromotion, true)
            else
                Unit:SetHasPromotion(UAPromotion, false)
            end
        end
    end
end
--
if IsCivActive(civilisationID) then
    GameEvents.UnitSetXY.Add(CombatBonus)
end
Can I get some input?
 
I've got a another request/question: is it possible to change a hard-coded variable (in this example, changing how far a City can perform a Ranged Strike against an Enemy Unit) using .Lua? I'm curious because I'd like to try implementing this by setting the "CITY_ATTACK_RANGE" Variable in the GameDefines to "1" and then using the .Lua code to check if they player has a certain Social Policy (in this example: Oligarchy), and then setting the value to "2" if they do (and obviously having this check during PlayerDoTurn so that this variable can be altered to different values for every player as their turns come up).

I've a vague idea on how this is possible, I'm just not familiar enough with the means to achieve this the moment.

-EDIT-
I found a solution to this problem. It seems that my answer lied in the Bombardment.lua file within the InGame Folder. Though to my understanding, it won't be a good idea to change every GameDefines variable this, way, it will at least work with the range of City attacks for each player.

-EDIT EDIT-
Well, after hours of trying, I found out the only thing I could alter (without doing a .DLL Mod, which is something I'm trying to avoid) was the distance at which the border for the City Ranged Attack would display. Not very useful. The GameDefines variable could be modified yes, but not in-game like I wanted (the game hates trying to modify "Read-Only" variables in-game). Guess that's the end of that Mod idea. Hopefully this information is useful for anyone that manages to stumble upon it.
 
Last edited:
Top Bottom