Quick Modding Questions Thread

Just looking at the code, there is still iPlayerX in the addMessage call. FeatureInfo also isn't defined here. Untested attempt at a correction:
Spoiler :
Python:
            # Trigger the wonder movie popup
            popupInfo = CyPopupInfo()
            popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON_SCREEN)
            popupInfo.setData1(iFeature)
            popupInfo.setData3(3)
            popupInfo.setText(u"showWonderMovie")
            popupInfo.addPopup(pCity.getOwner())
            FeatureInfo = gc.getFeatureInfo(iFeature)
            CyInterface().addMessage(
                    pCity.getOwner(), # recipient
                    True, 10, # force immediate delivery, display duration
                    CyTranslator().getText("TXT_KEY_WONDERDISCOVERED_YOU", (FeatureInfo.getDescription(),)),
                    '', 0, FeatureInfo.getButton(), ColorTypes(11), # sound, indicator button and color
                    pPlot.getX(), pPlot.getY(), # coordinates
                    True, True) # offscreen/ onscreen arrows
Could still have other syntax errors (PythonErr.log would say where and what) or might have no apparent effect, in which case either placeWonderBuilding isn't called or the popup isn't processed as intended; either way, print statements (in placeWonderBuilding or in CvWonderBuildingScreen.py) should shed more light.
Oh, thank you so much! That worked!
:bowdown::bowdown::bowdown: :bowdown:


Now my next goal is twofold:
1) Find out how to make the wonder built only when it's inside the city's culture because now it can be outside:
1742894847354.png

Just looking around how to continue: Should I use cultureExpansion or onCultureExpansion or cultureDistance or getCultureRangeCities or isCultureRangeCity or isWithinCultureRange ?

I'm trying to find some relevant code with those above to start with but nothing yet...


2) In CoM cities can work tiles up to the 3rd ring, so when a city reaches a Natural Wonder on the 3rd tile that should also be built.
As I understand this is the responsible code for defining the area:
Python:
    def placeWonderBuilding(self, pCity):
        for i in xrange(21):
            pPlot = pCity.getCityIndexPlot(i)
            iFeature = pPlot.getFeatureType()
            if iFeature == -1:
                continue
So I just increase 21 to 37, right?
 
Just looking around how to continue: Should I use cultureExpansion or onCultureExpansion or cultureDistance or getCultureRangeCities or isCultureRangeCity or isWithinCultureRange ?

I'm trying to find some relevant code with those above to start with but nothing yet...
I would use onCultureExpansion. It triggers for a city when it reaches a new culture level and its borders expand, which seems to be what you want.

Semi Python pseudocode for what I would do:
Code:
# this needs to be registered for the "onCultureExpansion" event
def onCultureExpansion(self, city, iOwner):
    for index in range(21):
        plot = city.getCityIndexPlot(index)
        iFeature = plot.getFeatureType()
        if plot.getOwner() == iOwner and isNaturalWonder(iFeature): # however this is determined
            iNaturalWonderBuilding = getBuildingForNaturalWonder(iFeature) # however this is determined

            if iNaturalWonderBuilding != -1:
                if not gc.getGame().isBuildingClassMaxedOut(gc.getBuildingInfo(iNaturalWonderBuilding).getBuildingClassType(), 0):
                    city.setNumRealBuilding(iNaturalWonderBuilding, 1)

This relies on the following assumptions:
  • The first city to cover a natural wonder tile gets the natural wonder building (even if another city is closer)
  • Cities only get natural wonders from its workable tiles
  • The natural wonder building cannot change, i.e. it does not move to another city if its civ takes over the tile
  • The XML for the natural wonder building class defines it as max 1 in the game - I would recommend this so you can use isBuildingClassMaxedOut. That way if other cities also cover the natural wonder tile you won't get duplicates.
Another note: onCultureExpansion does not trigger for the first ring the city gets from the start. So I think you need to trigger the same code for "onCityBuilt" as well.

2) In CoM cities can work tiles up to the 3rd ring, so when a city reaches a Natural Wonder on the 3rd tile that should also be built.
As I understand this is the responsible code for defining the area:
Python:
    def placeWonderBuilding(self, pCity):
        for i in xrange(21):
            pPlot = pCity.getCityIndexPlot(i)
            iFeature = pPlot.getFeatureType()
            if iFeature == -1:
                continue
So I just increase 21 to 37, right?
That depends on how CoM implemented it. Maybe it makes sense to double check how getCityIndexPlot is used in other places in the code. Or you can just try if it works, it should be the likeliest situation.
 
Just looking at the code, there is still iPlayerX in the addMessage call. FeatureInfo also isn't defined here. Untested attempt at a correction:
Spoiler :
Python:
            # Trigger the wonder movie popup
            popupInfo = CyPopupInfo()
            popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON_SCREEN)
            popupInfo.setData1(iFeature)
            popupInfo.setData3(3)
            popupInfo.setText(u"showWonderMovie")
            popupInfo.addPopup(pCity.getOwner())
            FeatureInfo = gc.getFeatureInfo(iFeature)
            CyInterface().addMessage(
                    pCity.getOwner(), # recipient
                    True, 10, # force immediate delivery, display duration
                    CyTranslator().getText("TXT_KEY_WONDERDISCOVERED_YOU", (FeatureInfo.getDescription(),)),
                    '', 0, FeatureInfo.getButton(), ColorTypes(11), # sound, indicator button and color
                    pPlot.getX(), pPlot.getY(), # coordinates
                    True, True) # offscreen/ onscreen arrows
Could still have other syntax errors (PythonErr.log would say where and what) or might have no apparent effect, in which case either placeWonderBuilding isn't called or the popup isn't processed as intended; either way, print statements (in placeWonderBuilding or in CvWonderBuildingScreen.py) should shed more light.
So it is working in the basic version (if I'm editing Platyping's mod he uploaded) but when I merge the it with CoM I get this:
1743082629462.png

1743082654719.png

I have double checked, triple checked, quatro checked, etc for any mistakes while merging but found nothing.
How is it that the same code works in one place and not the other?! :confused:
Where should I look?
 
Last edited:
Well, it's clearly saying there's a missing link to that feature, most likely a typo or a mod-dependent path in the xml art loadfile. Search through all files in your directory for the that text, and see what it's pointing to, then follow that file path and see what's there, or adjust it to where it should be pointing at.
 
Well, it's clearly saying there's a missing link to that feature, most likely a typo or a mod-dependent path in the xml art loadfile. Search through all files in your directory for the that text, and see what it's pointing to, then follow that file path and see what's there, or adjust it to where it should be pointing at.
Well, clearly I did all those:
I have double checked, triple checked, quatro checked, etc for any mistakes while merging but found nothing.
I'm expecting something nastier.
ChatGPT even suggested clearing the cache but that didn't help either :rolleyes:
 
I mean, if the art info for FEATURE_PLATY_KILIMANJARO is linked to e.g. /oldmod/assets/art , and you copy that path exactly, well that ain't gonna work on newmod right?

Post here the xml entry for the feature and its art path, and the xml entry for the art def itself; that's what the game thinks is in error, so... let's see.

Could also be something related to unpacked vs packed art, but I think the game is smart enough to load both/either if present. Rarely touch art stuff myself.
 
I mean, if the art info for FEATURE_PLATY_KILIMANJARO is linked to e.g. /oldmod/assets/art , and you copy that path exactly, well that ain't gonna work on newmod right?

Post here the xml entry for the feature and its art path, and the xml entry for the art def itself; that's what the game thinks is in error, so... let's see.

Could also be something related to unpacked vs packed art, but I think the game is smart enough to load both/either if present. Rarely touch art stuff myself.
The feature:
XML:
        <FeatureInfo>
            <Type>FEATURE_PLATY_KILIMANJARO</Type>
            <Description>TXT_KEY_FEATURE_KILIMANJARO</Description>
            <Civilopedia>TXT_KEY_FEATURE_KILIMANJARO_PEDIA</Civilopedia>
            <ArtDefineTag>ART_DEF_FEATURE_KILIMANJARO</ArtDefineTag>
            <YieldChanges/>
            <RiverYieldChange/>
            <HillsYieldChange/>
            <iMovement>2</iMovement>
            <iSeeThrough>1</iSeeThrough>
            <iHealthPercent>0</iHealthPercent>
            <iDefense>0</iDefense>
            <iAppearance>0</iAppearance>
            <iDisappearance>0</iDisappearance>
            <iGrowth>0</iGrowth>
            <iTurnDamage>0</iTurnDamage>
            <bNoCoast>0</bNoCoast>
            <bNoRiver>0</bNoRiver>
            <bNoAdjacent>0</bNoAdjacent>
            <bRequiresFlatlands>0</bRequiresFlatlands>
            <bRequiresRiver>0</bRequiresRiver>
            <bAddsFreshWater>0</bAddsFreshWater>
            <bImpassable>1</bImpassable>
            <bNoCity>1</bNoCity>
            <bNoImprovement>1</bNoImprovement>
            <bVisibleAlways>0</bVisibleAlways>
            <bNukeImmune>1</bNukeImmune>
            <OnUnitChangeTo/>
            <TerrainBooleans>
                <TerrainBoolean>
                    <TerrainType>TERRAIN_GRASS</TerrainType>
                    <bTerrain>1</bTerrain>
                </TerrainBoolean>
                <TerrainBoolean>
                    <TerrainType>TERRAIN_PLAINS</TerrainType>
                    <bTerrain>1</bTerrain>
                </TerrainBoolean>
            </TerrainBooleans>
            <FootstepSounds/>
            <WorldSoundscapeAudioScript>ASSS_OASIS_SELECT_AMB</WorldSoundscapeAudioScript>
            <EffectType>NONE</EffectType>
            <iEffectProbability>0</iEffectProbability>
            <iAdvancedStartRemoveCost>0</iAdvancedStartRemoveCost>
        </FeatureInfo>

The feature artdef:
XML:
        <FeatureArtInfo>
            <Type>ART_DEF_FEATURE_KILIMANJARO</Type>
            <bAnimated>0</bAnimated>
            <bRiverArt>0</bRiverArt>
            <TileArtType>TILE_ART_TYPE_NONE</TileArtType>
            <LightType>LIGHT_TYPE_SUN</LightType>
            <fScale>6</fScale>
            <fInterfaceScale>1.0</fInterfaceScale>
            <Button>Modules/Natural Wonders/Art/Natural Wonders/Kilimanjaro/button.dds</Button>
            <FeatureVariety>
                <FeatureArtPieces>
                    <FeatureArtPiece>
                        <ModelFile>Modules/Natural Wonders/Art/Natural Wonders/Kilimanjaro/Kilimanjaro.nif</ModelFile>
                        <Connections></Connections>
                    </FeatureArtPiece>
                </FeatureArtPieces>
                <FeatureDummyNodes/>
                <bGenerateRotations>0</bGenerateRotations>
                <VarietyButton>Modules/Natural Wonders/Art/Natural Wonders/Kilimanjaro/button.dds</VarietyButton>
            </FeatureVariety>
        </FeatureArtInfo>
And the thing that the game really should be looking for:
XML:
        <MovieArtInfo>
            <Type>ART_DEF_MOVIE_KILIMANJARO</Type>
            <Path>Modules/Natural Wonders/Art/Movies/Kilimanjaro.bik</Path>
        </MovieArtInfo>

So, the game should play a wonder movie when discovering a Natural Wonder, and it really does so when I edit this:
But when I merge it with my mod, there is the above error (not just Kilimanjaro, any 🏞️Wonder).
The really absurd part is that it's missing a FEATURE_PLATY_* because:
1) It should be looking for a ART_DEF_MOVIE_*, which it could find in Platy's version.
2) If FEATURE_PLATY_* were really missing, than the feature wouldn't show up on the map either but there would be only a pink blob.

So I believe that there is something wrong with the python code that converts FEATURE_PLATY_* into ART_DEF_MOVIE_*
For your instance this is the related code:
Python:
    def checkReveal(self, pPlot, iTeam):
        iFeature = pPlot.getFeatureType()
        if iFeature == -1: return
        pTeam = gc.getTeam(iTeam)
        if pTeam.isBarbarian(): return
        FeatureInfo = gc.getFeatureInfo(iFeature)
        sType = FeatureInfo.getType()
        if sType.find("FEATURE_PLATY_") == -1: return

        if sType in self.lBigWonder:
            bFound = False
            for x in xrange(pPlot.getX() - 1, pPlot.getX() + 2):
                for y in xrange(pPlot.getY() - 1, pPlot.getY() + 2):
                    pAdjacentPlot = CyMap().plot(x, y)
                    if x == pPlot.getX() and y == pPlot.getY(): continue
                    if pAdjacentPlot.getFeatureType() == iFeature:
                        bFound = True
                        break
                if bFound: break
            if pAdjacentPlot.isRevealed(iTeam, False): return

        for iPlayerX in xrange(gc.getMAX_CIV_PLAYERS()):
            pPlayerX = gc.getPlayer(iPlayerX)
            if pPlayerX.getTeam() == iTeam:
                if pPlayerX.isHuman() and not CyGame().GetWorldBuilderMode():
                    popupInfo = CyPopupInfo()
                    popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON_SCREEN)
                    popupInfo.setData1(iFeature)
                    popupInfo.setData3(3)
                    popupInfo.setText(u"showWonderMovie")
                    popupInfo.addPopup(iPlayerX)
                    CyInterface().addMessage(iPlayerX,True,10,CyTranslator().getText("TXT_KEY_WONDERDISCOVERED_YOU",(FeatureInfo.getDescription(),)),'',0, FeatureInfo.getButton(),ColorTypes(11),pPlot.getX(),pPlot.getY(), True,True)
        return



And obviously there is never an /oldmod/assets/art, only art. The game always assumes that things are in the Assets folder. (At least I've never seen it a different way)
 
Ah, this is a more interesting issue than what was obvious in the first post.

Re: path yes that's my bad, I'm now used to FfH which can have funky path issues.

From the snippet here I'm actually not seeing where it's supposed to be pulling the artdef for the movie; I assume the button call is for the yellow notification circle, but I think there is separate code where "u"showWonderMovie"" is checked, though not sure.

No idea how the artdef movie is supposed to work, but something about the lack of xml connection between them is bothering me; there's no way go get to the MovieArtInfo from the other two.
 
From the snippet here I'm actually not seeing where it's supposed to be pulling the artdef for the movie; I assume the button call is for the yellow notification circle, but I think there is separate code where "u"showWonderMovie"" is checked, though not sure.
I haven't read all of these posts, but maybe this is the missing bit (CvWonderMovieScreen.py in Platy's mod):
szArtDef = CyArtFileMgr().getMovieArtInfo("ART_DEF_MOVIE_" + sType[sType.find("_PLATY_") +7:])
Looks like the features need to have "PLATY" in their type strings for this to work.
--
Very close to what I want, just have all the text piled up now (I still have to make the unavailable icons). Thanks for all the help so far! Python isn't my expertise.
Forgot about that remaining issue. For a simple improvement, you could move every second religion name down a bit and perhaps decrease the text size from GAME_FONT to SMALL_FONT:
Python:
			if i % 2 == 0:
				yLoop = self.Y_RELIGION_NAME # 58
			else:
				yLoop = 72
			screen.setText(szName, szArea, szLabel, CvUtil.FONT_CENTER_JUSTIFY, xLoop + self.X_RELIGION_AREA, self.Y_RELIGION_AREA + yLoop, 2*self.DZ,
					FontTypes.SMALL_FONT, WidgetTypes.WIDGET_GENERAL, -1, -1)
For date founded, if the text just says "n/a" instead of "Not Founded," it might fit. Well, if dates like "1020 AD" go in there, then probably not. May also need to be put on alternating rows, which will require more space at the bottom and a higher background panel.
 
I haven't read all of these posts, but maybe this is the missing bit (CvWonderMovieScreen.py in Platy's mod):
szArtDef = CyArtFileMgr().getMovieArtInfo("ART_DEF_MOVIE_" + sType[sType.find("_PLATY_") +7:])
Looks like the features need to have "PLATY" in their type strings for this to work.
Thanks, I'll try that :)
 
I haven't read all of these posts, but maybe this is the missing bit (CvWonderMovieScreen.py in Platy's mod):
szArtDef = CyArtFileMgr().getMovieArtInfo("ART_DEF_MOVIE_" + sType[sType.find("_PLATY_") +7:])
Looks like the features need to have "PLATY" in their type strings for this to work.
Yepp! That was the missing part. Thank you! :)
I don't know how I missed it though :twitch:
 
Now my next goal is twofold:
1) Find out how to make the wonder built only when it's inside the city's culture because now it can be outside:
1742894847354.png
So I have tried this:
Python:
    def placeWonderBuilding(self, pCity):
        for i in xrange(21):
            pPlot = pCity.getCityIndexPlot(i)
            
            # Check if the plot is valid
            if not pPlot:
                continue
            
            # Check if the plot is owned by the city owner
            if pPlot.getOwner() != pCity.getOwner():
                continue
            
            iFeature = pPlot.getFeatureType()
            if iFeature == -1:
                continue
            
            sType = gc.getFeatureInfo(iFeature).getType()
            if sType.find("FEATURE_PLATY_") == -1:
                continue
            
            sNature = sType[sType.find("_PLATY_") + 7:]
            sBuildingType = "BUILDING_" + sNature
            iBuilding = gc.getInfoTypeForString(sBuildingType)
            
            if iBuilding == -1:
                continue
            if self.checkWonderBuilt(iBuilding):
                continue
            
            # Place the wonder in the city
            pCity.setNumRealBuilding(iBuilding, 1)
            
            # Trigger the wonder movie popup
            popupInfo = CyPopupInfo()
            popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON_SCREEN)
            popupInfo.setData1(iFeature)
            popupInfo.setData3(3)
            popupInfo.setText(u"showWonderMovie")
            popupInfo.addPopup(pCity.getOwner())
            FeatureInfo = gc.getFeatureInfo(iFeature)
            CyInterface().addMessage(
                    pCity.getOwner(), # recipient
                    True, 10, # force immediate delivery, display duration
                    CyTranslator().getText("TXT_KEY_WONDERDISCOVERED_YOU", (FeatureInfo.getDescription(),)),
                    '', 0, FeatureInfo.getButton(), ColorTypes(11), # sound, indicator button and color
                    pPlot.getX(), pPlot.getY(), # coordinates
                    True, True) # offscreen/ onscreen arrows
        return
along this:
Python:
    def onCultureExpansion(self, argsList):
        'City Culture Expansion'
        pCity = argsList[0]
        iPlayer = argsList[1]
        CvUtil.pyPrint("City %s's culture has expanded" %(pCity.getName(),))
## Natural Wonders Start ##
        NaturalWonders.NaturalWonders().placeWonderBuilding(pCity)
## Natural Wonders End ##
Now the city won't get the wonder if the feature is in the second ring when the city is found but won't get it on culture expansion. So what am I missing?
It does get the wonder in the 1st ring though.
 
My guess is that, given placeWonderBuilding does work on initial placement, onCultureExpansion is being triggered before the plot owner is updated for the case of border expansion. You can probably test this by putting the feature in range of two cities' 2nd expansion. If I'm right, the first one that expands won't get the wonder, but the second one will, since the tile is owned by the player. Also possibly will work for one city, but on the 3rd culture expansion.

Regardless of whether this is correct, you can probably help yourself narrow down the issue by some print statements in this loop, checking what is being evaluated for each plot. Also checks that this call in fact works, since it seems to maybe not be implemented the same way for initial founding vs on culture growth.
 
My guess is that, given placeWonderBuilding does work on initial placement, onCultureExpansion is being triggered before the plot owner is updated for the case of border expansion. You can probably test this by putting the feature in range of two cities' 2nd expansion. If I'm right, the first one that expands won't get the wonder, but the second one will, since the tile is owned by the player. Also possibly will work for one city, but on the 3rd culture expansion.
You were right about both cases (2 cities 2nd ring and 1 city 3rd ring).
Now how to fix that?
 
Do you need to make the ownership check? The first city that gets a second (or third) ring that includes the wonder will always be the first to own its tile, so you can just give it to that city. If another city expands its ring to include the wonder, it does not matter who ends up owning the tile, because the previous city already has the wonder.
 
Do you need to make the ownership check?
I think yes, because settling a city will have only the first ring claimed but not the second. So I'd like to get the Natural Wonder only when it is really inside the city's working area. But I'm open to all sorts of ideas and workarounds too :)
 
settling a city will have only the first ring claimed but not the second.
You could dynamically set xrange in your loop based on the culture level of the city. That way it will only search thru the first loop if the city is just placed, and 21 otherwise.

Hmm, there probably is a function... somewhere... to see "can a citizen from this city be assigned to this tile". Don't know if it is exposed to python, but such a method would better handle edge cases such as working 3 range, weird culture rules, etc.
 
You could dynamically set xrange in your loop based on the culture level of the city. That way it will only search thru the first loop if the city is just placed, and 21 otherwise.
Hmmm... What makes things more complicated is that the mod has Realistic Culture Spread, that means that on culture expansion it won't claim all tiles in range. Culture spreads more easily to plain and flat tiles, while hills and forests are harder. Rivers are also involved and maybe a few other things but you get the point.

I think this' approach is getting unnecessarily complicated. There's an other to build the Natural Wonder: From C2C we have inherited autobuild function. This means, that when the requirements are met the building is built without any player involvement. It can even have a cost of -1. I have several buildings in CoM using this function.
BUT the problem as @f1rpo highlighted is that this is not considered onBuildingBuilt, so it won't launch wonder movies. Actually this is where all it started.
So maybe a code that checks if a certain building is present in a city could work? Just make sure it won't be played more than once. Is that doable?
 
Hmmm... What makes things more complicated is that the mod has Realistic Culture Spread, that means that on culture expansion it won't claim all tiles in range. Culture spreads more easily to plain and flat tiles, while hills and forests are harder. Rivers are also involved and maybe a few other things but you get the point.
I see, that makes it more difficult. I assume this is implemented as a DLL change? Would it be possible to add the check for gaining control of the natural wonder tile to the logic that implements the new culture spread rules?
 
Back
Top Bottom