Quick Modding Questions Thread

Hi,

Could someone post an example code of how you would do the following in the DLL?
Loop thru the tiles surrounding a plot
Check each to see if they meet a condition
Randomly select one of the tiles that met the condition

thanks
 
off the top of my head, may or may not work:
Code:
CvPlot* plot = yourPlot;
std::vector<CvPlot*> plots;
for(int x = plot->getX()-1; x<=plot->getX()+1; ++x) {
    for(int y = plot->getY()-1; y<=plot->getY()+1; ++y) {
        if(GC.getMap().isPlot(x, y)) {
            if(yourCondition) {
                plots.push_back(GC.getMap().plot(x, y));
            }
        }
    }
}
if(plots.size() > 0) {
    CvPlot* randomPlot = plots[GC.getGame().getSorenRandNum(plots.size(), "plot selection roll")];
}
 
There is another way to loop surrounding plots.
PHP:
for (int i = 0; i < NUM_DIRECTION_TYPES; ++i)
{
    CvPlot* pLoopPlot = ::plotDirection(plot->getX_INLINE(), plot->getY_INLINE(), (DirectionTypes) i);
    if (pLoopPlot != NULL)
    {

    }
}
It's part of vanilla and it will check for map edges and return NULL if the plot doesn't exist. It only checks the 8 neighboring plots. If you want to include the plot itself, then let i start as NO_DIRECTION (-1).

This is vanilla code and it has builtin checks for maps wrapping around and stuff like that meaning it will likely be more stable in edge cases. It will also make the code cleaner as in you will use the same coding style as vanilla is already using.
 
What's the best way of finding out which line of Python code makes the call to a DLL API that causes an error? Often I have the problem that I detect an error using the debug DLL and it turns out that the actual cause is a wrong function call from Python. Obviously the call stack in the DLL does not help in finding this out.

My best idea is to throw an error in the DLL (even if it is just temporarily inserted code) that causes a Python stacktrace which allows to locate the responsible call. But how to best throw such an error in C++?
 
Two quick questions;
1 Does anyone know how to stop the GP from giving free technologies? I can't find a quick tag that causes it in the xml.

2 What's the python to make a unit go to sleep?
 
1. in UnitInfos.xml, set <iBaseDiscover> to 0.

2. You can't set an unit to sleep, but you have to set the group the unit belongs to to sleep. I found the code below in Monkeytools from the BUG mod. You can find all functions in this.
Code:
    def doReInit(self):
        iPlayer = PyPlayer(CyGame().getActivePlayer())
        unitList = iPlayer.getUnitList()
        # Loop for all players cities
        for ii in range(len(unitList)):
            pLoopUnit = unitList[ii]
            # get the group the unit belongs to.
            pGroup = pLoopUnit.getGroup()
            # check if unit is doing air patrol
            if (pGroup.getActivityType() == ActivityTypes.ACTIVITY_INTERCEPT):
                # remove mission (this one shold be obsolete, but who knows ;)
                pGroup.popMission()
                pPlot = pGroup.plot()
                # add new mission -> fortify
                pGroup.pushMission(MissionTypes.MISSION_FORTIFY, 0, 0, 0, false, false, MissionAITypes.NO_MISSIONAI, pPlot, pLoopUnit)
                # add new mission -> air patrol
                pGroup.pushMission(MissionTypes.MISSION_AIRPATROL, 0, 0, 0, false, false, MissionAITypes.NO_MISSIONAI, pPlot, pLoopUnit)
 
1. in UnitInfos.xml, set <iBaseDiscover> to 0.
Thanks, I did this and also set <iDiscoverMultiplier> to 0 also.

I tried the following for putting the unit to sleep. It doesn't seem to work. Am I missing something obvious?
Code:
    pPlot = caster.plot()
# Set unit to sleep
   pGroup = caster.getGroup()
   pGroup.popMission()
   pGroup.pushMission(MissionTypes.MISSION_FORTIFY, 0, 0, 0, False, False, MissionAITypes.NO_MISSIONAI, pPlot, caster)
 
Maybe the functions that I gave aren't the ones that should be used. I only did a quick search and the code I found above seemed to do what you were asking for. (I didn't test it)

Anyway, I tried to check if you maybe did something wrong with the arguments of the function. While doing this I found this function in the API:
VOID setActivityType (ActivityType eNewValue)
(function 57 in the CySelectionGroup section, use ctrl-f to find it quickly)

I'm pretty sure that this IS the function you are looking for.
 
VOID setActivityType (ActivityType eNewValue)
Looks to me like this is an action, which will cause out of sync issues in network games unless you make sure it is handled in sync.

If the line where you add this is handled in sync already, then there is no problem, but for the rest of this post I will assume it isn't. The player clicks something and you catch this UI event and want to put the unit to sleep. To get an unsynced method to do something in sync, you call:
PHP:
CyMessageControl().sendModNetMessage(INT iData1, INT iData2, INT iData3, INT iData4, INT iData5)

This will call onModNetMessage in CvEventManager.py. The exe will take care of network activity meaning by the time it reaches onModNetMessage , it will be in sync.

You then write code like (untested)
PHP:
if (iData1 == 0):
    player = gc.getPlayer(iData2)
    unit = player.getUnit(iData3)
    unit.getGroup().setActivityType(ActivityTypes.ACTIVITY_SLEEP)
iData1 is message ID. You assign them as you like, but the easiest expandable way is to use it like an enum as in you start with 0, the next is 1 etc. Perhaps name them and write the name instead.
iData2 and iData 3 are getID() for player and unit in question. Just use any unit in the group.

iData4 and iData5 are unused in this case and it doesn't matter what they are. Vanilla sets unused ints to -1 because they have to have a value.

Sure you can ignore network issues until you manage to get it working in singleplayer. However because a number of mods kill multiplayer support, I felt like sharing how to handle network sync using python only.
 
Looks to me like this is an action, which will cause out of sync issues in network games unless you make sure it is handled in sync.

It'll be a long time before I have anything that a single other person can play, let alone multi-players but I'll include your code as a reminder for how to handle it, if I ever get that far.

How do you identify which actions are likely to cause out of sync issues in multi-player (only ever played single player)?

Where should I write the code,
Code:
    if (iData1 = 0):
       player = gc.getPlayer(iData2)
       unit = player.getUnit(iData3)
       unit.getGroup().setActivityType(ActivityTypes.ACTIVITY_SLEEP)
?
I'm setting the group to sleep from within python for an FFH2 type spell.
 
Last edited:
How do you identify which actions are likely to cause out of sync issues in multi-player (only ever played single player)?
At times it can be tricky, but generally speaking there are two types of code:
  • syncronized code
    Executed on all computers in parallel
  • asyncronized code
    Executed on one computer only
The issue is game state meaning anything, which is in savegames (unit location or whatever). All computers need the same game state. If the code is syncronized, then there is no problem. If the code isn't syncronized, then the game state should be read only or changed through network messages (also called network packages, but not in vanilla).

This brings up the next question: how to identify syncronized code? A a general rule, user inputs aren't syncronized because other computers will not know where you click on the screen. This means if it's an action, which requires any sort of user input, then it isn't syncronized. Anything else tend to be syncronized.

One big exception: popup windows are synced because CyPopup does that in the exe.

I can't tell for certain if your code needs to be synced or not because you haven't revealed where in the code you request the unit to sleep. I would consider it poor design if that happens without user interaction hence why I assumed it would require network activity.
 
Hi,

I'm trying to create a model in blender. I thought I had set up mirroring correctly and it looks fine within Blender. However when I export to nif, half of the structure is missing.

Does anyone know how I fix this?

Resolved: I had to select all, go to Object mode and apply the mirror and then sort the texture again in Blender before exporting.
 

Attachments

  • Blenderfull.PNG
    Blenderfull.PNG
    318 KB · Views: 49
  • Nifskopemissinghalf.PNG
    Nifskopemissinghalf.PNG
    122.3 KB · Views: 54
Last edited:
I remember that someone (IIRC @f1rpo to @keldath ) had explained how to run multiple instances of Civ4 at once. Unfortunately I didn't make notes, though it wouldbe a very useful knowledge.
 
@Zeta Nexus: That's done through the multiple command line option of the EXE. So one just needs to edit the shortcut that launches the mod. (Or, for debugging, add it to the LocalDebuggerCommandArguments in Visual Studio.)

What's the best way of finding out which line of Python code makes the call to a DLL API that causes an error? Often I have the problem that I detect an error using the debug DLL and it turns out that the actual cause is a wrong function call from Python. Obviously the call stack in the DLL does not help in finding this out.

My best idea is to throw an error in the DLL (even if it is just temporarily inserted code) that causes a Python stacktrace which allows to locate the responsible call. But how to best throw such an error in C++?
I'm getting a Python stacktrace (screenshot attached just to make sure that we're talking about the same thing; I don't know at all how that popup gets created) if I crash the DLL through a division by 0:
int xx=0; xx/=xx;
I've tried some more straightforward methods from this StackOverflow answer, but neither exit(), nor abort(), nor raise(SIGSEGV) result in a stacktrace. The division by 0 above will probably get removed by an optimizing compiler, but, in debug builds, it should work reliably.
 

Attachments

  • traceback.jpg
    traceback.jpg
    94.1 KB · Views: 61
I'm getting a Python stacktrace (screenshot attached just to make sure that we're talking about the same thing; I don't know at all how that popup gets created) if I crash the DLL through a division by 0:
int xx=0; xx/=xx;
You could try using a NULL pointer exception.

PHP:
CvUnit *pUnit = NULL;
pUnit->reset();
I haven't tried it, but it's much less likely to be optimized away and it will cause a C++ failure, hence displaying the stack trace.

Alternatively try something like:
PHP:
GC.getUnitInfo(NO_UNIT).getType();

EDIT:
Alternatively you can enable python logging and then print the call stack using pure python. Depending on the specifics of the problem, this may or may not solve the issue.
 
Last edited:
You could try using a NULL pointer exception.
PHP:
CvUnit *pUnit = NULL;
pUnit->reset();
That works for me with a release build. :thumbsup: (I've tried it with pUnit->scrap() because I no longer have CvUnit::reset in my mod.) HidePythonExceptions=0 required, obviously.
 
Can someone help me out with modding the diplo AI? I am wondering how feasible what I am thinking about is, and where I would best need to make those changes.

My goal is the following: 1) when offering capitulation, allow to return some of their cities in the same trade, and 2) if sufficient cities are offered, override a denial to allow the AI to accept the deal.

My specific intention here is that in RFC, if you attack the core territory of a civilization, it destabilises them and may kill the civ before it is able/willing to accept the capitulation. So the usual strategy of waiting for an enemy to be willing to capitulate does not work because the target may collapse under you, which is frustrating. But I also do not want to address this problem by making the AI more willing to capitulate in general, because that would make vassalising them too easy. So the idea is to make the losing AI more willing to capitulate if you restore their important core cities as part of the deal. This is in their interest, because refusing will just lead to their collapse, so it is a choice between capitulation or complete loss, a fact the vanilla AI is not aware of.

How can I make this happen? For 1) I have seen that currently it is generally forbidden to add anything to vassal trades (in CvDeal::addTrades), so I guess that would need to be relaxed, but then also CvPlayer::canTradeItem would need to be modified to limit the cities that can be offered in this specific situation (e.g. I would like to allow offering cities still in occupation), but how can I tell what the context of the trade is? Do I need to add another custom boolean argument?

For 2) currently it seems that AI always evaluates a surrender trade as value 0. Which is fine because it cannot be traded for anything else for the time being, and everything is handled through the denials. But how do I modify this to make the AI weigh the cities it receives in return to judge if the deal is worth it? I would like the AI to only accept (barring other reasons to deny) if it regains all core cities it has lost in the war, so that it is no longer in danger of collapse.

Is trying to implement something like that worth it, or would I have to break up too many existing mechanisms that weren't designed for that to make it worth it?
 
Back
Top Bottom