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

Quick Modding Questions Thread

Discussion in 'Civ4 - Creation & Customization' started by kiwitt, Jan 27, 2010.

  1. devolution

    devolution Warlord

    Joined:
    Oct 7, 2016
    Messages:
    296
    Gender:
    Male
    Location:
    Stavanger, Norway
    Why are you guys using the ancient VS 2010 when VS 2017 Community Edition is available for free ? I recommend running the game in windowed mode when debugging to avoid issues like this.
     
  2. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    I know :(

    Honestly part of the reason is that using VS2017 still feels like using a ten year old IDE so with VS2010 I'm at least emotionally prepared for things being so terrible.
     
    devolution and Thorvald of Lym like this.
  3. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    More questions! I am currently trying to fix an issue with the espionage advisor that I originally got from History Rewritten:

    Spoiler :
    Civ4ScreenShot0400.JPG


    The issue is with the city selection list titled "known cities". You can click the individual rows and the row is highlighted (here with Saint-Louis du Sénegal) but the selected city isn't updated in the backend. This is indicated by the cyan coloured text highlighting, which should have updated from Port-au-Prince to Saint-Louis along with the selection.

    Here's the code that populates the table:
    Code:
           if self.iTargetPlayer > -1:
               pTargetPlayer = gc.getPlayer(self.iTargetPlayer)
               iEspionage = self.pActiveTeam.getEspionagePointsAgainstTeam(pTargetPlayer.getTeam())
    
               # City Table
               screen.addTableControlGFC("CityTable", 3, self.X_CITY_LIST, self.Y_CITY_LIST, self.W_CITY_LIST, self.H_CITY_LIST, True, False, 24, 24, TableStyles.TABLE_STYLE_STANDARD)
               screen.setTableColumnHeader("CityTable", 0, u"<font=2>Known Cities</font>", self.W_CITY_LIST - 102)
               screen.setTableColumnHeader("CityTable", 1, u"<font=2>Spies</font>", 50)
               screen.setTableColumnHeader("CityTable", 2, u"<font=2>Cost</font>", 50)
               screen.setStyle("CityTable", "Table_StandardCiv_Style")
               screen.enableSelect("CityTable", True)
    
               (loopCity, iter) = pTargetPlayer.firstCity(False)
               while(loopCity):
                   if loopCity.isRevealed(self.iActiveTeam, False):
                       iRow = screen.appendTableRow("CityTable")
    
                       if self.iTargetCity == -1:
                           self.iTargetCity = loopCity.getID()
    
                       if self.iTargetCity == loopCity.getID():
                           screen.selectRow("CityTable", iRow, True)
    
                       # Status
                       if loopCity.isCapital():
                           szStatus = u"%c" % CyGame().getSymbolID(FontSymbols.STAR_CHAR)
                       elif loopCity.isGovernmentCenter():
                           szStatus = u"%c" % CyGame().getSymbolID(FontSymbols.SILVER_STAR_CHAR)
                       else:
                           szStatus = u"%c" % CyGame().getSymbolID(FontSymbols.BULLET_CHAR)
    
                       # Name
                       szName = loopCity.getName()
                       if self.iTargetCity == loopCity.getID():
                           szName = CyTranslator().changeTextColor(szName, gc.getInfoTypeForString('COLOR_HIGHLIGHT_TEXT'))
    
                       screen.setTableText("CityTable", 0, iRow, szStatus + szName, "", WidgetTypes.WIDGET_ESPIONAGE_SELECT_CITY, loopCity.getID(), 0, CvUtil.FONT_LEFT_JUSTIFY)
    
                       # Spies
                       if self.iTargetCity == loopCity.getID():
                           del self.CitySpies[:]
    
                       iNumSpies = 0
                       loopPlot = loopCity.plot()
                       for iUnit in xrange(loopPlot.getNumUnits()):
                           pUnit = loopPlot.getUnit(iUnit)
                           if not pUnit.isNone():
                               if pUnit.isCounterSpy() and pUnit.getOwner() == self.iActivePlayer:
                                   iNumSpies += 1
                                   if self.iTargetCity == loopCity.getID():
                                       self.CitySpies.append(pUnit)
    
                       if iNumSpies > 0:
                           screen.setTableInt("CityTable", 1, iRow, str(iNumSpies), "", WidgetTypes.WIDGET_ESPIONAGE_SELECT_CITY, loopCity.getID(), 0, CvUtil.FONT_RIGHT_JUSTIFY)
    
                       # Mission Cost
                       if self.iMission > -1:
                           iCost = self.pActivePlayer.getEspionageMissionCost(self.iMission, self.iTargetPlayer, loopPlot, -1)
                           if iCost > -1:
                               if iEspionage < iCost:
                                   szCost = CyTranslator().changeTextColor(str(iCost), gc.getInfoTypeForString('COLOR_NEGATIVE_TEXT'))
                               else:
                                   szCost = CyTranslator().changeTextColor(str(iCost), gc.getInfoTypeForString('COLOR_POSITIVE_TEXT'))
    
                               screen.setTableInt("CityTable", 2, iRow, szCost, "", WidgetTypes.WIDGET_ESPIONAGE_SELECT_CITY, loopCity.getID(), 0, CvUtil.FONT_RIGHT_JUSTIFY)
    
                       iRow += 1
                   (loopCity, iter) = pTargetPlayer.nextCity(iter, False)
    Here is the handleInput function:
    Code:
        def handleInput(self, inputClass):
           print "handleInput: button type: %d, data 1: %d, widget_espionage_select_city: %d" % (inputClass.getButtonType(), inputClass.getData1(), WidgetTypes.WIDGET_ESPIONAGE_SELECT_CITY)
       
           screen = self.getScreen()
           if self.iTargetPlayer > -1:
               if inputClass.getButtonType() == WidgetTypes.WIDGET_ESPIONAGE_SELECT_PLAYER:
                   screen.setState("LeaderIcon" + str(self.iTargetPlayer), False)
                   self.iTargetPlayer = inputClass.getData1()
                   screen.setState("LeaderIcon" + str(self.iTargetPlayer), True)
                   self.iTargetCity = -1
                   self.updateRightPanel()
    
               elif inputClass.getFunctionName().startswith("WeightIncrease"):
                   self.changeEspionageWeight(inputClass.getData1() - 1, self.iIncrement)
    
               elif inputClass.getFunctionName().startswith("WeightDecrease"):
                   self.changeEspionageWeight(inputClass.getData1() - 1, -self.iIncrement)
    
           if inputClass.getFunctionName() == "EspionagePlus" or inputClass.getFunctionName() == "EspionageMinus":
               iChange = inputClass.getData2()
               if CyInterface().shiftKey():
                   if iChange > 0:
                       iChange = 100
                   elif iChange < 0:
                       iChange = -100
    
               CyMessageControl().sendModNetMessage(lNetworkEvents['CHANGE_COMMERCE_PERCENT'],  self.iActivePlayer, inputClass.getData1(), iChange, -1)
    
           elif inputClass.getButtonType() == WidgetTypes.WIDGET_ESPIONAGE_SELECT_CITY:
               self.iTargetCity = inputClass.getData1()
               self.updateRightPanel()
    
           elif inputClass.getButtonType() == WidgetTypes.WIDGET_ESPIONAGE_SELECT_MISSION:
               iSelectedMission = inputClass.getData1()
               if self.iMission == iSelectedMission:
                   self.iMission = -1
               else:
                   self.iMission = iSelectedMission
    
               self.updateRightPanel()
    
           elif inputClass.getButtonType() == WidgetTypes.WIDGET_GO_TO_CITY:
               screen.hideScreen()
               pCity = gc.getPlayer(inputClass.getData1()).getCity(inputClass.getData2())
               CyCamera().JustLookAtPlot(pCity.plot())
               if len(self.CitySpies) > 0:
                   pSelectedUnit = self.CitySpies[0]
                   for pSpy in self.CitySpies:
                       if not pSpy.hasMoved():
                           pSelectedUnit = pSpy
                           break
                   CyInterface().selectUnit(pSelectedUnit, True, True, False)
    
           elif inputClass.getButtonType() == WidgetTypes.WIDGET_ZOOM_CITY:
               screen.hideScreen()
               CyInterface().selectCity(gc.getPlayer(inputClass.getData1()).getCity(inputClass.getData2()), True)
    
           if inputClass.getFunctionName() == "WeightIncrementSelect":
               iIndex = screen.getSelectedPullDownID("WeightIncrementSelect")
               self.iIncrement = screen.getPullDownData("WeightIncrementSelect", iIndex)
    
           elif inputClass.getFunctionName() == "WeightResetButton":
               self.resetEspionageWeights()
    
           elif inputClass.getFunctionName() == "DebugPlayerSelect":
               iIndex = screen.getSelectedPullDownID("DebugPlayerSelect")
               self.iActivePlayer = screen.getPullDownData("DebugPlayerSelect", iIndex)
               self.iTargetPlayer = -1
               self.iTargetCity = -1
               self.updateLeftPanel()
               self.updateRightPanel()
    
           return 0
    As you can see, I have logged the metadata passed to the function when something is clicked. However, when I click around in the table, this is what I get:
    Code:
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    
    handleInput: button type: 0, data 1: 0, widget_espionage_select_city: 189
    This suggests that no button type is associated with the rows in the table. However, in setTableText it is clearly set to WidgetTypes.WIDGET_ESPIONAGE_SELECT_CITY, so I'd expect that to be passed as the button type (and also, the data1 field should be set to a city ID, not 0). I included this much information because I am not sure what is going on, because from my understanding everything should work. Am I missing something? The same issue exists in History Rewritten too by the way.
     
  4. merijn_v1

    merijn_v1 Black Belt

    Joined:
    Dec 29, 2008
    Messages:
    5,632
    Location:
    The city of the original vlaai
    The problem is related to BUG. The getWidgetHelp() in WidgetUtil.py (assets/python/BUG in DoC, assets/python/Utilities in HR) enables you to give custom hoover text for screen functions. But somehow, it eats up the input data when it returns an empty string.

    By adding the code below to that function, it will return a non-empty string. This fixes the behaviour when clicking in the table, but it also forces you to use a hoover text.

    Code:
        elif eWidgetType == WidgetTypes.WIDGET_ESPIONAGE_SELECT_CITY:
            return u"x"
     
  5. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    That's interesting, maybe there is another mapping in the call chain that uses the hover text for some reason? I'll look into it more.
     
  6. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    Yeah, that fixed it, thanks. But I would like to find a way that also gets rid of the empty tooltip.
     
    Last edited: Mar 21, 2019
  7. Deder

    Deder Chieftain

    Joined:
    Mar 21, 2019
    Messages:
    2
    Gender:
    Male
    Hi! Is there an easy way ( or hard way ) to edit open borders and defensive pacts?
    I have noticed that you can pass through yours vassal terrain ( without open borders ) , and i want to do the same in defensive pact.

    And if it is possible - open borders leave with only trade character? ( no crossing border )
     
  8. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    Welcome to the forums! There is no easy way, but there is a hard(er) way depending on your knowledge. It is possible to edit the rules of how diplomatic agreements and territorial access rules work, but you need to be able to compile a new DLL and probably edit the source code in many places.
     
  9. Deder

    Deder Chieftain

    Joined:
    Mar 21, 2019
    Messages:
    2
    Gender:
    Male
    Thank you for respond. Could you show me those places of code which should i edit? Is Microsoft Visual C++ 2010 Express a good program for that purpose?
     
  10. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    You don't just have to be able to edit the files, you also need to be able to compile them to create a new CvGameCoreDLL file, so it's necessary to start with that. I have a created a step by step guide for it here.
     
  11. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    Another question around the AI of waking up units building improvements. The code above only affects human units, but aren't AI workers also canceling their improvement building when they are threatened by an enemy unit moving close to them? I've looked into CvUnitAI::AI_update() and it is only called if the unit is not currently performing a mission (including building), so I assume this has to be the case. But where does it happpen?

    For a bit of context, I have given a combat unit the ability to build roads, but they are not woken up by nearby enemies, which I assume is because they can theoretically defend themselves. I would still like to wake them up because they should reconsider their purpose if enemies are nearby and start defending cities or attacking enemies.
     
  12. f1rpo

    f1rpo plastics

    Joined:
    May 22, 2014
    Messages:
    527
    Location:
    Germany
    That should be in CvSelectionGroup::doTurn:
    Code:
    if(AI_isControlled()) {
      if(getActivityType() != ACTIVITY_MISSION ||
          (!canFight() && GET_PLAYER(getOwner()).AI_getPlotDanger(plot(), 2) > 0))
        setForceUpdate(true);
    }
    You could try checking if getMissionType(0) equals MISSION_ROUTE_TO or MISSION_BUILD.
     
    Leoreth likes this.
  13. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    Ah, nice. Thanks a lot!
     
  14. raxo2222

    raxo2222 Time Traveller

    Joined:
    Jun 10, 2011
    Messages:
    6,611
    Location:
    Poland
    How I can check if events have text in them?

    For example sometimes event appears but has missing text - sometimes main part and sometimes options in them.
    blablabla

    Option 1: blablabla
    Option 2: blablabla
    .......
    Option X: TXT_KEY missing.

    It seems like only few events suffers from this in huge mod that is Caveman2Cosmos.
     
  15. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    You mean find text keys that aren't defined yet? I think event infos are exposed to Python, I suggest you go into the Python console and do something like (note: I didn't test this):
    Code:
    for i in range(gc.getNumEventInfos()): print gc.getEventInfo(i).getText()
    And then see which undefined text keys are shown (EventInfo are the event options while EventTriggerInfos are the events themselves, in case you need both).
     
    MattCA and raxo2222 like this.
  16. raxo2222

    raxo2222 Time Traveller

    Joined:
    Jun 10, 2011
    Messages:
    6,611
    Location:
    Poland
    Yep, it worked with "shift+~" to enter python console.

    It doesn't work with events themselves that is:
    Code:
    for i in range(gc.getNumEventTriggerInfos()): print gc.getEventTriggerInfo(i).getText()
    That is it only displays single event and that's all.
     
    Last edited: Mar 25, 2019
  17. Leoreth

    Leoreth 心の怪盗団 Moderator

    Joined:
    Aug 23, 2009
    Messages:
    33,365
    Gender:
    Male
    Location:
    Leblanc
    Are you sure? If you have logging enabled, the output should also show up in PythonDbg.log. Or something is different in C2C, it works in my mod at least.
     
  18. raxo2222

    raxo2222 Time Traveller

    Joined:
    Jun 10, 2011
    Messages:
    6,611
    Location:
    Poland
    Yeah first one displayed all entries and second one displayed only one entry.
     
  19. Jarlaxe Baenre

    Jarlaxe Baenre Emperor

    Joined:
    Feb 17, 2010
    Messages:
    1,959
    Location:
    Calgary, Alberta, Canada
    How does iTotalCultureRatio in Civ4VictoryInfo work?

    In CvGame.cpp, it seems to indicate that it works if you have more culture than a specific threshold, based off of iTotalCultureRatio and how much culture other civs have. But CvVictoryScreen.py seems to be written to require you to get X times the best other player's culture.


    Spoiler CvGame.cpp :
    Code:
    if (GC.getVictoryInfo(eVictory).getTotalCultureRatio() > 0)
            {
                int iThreshold = ((GET_TEAM(eTeam).countTotalCulture() * 100) / GC.getVictoryInfo(eVictory).getTotalCultureRatio());
    
                bool bFound = false;
    
                for (int iK = 0; iK < MAX_CIV_TEAMS; iK++)
                {
                    if (GET_TEAM((TeamTypes)iK).isAlive())
                    {
                        if (iK != eTeam)
                        {
                            if (GET_TEAM((TeamTypes)iK).countTotalCulture() > iThreshold)
                            {
                                bFound = true;
                                break;
                            }
                        }
                    }
                }
    
                if (bFound)
                {
                    bValid = false;
                }
            }


    I'm not quite sure what's going on here.
    The threshold is based on a given team's culture, but which given team? How does this victory condition trigger, and what's it checking against?
     
  20. f1rpo

    f1rpo plastics

    Joined:
    May 22, 2014
    Messages:
    527
    Location:
    Germany
    That's what I'm reading in the C++ code. Let's say iTotalCultureRatio in Civ4VictoryInfos.xml is 500. Then eTeam has not won the game (bValid=false) so long as any other team has a greater total city culture than eTeam's total city culture divided by 5 (bFound=true). In other words, eTeam wins once its total city culture is at least 5 times the total city culture of every other team. (The two booleans are really superfluous – might as well return false right away instead of setting bFound=true.) I'm not sure if this had been intended as a standalone victory condition or perhaps an extra condition for the 3-city culture victory.
    On the victory screen I get e.g. "7944% of culture
    firpo: 20693 Suryavarman: 39724"
    The 7944 is Sury's culture divided by 5. I don't see how that's helpful. Someone needs to have that much culture to prevent Sury's victory? But then it shouldn't be presented as a percentage. Instead, I guess it should show Sury's culture divided by the next best, which would not be me but Elizabeth, who has about 24000, so 166%. Or that divided by the target ratio. That would be 33%.
     
    Jarlaxe Baenre likes this.

Share This Page