Quick Modding Questions Thread

I assume this is implemented as a DLL change.
Yes. I have access to the dll and made some tiny tweaks in it before but I'm no better in C++ than in python 😅

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?
I don't know :dunno: but will check the source code...
 
Yeah, it makes sense that you want to avoid editing the DLL if possible. But considering that you are trying to work with a new game rule that is also implemented in the DLL (gradual culture expansion) it might be easier to slot in where that is happening. For example, culture expansion in the base BtS DLL happens in CvCity::doPlotCulture, which includes this loop:

Code:
    int iFreeCultureRate = GC.getDefineINT("CITY_FREE_CULTURE_GROWTH_FACTOR");
    if (getCultureTimes100(ePlayer) > 0)
    {
        if (eCultureLevel != NO_CULTURELEVEL)
        {
            for (iDX = -eCultureLevel; iDX <= eCultureLevel; iDX++)
            {
                for (iDY = -eCultureLevel; iDY <= eCultureLevel; iDY++)
                {
                    iCultureRange = cultureDistance(iDX, iDY);

                    if (iCultureRange <= eCultureLevel)
                    {
                        pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);

                        if (pLoopPlot != NULL)
                        {
                            if (pLoopPlot->isPotentialCityWorkForArea(area()))
                            {
                                pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                            }
                        }
                    }
                }
            }
        }
    }
What's nice about this is that the pLoopPlot->changeCulture call is exactly where the current city covers a plot first. So you'd only need to check if the plot contains a natural wonder and if so, give the associated wonder to the city. No complicated conditions necessary because you're exactly at the point that you actually want to trigger this effect. This loop probably looks a bit different in your version due to the different culture rules but there should be a similar point where the tile to be covered has been determined that you could slot into.

Another option if you don't want to implement this in C++ (I would understand why, the mapping of natural wonder to city wonder seems troublesome compared to Python...) is to only do minimal DLL work to create a new Python event like onPlotCovered(pCity, pPlot) and then use that to implement the rest in Python. Adding an event like this is very simple because it's basically just copy/pasting existing events. If you are interested in this route I could give you a blueprint too.
 
Another option if you don't want to implement this in C++ (I would understand why, the mapping of natural wonder to city wonder seems troublesome compared to Python...) is to only do minimal DLL work to create a new Python event like onPlotCovered(pCity, pPlot) and then use that to implement the rest in Python. Adding an event like this is very simple because it's basically just copy/pasting existing events. If you are interested in this route I could give you a blueprint too.
Yes, I think I would start with this. And I'm interested in the blueprint too 🙂
 
Alright, give me some time to get to my code and I will share an example that would work for base BtS. If you have the source code you are working with, or another mod that has it, I could try working it into that as well.

Edit: here you can see how I would add this event in the BtS DLL - the code compiles but I haven't checked it in the game.

The next step would be to integrate into CvEventManager.py like this:
Code:
        ## EVENTLIST
        self.EventHandlerMap = {
            # ...
            "plotCovered"            : self.onPlotCovered,
        }

    # ...

    def onPlotCovered(self, argsList):
        pCity, pPlot = argsList
       
        # remaining logic goes here
 
Last edited:
Alright, give me some time to get to my code and I will share an example that would work for base BtS. If you have the source code you are working with, or another mod that has it, I could try working it into that as well.

Edit: here you can see how I would add this event in the BtS DLL - the code compiles but I haven't checked it in the game.

The next step would be to integrate into CvEventManager.py like this:
Code:
        ## EVENTLIST
        self.EventHandlerMap = {
            # ...
            "plotCovered"            : self.onPlotCovered,
        }

    # ...

    def onPlotCovered(self, argsList):
        pCity, pPlot = argsList
      
        # remaining logic goes here
Wow! Thanks! :)
Here should be the link for CoM's source code (I hope I gave it in the right way)

I hope I can check your code tomorrow.
THX again :)
 
You're welcome! I can take a look at the CoM code later today too, but the only change should be where the plotCovered method is being invoked.
 
Hi there; I am having some issues getting a new leaderhead to show up in my mod and I'm not sure why. If anyone could guide me to a step I might be missing below, I would appreciate it.
.
*Note: All the below folders are added into the appropriate place within my mod's assets folder
#1: Create new leader in LeaderheadInfos [using Leonidas, for example]
#2: Make new leaderhead available to the appropriate civ [using Greece]
#3: Adding Leonidas in ArtDefines_Leaderhead
#4: Adding the appropriate Assets > Art > Leaderheads
#5: Adding Leonidas to GameText_Objects_BTS
.
I start the game ... and no Leonidas playable under Greece. Any ideas what I may be missing?
Sounds like you went into all the necessary files, so something with the entries must have gone wrong. My first guesses would be that you are either have a modular mod and ModularLoading is set to 0 or that the bLeaderAvailability in CIV4CivilizationInfos file is set to 0. If it is none of the two, it would be good if you could post the entries you made to check.
 
Sounds like you went into all the necessary files, so something with the entries must have gone wrong. My first guesses would be that you are either have a modular mod and ModularLoading is set to 0 or that the bLeaderAvailability in CIV4CivilizationInfos file is set to 0. If it is none of the two, it would be good if you could post the entries you made to check.
I appreciate the help. The issue was that varietas delectat has its own civinfos folder which is actually hidden in the middle of other xml files
 
You're welcome! I can take a look at the CoM code later today too, but the only change should be where the plotCovered method is being invoked.
Okay, thanks again for your help but... errr... so I was thinking about an easier more 'noob-proof' solution that suits me better and found a different approach/workaround:
Since in my mod I have this bAutoBuild tag for buildings, I actually don't need to find a way to "fix" the code that places the wonder. Even better, it works also works with the third ring. So I don't need to risk breaking anything. 😅
I can use the events system to recognise that the building is built and fire a one time event.
All I have to find out is how to activate the wonder movie with an event :)
 
Okay, thanks again for your help but... errr... so I was thinking about an easier more 'noob-proof' solution that suits me better and found a different approach/workaround:
Since in my mod I have this bAutoBuild tag for buildings, I actually don't need to find a way to "fix" the code that places the wonder. Even better, it works also works with the third ring. So I don't need to risk breaking anything. 😅
I can use the events system to recognise that the building is built and fire a one time event.
All I have to find out is how to activate the wonder movie with an event :)
Okay, that doesn't seem like any more simple.

Can the dll also call to play a movie?
As I understand this code is responsible for autobuilding:
C++:
void CvCity::doAutobuild(void)
{
    int iI;

    //    Auto-build any auto-build buildings we can
    for(iI = 0; iI < GC.getNumBuildingInfos(); iI++)
    {
        if ( GC.getBuildingInfo((BuildingTypes)iI).isAutoBuild() &&
             getNumBuilding((BuildingTypes)iI) == 0 &&
             canConstruct((BuildingTypes)iI, false, false, true) )
        {
            CvWString szBuffer;

            setNumRealBuilding((BuildingTypes)iI, 1);

            szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_COMPLETED_AUTO_BUILD", GC.getBuildingInfo((BuildingTypes)iI).getTextKeyWide(), getName().GetCString()));
            AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, NULL, MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"));
        }
    }
}

Could it also play/trigger the MovieDefineTag of the building somehow?


EDIT

Does this code look good? Haven't tested yet...
C++:
void CvCity::doAutobuild(void)
{
    int iI;

    // Auto-build any auto-build buildings we can
    for(iI = 0; iI < GC.getNumBuildingInfos(); iI++)
    {
        BuildingTypes eBuilding = (BuildingTypes)iI;

        if (GC.getBuildingInfo(eBuilding).isAutoBuild() &&
            getNumBuilding(eBuilding) == 0 &&
            canConstruct(eBuilding, false, false, true))
        {
            // Set building in city
            setNumRealBuilding(eBuilding, 1);

            // Show completion message
            CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_COMPLETED_AUTO_BUILD",
                GC.getBuildingInfo(eBuilding).getTextKeyWide(), getName().GetCString()));
            AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, NULL,
                MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"));

            // Trigger Wonder movie if applicable
            const TCHAR* szMovieTag = GC.getBuildingInfo(eBuilding).getMovieDefineTag();
            if (szMovieTag != NULL && szMovieTag[0] != '\0')
            {
                PlayerTypes eOwner = getOwnerINLINE();

                if (GET_PLAYER(eOwner).isHuman())
                {
                    CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_PYTHON_SCREEN);
                    if (pInfo != NULL)
                    {
                        pInfo->setText(CvWString::format(L"showWonderMovie('%S')", szMovieTag));
                        gDLL->getInterfaceIFace()->addPopup(pInfo, eOwner, true);
                    }
                }
            }
        }
    }
}

EDIT2:

Yeah, as I expected: The code did compile but didn't show the movie.
Any advise?
 
Last edited:
Where does this line come from?
pInfo->setText(CvWString::format(L"showWonderMovie('%S')", szMovieTag));
Doesn't look like any of the wonder movie code in Python. The Platy-style code with a feature ID would presumably be
C++:
pInfo->setData1(iFeature);
pInfo->setData3(3);
pInfo->setText(L"showWonderMovie");
instead. But I don't think you can obtain the feature ID in this context here. Maybe if the movie also is attached to the building as its regular wonder movie? In that case perhaps (based on CvEventManager.py)
C++:
pInfo->setData1(eBuilding);
pInfo->setData2(getID());
pInfo->setData3(0);
pInfo->setText(u"showWonderMovie");
(Or maybe I no longer remember correctly how Platy's setup worked.)
Edit: My bad about the unicode string literal. Yes, should be L"showWonderMovie".
 
Last edited:
Where does this line come from?
pInfo->setText(CvWString::format(L"showWonderMovie('%S')", szMovieTag));
ChatGPT :blush:
I asked it to modify the dll for me.

But I don't think you can obtain the feature ID in this context here. Maybe if the movie also is attached to the building as its regular wonder movie?
I didn't mean to get the feature ID but the building ID, since there is this xml tag <bAutoBuild>


In that case perhaps (based on CvEventManager.py) pInfo->setData1(eBuilding); pInfo->setData2(getID()); pInfo->setData3(0); pInfo->setText(u"showWonderMovie");(Or maybe I no longer remember correctly how Platy's setup worked.)
I tried this but doesn't compile:
Code:
1>------ Build started: Project: CvGameCoreDLL, Configuration: Release-fast Win32 ------
1>  Building source list
1>  Running fastdep
1>  CvCity.cpp
1>..\CvCity.cpp(1933): error C2065: 'u' : undeclared identifier
1>..\CvCity.cpp(1933): error C2143: syntax error : missing ')' before 'string'
1>..\CvCity.cpp(1933): error C2059: syntax error : ')'
1>  jom: I:\Games\Civilization 4\Beyond the Sword\Mods\Chronicles of Mankind\CvGameCoreDLL\project\Makefile [temp_files\Release\.\CvCity.obj] Error 2
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: The command "set TARGET=Release
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: nmake source_list /NOLOGO
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: nmake fastdep /NOLOGO
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: nmake precompile /NOLOGO
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: bin\jom build /NOLOGO" exited with code 2.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

EDIT:
Okay, it's pInfo->setText(L"showWonderMovie"); instead of pInfo->setText(u"showWonderMovie");

Now it compiled, so testing...
 
Last edited:
Spoiler :

1743972278598.png

1743972454884.jpeg


It worked. Thank you! :clap:
 
These Natural Wonders really keep giving me headaches :twitch:

It should show them when pressing CTR+F and this thing works in Platy's original version but not when I merge it.
Here's the responsible code:

CvEventManager:
Python:
## Show Natural Wonders with CTRL F ##
            if theKey == int(InputTypes.KB_F) and self.bCtrl:
                NaturalWonders.NaturalWonders().showNaturalWonders()
                return 1
## Show Natural Wonders with CTRL F ##

NaturalWonders
Python:
    def showNaturalWonders(self):
        iPlayer = CyGame().getActivePlayer()
        pPlayer = gc.getPlayer(iPlayer)
        for i in xrange(CyMap().numPlots()):
            pPlot = CyMap().plotByIndex(i)
            iFeature = pPlot.getFeatureType()
            if iFeature == -1: continue
            if CyGame().GetWorldBuilderMode() or pPlot.isRevealed(pPlayer.getTeam(), False):
                FeatureInfo = gc.getFeatureInfo(iFeature)
                if FeatureInfo.getType().find("FEATURE_PLATY_") == -1: continue
                sText = FeatureInfo.getDescription() + " (" + str(pPlot.getX()) + ", " + str(pPlot.getY()) +")"
                CyInterface().addMessage(iPlayer,True,60,sText,'',0, FeatureInfo.getButton(),ColorTypes(11),pPlot.getX(),pPlot.getY(), True,True)
        return

These lines are the same in both places. Yet, it works for Platy's vanilla version but not in CoM.
I guess if CTR+F is assigned to some other function in CoM the things would happen simultaneously, right?*
Any ideas where to look?

EDIT:
*That was probably a wrong assumption :undecide:
I have tried a few different combinations but those didn't work. They either did something else or just nothing.
 
Last edited:
Hello all,
.
If I want to change the button icon for a temple and monastery from the current [picture of a building with the religion symbol in the corner], and instead simply make the button an icon picture of the religion, can this be done by simply updating the button in art/interface/buttons/buildings to a dds picture? And if so, what size does the button need to be?

-Edit: I tried this got it to work. So, yes, the above can be done and the answer to the button size is 64x64
 
Last edited:
Hi all,

I was wondering if anyone ever solved the vanilla problem of resource bubbles defaulting to off every turn in hotseat? Played a hotseat game for once and was quite annoyed by every player having to switch them on every turn.
 
@<Nexus>: Do your features have that FEATURE_PLATY_ thing in their type strings that the Platy code looks for? Is showNaturalWonders getting called (print would tell)?

@Walter Hawkwood: Recently cherry-picked this for my fork of BULL: Git commit
Another significant UI problem with Hotseat in my experience is that announcements don't get reliably shown and that the BUG alerts don't really work. I can dig for my bugfixes for those issues, but I don't think they'll be as easily portable. (Just mentioning it in case that these problems turn out to be difficult to live with.)
 
Thanks, knew if anyone would have fixed it, it would be you! From reading the commit, I think I actually prefer your original approach. Do I understand correctly that this is achieved simply by deleting setting it to "false"?
 
Last edited:
@<Nexus>: Do your features have that FEATURE_PLATY_ thing in their type strings that the Platy code looks for? Is showNaturalWonders getting called (print would tell)?
Yes for FEATURE_PLATY_

If I try this...
Python:
    def showNaturalWonders(self):
        print "showNaturalWonders was called"
        iPlayer = CyGame().getActivePlayer()
        pPlayer = gc.getPlayer(iPlayer)
        for i in xrange(CyMap().numPlots()):
            pPlot = CyMap().plotByIndex(i)
            iFeature = pPlot.getFeatureType()
            if iFeature == -1: continue
            if CyGame().GetWorldBuilderMode() or pPlot.isRevealed(pPlayer.getTeam(), False):
                FeatureInfo = gc.getFeatureInfo(iFeature)
                if FeatureInfo.getType().find("FEATURE_PLATY_") == -1: continue
                sText = FeatureInfo.getDescription() + " (" + str(pPlot.getX()) + ", " + str(pPlot.getY()) +")"
                CyInterface().addMessage(iPlayer,True,60,sText,'',0, FeatureInfo.getButton(),ColorTypes(11),pPlot.getX(),pPlot.getY(), True,True)
        return
...but nothing shows up on screen.

In the BUG menu I turned System -> Debug output to Debug and BBAI to 3 (highest).

I also edited Platy's files adding the same lines where CTR+F works but see nothing printed about it on the screen.
So what am I doing something wrong?
 
Back
Top Bottom