Quick Modding Questions Thread

Can someone explain how to merge the GEM mod and BUG (less urgently, BAT)? Genghis_Kai's guide was very straightforward, easy to follow, and doesn't work. The UI is completely missing.

Also, I switched around literature and drama on the tech tree (so that literature leads to philosophy and drama to music), but that doesn't seem to affect their physical location on it; their paths just awkwardly cross one another. How do I do this?
 
Last edited:
Also, I switched around literature and drama on the tech tree (so that literature leads to philosophy and drama to music), but that doesn't seem to affect their physical location on it; their paths just awkwardly cross one another. How do I do this?
I can answer this 2nd part only:
Each tech has X and Y coordinates in TechInfo.xml. You have to edit those.
 
Hi,

I'm getting an error from this assert,
Spoiler :
Code:
void CvPlayer::changeBuildingClassMaking(BuildingClassTypes eIndex, int iChange)
{
   FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
   FAssertMsg(eIndex < GC.getNumBuildingClassInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");

   if (iChange != 0)
   {
       m_paiBuildingClassMaking[eIndex] = (m_paiBuildingClassMaking[eIndex] + iChange);
       FAssert(getBuildingClassMaking(eIndex) >= 0);

       if (getID() == GC.getGameINLINE().getActivePlayer())
       {
           gDLL->getInterfaceIFace()->setDirty(Help_DIRTY_BIT, true);
       }
   }
}

when I trigger pNamePlayer.acquireCity(pCity,false,false) based on onBuildingBuilt.
How do I fix it so this is not a problem?
What is the meaning of the last two entries in acquireCity, i.e. the false, false above?

-----------------------------
I get a similar assert error,
Spoiler :

Assert Failed

File: .\.\CvPlayer.cpp
Line: 11988
Expression: getUnitClassMaking(eIndex) >= 0
Message:

when I trigger acquireCity based on onUnitBuilt.

It suggests to me that my problem is that after the building/unit is built, the relevant production queue is empty and this is causing the error when acquirecity tries to run.
How do I avoid these errors?
 
Last edited:
So there is this popup after discovering a tech that asks you if you want to change to the newly available civic. Can I disable this popup somehow?
Look in the CivilizationIV.ini file for NoTechSplash

; Set to 1 for no tech splash screens
NoTechSplash = 1
 
@LPlate2

I assume you get the third assert ("FAssert(getBuildingClassMaking(eIndex) >= 0);") in your first example.
My theory is the following:
acquireCity() actually removes (via CvCity::kill()) the city and replaces it with a new one. onBuildingBuilt is called from CvCity::popOrder(), which also does some other stuff with the city after calling onBuildingBuilt. However, as the city is already destroyed via acquireCity(), the game tries to access garbage memory. However, I'm not completely sure why changeBuildingClassMaking() is called after onBuildingBuilt.

So you probably simply can't change the owner of a city in onBuildingBuilt (in the same city), you have to do it via some workaround. For example, when you build the Mercurian Gate in FfH, the Mercurians don't get that city directly. Instead, Basium (the unit) has a "convert city" spell for the mercurian gate city that the AI always uses immediately.
 
Look in the CivilizationIV.ini file for NoTechSplash

; Set to 1 for no tech splash screens
NoTechSplash = 1
Are you refering to this popup?
Spoiler :
 
Looks like you have to mod the DLL to suppress that popup. Look for BUTTONPOPUP_CHANGECIVIC in CvTeam::setHasTech().
Thx! At least I know I cannot do that :shake:
 
Thanks @Ifgr, I'll create a temporary building in the city, when the condition is met and then apply the acquirecity at the start of a player's turn, if the city has that building. That should work to avoid my issue with onBuildingBuilt and onUnitBuilt.
 
How do I go about getting a building to modify the value of terrain that isn't water/river tiles? I'm very new to modding Civ IV and I've been combing through the code but I haven't been able to find any similarly functioning lines. I had no issue working it in Civ V (IE. copy code from the Petra), but I can't quite seem to find anything that functions in that way for IV. Specifically, I'm trying to create a special civilization for a personal mod, and I want to add a monument replacement that adds + 1 :food: to snow and tundra tiles. Is this even possible in the Civ IV engine?
 
How do I go about getting a building to modify the value of terrain that isn't water/river tiles? I'm very new to modding Civ IV and I've been combing through the code but I haven't been able to find any similarly functioning lines. I had no issue working it in Civ V (IE. copy code from the Petra), but I can't quite seem to find anything that functions in that way for IV. Specifically, I'm trying to create a special civilization for a personal mod, and I want to add a monument replacement that adds + 1 :food: to snow and tundra tiles. Is this even possible in the Civ IV engine?
Not, if you can mod only xml stuff.
Yes, if you can work with the dll.
Maybe python is sufficient too but I doubt.
 
Hi,

Couple of questions:
1) What python command do I use in order to make information appear in the Event Log, in the top left of the screen?
2) What python command do I use to have information appear in the end of game report?
 
I use CyInterface().addMessage to generate messages that appear both on-screen and in the event log.

I have never tried to add information to the end-of-game report. The Revolutions mod uses CyGame().addreplaymessage but I don't see that function in the Python Class Reference so it might have been added to the DLL and so whether or not you can actually use it will depend on what DLL you are using.
 
Hi,

I am using CyInterface().addMessage but this is only giving me the onscreen messages (which do not last).
Code:
               CyInterface().addMessage(CyGame().getActivePlayer(),True,10,"%s" %(myResult),'',1,'',ColorTypes(8),-1,-1,True,True)

Do I need to set one or more of the True to False, to get the messages to appear on the Event Log or change one of the -1 values?

---
I'm also open to modding the DLL I use, if someone can advise where to edit to get information included in the final game reports.
 
This is the Python line that I include when I am trying to debug Python code:
Code:
CyInterface().addMessage(iPlayer, True, 2, str(), None, 0, None, ColorTypes(44), 0, 0, True, True)

I am generally using this with an onBuildingBuilt trigger so iPlayer is already defined as pCity.getOwner(). The message goes in quotes between the parentheses after str.
 
How do I correctly expose an AI method in the DLL to Python?

Specifically, I want to expose CvPlayerAI::AI_getAttitudeVal(PlayerTypes ePlayer, boolean bForced). I cannot simply call it in a CyPlayer wrapping method because it is not a member of CvPlayer. I see that other exposed AI methods, such as CvPlayerAI::AI_getAttitude(PlayerTypes ePlayer, boolean bForced), have been added to CvPlayer.h with the "virtual" keyword and then set to 0. I assume this is to let the compiler know that the implementation will be supplied later by CvPlayerAI instead.

However, if I add
Code:
virtual int AI_getAttitudeVal(PlayerTypes ePlayer, boolean bForced = false) const = 0;
to CvPlayer.h then loading a savegame crashes with a read access violation. As of writing this I realised that I never started a new game after this change, but even when just making this simple change (i.e. no other changes compared to a working DLL) I get a crash on load. Is it a save compatibility thing? Regardless I am surprised by this behaviour. Can someone explain?
 
Hi @Leoreth

I don't think we can add a new virtual to CvPlayer.h since that would affect the memory layout of the DLL in way that the exe can't deal with (pure virtual or not does not matter). Since CvPlayerAI is derived from CvPlayer, a new virtual chain in CvPlayer would have to "shift and\or expand the vtable, depending on number and relative positioning of the new virtuals" (kludgy explanation, I know). We could have done this if we could recompile the exe but for now we are stuck with the fixed offsets in the CvPlayer(AI) vtable that the exe knows about and knows how to call correctly.

See these commit from @f1rpo for a "cleaner" workaround with respect to separating game mechanics and AI:
https://github.com/f1rpo/AdvCiv/commit/3028ffa87b59a735f369fabe624741a377e02c0e
https://github.com/f1rpo/AdvCiv/commit/fedca1cf525955e008cfd75ce5ae3dd68de17570

You can freely add non-virtual member functions to CvPlayer though.

When implementing new AI features a common workaround for not having the ability to add virtual functions is to static_cast the pointer or reference from CvPlayer to CvPlayerAI. We can safely perform this downcast since all CvPlayer instances actually are CvPlayerAI instances even if we have "the wrong pointer\reference" in any given context. This will give you access to new functionality that is implemented in CvPlayerAI without requiring new virtual functions in the ancestor class. It is ugly, I agree but those are the cards that we've been dealt :(

TL;DR summary:

- Put your python wrapper in CvPlayer and expose it to python
- The wrapper downcasts CvPlayer to CvPlayerAI and invokes AI_getAttitudeVal(...)
- The wrapper returns the result to your python method
 
Last edited:
Top Bottom