Quick Modding Questions Thread

Is it in any way possible to install (Python 2.4 compatible) libraries for the ingame Python context? I assume it's bundled in the exe somewhere but I thought I'd ask on the off chance it isn't.
Make a proof of concept and see what happens. I have added .py files in the python folder and it just works. Vanilla use .pyc too. My guess is that it scans the entire folder (including non-vanilla subfolders) and makes use of all the files.
 
There are 3 types of city sizes according to EraInfos:
Code:
 <CitySoundscapes>
  <CitySoundscape>
   <CitySizeType>CITYSIZE_SMALL</CitySizeType>
   <SoundscapeScript>ASSS_CITY_ANCIENT_SMALL_SELECT_AMB</SoundscapeScript>
  </CitySoundscape>
   <CitySoundscape>
   <CitySizeType>CITYSIZE_MEDIUM</CitySizeType>
   <SoundscapeScript>ASSS_CITY_ANCIENT_SMALL_SELECT_AMB</SoundscapeScript>
  </CitySoundscape>
  <CitySoundscape>
   <CitySizeType>CITYSIZE_LARGE</CitySizeType>
   <SoundscapeScript>ASSS_CITY_ANCIENT_LARGE_SELECT_AMB</SoundscapeScript>
  </CitySoundscape>
 </CitySoundscapes>
But where are those sizes defined? When is a city considered small, medium or large?
 
There are 3 types of city sizes according to EraInfos:
Spoiler :
Code:
 <CitySoundscapes>
  <CitySoundscape>
   <CitySizeType>CITYSIZE_SMALL</CitySizeType>
   <SoundscapeScript>ASSS_CITY_ANCIENT_SMALL_SELECT_AMB</SoundscapeScript>
  </CitySoundscape>
   <CitySoundscape>
   <CitySizeType>CITYSIZE_MEDIUM</CitySizeType>
   <SoundscapeScript>ASSS_CITY_ANCIENT_SMALL_SELECT_AMB</SoundscapeScript>
  </CitySoundscape>
  <CitySoundscape>
   <CitySizeType>CITYSIZE_LARGE</CitySizeType>
   <SoundscapeScript>ASSS_CITY_ANCIENT_LARGE_SELECT_AMB</SoundscapeScript>
  </CitySoundscape>
 </CitySoundscapes>
But where are those sizes defined? When is a city considered small, medium or large?

I found this is CvCity.cpp. If I'm not mistaken it means that a population of 1-6 is small, 7-13 is medium and >= 14 is large.

Code:
CitySizeTypes CvCity::getCitySizeType() const
{
    return ((CitySizeTypes)(range((getPopulation() / 7), 0, (NUM_CITYSIZE_TYPES - 1))));
}
 
Anyone know how <iRefuseToTalkWarThreshold> with the leader heads work? Is it the amount of turns that have to go by before they will talk again, or is it maybe the random probability
that they will talk again?
 
Yes, it's the base number of turns they will refuse to talk to you, but it's also influenced by additional factors, such as how the war is going and if they chose to go to war with you themselves.
 
Hi,

What would cause this kind of error message?
upload_2020-1-21_6-27-40.png


How do I track it down?
Is it more likely to be a DLL, python or XML issue (I'm slowly working on them all)?

That const_iterator break point is in
upload_2020-1-21_6-29-2.png


Spoiler :
Code:
    class const_iterator
       : public _Ranit<_Ty, _Dift, _Ctptr, const_reference>
       {   // iterator for nonmutable vector
   public:
       typedef random_access_iterator_tag iterator_category;
       typedef _Ty value_type;
       typedef _Dift difference_type;
       typedef _Ctptr pointer;
       typedef const_reference reference;

       const_iterator()
           {   // construct with null pointer
           _Myptr = 0;
           }

       const_iterator(_Tptr _Ptr)
           {   // construct with pointer _Ptr
           _Myptr = _Ptr;
           }

       const_reference operator*() const
           {   // return designated object


           return (*_Myptr);
           }

       _Ctptr operator->() const
           {   // return pointer to class object
           return (&**this);
           }

       const_iterator& operator++()
           {   // preincrement
           ++_Myptr;
           return (*this);
           }

       const_iterator operator++(int)
           {   // postincrement
           const_iterator _Tmp = *this;
           ++*this;
           return (_Tmp);
           }

       const_iterator& operator--()
           {   // predecrement
           --_Myptr;
           return (*this);
           }

       const_iterator operator--(int)
           {   // postdecrement
           const_iterator _Tmp = *this;
           --*this;
           return (_Tmp);
           }

       const_iterator& operator+=(difference_type _Off)
           {   // increment by integer
           _Myptr += _Off;
           return (*this);
           }

       const_iterator operator+(difference_type _Off) const
           {   // return this + integer
           const_iterator _Tmp = *this;
           return (_Tmp += _Off);
           }

       const_iterator& operator-=(difference_type _Off)
           {   // decrement by integer
           return (*this += -_Off);
           }

       const_iterator operator-(difference_type _Off) const
           {   // return this - integer
           const_iterator _Tmp = *this;
           return (_Tmp -= _Off);
           }

       difference_type operator-(const const_iterator& _Right) const
           {   // return difference of iterators


           return (_Myptr - _Right._Myptr);
           }

       const_reference operator[](difference_type _Off) const
           {   // subscript
           return (*(*this + _Off));
           }

       bool operator==(const const_iterator& _Right) const
           {   // test for iterator equality


           return (_Myptr == _Right._Myptr);
           }

       bool operator!=(const const_iterator& _Right) const
           {   // test for iterator inequality
           return (!(*this == _Right));
           }

       bool operator<(const const_iterator& _Right) const
           {   // test if this < _Right


           return (_Myptr < _Right._Myptr);
           }

       bool operator>(const const_iterator& _Right) const
           {   // test if this > _Right
           return (_Right < *this);
           }

       bool operator<=(const const_iterator& _Right) const
           {   // test if this <= _Right
           return (!(_Right < *this));
           }

       bool operator>=(const const_iterator& _Right) const
           {   // test if this >= _Right
           return (!(*this < _Right));
           }

       friend const_iterator operator+(difference_type _Off,
           const const_iterator& _Right)
           {   // return iterator + integer
           return (_Right + _Off);
           }


       _Tptr _Myptr;   // offset of element in vector
       };

       // CLASS iterator
 

Attachments

  • upload_2020-1-21_6-25-13.png
    upload_2020-1-21_6-25-13.png
    21.2 KB · Views: 58
The code you have provided is entirely unlikely to be the problem (I guess it's the STL implementation of a vector iterator or something like that, can't check at the moment since I don't have access to a modding pc atm)
It would be more helpful if you pasted a picture of the callstack and also the call site where you actually mutate\modify the collection (probably a vector or a close analog).
Since you have a const_iterator you should not be able to cause a write AV, so perhaps you have a dodgy cast somewhere or the underlying collection is uninitialized (no memory allocated) and you'e trying to write to it somehow.
 
Last edited:
Hi,

Thanks for the reply. I was unaware of callstack.
The callstack has;
upload_2020-1-22_6-11-10.png


I have not edited CvGlobals.cpp and line 2138 in this is
upload_2020-1-22_6-30-52.png
, which seems relevant to the third line from the top in the call stack.

In CvGlobals.h, line 555 is
upload_2020-1-22_6-28-16.png
, which seems relevant to the second from the top line in the call stack.

Line 462 in both CvGlobals.h and CvGlobals.cpp is blank. Which file should I be looking at for this?

Should my efforts be focussed on trying to find an error within CIV4TechInfos.xml? I have done no editing to CvGlobals.h or CvGlobals.cpp so I know the problem does not come from these.
 
You're on the right track :)

Check that all of the parameters that you're passing down the call chain has sane values (in this context you'd watch out for any parameter being equal to NO_TECH or -1).
Perhaps CvGlobals::getTechInfo is doing something dodgy when using the subscript operator [], perhaps eTechNum is equal to NO_TECH ?
I assume that you have edited or added techs ? check that you've set the prerequisites correctly.

Update: Maybe eTech is NO_TECH here:
Code:
ePreReq = (TechTypes)GC.getTechInfo(eTech).getPrereqOrTechs(i);

ePreReq = (TechTypes)GC.getTechInfo(eTech).getPrereqAndTechs(i);
 
Last edited:
Hiya, arriving late to the Civ IV modding party, but after lurking and trawling these forums for a solution to a problem, I decided to join.

Is it at all possible, using XML and/or Python, to dynamically change a unit's iMaxPlayerInstances value, or otherwise a method of "banning" a unit, no matter how quick and dirty this code would be, please? I note that ICBMs and Tactical Nukes are "banned" via the United Nations, thus I would have thought this mechanism would exist in Python.

If C++ is my only hope, has anyone attempted such a thing before?

Thank you.
 
You can use the canTrain and cannotTrain functions in CvGameUtils for this, although you'd have to count the number of instances yourself.

Thanks! I tried out this code to ban warriors flat-out, as a test. Unfortunately, it doesn't seem to do anything at all.

Please can you, or anyone, shed some light on what I'm doing wrong?
Code:
def cannotTrain(self,argsList):
        pCity = argsList[0]
        eUnit = argsList[1]
        bContinue = argsList[2]
        bTestVisible = argsList[3]
        bIgnoreCost = argsList[4]
        bIgnoreUpgrades = argsList[5]
        if eUnit == gc.getInfoTypeForString('UNIT_WARRIOR'):
                return True
        return False

EDIT: I had forgotten to change USE_CANNOT_TRAIN_CALLBACK from 0 to 1 in "PythonCallbackDefines.xml", like this:
Code:
<Define>
        <DefineName>USE_CANNOT_TRAIN_CALLBACK</DefineName>
        <iDefineIntVal>1</iDefineIntVal>
</Define>

The code now works perfectly. I shall reply if there is another issue as I edit the code further toward what I want to accomplish.
 
Last edited:
Hiya, got a bit of a head-scratcher with this simple script, and I would appreciate any help.

This is supposed to prevent a city from being able to build Warriors if the plot the city occupies already has Warriors on it.

It doesn't seem to do anything at all but doesn't return any errors.

Code:
    def cannotTrain(self,argsList):
        pCity = argsList[0]
        eUnit = argsList[1]
        bContinue = argsList[2]
        bTestVisible = argsList[3]
        bIgnoreCost = argsList[4]
        bIgnoreUpgrades = argsList[5]

## New Code

        if eUnit == gc.getInfoTypeForString("UNIT_WARRIOR"):

            i = 0
            pPlot = pCity.plot()

            if pPlot.getNumUnits > 0:

                while i <= pPlot.getNumUnits():

                    if pPlot.getUnit(i) == gc.getInfoTypeForString("UNIT_WARRIOR"):

                        return True


                    i += 1

                return False

### New Code End

        return False

Thanks for any help and suggestions.
 
Looks like the error is this line:
Code:
if pPlot.getNumUnits > 0
it should look like this:
Code:
if pPlot.getNumUnits() > 0

Please check if you have Python exceptions enabled, I am pretty sure that you should have seen an error message along the lines of "function instance cannot be compared to int".

Also, a shorter and more "Pythonic" version of your loop would be:
Code:
for i in range(pPlot.getNumUnits()):
    if pPlot.getUnit(i) == gc.getInfoTypeForString("UNIT_WARRIOR"):
        return True
 
Hiya, thanks for the quick response!

I have made the changes you have suggested but, unfortunately, it still doesn't work. Any more ideas, please?

I definitely do have Python exceptions enabled, and have double-checked. That's probably how I missed the lack of braces after getNumUnits.

Also, thanks for making my code more "Pythonic"; any improvements are always welcome. I have to keep the blank lines between statements, though, as I have vision problems.

My code now looks like this:

Code:
def cannotTrain(self,argsList):
        pCity = argsList[0]
        eUnit = argsList[1]
        bContinue = argsList[2]
        bTestVisible = argsList[3]
        bIgnoreCost = argsList[4]
        bIgnoreUpgrades = argsList[5]

## New Code

        if eUnit == gc.getInfoTypeForString("UNIT_WARRIOR"):

            i = 0
            pPlot = pCity.plot()

            if pPlot.getNumUnits() > 0:

                for i in range(pPlot.getNumUnits()):

                    if pPlot.getUnit(i) == gc.getInfoTypeForString("UNIT_WARRIOR"):

                        return True

                return False

### New Code End

        return False
 
How do you make a mapscript generate a map with an exact range of latitude coordinates? I know I can just edit the range on a worldbuilder save but that causes CTDs with the mod I'm using, which uses the latitude values for agricultural yields.

I'm only interested in what to type to limit the final map range. It seems to take limitations into account for generating terrain features, but not the actual latitude value of each individual cell.
 
Does anyone know how exactly the AI decides between a limited war or a full war? Obviously there is the rands in the leaderhead file but is that the only factor?
Or is there other factors that make the ai choose between the 2?
 
Please check if you have Python exceptions enabled
I never understood the idea behind hiding python errors. If there is an error, then people will notice something if wrong, error message or no error message. Even worse, the default is to hide the errors and sometimes the ini file resets itself to hide (related to crashing game? switching active mod?).

I changed it to always be active after I missed a bug due to the hiding setting having reset itself without telling me. I added the following:

PHP:
DllExport bool CvXMLLoadUtility::LoadPlayerOptions()
{
   if (!CreateFXml())
       return false;
   gDLL->ChangeINIKeyValue("CONFIG", "HidePythonExceptions", "0");
Add the last line to the DLL and your mod will never again suffer from having the python errors hidden, regardless of user settings.
 
Top Bottom