Terrain/Technology Limiting Culture Spread

LPlate2

Warlord
Joined
Dec 27, 2018
Messages
299
Hi,

Is there a mod comp or module out there which prevents the spread of culture to certain terrain, if a prerequisite technology is not possessed?

If there isn't, what are the files that I would need to edit to implement something like this?

I'm looking to prevent culture spread across seas/oceans and mountains.
 
Last edited:
I got it working using just python. I had to create a "Stake Claim" immobile unit. I position this as a barbarian unit in onGameStart:
Spoiler :
Code:
       bPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
       iOcean = gc.getInfoTypeForString('TERRAIN_OCEAN')
       iCoast = gc.getInfoTypeForString('TERRAIN_COAST')
       iDeep = gc.getInfoTypeForString('TERRAIN_OCEAN_DEEP')
       for i in range (CyMap().numPlots()):
           pPlot = CyMap().plotByIndex(i)
           if pPlot.getTerrainType() == iOcean or pPlot.getTerrainType() == iCoast or pPlot.getTerrainType() == iDeep or pPlot.isPeak() == True:
               newUnit = bPlayer.initUnit(gc.getInfoTypeForString('UNIT_STAKECLAIM'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
This is then followed up in onEndGameTurn with;
Spoiler :
Code:
       iStakeClaim = gc.getInfoTypeForString('UNIT_STAKECLAIM')
       iOcean = gc.getInfoTypeForString('TERRAIN_OCEAN')
       iCoast = gc.getInfoTypeForString('TERRAIN_COAST')
       iDeep = gc.getInfoTypeForString('TERRAIN_OCEAN_DEEP')
       iPath = gc.getInfoTypeForString('TECH_UNDERDARK_PATHS')
       iFishing = gc.getInfoTypeForString('TECH_FISHING')
       iShipB = gc.getInfoTypeForString('TECH_SHIP_BUILDING')
       iNav = gc.getInfoTypeForString('TECH_NAVIGATION')

#L3   LPlate Terrain Culture Control - no spread over mountains or water
       bPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
       for iLoopUnit in xrange(bPlayer.getNumUnits()):
           pLoopUnit = bPlayer.getUnit(iLoopUnit)
           if pLoopUnit.getUnitType() == iStakeClaim:
               pLoopPlot = pLoopUnit.plot()
               if pLoopPlot.isOwned() == True:
                   iOwnerPlayer = pLoopPlot.getOwner()
                   pOwnerPlayer = gc.getPlayer(iOwnerPlayer)
                   eOwnerTeam = gc.getTeam(pOwnerPlayer.getTeam())
                   if (pLoopPlot.getTerrainType() == iOcean and not eOwnerTeam.isHasTech(iShipB))\
                   or (pLoopPlot.getTerrainType() == iCoast and not eOwnerTeam.isHasTech(iFishing))\
                   or (pLoopPlot.getTerrainType() == iDeep and not eOwnerTeam.isHasTech(iNav))\
                   or (pLoopPlot.isPeak() == True and not eOwnerTeam.isHasTech(iPath)):
                       iOwnerCulture = -(pLoopPlot.getCulture(iOwnerPlayer))
                       pLoopPlot.changeCulture(iOwnerPlayer,iOwnerCulture,1)
                   else:
                       pLoopUnit.kill(False, gc.getBARBARIAN_PLAYER())
                   iLoopUnit = iLoopUnit - 1

I need to add a bit more python to deal with the case where a city is conquered (captured or razed), to add back in the stake claim units in the applicable surrounding tiles. Other than that its done.
 
So above was my attempt implement this through python.

After several tangents and blind alleys, I've managed to implement it through the DLL. Obviously, this is a much more preferable approach as it avoids having pseudo units scattered throughout the map and also avoids the plots being revealed as plots are given to a civ, only to have them removed moments later. This blocks the claiming of tiles as terrain, where the civilization does not have the requisite tech. The exception is all of the tiles adjacent to a city can always be claimed when a city is built.

I created a boolean for Terrains to identify where the further checks to see if a team has the requisite tech to claim it is required.
I created TerrainCultures based on TerrainTrades for the Technologies. Terraintrades leads on to isNetworkTerrain in the existing code, so I implemented isOwnableTerrain based on this.

Once a civ has the requisite tech(s), the tile acquisition around the cities is in the usual symmetrical pattern.
This means this will work best in mods where some civs will never develop a technology, e.g. I'm using it for tunnels, where most civs will be incapable of researching the tech and for peaks, where noone will ever get the technology to claim them. It will also work for Ocean and Deep Ocean, where you don't want civs to be able to claim water over which they cannot construct ships to enforce a claim.

---
XML
Spoiler :

There are two changes to Schema;
CIV4TerrainSchema.xml
Spoiler :
Code:
   <ElementType name="bNeedsTechToClaim" content="textOnly" dt:type="boolean"/> <!-- LPlate - Terrain-Culture -->
Code:
       <element type="bNeedsTechToClaim" minOccurs="0"/> <!-- LPlate - Terrain-Culture -->
CIV4TechnologiesSchema.xml
Spoiler :
Code:
   <ElementType name="bTerrainCulture" content="textOnly" dt:type="boolean"/>   <!-- LPlate - Terrain-Culture -->
   <ElementType name="TerrainCulture" content="eltOnly">   <!-- LPlate - Terrain-Culture -->
       <element type="TerrainType"/>
       <element type="bTerrainCulture"/>
   </ElementType>
   <ElementType name="TerrainCultures" content="eltOnly">   <!-- LPlate - Terrain-Culture -->
       <element type="TerrainCulture" minOccurs="0" maxOccurs="*"/>
   </ElementType>
Code:
       <element type="TerrainCultures" minOccurs="0"/> <!-- LPlate - Terrain-Culture -->

These then transfer into the Infos.xml like this;
CIV4TerrainInfos.xml
Spoiler :
Code:
           <bNeedsTechToClaim>1</bNeedsTechToClaim>
CIV4TechInfos.xml
Spoiler :
Code:
            <TerrainCultures>
                <TerrainCulture>
                    <TerrainType>TERRAIN_TUNNELS</TerrainType>
                    <bTerrainCulture>1</bTerrainCulture>
                </TerrainCulture>
                <TerrainCulture>
                    <TerrainType>TERRAIN_PEAK</TerrainType>
                    <bTerrainCulture>1</bTerrainCulture>
                </TerrainCulture>
            </TerrainCultures>

The final key bit of code where the culture to claim the plots occurs is in CvCity.cpp, as part of void CvCity::doPlotCulture(bool bUpdate, PlayerTypes ePlayer, int iCultureRate)
Spoiler :
Code:
/* LPlate - Terrain-Culture Modified Code */
                       if (pLoopPlot != NULL)
                       {
                           if (pLoopPlot->isNeedsTechToClaim())
                           {
                               if (pLoopPlot->isOwnableTerrain(GET_PLAYER(getOwnerINLINE()).getTeam()))
                               {
                                   if (pLoopPlot->isPotentialCityWorkForArea(area()))
                                   {
                                       pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                                   }
                               }
                           }
                           else
                           {
                               pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                           }
                       }
/* LPlate - Terrain-Culture Unmodified Code */
/*                       if (pLoopPlot != NULL)
                       {
                           if (pLoopPlot->isPotentialCityWorkForArea(area()))
                           {
                               pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                           }
                       }*/
/* End LPlate - Terrain-Culture Unmodified Code */

To get from those initial entries in the xml to that bit of functional code above, these are the other source files I edited;
Spoiler :

CvInfos.h
Spoiler :
Code:
/* LPlate Addition - Terrain-Culture */
   bool isTerrainCulture(int i) const;           // Exposed to Python
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate Addition - Terrain-Culture */
   bool* m_pbTerrainCulture;
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   bool isNeedsTechToClaim() const;                               // Exposed to Python
/* End LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   bool m_bNeedsTechToClaim;
/* End LPlate - Terrain-Culture */


CvInfos.cpp
Spoiler :
Code:
/* LPlate Addition - Terrain-Culture */
,m_pbTerrainCulture(NULL)
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate Addition - Terrain-Culture */
   SAFE_DELETE_ARRAY(m_pbTerrainCulture);
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate Addition - Terrain-Culture */
bool CvTechInfo::isTerrainCulture(int i) const
{
   return m_pbTerrainCulture ? m_pbTerrainCulture[i] : false;
}
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate Addition - Terrain-Culture */
   SAFE_DELETE_ARRAY(m_pbTerrainCulture);
   m_pbTerrainCulture = new bool[GC.getNumTerrainInfos()];
   stream->Read(GC.getNumTerrainInfos(), m_pbTerrainCulture);
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate Addition - Terrain-Culture */
   stream->Write(GC.getNumTerrainInfos(), m_pbTerrainCulture);
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate Addition - Terrain-Culture */
   pXML->SetVariableListTagPair(&m_pbTerrainCulture, "TerrainCultures", sizeof(GC.getTerrainInfo((TerrainTypes)0)), GC.getNumTerrainInfos(), false);
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
,m_bNeedsTechToClaim(false)
/* LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
bool CvTerrainInfo::isNeedsTechToClaim() const
{
   return m_bNeedsTechToClaim;
}
/* LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   pXML->GetChildXmlValByName(&m_bNeedsTechToClaim, "bNeedsTechToClaim");
/* End LPlate - Terrain-Culture */

CyInfoInterface1.cpp
Spoiler :
Code:
/* LPlate Terrain-Culture */
       .def("isTerrainCulture", &CvTechInfo::isTerrainCulture, "bool (int i)")
/* End LPlate Terrain-Culture */

CyInfoInterface3.cpp
Spoiler :
Code:
/* LPlate - Terrain-Culture */
       .def("isNeedsTechToClaim", &CvTerrainInfo::isWater, "bool ()")
/* End LPlate - Terrain-Culture */

CvTeam.h
Spoiler :
Code:
/* LPlate - Terrain-Culture */
   int getTerrainCultureCount(TerrainTypes eIndex) const;
   bool isTerrainCulture(TerrainTypes eIndex) const;                                                                                                               // Exposed to Python
   void changeTerrainCultureCount(TerrainTypes eIndex, int iChange);
/* End LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   int* m_paiTerrainCultureCount;
/* End LPlate - Terrain-Culture */

CvTeam.cpp
Spoiler :
Code:
/* LPlate - Terrain-Culture */
   m_paiTerrainCultureCount = NULL;
/* LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   SAFE_DELETE_ARRAY(m_paiTerrainTradeCount);
/* End LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
       FAssertMsg(m_paiTerrainCultureCount==NULL, "about to leak memory, CvTeam::m_paiTerrainCultureCount");
       m_paiTerrainCultureCount = new int[GC.getNumTerrainInfos()];
       for (iI = 0; iI < GC.getNumTerrainInfos(); iI++)
       {
           m_paiTerrainCultureCount[iI] = 0;
       }
/* End LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
int CvTeam::getTerrainCultureCount(TerrainTypes eIndex) const
{
   FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
   FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
   return m_paiTerrainCultureCount[eIndex];
}


bool CvTeam::isTerrainCulture(TerrainTypes eIndex) const
{
   return (getTerrainCultureCount(eIndex) > 0);
}

void CvTeam::changeTerrainCultureCount(TerrainTypes eIndex, int iChange)
{
   int iI;

   FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
   FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");

   if (iChange != 0)
   {
       m_paiTerrainCultureCount[eIndex] = (m_paiTerrainCultureCount[eIndex] + iChange);
       FAssert(getTerrainCultureCount(eIndex) >= 0);

       for (iI = 0; iI < MAX_PLAYERS; iI++)
       {
           if (GET_PLAYER((PlayerTypes)iI).isAlive())
           {
               if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
               {
                   GET_PLAYER((PlayerTypes)iI).updatePlotGroups();
               }
           }
       }
   }
}
/* End LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   for (iI = 0; iI < GC.getNumTerrainInfos(); iI++)
   {
       if (GC.getTechInfo(eTech).isTerrainCulture(iI))
       {
           changeTerrainCultureCount(((TerrainTypes)iI), iChange);
       }
   }
/* End LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   pStream->Read(GC.getNumTerrainInfos(), m_paiTerrainCultureCount);
/* End LPlate - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   pStream->Write(GC.getNumTerrainInfos(), m_paiTerrainCultureCount);
/* End LPlate - Terrain-Culture */

CyTeam.h
Spoiler :
Code:
/* LPlate - Terrain-Culture */
   bool isTerrainCulture(int /*TerrainTypes*/ eIndex);
/* End LPlate - Terrain-Culture */

CyTeam.cpp
Spoiler :
Code:
/* LPlate - Terrain-Culture */
bool CyTeam::isTerrainCulture(int /*TerrainTypes*/ eIndex)
{
   if (m_pTeam)
   {
       return m_pTeam->isTerrainCulture((TerrainTypes)eIndex);
   }
   return false;
}
/* End LPlate - Terrain-Culture */

CyTeamInterface.cpp
Spoiler :
Code:
/* LPlate - Terrain-Culture */
       .def("isTerrainCulture", &CyTeam::isTerrainCulture, "bool (int iTerrainType) - will let us know if this terrain type requires a tech to be culturally claimed")
/* End LPlate - Terrain-Culture */

CvPlot.h
Spoiler :
Code:
/* LPlate Addition - Terrain-Culture */
   bool isOwnableTerrain(TeamTypes eTeam) const;                                                                                                           // Exposed to Python
/* End LPlate Addition - Terrain-Culture */
Code:
/* LPlate - Terrain-Culture */
   bool isNeedsTechToClaim() const;                                                                                                                                                               // Exposed to Python
/* LPlate - Terrain-Culture */

CvPlot.cpp
Spoiler :
Code:
/* LPlate Addition - Terrain-Culture */
bool CvPlot::isOwnableTerrain(TeamTypes eTeam) const
{
   FAssertMsg(eTeam != NO_TEAM, "eTeam is not assigned a valid value");
   FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

   if (GET_TEAM(eTeam).isTerrainCulture(getTerrainType()))
   {
       return true;
   }

   return false;
}

bool CvPlot::isNeedsTechToClaim() const
{
   FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

   return GC.getTerrainInfo(getTerrainType()).isNeedsTechToClaim();
}
/* End LPlate Addition - Terrain-Culture */

CyPlot.h
Spoiler :
Code:
/* LPlate Addition - Terrain-Culture */
   bool isOwnableTerrain(int /*TeamTypes*/ eTeam);
/* End LPlate Addition - Terrain-Culture */

CyPlot.cpp
Spoiler :
Code:
/* LPlate Addition - Terrain-Culture */
bool CyPlot::isOwnableTerrain(int /*TeamTypes*/ eTeam)
{
   return m_pPlot ? m_pPlot->isOwnableTerrain((TeamTypes) eTeam) : false;
}
/* End LPlate Addition - Terrain-Culture */

CyPlotInterface1.cpp
Spoiler :
Code:
/* LPlate Addition - Terrain-Culture */
       .def("isOwnableTerrain", &CyPlot::isOwnableTerrain, "bool (int (TeamTypes) eTeam)")
/* End LPlate Addition - Terrain-Culture */

---

There's possibly some unnecessary links in the code above as its primarily built on a copy and paste basis.

If I was to come back to this in the future, the way to try to develop this further would be to;
Spoiler :
1 Only start accumulating culture on the ownable plots (where a tech is required), after the requisite tech is acquired, so that assymettrical culture claiming is retained.
2 Allow buildings to increase the cultural claim over certain terrains (e.g. lighthouses, naval bases). UB could also be used by civs such as FFH's Malakim to boost their cultural claim over the terrain they are suited to.


Spoiler :
upload_2019-11-30_0-58-29.png
 
Last edited:
I've revisited this with a view to implementing the continuation of the assymettric cultural borders as outlined in the previous post. Finally got it to work. So now
Tiles can not be claimed if the terrain requires a technology, which is not known.
The accumulation of culture on tiles, where there is a technology requirement only begins after that technology has been acquired
Buildings can contribute(reduce) the cultural claim over tiles of a certain terrain.

The attached pictures show how it works
Image
1 Culturally claimed territory has expanded but has not claimed the Tunnel or Terrain tiles
7 Culturally claimed territory has expanded further but has not claimed the Tunnel or Terrain tiles
8 After getting the tech allowing claiming of Tunnels, the Tunnel terrain tile in the second ring has been claimed but not the one in the third ring
9 After getting the tech allowing claiming of Coasts, the Coastal terrain tiles in the second ring have been claimed but not the ones in the third ring
14 Cultural expansion continues with the Tunnel tile in the third ring being claimed.
15 I created a lighthouse in the city, which was set to give a large number of cultural points for coastal tiles and so the coastal claim increase to include the coastal tiles three rings out


The main steps in this revision where;
Adding in the building parameters
Adding in the multidimensional (2D) arrays (players and terrains) for the cities to track the culture values for the terrains (https://forums.civfanatics.com/threads/multidimensional-arrays.335570/ was my main help for this)
Adding in the functionality to drive plot cultural claiming/loss based on the terrain cultural values

Adding in the Building parameters
Spoiler :

CIV4BuildingsSchema.xml Changes
Spoiler :
Code:
   <ElementType name="bTerrainCulture" content="textOnly" dt:type="boolean"/><!-- LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific -->
   <ElementType name="TerrainType" content="textOnly"/><!-- LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific -->
   <ElementType name="iTerrainCulture" content="textOnly" dt:type="int"/><!-- LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific -->
   <ElementType name="TerrainCulture" content="eltOnly"><!-- LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific -->
       <element type="TerrainType"/>
       <element type="iTerrainCulture"/>
   </ElementType>
   <ElementType name="TerrainCultures" content="eltOnly"><!-- LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific -->
       <element type="TerrainCulture" minOccurs="0" maxOccurs="*"/>
   </ElementType>
Spoiler :
Code:
       <element type="bTerrainCulture" minOccurs="0"/><!-- LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific -->
       <element type="TerrainCultures" minOccurs="0"/><!-- LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific -->
Example from my BUILDING_LIGHTHOUSE of how its used in CIV4BuildingInfos.xml
Spoiler :
Code:
           <bTerrainCulture>1</bTerrainCulture>
           <TerrainCultures>
               <TerrainCulture>
                   <TerrainType>TERRAIN_COAST</TerrainType>
                   <iTerrainCulture>1</iTerrainCulture>
               </TerrainCulture>
           </TerrainCultures>
Text xml update
Spoiler :
Code:
   <TEXT>
       <Tag>TXT_KEY_BUILDING_TERRAIN_CULTURE</Tag>
       <English>[ICON_BULLET]Changes culture spread by %d1 on [LINK=literal]%s2_Terrain[\LINK]</English>
   </TEXT>

DLL edits, CvInfos.h,
Spoiler :
Code:
// LPlate - LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   bool isTerrainCulture() const;               // Exposed to Python
// End LPlate - LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   int getTerrainCulture(int i) const;               // Exposed to Python
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   bool m_bTerrainCulture;
// End LPlate - LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   int* m_piTerrainCulture;
// End LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits, CvInfos.cpp,
Spoiler :
Code:
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
m_bTerrainCulture(false),
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
m_piTerrainCulture(NULL),
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   SAFE_DELETE_ARRAY(m_piTerrainCulture);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
bool CvBuildingInfo::isTerrainCulture() const
{
   return m_bTerrainCulture;
}
// End LPlate - LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
int CvBuildingInfo::getTerrainCulture(int i) const
{
   FAssertMsg(i < GC.getNumTerrainInfos(), "Index out of bounds");
   FAssertMsg(i > -1, "Index out of bounds");
   return m_piTerrainCulture ? m_piTerrainCulture[i] : -1;
}
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   stream->Read(&m_bTerrainCulture);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   SAFE_DELETE_ARRAY(m_piTerrainCulture);
   m_piTerrainCulture = new int[GC.getNumTerrainInfos()];
   stream->Read(GC.getNumTerrainInfos(), m_piTerrainCulture);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   stream->Write(m_bTerrainCulture);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   stream->Write(GC.getNumTerrainInfos(), m_piTerrainCulture);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   pXML->SetVariableListTagPair(&m_piTerrainCulture, "TerrainCulture", sizeof(GC.getTerrainInfo((TerrainTypes)0)), GC.getNumTerrainInfos());
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   pXML->SetVariableListTagPair(&m_piTerrainCulture, "TerrainCultures", sizeof(GC.getTerrainInfo((TerrainTypes)0)), GC.getNumTerrainInfos(), false);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits, CyInfoInterface1.cpp,
Spoiler :
Code:
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
       .def("isTerrainCulture", &CvBuildingInfo::isTerrainCulture, "bool ()")
       .def("getTerrainCulture", &CvBuildingInfo::getTerrainCulture, "int (int i)")
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits to CvCity.h to use this
Spoiler :
Code:
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   int getTerrainCultureFromBuildings(TerrainTypes eTerrain) const;                       // Exposed to Python

DLL edits to CvCity.cpp to use this
Spoiler :
Code:
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
int CvCity::getTerrainCultureFromBuildings(TerrainTypes eTerrain) const
{
   int iCultureFromBuilding = 0;
   int iI;

   for (iI = 0; iI < GC.getNumBuildingInfos(); ++iI)
   {
       if (getNumBuilding((BuildingTypes)iI) > 0)
       {
           iCultureFromBuilding += (GC.getBuildingInfo((BuildingTypes)iI)).getTerrainCulture(eTerrain);
       }
   }
   return iCultureFromBuilding;
}

Setting up the Multi-dimensional(2D) arrays for the city
Spoiler :

DLL edits in CvCity.h
Spoiler :
Code:
   int getTerrainCulture(PlayerTypes eIndex, TerrainTypes eTerrain) const;                       // Exposed to Python
   int getTerrainCultureLevel(PlayerTypes ePlayer, TerrainTypes eTerrain) const;                       // Exposed to Python
   void setTerrainCulture(PlayerTypes eIndex, TerrainTypes eTerrain, int iNewValue);                       // Exposed to Python
   void setTerrainCultureLevel(PlayerTypes eIndex, TerrainTypes eTerrain, int iNewValue);                       // Exposed to Python
   void changeTerrainCulture(PlayerTypes eIndex, TerrainTypes eTerrain, int iChange);                       // Exposed to Python


// LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   int** m_aaiTerrainCulture;
   int** m_aaiTerrainCultureLevel;
// End LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CvCity.cpp, CvCity::CvCity()
Spoiler :
Code:
// LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   m_aaiTerrainCulture = NULL;
   m_aaiTerrainCultureLevel = NULL;
// End LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CvCity.cpp, CvCity::~CvCity()
Spoiler :
Code:
// LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   if (m_aaiTerrainCulture != NULL)
   {
       for (int iI = 0; iI < MAX_PLAYERS; iI++)
       {
           SAFE_DELETE_ARRAY(m_aaiTerrainCulture[iI]);
       }
       SAFE_DELETE_ARRAY(m_aaiTerrainCulture);
   }
   if (m_aaiTerrainCultureLevel != NULL)
   {
       for (int iI = 0; iI < MAX_PLAYERS; iI++)
       {
           SAFE_DELETE_ARRAY(m_aaiTerrainCultureLevel[iI]);
       }
       SAFE_DELETE_ARRAY(m_aaiTerrainCultureLevel);
   }
// End LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CvCity.cpp, void CvCity::uninit()
Spoiler :
Code:
// LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   if (NULL != m_aaiTerrainCulture)
   {
       for (int iI = 0; iI < MAX_PLAYERS; ++iI)
       {
           SAFE_DELETE_ARRAY(m_aaiTerrainCulture[iI]);
       }
       SAFE_DELETE_ARRAY(m_aaiTerrainCulture);
   }
       if (NULL != m_aaiTerrainCultureLevel)
   {
       for (int iI = 0; iI < MAX_PLAYERS; ++iI)
       {
           SAFE_DELETE_ARRAY(m_aaiTerrainCultureLevel[iI]);
       }
       SAFE_DELETE_ARRAY(m_aaiTerrainCultureLevel);
   }
// End LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CvCity.cpp, void CvCity::reset(int iID, PlayerTypes eOwner, int iX, int iY, bool bConstructorCall)
Spoiler :
Code:
// LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   m_aaiTerrainCulture = new int* [MAX_PLAYERS];
   m_aaiTerrainCultureLevel = new int* [MAX_PLAYERS];
   for (iI = 0; iI < MAX_PLAYERS; iI++)
   {
       m_aaiTerrainCulture[iI] = new int[NUM_TERRAIN_TYPES];
       m_aaiTerrainCultureLevel[iI] = new int[NUM_TERRAIN_TYPES];
       for (int iJ = 0; iJ < NUM_TERRAIN_TYPES; iJ++)
       {
           m_aaiTerrainCulture[iI][iJ] = 0;
           m_aaiTerrainCultureLevel[iI][iJ] = 0;
       }
   }
// End LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CvCity.cpp, set, get and change for the arrays
Spoiler :
Code:
int CvCity::getTerrainCulture(PlayerTypes eIndex, TerrainTypes eTerrain) const
{
   return m_aaiTerrainCulture[eIndex][eTerrain];
}

int CvCity::getTerrainCultureLevel(PlayerTypes eIndex, TerrainTypes eTerrain) const
{
   return m_aaiTerrainCultureLevel[eIndex][eTerrain];
}

void CvCity::setTerrainCulture(PlayerTypes eIndex, TerrainTypes eTerrain, int iNewValue)
{
   FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
   FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
   FAssertMsg(eTerrain >= 0, "eIndex expected to be >= 0");
   FAssertMsg(eTerrain < NUM_TERRAIN_TYPES, "eTerrain expected to be < NUM_TERRAIN_TYPES");

   m_aaiTerrainCulture[eIndex][eTerrain] = iNewValue;
}

void CvCity::setTerrainCultureLevel(PlayerTypes eIndex, TerrainTypes eTerrain, int iNewValue)
{
   FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
   FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
   FAssertMsg(eTerrain >= 0, "eIndex expected to be >= 0");
   FAssertMsg(eTerrain < NUM_TERRAIN_TYPES, "eTerrain expected to be < NUM_TERRAIN_TYPES");

   m_aaiTerrainCultureLevel[eIndex][eTerrain] = iNewValue;
}

void CvCity::changeTerrainCulture(PlayerTypes eIndex, TerrainTypes eTerrain, int iChange)
{
   setTerrainCulture(eIndex, eTerrain, (getTerrainCulture(eIndex,eTerrain) + iChange));
}

DLL edits in CvCity.cpp, void CvCity::read(FDataStreamBase* pStream)
Spoiler :
Code:
// LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   if (m_aaiTerrainCulture != NULL)
   {
       for (iI = 0; iI < MAX_PLAYERS; iI++)
       {
           for (int iJ = 0; iJ < NUM_TERRAIN_TYPES; iJ++)
           {
               pStream->Read(NUM_TERRAIN_TYPES, m_aaiTerrainCulture[iI]);
           }
       }
   }
   if (m_aaiTerrainCultureLevel != NULL)
   {
       for (iI = 0; iI < MAX_PLAYERS; iI++)
       {
           for (int iJ = 0; iJ < NUM_TERRAIN_TYPES; iJ++)
           {
               pStream->Read(NUM_TERRAIN_TYPES, m_aaiTerrainCultureLevel[iI]);
           }
       }
   }
// End LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CvCity.cpp, void CvCity::write(FDataStreamBase* pStream)
Spoiler :
Code:
// LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   for (iI = 0; iI < MAX_PLAYERS; iI++)
   {
       for (int iJ = 0; iJ < NUM_TERRAIN_TYPES; iJ++)
       {
           pStream->Write(NUM_TERRAIN_TYPES, m_aaiTerrainCulture[iI]);
       }
       for (int iJ = 0; iJ < NUM_TERRAIN_TYPES; iJ++)
       {
           pStream->Write(NUM_TERRAIN_TYPES, m_aaiTerrainCultureLevel[iI]);
       }
   }
// End LPlate addition, Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CvEnums.h, as using NUM_TERRAIN_TYPES in CvCity.cpp,
Spoiler :
Code:
enum TerrainTypes                       // Exposed to Python
{
   NO_TERRAIN = -1,
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific, listing all terrains and including the NUM_TERRAIN_TYPES line
   TERRAIN_TUNNELS,
   TERRAIN_MARSH,
   TERRAIN_GRASS,
   TERRAIN_PLAINS,
   TERRAIN_DESERT,
   TERRAIN_TUNDRA,
   TERRAIN_SNOW,
   TERRAIN_BURNING_SANDS,
   TERRAIN_BROKEN_LANDS,
   TERRAIN_FIELDS_OF_PERDITION,
   TERRAIN_SHALLOWS,
   TERRAIN_COAST,
   TERRAIN_OCEAN,
   TERRAIN_PEAK,
   TERRAIN_HILL,

#ifdef _USRDLL
   NUM_TERRAIN_TYPES
#endif
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific, listing all terrains and including the NUM_TERRAIN_TYPES line
};


DLL edits in CyCity.h
Spoiler :
Code:
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   int getTerrainCulture(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain);
   int getTerrainCultureLevel(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain);
   int getTerrainCultureFromBuildings(int /*TerrainTypes*/ eTerrain);
   void setTerrainCulture(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain, int iNewValue);
   void setTerrainCultureLevel(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain, int iNewValue);
   void changeTerrainCulture(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain, int iChange);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CyCity.cpp
Spoiler :
Code:
int CyCity::getTerrainCulture(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain)
{
   return m_pCity ? m_pCity->getTerrainCulture((PlayerTypes)eIndex, (TerrainTypes)eTerrain) : -1;
}
int CyCity::getTerrainCultureLevel(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain)
{
   return m_pCity ? m_pCity->getTerrainCultureLevel((PlayerTypes)eIndex, (TerrainTypes)eTerrain) : -1;
}
void CyCity::setTerrainCulture(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain, int iNewValue)
{
   if (m_pCity)
       m_pCity->setTerrainCulture((PlayerTypes)eIndex, (TerrainTypes)eTerrain, iNewValue);
}
void CyCity::setTerrainCultureLevel(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain, int iNewValue)
{
   if (m_pCity)
       m_pCity->setTerrainCultureLevel((PlayerTypes)eIndex, (TerrainTypes)eTerrain, iNewValue);
}
void CyCity::changeTerrainCulture(int /*PlayerTypes*/ eIndex, int /*TerrainTypes*/ eTerrain, int iChange)
{
   if (m_pCity)
       m_pCity->changeTerrainCulture((PlayerTypes)eIndex, (TerrainTypes)eTerrain, iChange);
}
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits in CyCityInterface1.cpp
Spoiler :
Code:
       .def("getTerrainCulture", &CyCity::getTerrainCulture, "int (int /*PlayerTypes*/, int /*TerrainTypes*/ )")
       .def("getTerrainCultureLevel", &CyCity::getTerrainCultureLevel, "int /*PlayerTypes*/, int /*TerrainTypes*/ )")
       .def("setTerrainCulture", &CyCity::setTerrainCulture, "void (int PlayerTypes eIndex, int /*TerrainTypes*/, int iNewValue)")
       .def("setTerrainCultureLevel", &CyCity::setTerrainCultureLevel, "void (int PlayerTypes eIndex, int /*TerrainTypes*/, int iNewValue)")
       .def("changeTerrainCulture", &CyCity::changeTerrainCulture, "void (int PlayerTypes eIndex, int /*TerrainTypes*/, int iChange)")
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

Adding in the functionality
Spoiler :

New functions addition in CvCity.h
Spoiler :
Code:
   void updateTerrainCulture(PlayerTypes eIndex, int iChange);
   void updateTerrainCultureLevel(PlayerTypes eIndex, bool bUpdatePlotGroups);
   void doTerrainCulture(int iCultureRate);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

New function addition in CvCity.cpp
Spoiler :
Code:
void CvCity::updateTerrainCulture(PlayerTypes eIndex, int iChange)//update the terrainculture when the culture in a city changes
{
   if (getCultureLevel() != NO_CULTURELEVEL)
   {
       for (int iJ = 0; iJ < NUM_TERRAIN_TYPES; iJ++)
       {
           changeTerrainCulture(eIndex, (TerrainTypes)iJ, iChange);
       }
   }
}

void CvCity::updateTerrainCultureLevel(PlayerTypes ePlayer, bool bUpdatePlotGroups)//Terrain culture level is based on the culture in a city and the terrainculture value in the city, specific to the terrain types.  This is the calculation, which is then used in setTerrainCultureLevel.  This function is called from updateCultureLevel
{
   CultureLevelTypes eOldValue = NO_CULTURELEVEL;
   CultureLevelTypes eNewValue = NO_CULTURELEVEL;
   CvPlot* pLoopPlot;
   int iCultureRange;
   int iDX, iDY;
   int iI;

   if (!isOccupation())
   {
       for (int iJ = 0; iJ < NUM_TERRAIN_TYPES; iJ++)
       {
           eOldValue = (CultureLevelTypes)getTerrainCultureLevel(ePlayer, (TerrainTypes)iJ);
           {
           for (iI = (GC.getNumCultureLevelInfos() - 1); iI > 0; iI--)
               {
                   if (getCultureTimes100(ePlayer) == -getTerrainCulture(ePlayer, (TerrainTypes)iJ)*100)
                   {
                       setTerrainCultureLevel(ePlayer,(TerrainTypes)iJ,0);
                       break;
                   }
                   if ((getCultureTimes100(ePlayer)+getTerrainCulture(ePlayer, (TerrainTypes)iJ)*100) >= 100 * GC.getGameINLINE().getCultureThreshold((CultureLevelTypes)iI))
                   {
                       eNewValue = ((CultureLevelTypes)iI);
                       break;
                   }
               }
           }

           if (eOldValue != eNewValue)
           {
               setTerrainCultureLevel(ePlayer, (TerrainTypes)iJ, iI);
               if (eOldValue != NO_CULTURELEVEL && eOldValue > eNewValue)
               {
                   for (iDX = -eOldValue; iDX <= eOldValue; iDX++)
                   {
                       for (iDY = -eOldValue; iDY <= eOldValue; iDY++)
                       {
                           iCultureRange = cultureDistance(iDX, iDY);

                           if (iCultureRange > eNewValue)
                           {
                               if (iCultureRange <= eOldValue)
                               {
                                   FAssert(iCultureRange <= GC.getNumCultureLevelInfos());

                                   pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);

                                   if (pLoopPlot != NULL && ((pLoopPlot->getTerrainType() == (TerrainTypes)iJ)))
                                   {
                                       pLoopPlot->changeCultureRangeCities(getOwnerINLINE(), iCultureRange, -1, bUpdatePlotGroups);
                                   }
                               }
                           }
                       }
                   }
               }

               if (eNewValue != NO_CULTURELEVEL && eOldValue < eNewValue)

               {
                   for (iDX = -eNewValue; iDX <= eNewValue; iDX++)
                   {
                       for (iDY = -eNewValue; iDY <= eNewValue; iDY++)
                       {
                           iCultureRange = cultureDistance(iDX, iDY);

                           if (iCultureRange > eOldValue)
                           {
                               if (iCultureRange <= eNewValue)
                               {
                                   FAssert(iCultureRange <= GC.getNumCultureLevelInfos());

                                   pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);

                                   if (pLoopPlot != NULL && ((pLoopPlot->getTerrainType() == (TerrainTypes)iJ)))
                                   {
                                       pLoopPlot->changeCultureRangeCities(getOwnerINLINE(), iCultureRange, 1, bUpdatePlotGroups);
                                   }
                               }
                           }
                       }
                   }
               }
           }
       }
   }
}

void CvCity::doTerrainCulture(int iCultureRate)//adjust terrainculture values so it counteracts the city's culture if the required tech is not known yet and gets benefit of relevant buildings if it has the tech or tech is not required
{
   int iJ;
   for (iJ = 0; iJ < NUM_TERRAIN_TYPES; ++iJ)
   {
       if ((GC.getTerrainInfo((TerrainTypes)iJ)).isNeedsTechToClaim())
       {
           if (GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isTerrainCulture((TerrainTypes)iJ))
           {
               changeTerrainCulture(getOwnerINLINE(),(TerrainTypes)iJ, (getTerrainCultureFromBuildings((TerrainTypes)iJ)));
           }
           else
           {
               setTerrainCulture(getOwnerINLINE(),(TerrainTypes)iJ, -(getCulture(getOwnerINLINE())));
           }
       }
       else
       {
           changeTerrainCulture(getOwnerINLINE(),(TerrainTypes)iJ, (getTerrainCultureFromBuildings((TerrainTypes)iJ)));
       }
   }
}
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

DLL edits to CvCity.cpp to tie in the new functions and arrays with the existing functions, void CvCity::doTurn()
Spoiler :
Code:
//FfH: End Add
   doCulture();
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   doTerrainCulture(getCommerceRate(COMMERCE_CULTURE));
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   doPlotCulture(false, getOwnerINLINE(), getCommerceRate(COMMERCE_CULTURE));

DLL edits to CvCity.cpp to tie in the new functions and arrays with the existing functions, void CvCity::updateCultureLevel(bool bUpdatePlotGroups)
Spoiler :
Code:
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific - Unmodified FFH2 Code
//   setCultureLevel(eCultureLevel, bUpdatePlotGroups);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific - Unmodified FFH2 Code
// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   setCultureLevel(eCultureLevel, 0);
   updateTerrainCultureLevel(getOwnerINLINE(),bUpdatePlotGroups);
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
}

DLL edits to CvCity.cpp to tie in the new functions and arrays with the existing functions, void CvCity::doPlotCulture(bool bUpdate, PlayerTypes ePlayer, int iCultureRate)
Spoiler :
Code:
{
   CvPlot* pLoopPlot;
   int iDX, iDY;
   int iCultureRange;
   CultureLevelTypes eCultureLevel = (CultureLevelTypes)0;

   CyCity* pyCity = new CyCity(this);
   CyArgsList argsList;
   argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity));   // pass in city class
   argsList.add(bUpdate);
   argsList.add(ePlayer);
   argsList.add(iCultureRate);
   long lResult=0;
   gDLL->getPythonIFace()->callFunction(PYGameModule, "doPlotCulture", argsList.makeFunctionArgs(), &lResult);
   delete pyCity;   // python fxn must not hold on to this pointer
   if (lResult == 1)
   {
       return;
   }

   FAssert(NO_PLAYER != ePlayer);

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific
   int iMaxTerrainCultureLevel = 0;
   for (int iJ = 0; iJ < NUM_TERRAIN_TYPES; iJ++)
   {
       if (getTerrainCultureLevel(ePlayer, (TerrainTypes)iJ) > iMaxTerrainCultureLevel)
       {
           iMaxTerrainCultureLevel = getTerrainCultureLevel(ePlayer, (TerrainTypes)iJ);
       }
   }
   eCultureLevel = (CultureLevelTypes)iMaxTerrainCultureLevel;

   int iFreeCultureRate = GC.getDefineINT("CITY_FREE_CULTURE_GROWTH_FACTOR");
   TerrainTypes eTerrain = NO_TERRAIN;
   if (getCultureTimes100(ePlayer) > 0)
   {
       if (eCultureLevel != NO_CULTURELEVEL)
       {
           for (iDX = -eCultureLevel; iDX <= eCultureLevel; iDX++)
           {
               for (iDY = -eCultureLevel; iDY <= eCultureLevel; iDY++)
               {
                   iCultureRange = cultureDistance(iDX, iDY);
                   pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
                   if ((pLoopPlot != NULL) && ((pLoopPlot->getTerrainType() != NO_TERRAIN)))
                   {
                       eTerrain = pLoopPlot->getTerrainType();
                       if (iCultureRange <= getTerrainCultureLevel(ePlayer, eTerrain))
                       {
                           if (pLoopPlot->isNeedsTechToClaim())
                           {
                               if (pLoopPlot->isOwnableTerrain(GET_PLAYER(getOwnerINLINE()).getTeam()))
                               {
                                   if (pLoopPlot->isPotentialCityWorkForArea(area()))
                                   {
                                       pLoopPlot->changeCulture(ePlayer, (((getTerrainCultureLevel(ePlayer, eTerrain)-iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                                   }
                               }
                           }
                           else
                           {
                               if (pLoopPlot->isPotentialCityWorkForArea(area()))
                               {
                                   pLoopPlot->changeCulture(ePlayer, (((getTerrainCultureLevel(ePlayer, eTerrain)-iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                               }
                           }
                       }
                       else
                       {
                           if (pLoopPlot->getCulture(ePlayer) < ((iCultureRange-getTerrainCultureLevel(ePlayer, eTerrain)) * iFreeCultureRate))
                           {
                               pLoopPlot->changeCulture(ePlayer, -(pLoopPlot->getCulture(ePlayer)), (bUpdate || !(pLoopPlot->isOwned())));
                           }
                           else
                           {
                               pLoopPlot->changeCulture(ePlayer, (((iCultureRange-getTerrainCultureLevel(ePlayer, eTerrain)) * iFreeCultureRate)), (bUpdate || !(pLoopPlot->isOwned())));
                           }
                       }
                   }
               }
           }
       }
   }
}

// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific

// LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific - Original unmodified FFH2 Code
/*   if (getOwnerINLINE() == ePlayer)
   {
       eCultureLevel = getCultureLevel();
   }

   else
   {
       for (int iI = (GC.getNumCultureLevelInfos() - 1); iI > 0; iI--)
       {
           if (getCultureTimes100(ePlayer) >= 100 * GC.getGameINLINE().getCultureThreshold((CultureLevelTypes)iI))
           {
               eCultureLevel = (CultureLevelTypes)iI;
               break;
           }
       }
   }

   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);*/
// End LPlate - Terrain-Culture Part 2, Allowing for Culture Spread rates to be Terrain specific - Original unmodified FFH2 Code
// LPlate - Terrain-Culture Modified Code - First Version - commented out for Terrain-Culture Part 2
/*                       if (pLoopPlot != NULL)
                       {
                           if (pLoopPlot->isNeedsTechToClaim())
                           {
                               if (pLoopPlot->isOwnableTerrain(GET_PLAYER(getOwnerINLINE()).getTeam()))
                               {
                                   if (pLoopPlot->isPotentialCityWorkForArea(area()))
                                   {
                                       pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                                   }
                               }
                           }
                           else
                           {
                               pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                           }
                       }*/
// End LPlate - Terrain-Culture Modified Code - First Version - commented out for Terrain-Culture Part 2
/* LPlate - Terrain-Culture Unmodified Code */
/*                       if (pLoopPlot != NULL)
                       {
                           if (pLoopPlot->isPotentialCityWorkForArea(area()))
                           {
                               pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                           }
                       }
                   }
               }
           }
       }
   }
}*/
/* End LPlate - Terrain-Culture Unmodified Code */

Edited to include CvEnums.h
 

Attachments

  • 1.PNG
    1.PNG
    582 KB · Views: 130
  • 7.PNG
    7.PNG
    589.9 KB · Views: 116
  • 8.PNG
    8.PNG
    590.7 KB · Views: 126
  • 9.PNG
    9.PNG
    623.1 KB · Views: 120
  • 14.PNG
    14.PNG
    498.9 KB · Views: 154
  • 15.PNG
    15.PNG
    613.8 KB · Views: 120
Last edited:
Looks great.

What happens when a resource you could use spawns on a terrain that you could not claim? Caveman2Cosmos had a similar mechanism to yours and it often meant that you could not get a resource that you needed because the terrain was not yet open. For example Stone in the Stone Age because the Terrain and Terrain Feature combination would not allow you to claim it until you had Bronze Tools.
 
What happens when a resource you could use spawns on a terrain that you could not claim?

I have not played C2C, so can not comment on that specific case but it does seem strange if stone could not be accessed until the Bronze Age, as suggested.

Generally, if a modder chose to use something like the technology limiting culture spread, it’s down to them how they want to set up their mod. Any of the following scenarios (and more) are possible;
1) Bonus is made available on more than one type of terrain, one of which requires a tech to culturally claim it. Any civ can access the bonus with the detection tech and the improvement tech but a civ which can culturally claim all of the terrains it is found on will potentially have more sources of it.
2) A modder could deliberately want to reveal what resources are available in a terrain before a player makes a decision to invest research into culturally claiming that type of terrain.
3) A modder may have simply misaligned the various xml entries governing when a resource is revealed, can be traded, when the relevant improvement can be built and when the tiles that it appears on can be culturally claimed. If you think you encounter something like that in a mod, just ask the modder. It could be intentional for a reason you had not considered or it could be a genuine error and they’ll be glad of the feedback.
 
Hi LPlate2,

I know this thread is a bit old, but I take a chance...

I would like to have a kind of territory system for my mods/scenarii, which are using pre-made maps. I tried your modcomp to develop this feature.
I made a clone of each terrain to block culture expansion and design the territories/sectors; I added a building giving a massive culture boost for each city and a non-researchable dummy tech to prevent those clone terrains to accept culture.
The problem is that frontier terrains create "no mans land" tiles. Of course, this is not critical but not very, let s say, elegant.
After a bit of thinking, I would like to know if you would be able to change the original game rivers system into cultural frontier. Better, create a clone of rivers, which would allow rivers to be rivers with their original properties, and a cloned river that would be a cultural blocking line?
 
Hi,

Sounds interesting. Is the intention with your clone terrains, that specific plots will only ever be owned by whoever owns a specific city? Does territory only change hands with the cities or are their some plots that will be claimed by cultural growth in cities?
As regards the rivers, I had considered trying to do something with the rivers initially as I thought civilisations are going to spread along rivers faster than elsewhere. I didn't have a tech to tie that to and if I was just giving a flat x% boost to cultural claims of all players on river tiles, it would mean that civs would gain the river tiles a little faster than other tiles but when two civilisations met the boosts would cancel each other out. I didn't think that effect was worth the effort involved.
I like the idea of rivers helping to define natural cultural borders. I'm not sure how I'd go about it. The code would need to identify which side of a tile the river is on (I don't know how to do that in the code), check whether the city in question (i.e. the one running doPlotCulture) is on the same side of the river as the tile and then apply a modifier to the changeculture that would be applied on the plot for that player.
 
Sounds interesting. Is the intention with your clone terrains, that specific plots will only ever be owned by whoever owns a specific city? Does territory only change hands with the cities or are their some plots that will be claimed by cultural growth in cities?
Yes, the concept is that you capture the city, you control the territory. The major problem (for me :D) with the basegame culture system is that if you capture a city from a strong cultural opponent -especially in late eras- it takes a long time to take control of the area. Although I completly understand the intention behind this, with the mind of materializing the resitance of previous owner s people, it clearly doesn t fit to the approach I have in my mod, which is more tactical.
I like the idea of rivers helping to define natural cultural borders. I'm not sure how I'd go about it. The code would need to identify which side of a tile the river is on (I don't know how to do that in the code), check whether the city in question (i.e. the one running doPlotCulture) is on the same side of the river as the tile and then apply a modifier to the changeculture that would be applied on the plot for that player.
That s why I was asking for feasibility. I don t know anything about SDK, thus I don t know how it considers the river itself. And there are no mods dealing with, as far as I know. Ironically, vanilla rivers have near than no interest in my gameplay, because I use a specific river terrain.
 
Top Bottom