1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

C++/Lua Request Thread

Discussion in 'Civ5 - SDK / LUA' started by Nefliqus, Jun 11, 2012.

  1. ThanOscar

    ThanOscar Chieftain

    Joined:
    Feb 13, 2019
    Messages:
    41
    ughhhh making it modular is a swamp, i'll fall back to lua i guess
     
  2. ThanOscar

    ThanOscar Chieftain

    Joined:
    Feb 13, 2019
    Messages:
    41
    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
     
  3. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,350
    Location:
    Near Portsmouth, UK
    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
     
  4. ThanOscar

    ThanOscar Chieftain

    Joined:
    Feb 13, 2019
    Messages:
    41
    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: May 26, 2019
  5. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,350
    Location:
    Near Portsmouth, UK
    See the three DLL tutorials I mentioned, as they'll walk through most (if not all) of what you need to know
     
  6. justanediblefriend

    justanediblefriend Chieftain

    Joined:
    Jun 6, 2019
    Messages:
    10
    Gender:
    Female
    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?
     
  7. justanediblefriend

    justanediblefriend Chieftain

    Joined:
    Jun 6, 2019
    Messages:
    10
    Gender:
    Female
    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.
     
  8. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,350
    Location:
    Near Portsmouth, UK
    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
     
  9. Clarkimedes

    Clarkimedes Chieftain

    Joined:
    Aug 23, 2019
    Messages:
    2
    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
     
  10. Troller0001

    Troller0001 Not an actual Troll

    Joined:
    Mar 9, 2016
    Messages:
    730
    Gender:
    Male
    Location:
    The Netherlands
    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'
     
  11. Clarkimedes

    Clarkimedes Chieftain

    Joined:
    Aug 23, 2019
    Messages:
    2
    @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!
     
  12. Troller0001

    Troller0001 Not an actual Troll

    Joined:
    Mar 9, 2016
    Messages:
    730
    Gender:
    Male
    Location:
    The Netherlands
    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
     

    Attached Files:

  13. Harkodos

    Harkodos Warlord

    Joined:
    Feb 9, 2016
    Messages:
    178
    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!
     
  14. KyubeyD3

    KyubeyD3 Chieftain

    Joined:
    Oct 6, 2019
    Messages:
    13
    Gender:
    Male
    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?
     
  15. Troller0001

    Troller0001 Not an actual Troll

    Joined:
    Mar 9, 2016
    Messages:
    730
    Gender:
    Male
    Location:
    The Netherlands
    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).
     
  16. Harkodos

    Harkodos Warlord

    Joined:
    Feb 9, 2016
    Messages:
    178
    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!
     
  17. Troller0001

    Troller0001 Not an actual Troll

    Joined:
    Mar 9, 2016
    Messages:
    730
    Gender:
    Male
    Location:
    The Netherlands
    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.
     
  18. KyubeyD3

    KyubeyD3 Chieftain

    Joined:
    Oct 6, 2019
    Messages:
    13
    Gender:
    Male
    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: Oct 13, 2019
  19. KyubeyD3

    KyubeyD3 Chieftain

    Joined:
    Oct 6, 2019
    Messages:
    13
    Gender:
    Male
    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?
     
  20. Harkodos

    Harkodos Warlord

    Joined:
    Feb 9, 2016
    Messages:
    178
    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: Oct 24, 2019

Share This Page