[SDK] LPlate DLL Bits

LPlate2

Warlord
Joined
Dec 27, 2018
Messages
299
I've uploaded a few bits of dll coding (https://forums.civfanatics.com/resources/wonder-blocking-culture.28702/, https://forums.civfanatics.com/resources/forced-cityartstyle.28475/, https://forums.civfanatics.com/resources/sdk-assymetric-cultural-expansion.28436/) but given that in order for anyone to use any of it, they need to cut and paste it into their own files and then recompile, it seems better to just give the code directly in a thread. This way the information is easy to read, cut and paste without downloading anything. As far as I can see, the only advantage in uploading the full h and cpp files is that it makes it absolutely explicit where the edits were made. Anyone who's actually editing the dll files is well able to work that out.

Instead I've decided for any future bits of dll editing that might be useful for another modder, I'll just post the code here.
 
Coastal Raiders

These edits are to allow for promotions to benefit sea based civs. I picture them being used by Viking and Lanun type civs but could be useful for any civ on archipelago maps or maps with lots of rivers. They add three new xml tags in Promotions, to halve movement on river tiles, provide a modifier boost when attacking from river tiles and to provide a modifier boost when attacking from coast/water tiles.

xml edits
Spoiler :
CIV4UnitSchema.xml
Code:
    <ElementType name="bRiversDoubleMove" content="textOnly" dt:type="boolean"/><!-- LPlate Addition - Double movement on river -->
   <ElementType name="iRiverAttackBoostModifier" content="textOnly" dt:type="int"/><!-- LPlate Addition - Modifier to attack value from river tiles -->
   <ElementType name="iCoastalAttackModifier" content="textOnly" dt:type="int"/><!-- LPlate Addition - Modifier to attack value from coastal or coast tiles -->
and within the PromotionInfo elementtype,
Code:
       <element type="bRiversDoubleMove" minOccurs="0"/><!-- LPlate Addition - Doubles movement on river -->
       <element type="iRiverAttackBoostModifier" minOccurs="0"/><!-- LPlate Addition - Bonus to attack from rivers -->
       <element type="iCoastalAttackModifier" minOccurs="0"/><!-- LPlate Addition - Bonus to attack from coastal or coast tiles -->
An example of the use of the tags in CIV4PromotionInfo.xml
Code:
       <PromotionInfo><!-- River Boat -->
           <Type>PROMOTION_RIVER_BOATS</Type>
           <Description>TXT_KEY_PROMOTION_RIVER_BOATS</Description>
           <Sound>AS2D_IF_LEVELUP</Sound>
           <TechPrereq>TECH_NEVER</TechPrereq>
           <UnitCombats>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_ARCHER</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_MELEE</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_MOUNTED</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_RECON</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_SIEGE</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
           </UnitCombats>
           <Button>Art/Interface/Buttons/Promos/PromoRiverboats.dds</Button>
           <bRiversDoubleMove>1</bRiversDoubleMove>
       </PromotionInfo>
       <PromotionInfo><!-- River Raider -->
           <Type>PROMOTION_RIVER_RAIDER</Type>
           <Description>TXT_KEY_PROMOTION_RIVER_RAIDER</Description>
           <Sound>AS2D_IF_LEVELUP</Sound>
           <TechPrereq>TECH_NEVER</TechPrereq>
           <UnitCombats>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_ARCHER</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_MELEE</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_MOUNTED</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_RECON</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_SIEGE</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
           </UnitCombats>
           <Button>Art/Interface/Buttons/Promos/PromoRiverraiders.dds</Button>
           <iRiverAttackBoostModifier>30</iRiverAttackBoostModifier>
       </PromotionInfo>
       <PromotionInfo><!-- Coastal Raider -->
           <Type>PROMOTION_COASTAL_RAIDER</Type>
           <Description>TXT_KEY_PROMOTION_COASTAL_RAIDER</Description>
           <Sound>AS2D_IF_LEVELUP</Sound>
           <TechPrereq>TECH_NEVER</TechPrereq>
           <UnitCombats>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_ARCHER</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_MELEE</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_MOUNTED</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_RECON</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
               <UnitCombat>
                   <UnitCombatType>UNITCOMBAT_SIEGE</UnitCombatType>
                   <bUnitCombat>1</bUnitCombat>
               </UnitCombat>
           </UnitCombats>
           <Button>Art/Interface/Buttons/Promos/PromoCoastalraider.dds</Button>
           <iCoastalAttackModifier>30</iCoastalAttackModifier>
       </PromotionInfo>
[CODE]
Within the text xml[CODE]
    <TEXT>
       <Tag>TXT_KEY_COMBAT_PLOT_ODDS_COASTAL_RAIDER</Tag>
       <English>[ICON_BULLET]+%d1% attacking from water/coastal tile</English>
   </TEXT>
   <TEXT>
       <Tag>TXT_KEY_COMBAT_PLOT_ODDS_RIVER_RAIDER</Tag>
       <English>[ICON_BULLET]+%d1% attacking from river tile</English>
   </TEXT>
   <TEXT>
       <Tag>TXT_KEY_COMBAT_PLOT_ODDS_COASTAL_RAIDER</Tag>
       <English>[ICON_BULLET]+%d1% attacking from water/coastal tile</English>
   </TEXT>
   <TEXT>
       <Tag>TXT_KEY_PROMOTION_RIVERS_DOUBLE_MOVE_TEXT</Tag>
       <English>[ICON_BULLET]Double movement on river tiles</English>
   </TEXT>
   <TEXT>
       <Tag>TXT_KEY_PROMOTION_RIVER_ATTACK_TEXT</Tag>
       <English>[ICON_BULLET]Attack strength from River tiles increased by %d1%</English>
   </TEXT>
   <TEXT>
       <Tag>TXT_KEY_PROMOTION_COASTAL_ATTACK_TEXT</Tag>
       <English>[ICON_BULLET]Attack strength from Coast and Coastal tiles increased by %d1%</English>
   </TEXT>

All of the associated dll edits are framed with Coastal Raiders.
The edits to set up the tags in the Info files
CvInfos.h
Spoiler :
In class CvPromotionInfo : public CvHotkeyInfo in CvInfos.h,
Code:
// LPlate - Coastal Raiders
   bool isRiversDoubleMove() const;               // Exposed to Python
   int getRiverAttackBoostModifier() const;               // Exposed to Python
   int getCoastalAttackModifier() const;               // Exposed to Python
// End LPlate - Coastal Raiders
   int getUnitArtStyleType() const;

// LPlate - Coastal Raiders
   bool isRiversDoubleMove() const;               // Exposed to Python
   int getRiverAttackBoostModifier() const;               // Exposed to Python
   int getCoastalAttackModifier() const;               // Exposed to Python
// End LPlate - Coastal Raiders
   int getUnitArtStyleType() const;
and in protected:
Code:
// LPlate - Coastal Raiders
   bool m_bRiversDoubleMove;
   int m_iRiverAttackBoostModifier;
   int m_iCoastalAttackModifier;
// End LPlate - Coastal Raiders
   int m_iUnitArtStyleType;

CvInfos.cpp
Spoiler :
In CvPromotionInfo::CvPromotionInfo() :
Code:
// LPlate - Coastal Raiders
m_bRiversDoubleMove(false),
m_iRiverAttackBoostModifier(0),
m_iCoastalAttackModifier(0),
// End LPlate - Coastal Raiders
m_iUnitArtStyleType(NO_UNIT_ARTSTYLE),
In CvPromotionInfo::~CvPromotionInfo()
Code:
// LPlate - Coastal Raiders
bool CvPromotionInfo::isRiversDoubleMove() const
{
   return m_bRiversDoubleMove;
}
int CvPromotionInfo::getRiverAttackBoostModifier() const
{
   return m_iRiverAttackBoostModifier;
}
int CvPromotionInfo::getCoastalAttackModifier() const
{
   return m_iCoastalAttackModifier;
}
// End LPlate - Coastal Raiders
int CvPromotionInfo::getGroupSize() const
In void CvPromotionInfo::read(FDataStreamBase* stream)
Code:
// LPlate - Coastal Raiders
   stream->Read(&m_bRiversDoubleMove);
   stream->Read(&m_iRiverAttackBoostModifier);
   stream->Read(&m_iCoastalAttackModifier);
// End LPlate - Coastal Raiders

In void CvPromotionInfo::write(FDataStreamBase* stream)
Code:
// LPlate - Coastal Raiders
   stream->Write(m_bRiversDoubleMove);
   stream->Write(m_iRiverAttackBoostModifier);
   stream->Write(m_iCoastalAttackModifier);
// End LPlate - Coastal Raiders
In bool CvPromotionInfo::read(CvXMLLoadUtility* pXML)
Code:
// LPlate - Coastal Raiders
   pXML->GetChildXmlValByName(&m_bRiversDoubleMove, "bRiversDoubleMove");
   pXML->GetChildXmlValByName(&m_iRiverAttackBoostModifier, "iRiverAttackBoostModifier");
   pXML->GetChildXmlValByName(&m_iCoastalAttackModifier, "iCoastalAttackModifier");
// End LPlate - Coastal Raiders

CyInfoInterface1.cpp
Spoiler :
In python::class_<CvPromotionInfo, python::bases<CvInfoBase> >("CvPromotionInfo")
Code:
// LPlate - Coastal Raiders
       .def("isRiversDoubleMove", &CvPromotionInfo::isRiversDoubleMove, "bool ()")
       .def("getRiverAttackBoostModifier", &CvPromotionInfo::getRiverAttackBoostModifier, "int ()")
       .def("getCoastalAttackModifier", &CvPromotionInfo::getCoastalAttackModifier, "int ()")
// End LPlate - Coastal Raiders

The Unit file edits
CvInfos.h
Spoiler :
In struct DllExport CombatDetails // Exposed to Python
Code:
// LPlate - Coastal Raiders
   int iRiverAttackBoostModifier;
   int iCoastalAttackModifier;
// End LPlate - Coastal Raiders
   PlayerTypes eOwner;
In class CvUnit : public CvDLLEntity
Code:
// LPlate - Coastal Raiders
   bool isRiversDoubleMove() const;                                                                               // Exposed to Python
   void changeRiversDoubleMove(int iNewValue);// Exposed to Python
   int getRiverAttackBoostModifier() const;                                                                               // Exposed to Python
   void setRiverAttackBoostModifier(int iChange);                                                                       // Exposed to Python
   void changeRiverAttackBoostModifier(int iChange);                                                                       // Exposed to Python
   int getCoastalAttackModifier() const;                                                                               // Exposed to Python
   void setCoastalAttackModifier(int iChange);                                                                       // Exposed to Python
   void changeCoastalAttackModifier(int iChange);                                                                       // Exposed to Python
// End LPlate - Coastal Raiders
   int experienceNeeded() const;                                                                                       // Exposed to Python
In protected:
Code:
// LPlate - Coastal Raiders
   int m_iRiversDoubleMove;
   int m_iRiverAttackBoostModifier;
   int m_iCoastalAttackModifier;
// End LPlate - Coastal Raiders

CvInfos.cpp
Spoiler :
In void CvUnit::reset(int iID, UnitTypes eUnit, PlayerTypes eOwner, bool bConstructorCall)
Code:
// LPlate - Coastal Raiders
   m_iRiversDoubleMove = 0;
   m_iRiverAttackBoostModifier = 0;
   m_iCoastalAttackModifier = 0;
// End LPlate - Coastal Raiders
function definitions
Code:
// LPlate - Coastal Raiders
bool CvUnit::isRiversDoubleMove() const
{
   return m_iRiversDoubleMove == 0 ? false : true;
}
void CvUnit::changeRiversDoubleMove(int iChange)
{
   m_iRiversDoubleMove += iChange;
}
int CvUnit::getRiverAttackBoostModifier() const
{
   CvPlot* pPlot = plot();
   if (!(pPlot->isRiver()))
   {
       return 0;
   }

   return m_iRiverAttackBoostModifier;
}
void CvUnit::setRiverAttackBoostModifier(int iNewValue)
{
   m_iRiverAttackBoostModifier = iNewValue;
}
void CvUnit::changeRiverAttackBoostModifier(int iChange)
{
   m_iRiverAttackBoostModifier += iChange;
}
int CvUnit::getCoastalAttackModifier() const
{
   CvPlot* pPlot = plot();
   if (!(pPlot->isWater() || pPlot->isCoastalLand(3)))
   {
       return 0;
   }

   return m_iCoastalAttackModifier;
}
void CvUnit::setCoastalAttackModifier(int iNewValue)
{
   m_iCoastalAttackModifier = iNewValue;
}
void CvUnit::changeCoastalAttackModifier(int iChange)
{
   m_iCoastalAttackModifier += iChange;
}
// End LPlate - Coastal Raiders
In int CvUnit::maxCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
Code:
    iModifier += iExtraModifier;
   if (pCombatDetails != NULL)
   {
       pCombatDetails->iExtraCombatPercent += iExtraModifier;
   }
// LPlate - Coastal Raiders
   iExtraModifier = getRiverAttackBoostModifier();
   iModifier += iExtraModifier;
   if (pCombatDetails != NULL)
   {
       pCombatDetails->iExtraCombatPercent += iExtraModifier;
   }
   iExtraModifier = getCoastalAttackModifier();
   iModifier += iExtraModifier;
   if (pCombatDetails != NULL)
   {
       pCombatDetails->iExtraCombatPercent += iExtraModifier;
   }
// End LPlate - Coastal Raiders
In void CvUnit::setHasPromotion(PromotionTypes eIndex, bool bNewValue), within the if (isHasPromotion(eIndex) != bNewValue) condition
Code:
// LPlate - Coastal Raiders
       changeRiversDoubleMove((GC.getPromotionInfo(eIndex).isRiversDoubleMove()) ? iChange : 0);
       changeRiverAttackBoostModifier((GC.getPromotionInfo(eIndex).getRiverAttackBoostModifier())*iChange);
       changeCoastalAttackModifier((GC.getPromotionInfo(eIndex).getCoastalAttackModifier())*iChange);
// End LPlate - Coastal Raiders
In void CvUnit::read(FDataStreamBase* pStream)
Code:
// LPlate - Coastal Raiders
   pStream->Read(&m_iRiversDoubleMove);
   pStream->Read(&m_iRiverAttackBoostModifier);
   pStream->Read(&m_iCoastalAttackModifier);
// End LPlate - Coastal Raiders
In void CvUnit::write(FDataStreamBase* pStream)
Code:
// LPlate - Coastal Raiders
   pStream->Write(m_iRiversDoubleMove);
   pStream->Write(m_iRiverAttackBoostModifier);
   pStream->Write(m_iCoastalAttackModifier);
// End LPlate - Coastal Raiders
CyUnit.h
Spoiler :
In class CyUnit public:
Code:
// LPlate - Coastal Raiders
   bool isRiversDoubleMove() const;
   void changeRiversDoubleMove(int iChange);
   int getRiverAttackBoostModifier() const;
   void setRiverAttackBoostModifier(int iNewValue);
   void changeRiverAttackBoostModifier(int iChange);
   int getCoastalAttackModifier() const;
   void setCoastalAttackModifier(int iNewValue);
   void changeCoastalAttackModifier(int iChange);
// End LPlate - Coastal Raiders
CyUnit.cpp
Spoiler :
Code:
// LPlate - Coastal Raiders
bool CyUnit::isRiversDoubleMove() const
{
   return m_pUnit ? m_pUnit->isRiversDoubleMove() : false;
}
void CyUnit::changeRiversDoubleMove(int iChange)
{
   m_pUnit->changeRiversDoubleMove(iChange);
}
int CyUnit::getRiverAttackBoostModifier() const
{
   return m_pUnit ? m_pUnit->getRiverAttackBoostModifier() : -1;
}
void CyUnit::setRiverAttackBoostModifier(int iNewValue)
{
   m_pUnit->setRiverAttackBoostModifier(iNewValue);
}
void CyUnit::changeRiverAttackBoostModifier(int iChange)
{
   m_pUnit->changeRiverAttackBoostModifier(iChange);
}
int CyUnit::getCoastalAttackModifier() const
{
   return m_pUnit ? m_pUnit->getCoastalAttackModifier() : -1;
}
void CyUnit::setCoastalAttackModifier(int iNewValue)
{
   m_pUnit->setCoastalAttackModifier(iNewValue);
}
void CyUnit::changeCoastalAttackModifier(int iChange)
{
   m_pUnit->changeCoastalAttackModifier(iChange);
}
// End LPlate - Coastal Raiders

CyUnitInterface1.cpp
Spoiler :
In void CyUnitPythonInterface1(python::class_<CyUnit>& x)
Code:
// LPlate - Coastal Raiders
       .def("isRiversDoubleMove", &CyUnit::isRiversDoubleMove, "bool ()")
       .def("changeRiversDoubleMove", &CyUnit::changeRiversDoubleMove, "void (int iChange)")
       .def("getRiverAttackBoostModifier", &CyUnit::getRiverAttackBoostModifier, "int ()")
       .def("setRiverAttackBoostModifier", &CyUnit::setRiverAttackBoostModifier, "void (int iNewValue)")
       .def("changeRiverAttackBoostModifier", &CyUnit::changeRiverAttackBoostModifier, "void (int iChange)")
       .def("getCoastalAttackModifier", &CyUnit::getCoastalAttackModifier, "int ()")
       .def("setCoastalAttackModifier", &CyUnit::setCoastalAttackModifier, "void (int iNewValue)")
       .def("changeCoastalAttackModifier", &CyUnit::changeCoastalAttackModifier, "void (int iChange)")
// End LPlate - Coastal Raiders

The plot file edits (for the halving of movement on river tiles)
CvPlot.cpp
Spoiler :
Code:
    if (bHasTerrainCost)
   {
// LPlate - Coastal Raiders, Unmodified FFH2 Code
/*       if (((getFeatureType() == NO_FEATURE) ? pUnit->isTerrainDoubleMove(getTerrainType()) : pUnit->isFeatureDoubleMove(getFeatureType())) ||
           (isHills() && pUnit->isHillsDoubleMove()))*/
// End LPlate - Coastal Raiders, Unmodified FFH2 Code

// LPlate - Coastal Raiders
       if (((getFeatureType() == NO_FEATURE) ? pUnit->isTerrainDoubleMove(getTerrainType()) : pUnit->isFeatureDoubleMove(getFeatureType())) ||
           (isHills() && pUnit->isHillsDoubleMove()) ||
           (isRiver() && pUnit->isRiversDoubleMove()))
// End LPlate - Coastal Raiders
       {
           iRegularCost /= 2;
       }
   }

and finally the text manager edits so someone knows whats going on
CvGameTextMgr.cpp
Spoiler :
In void CvGameTextMgr::setUnitHelp(CvWStringBuffer &szString, const CvUnit* pUnit, bool bOneLine, bool bShort)
Code:
           if (pUnit->isRiver())
           {
               szString.append(NEWLINE);
               szString.append(gDLL->getText("TXT_KEY_PROMOTION_RIVER_ATTACK_TEXT"));
           }
// LPlate - Coastal Raiders
           if (pUnit->isRiversDoubleMove())
           {
               szString.append(NEWLINE);
               szString.append(gDLL->getText("TXT_KEY_PROMOTION_RIVER_DOUBLE_MOVE_TEXT"));
           }
           if (pUnit->getRiverAttackBoostModifier()>0)
           {
               szString.append(NEWLINE);
               szString.append(gDLL->getText("TXT_KEY_PROMOTION_RIVER_RAIDER_TEXT"));
           }
           if (pUnit->getCoastalAttackModifier()>0)
           {
               szString.append(NEWLINE);
               szString.append(gDLL->getText("TXT_KEY_PROMOTION_COASTAL_RAIDER_TEXT"));
           }
// End LPlate - Coastal Raiders
In bool CvGameTextMgr::setCombatPlotHelp(CvWStringBuffer &szString, CvPlot* pPlot), within if (pDefender != NULL && pDefender != pAttacker && pDefender->canDefend(pPlot) && pAttacker->canAttack(*pDefender)) condition
Code:
// LPlate - Coastal Raiders
           iModifier = pAttacker->getRiverAttackBoostModifier();

           if (iModifier != 0)
           {
               szString.append(NEWLINE);
               szString.append(gDLL->getText("TXT_KEY_COMBAT_PLOT_ODDS_RIVER_RAIDER", iModifier));
           }

           iModifier = pAttacker->getCoastalAttackModifier();

           if (iModifier != 0)
           {
               szString.append(NEWLINE);
               szString.append(gDLL->getText("TXT_KEY_COMBAT_PLOT_ODDS_COASTAL_RAIDER", iModifier));
           }
// End LPlate - Coastal Raiders
In void CvGameTextMgr::parsePromotionHelp(CvWStringBuffer &szBuffer, PromotionTypes ePromotion, const wchar* pcNewline)
Code:
// LPlate - Coastal Raiders
   if (GC.getPromotionInfo(ePromotion).isRiversDoubleMove())
   {
       szBuffer.append(pcNewline);
       szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_RIVERS_DOUBLE_MOVE_TEXT"));
   }
   if (GC.getPromotionInfo(ePromotion).getRiverAttackBoostModifier() != 0)
   {
       szBuffer.append(pcNewline);
       szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_RIVER_ATTACK_TEXT",(GC.getPromotionInfo(ePromotion).getRiverAttackBoostModifier())));
   }
   if (GC.getPromotionInfo(ePromotion).getCoastalAttackModifier() != 0)
   {
       szBuffer.append(pcNewline);
       szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_COASTAL_ATTACK_TEXT",GC.getPromotionInfo(ePromotion).getCoastalAttackModifier()));
   }
// End LPlate - Coastal Raiders

Note: I used iRiverAttackBoostModifier, as iRiverAttackModifier is already used for the negative modifier that is applied for units that aren't amphibious.

Also attached are a few buttons that can be used for the promos.
 

Attachments

  • CoastalRaidersButtons.zip
    CoastalRaidersButtons.zip
    32.3 KB · Views: 51
  • Coastalraiderbuttons.png
    Coastalraiderbuttons.png
    19.7 KB · Views: 59
Rings and Squares
Checking rings around a plot for a resource.
I'm adding this into my DLL as I need to do these checks on multiple locations at ranges beyond the typical city fat square. This will simplify my python a bit. It's also an example of adding functions without having to add any new xml tags.

There are two functions, one returns a boolean, checking to see if the required bonus is in range. The second counts how many are in range.
iRange = distance from plot to the edge of the ring/square
bSquare; 1 if checking in a square around the plot, 0 if checking in rings similar to expanding city cultural plot ownership (thanks@1frpo)
bIncludeSelf; 0 ignores the plot from which the function is performed, 1 includes it
iOwnership; 0 = Ownership is irrelevant,1 = Only include plots owned by self, 2 = Only include plots not owned by someone else, 3 = Only include plots owned by someone else
bWorked; 1 if only worked plots are to be considered
bImproved; 1 if the plot needs to be improved

Code
Spoiler :

CvPlot.h
Spoiler :
Code:
/* LPlate - Rings and Squares */
    bool isRingsBonus(int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, PlayerTypes eOwner, bool bWorked, BonusTypes eType, bool bImproved, bool bRevealed) const;
   int getRingsBonus(int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, PlayerTypes eOwner, bool bWorked, BonusTypes eType, bool bImproved, bool bRevealed) const;
/* End LPlate - Rings and Squares */

CvPlot.cpp
Spoiler :
Code:
/* LPlate - Rings and Squares */
bool CvPlot::isRingsBonus(int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, PlayerTypes ePlayer, bool bWorked, BonusTypes eType, bool bImproved) const
{
// bSquare: 0 = rings, 1 = squares
//   iOwnership: 0 = Ownership is irrelevant,1 = Only owned by self, 2 = Not owned by someone else, 3 = Owned by someone else)
    TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam();

   for (int iDX = -iRange; iDX <= iRange; iDX++)
   {
       for (int iDY = -iRange; iDY <= iRange; iDY++)
       {
           CvPlot* pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);

           if (bSquare || (plotDistance(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), getX_INLINE(), getY_INLINE()) <= iRange))
           {

               if(bIncludeSelf || (pLoopPlot->getX_INLINE() != getX_INLINE() || pLoopPlot->getY_INLINE() != getY_INLINE()))
               {
                   if((iOwnership == 0) ||
                   (iOwnership == 1 && pLoopPlot->getOwnerINLINE() == ePlayer) ||
                   (iOwnership == 2 && (pLoopPlot->getOwnerINLINE() == ePlayer || (!(pLoopPlot->isOwned())))) ||
                   (iOwnership == 3 && (!(pLoopPlot->getOwnerINLINE() == ePlayer && (!(pLoopPlot->isOwned()))))))
                   {
                       if (!(bWorked) || pLoopPlot->isBeingWorked())
                       {
                           if (pLoopPlot->getBonusType() == eType)
                           {
                               if (!(bImproved) || pLoopPlot->getImprovementType() != NO_IMPROVEMENT)

                               {
                                   if (!(bRevealed) || pLoopPlot->isRevealed(eTeam, false))
                                   {
                                       return true;
                                   }
                               }
                           }
                       }
                   }
               }
           }
       }
   }
   return false;
}

int CvPlot::getRingsBonus(int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, PlayerTypes ePlayer, bool bWorked, BonusTypes eType, bool bImproved) const
{
// bSquare: 0 = rings, 1 = squares
//   iOwnership: 0 = Ownership is irrelevant,1 = Only owned by self, 2 = Not owned by someone else, 3 = Owned by someone else)

   int iCount = 0;
    TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam();

   for (int iDX = -iRange; iDX <= iRange; iDX++)
   {
       for (int iDY = -iRange; iDY <= iRange; iDY++)
       {
           CvPlot* pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
           if (bSquare || (plotDistance(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), getX_INLINE(), getY_INLINE()) <= iRange))
           {
               if(bIncludeSelf || (pLoopPlot->getX_INLINE() != getX_INLINE() || pLoopPlot->getY_INLINE() != getY_INLINE()))
               {
                   if((iOwnership == 0) ||
                   (iOwnership == 1 && pLoopPlot->getOwnerINLINE() == ePlayer) ||
                   (iOwnership == 2 && (pLoopPlot->getOwnerINLINE() == ePlayer || (!(pLoopPlot->isOwned())))) ||
                   (iOwnership == 3 && (!(pLoopPlot->getOwnerINLINE() == ePlayer && (!(pLoopPlot->isOwned()))))))
                   {
                       if (!(bWorked) || pLoopPlot->isBeingWorked())
                       {
                           if (pLoopPlot->getBonusType() == eType)
                           {
                               if (!(bImproved) || pLoopPlot->getImprovementType() != NO_IMPROVEMENT)
                               {
                                   if (!(bRevealed) || pLoopPlot->isRevealed(eTeam, false))
                                   {
                                       iCount += 1;
                                   }
                               }
                           }
                       }
                   }
               }
           }
       }
   }
   return iCount;
}
/* End LPlate - Rings and Squares */

CyPlot.h
Spoiler :
Code:
/* LPlate - Rings and Squares */
    bool isRingsBonus(int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, int /*PlayerTypes*/ eOwner, bool bWorked, int /*BonusTypes*/ eType, bool bImproved, bool bRevealed);
   int getRingsBonus(int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, int /*PlayerTypes*/ eOwner, bool bWorked, int /*BonusTypes*/ eType, bool bImproved, bool bRevealed);
/* End LPlate - Rings and Squares */

CyPlot.cpp
Spoiler :
Code:
/* LPlate - Rings and Squares */
bool CyPlot::isRingsBonus(int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, int /*PlayerTypes*/ eOwner, bool bWorked, int /*BonusTypes*/ eType, bool bImproved, bool bRevealed)
{
   return m_pPlot ? m_pPlot->isRingsBonus(iRange, bSquare, bIncludeSelf, iOwnership, (PlayerTypes) eOwner, bWorked, (BonusTypes) eType, bImproved, bRevealed) : false;
}
int CyPlot::getRingsBonus(int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, int /*PlayerTypes*/ eOwner, bool bWorked, int /*BonusTypes*/ eType, bool bImproved, bool bRevealed)
{
   return m_pPlot ? m_pPlot->getRingsBonus(iRange, bSquare, bIncludeSelf, iOwnership, (PlayerTypes) eOwner, bWorked, (BonusTypes) eType, bImproved, bRevealed) : -1;
}
/* End LPlate - Rings and Squares */

CyPlotInterface1.cpp
Spoiler :
Code:
/* LPlate - Rings and Squares */
       .def("isRingsBonus", &CyPlot::isRingsBonus, "bool (int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, int /*PlayerTypes*/ eOwner, bool bWorked, int /*BonusTypes*/ eType, bool bImproved, bool bRevealed)")
       .def("getRingsBonus", &CyPlot::getRingsBonus, "bool (int iRange, bool bSquare, bool bIncludeSelf, int iOwnership, int /*PlayerTypes*/ eOwner, bool bWorked, int /*BonusTypes*/ eType, bool bImproved, bool bRevealed)")
/* End LPlate - Rings and Squares */


Edit: Edited to include an isRevealed check.
 
Last edited:
Shouldn't the RingsBonus functions make some visibility checks? That is: CvPlot::isRevealed, getRevealedOwner and getRevealedImprovementType; getBonusType can take a team as parameter and will then check getTechReveal. Your functions would then need a TeamTypes parameter (NO_TEAM could mean all-seeing – if that ever makes sense). Edit: I guess getTechReveal is easy enough to check on the caller's side.
 
Shouldn't the RingsBonus functions make some visibility checks? That is: CvPlot::isRevealed, getRevealedOwner and getRevealedImprovementType; getBonusType can take a team as parameter and will then check getTechReveal. Your functions would then need a TeamTypes parameter (NO_TEAM could mean all-seeing – if that ever makes sense). Edit: I guess getTechReveal is easy enough to check on the caller's side.

Makes sense, I can add them in. I'll get the team based on who the player is.
How does getRevealedOwner and getRevealedImprovementType work if ownership has changed since the plot was revealed. Will these return the old owner/improvement, as opposed to the true current owner?
 
Makes sense, I can add them in. I'll get the team based on who the player is.
If you use those functions only for an extended radius around cities, then there probably isn't a problem because the surrounding borders provide visibility.
How does getRevealedOwner and getRevealedImprovementType work if ownership has changed since the plot was revealed. Will these return the old owner/improvement, as opposed to the true current owner?
Yes, what eTeam is seeing in the fog of war. (There's also getRevealedRoute. The original code isn't very conscientious in using those functions; e.g. the pathfinder leaks information about routes in the fog of war.)
 
This is some code to implement a suggestion from @Spillsandstains & @Zeta Nexus, in https://forums.civfanatics.com/threads/other-wonders.667745/, to give a Californication effect for a wonder. The idea is that the owner of the wonder has their culture spread in foreign cities, which have a particular building.

TargetBuildingClassType identifies the type of buildingclass that the wonder gives culture for in foreign cities.
iCounterCulture is the amount of culture that the buildings give.
xml edits for this
Spoiler :
CIV4BuildingsSchema.xml
Spoiler :
Code:
   <ElementType name="TargetBuildingClassType" content="textOnly"/><!-- LPlate - Californication - building class which will spread owner's culture when in another player's city-->
   <ElementType name="iCounterCulture" content="textOnly" dt:type="int"/><!-- LPlate - Californication - quantity of owner's culture spread in another player's city -->
Code:
       <element type="TargetBuildingClassType" minOccurs="0"/><!-- LPlate - Californication - building class which will spread owner's culture when in another player's city-->
       <element type="iCounterCulture" minOccurs="0"/><!-- LPlate - Californication - quantity of owner's culture spread in another player's city -->
Example of implementation in CIV4BuildingInfos.xml
Spoiler :
Code:
           <TargetBuildingClassType>BUILDINGCLASS_BROADCAST_TOWER</TargetBuildingClassType>
           <iCounterCulture>1</iCounterCulture>
I also added in some parameters into GlobalDefinesAlt.xml.
MAX_NUM_CALIFORNICATION is the maximum number of wonders that will be using the Californication, counter-culture effect. This is to minimise the size of the loops
MAX_CALIFORNICATION_PERCENT is a percentage value. This is used to limit the amount of counter culture that can be generated in a city (regardless of the amount of affected buildings) for a given player. Essentially, if the effect is supposed to be representing the spread of a sub-culture or counter culture, it should not be generating more culture than the primary culture of the civ which owns the city.
MIN_CALIFORNICATION_HOST_CULTURE is an integer value. Similar to MAX_CALIFORNICATION_PERCENT, it can be used to set a minimum primary culture generation rate in the target cities, before the counter culture effect kicks in.
e.g.
Spoiler :
Code:
    <Define>
       <DefineName>MAX_NUM_CALIFORNICATION</DefineName>
       <iDefineIntVal>3</iDefineIntVal>
   </Define>
   <Define>
       <DefineName>MAX_CALIFORNICATION_PERCENT</DefineName>
       <iDefineIntVal>75</iDefineIntVal>
   </Define>
   <Define>
       <DefineName>MIN_CALIFORNICATION_HOST_CULTURE</DefineName>
       <iDefineIntVal>1</iDefineIntVal>
   </Define>
Text.xml
Spoiler :
Code:
    <TEXT>
       <Tag>TXT_KEY_MANAGER_COUNTER_CULTURE_TARGET_TEXT</Tag>
       <English>[ICON_BULLET]Grants %d1 counter culture to owner of %s2 Wonder</English>
   </TEXT>
   <TEXT>
       <Tag>TXT_KEY_MANAGER_COUNTER_CULTURE_TEXT</Tag>
       <English>[ICON_BULLET]Provides %d1 culture from %s2 buildings in foreign cities</English>
   </TEXT>

DLL to set up the parameters used. (DLL edits are bounded by LPlate - Californication)
Spoiler :

CvInfos.h
Spoiler :
Code:
/* LPlate - Californication */
   int getTargetBuildingClassType() const;               // Exposed to Python
   int getCounterCulture() const;               // Exposed to Python
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   int m_iTargetBuildingClassType;
   int m_iCounterCulture;
/* End LPlate - Californication */
CvInfos.cpp
Spoiler :
Code:
CvGameTextMgr.cpp entries in void CvGameTextMgr::setBuildingHelp(CvWStringBuffer &szBuffer, BuildingTypes eBuilding, bool bCivilopediaText, bool bStrategyText, bool bTechChooserText, CvCity* pCity) to provide the explanation of the parameters[SPOILER][CODE]
/* LPlate - Californication */
       if (kBuilding.getCounterCulture() > 0 && (!(kBuilding.getTargetBuildingClassType() == NO_BUILDINGCLASS)))
       {
           BuildingClassTypes eTargetBuildingClass;
           eTargetBuildingClass = (BuildingClassTypes)kBuilding.getTargetBuildingClassType();
           szBuffer.append(NEWLINE);
           szBuffer.append(gDLL->getText("TXT_KEY_MANAGER_COUNTER_CULTURE_TEXT", kBuilding.getCounterCulture(), GC.getBuildingClassInfo(eTargetBuildingClass).getDescription()));
       }
       for (iI = 0; iI < GC.getNumBuildingInfos(); ++iI)
       {
           if (GC.getBuildingInfo((BuildingTypes)iI).getCounterCulture() > 0 && (GC.getBuildingInfo((BuildingTypes)iI).getTargetBuildingClassType() == kBuilding.getBuildingClassType()))
           {
               szBuffer.append(NEWLINE);
               szBuffer.append(gDLL->getText("TXT_KEY_MANAGER_COUNTER_CULTURE_TARGET_TEXT", GC.getBuildingInfo((BuildingTypes)iI).getCounterCulture(), GC.getBuildingInfo((BuildingTypes)iI).getDescription()));
           }
       }
/* End LPlate - Californication */

DLL to set up other parameters and functional code
Spoiler :

CvCity.h
Spoiler :
Code:
/* LPlate - Californication */
   int m_iCounterCultureTarget;
   int* m_aiCounterCultureTotal;
/* End LPlate - Californication */
CvCity.cpp
Spoiler :
Code:
CvCity::CvCity()
{
/* LPlate - Californication */
   m_aiCounterCultureTotal = new int[GC.getDefineINT("MAX_NUM_CALIFORNICATION")];
/* End LPlate - Californication */
Code:
CvCity::~CvCity()
{
   CvDLLEntity::removeEntity();           // remove entity from engine
   CvDLLEntity::destroyEntity();           // delete CvCityEntity and detach from us

   uninit();
/* LPlate - Californication */
   SAFE_DELETE_ARRAY(m_aiCounterCultureTotal);
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   m_iCounterCultureTarget = 0;
   for (iI = 0; iI < GC.getDefineINT("MAX_NUM_CALIFORNICATION"); iI++)
   {
       m_aiCounterCultureTotal[iI] = 0;
   }
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
bool CvCity::isCounterCultureTarget() const
{
   return (m_iCounterCultureTarget > 0);
}

int CvCity::getCounterCultureTarget() const
{
   return (m_iCounterCultureTarget);
}
void CvCity::changeCounterCultureTarget(int iChange)
{
   m_iCounterCultureTarget += iChange;
}
void CvCity::setCounterCultureTarget(int iValue)
{
   m_iCounterCultureTarget = iValue;
}
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   if (isCounterCultureTarget() && getCommerceRate(COMMERCE_CULTURE) >= GC.getDefineINT("MIN_CALIFORNICATION_HOST_CULTURE"))
   {
       PlayerTypes eCounterCulturePlayer = NO_PLAYER;
       int iCounterCulture = 0;
       bool bUncounted = true;
       for (int iPosition1 = 0; iPosition1 < GC.getDefineINT("MAX_NUM_CALIFORNICATION"); iPosition1++)
       {
           iCounterCulture = 0;
           bUncounted = true;

           if (isHasBuildingClass(GC.getGameINLINE().getCounterCultureBuildingClass(iPosition1)) && !((PlayerTypes)GC.getGameINLINE().getCounterCulturePlayer(iPosition1) == getOwnerINLINE()))
           {
               iCounterCulture = GC.getGameINLINE().getCounterCulture(iPosition1);
               for (int iPosition2 = 0; iPosition2 < GC.getDefineINT("MAX_NUM_CALIFORNICATION"); iPosition2++)
               {
                   if (iPosition2 <= iPosition1)
                   {
                       bUncounted = false;
                   }
                   if (bUncounted && GC.getGameINLINE().getCounterCulturePlayer(iPosition1) == GC.getGameINLINE().getCounterCulturePlayer(iPosition2) && (isHasBuildingClass(GC.getGameINLINE().getCounterCultureBuildingClass(iPosition2))))
                   {
                       iCounterCulture += GC.getGameINLINE().getCounterCulture(iPosition2);
                   }
               }
               if (iCounterCulture > GC.getDefineINT("MAX_CALIFORNICATION_PERCENT")*getCommerceRate(COMMERCE_CULTURE)/100)
               {
                   iCounterCulture = GC.getDefineINT("MAX_CALIFORNICATION_PERCENT")*getCommerceRate(COMMERCE_CULTURE)/100;
               }
               doCounterCulture(iPosition1, iCounterCulture);
               eCounterCulturePlayer = (PlayerTypes) GC.getGameINLINE().getCounterCulturePlayer(iPosition1);
               doPlotCounterCulture(false, iPosition1, iCounterCulture);
           }
       }
   }
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
bool CvCity::isCounterCultureTarget() const
{
   return (m_iCounterCultureTarget > 0);
}

int CvCity::getCounterCultureTarget() const
{
   return (m_iCounterCultureTarget);
}
void CvCity::changeCounterCultureTarget(int iChange)
{
   m_iCounterCultureTarget += iChange;
}
void CvCity::setCounterCultureTarget(int iValue)
{
   m_iCounterCultureTarget = iValue;
}
/* End LPlate - Californication */
Code:
void CvCity::setNumRealBuilding(BuildingTypes eIndex, int iNewValue)
{
/* LPlate - Californication */

   int iLoop;
   int iPosition;
   int iI;
   bool bNotFound = true;
   for (iPosition = 0; iPosition < GC.getDefineINT("MAX_NUM_CALIFORNICATION"); iPosition++)
   {
       if (iNewValue > 0)
       {
// If building is a counter culture wonder, store the associated information in the Game and loop all cities to tag current target cities
           if (GC.getBuildingInfo(eIndex).getTargetBuildingClassType() != NO_BUILDINGCLASS && GC.getBuildingInfo(eIndex).getCounterCulture() != 0)
           {
               if ((GC.getGameINLINE().getCounterCulture(iPosition) == 0) && (GC.getGameINLINE().getCounterCulturePlayer(iPosition) == NO_PLAYER) && !(GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isObsoleteBuilding(eIndex)) && bNotFound)
               {
                   GC.getGameINLINE().setCounterCulture(iPosition, GC.getBuildingInfo(eIndex).getCounterCulture());
                   GC.getGameINLINE().setCounterCulturePlayer(iPosition, getOwnerINLINE());
                   GC.getGameINLINE().setCounterCultureBuildingClass(iPosition, (BuildingClassTypes) GC.getBuildingInfo(eIndex).getTargetBuildingClassType());
                   GC.getGameINLINE().setCounterCultureBuildingMaster(iPosition, eIndex);
                   bNotFound = false;

                   for (iI = 0; iI < MAX_PLAYERS; iI++)
                   {
                       if (GET_PLAYER((PlayerTypes)iI).isAlive())
                       {
                           CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iI);
                           for (CvCity* pLoopCity = kLoopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kLoopPlayer.nextCity(&iLoop))
                           {
                               if (pLoopCity->isHasBuildingClass(GC.getBuildingInfo(eIndex).getTargetBuildingClassType()) && !(GC.getBuildingInfo(eIndex).getTargetBuildingClassType()==NO_BUILDINGCLASS))
                               {
                                   pLoopCity->changeCounterCultureTarget(1);
                               }
                           }
                       }
                   }
               }
           }
// If building is a target building for the wonder, identify the city as a target city
           for (iI = 0; iI < GC.getNumBuildingInfos(); ++iI)
           {
               if (GC.getBuildingInfo((BuildingTypes)iI).getTargetBuildingClassType() == GC.getBuildingInfo(eIndex).getBuildingClassType())
               {
                   changeCounterCultureTarget(1);
               }
           }
       }
       else if (iNewValue == 0)
       {
// If building is a counter culture wonder, clear the associated information in the Game and loop all cities to untag current target cities
           if ((PlayerTypes)GC.getGameINLINE().getCounterCulturePlayer(iPosition) == getOwnerINLINE() && GC.getGameINLINE().getCounterCultureBuildingClass(iPosition) == GC.getBuildingInfo(eIndex).getTargetBuildingClassType())
           {
               GC.getGameINLINE().setCounterCulture(iPosition, 0);
               GC.getGameINLINE().setCounterCulturePlayer(iPosition, NO_PLAYER);
               GC.getGameINLINE().setCounterCultureBuildingClass(iPosition, NO_BUILDINGCLASS);
               GC.getGameINLINE().setCounterCultureBuildingMaster(iPosition, NO_BUILDING);

               for (iI = 0; iI < MAX_PLAYERS; iI++)
               {
                   if (GET_PLAYER((PlayerTypes)iI).isAlive())
                   {
                       CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iI);
                       for (CvCity* pLoopCity = kLoopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kLoopPlayer.nextCity(&iLoop))
                       {
                           if (pLoopCity->isHasBuildingClass(GC.getBuildingInfo(eIndex).getTargetBuildingClassType()))
                           {
                               pLoopCity->changeCounterCultureTarget(-1);
                           }
                       }
                   }
               }
           }

// If building is a target building for the wonder, untag the city as a target city
           for (iI = 0; iI < GC.getNumBuildingInfos(); ++iI)
           {
               if (GC.getBuildingInfo((BuildingTypes)iI).getTargetBuildingClassType() == GC.getBuildingInfo(eIndex).getBuildingClassType())
               {
                   changeCounterCultureTarget(-1);
               }
           }
       }
   }
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
void CvCity::doCounterCulture(int iPosition, int iCounterCultureChange)
{
   CyCity* pyCity = new CyCity(this);
   CyArgsList argsList;
   argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity));   // pass in city class
   long lResult=0;
   gDLL->getPythonIFace()->callFunction(PYGameModule, "doCulture", argsList.makeFunctionArgs(), &lResult);
   delete pyCity;   // python fxn must not hold on to this pointer
   if (lResult == 1)
   {
       return;
   }

   changeCulture(GC.getGameINLINE().getCounterCulturePlayer(iPosition), iCounterCultureChange, false, true);
   changeCounterCultureTotal(iPosition, iCounterCultureChange);
}
void CvCity::doPlotCounterCulture(bool bUpdate, int iPosition, int iCultureRate)
{
   PlayerTypes ePlayer = GC.getGameINLINE().getCounterCulturePlayer(iPosition);

   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;
   }

   if (ePlayer == NO_PLAYER)
   {
       gDLL->getInterfaceIFace()->addMessage(GET_PLAYER(getOwnerINLINE()).getID(), true, GC.getEVENT_MESSAGE_TIME(), "Exiting doPlotCounterCulture as ePlayer == NO_PLAYER", "AS2D_CITYCAPTURE", MESSAGE_TYPE_MAJOR_EVENT, ARTFILEMGR.getInterfaceArtInfo("WORLDBUILDER_CITY_EDIT")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
       return;
   }

   if (getOwnerINLINE() == ePlayer)
   {
       return;
   }
   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);

                       if (pLoopPlot != NULL)
                       {
                           if (pLoopPlot->isPotentialCityWorkForArea(area()))
                           {
                               pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
                           }
                       }
                   }
               }
           }
       }
   }
}

int CvCity::getCounterCultureTotal(int iPosition) const
{
   return m_aiCounterCultureTotal[iPosition];
}
void CvCity::setCounterCultureTotal(int iPosition, int iValue)
{
   m_aiCounterCultureTotal[iPosition] = iValue;
}
void CvCity::changeCounterCultureTotal(int iPosition, int iChange)
{
   m_aiCounterCultureTotal[iPosition] += iChange;
}
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   pStream->Read(&m_iCounterCultureTarget);
   pStream->Read(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), m_aiCounterCultureTotal);
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   pStream->Write(m_iCounterCultureTarget);
   pStream->Write(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), m_aiCounterCultureTotal);
/* End LPlate - Californication */
CvGame.h
Spoiler :
Code:
/* LPlate - Californication */
   int getCounterCulture(int iPosition) const;                                                                                   // Exposed to Python
   void setCounterCulture(int iPosition, int iValue);                                                                                   // Exposed to Python
   void changeCounterCulture(int iPosition, int iChange);                                                                                   // Exposed to Python
   PlayerTypes getCounterCulturePlayer(int iPosition) const;                                                                                   // Exposed to Python
   void setCounterCulturePlayer(int iPosition, PlayerTypes eNewValue);                                                                                   // Exposed to Python
   BuildingClassTypes getCounterCultureBuildingClass(int iPosition) const;                                                                                   // Exposed to Python
   void setCounterCultureBuildingClass(int iPosition, BuildingClassTypes eNewValue);                                                                                   // Exposed to Python
   BuildingTypes getCounterCultureBuildingMaster(int iPosition) const;                                                                                   // Exposed to Python
   void setCounterCultureBuildingMaster(int iPosition, BuildingTypes eNewValue);                                                                                   // Exposed to Python
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   int* m_aiCounterCulture;
   PlayerTypes* m_aiCounterCulturePlayer;
   BuildingClassTypes* m_aiCounterCultureBuildingClass;
   BuildingTypes* m_aiCounterCultureBuildingMaster;
/* End LPlate - Californication */
CvGame.cpp
Spoiler :
Code:
CvGame::CvGame()
{

/* LPlate - Californication */
   m_aiCounterCulture = new int[GC.getDefineINT("MAX_NUM_CALIFORNICATION")];
   m_aiCounterCulturePlayer = new PlayerTypes[GC.getDefineINT("MAX_NUM_CALIFORNICATION")];
   m_aiCounterCultureBuildingClass = new BuildingClassTypes[GC.getDefineINT("MAX_NUM_CALIFORNICATION")];
   m_aiCounterCultureBuildingMaster = new BuildingTypes[GC.getDefineINT("MAX_NUM_CALIFORNICATION")];
/* End LPlate - Californication */[CODE][CODE]
CvGame::~CvGame()
{
   uninit();
/* LPlate - Californication */
   SAFE_DELETE_ARRAY(m_aiCounterCulture);
   SAFE_DELETE_ARRAY(m_aiCounterCulturePlayer);
   SAFE_DELETE_ARRAY(m_aiCounterCultureBuildingClass);
   SAFE_DELETE_ARRAY(m_aiCounterCultureBuildingMaster);
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   for (iI = 0; iI < GC.getDefineINT("MAX_NUM_CALIFORNICATION"); iI++)
   {
       m_aiCounterCulture[iI] = 0;
       m_aiCounterCulturePlayer[iI] = NO_PLAYER;
       m_aiCounterCultureBuildingClass[iI] = NO_BUILDINGCLASS;
       m_aiCounterCultureBuildingMaster[iI] = NO_BUILDING;
   }
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
int CvGame::getCounterCulture(int iPosition) const
{
   return m_aiCounterCulture[iPosition];
}
void CvGame::setCounterCulture(int iPosition, int iValue)
{
   m_aiCounterCulture[iPosition] = iValue;
}
void CvGame::changeCounterCulture(int iPosition, int iChange)
{
   m_aiCounterCulture[iPosition] += iChange;
}
PlayerTypes CvGame::getCounterCulturePlayer(int iPosition) const
{
   return m_aiCounterCulturePlayer[iPosition];
}
void CvGame::setCounterCulturePlayer(int iPosition, PlayerTypes ePlayer)
{
   m_aiCounterCulturePlayer[iPosition] = ePlayer;
}
BuildingClassTypes CvGame::getCounterCultureBuildingClass(int iPosition) const
{
   return m_aiCounterCultureBuildingClass[iPosition];
}
void CvGame::setCounterCultureBuildingClass(int iPosition, BuildingClassTypes eNewValue)
{
   m_aiCounterCultureBuildingClass[iPosition] = eNewValue;
}
BuildingTypes CvGame::getCounterCultureBuildingMaster(int iPosition) const
{
   return m_aiCounterCultureBuildingMaster[iPosition];
}
void CvGame::setCounterCultureBuildingMaster(int iPosition, BuildingTypes eNewValue)
{
   m_aiCounterCultureBuildingMaster[iPosition] = eNewValue;
}
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   pStream->Read(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), m_aiCounterCulture);
   pStream->Read(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), (int*)m_aiCounterCulturePlayer);
   pStream->Read(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), (int*)m_aiCounterCultureBuildingClass);
   pStream->Read(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), (int*)m_aiCounterCultureBuildingMaster);
/* End LPlate - Californication */
Code:
/* LPlate - Californication */
   pStream->Write(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), m_aiCounterCulture);
   pStream->Write(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), (int*)m_aiCounterCulturePlayer);
   pStream->Write(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), (int*)m_aiCounterCultureBuildingClass);
   pStream->Write(GC.getDefineINT("MAX_NUM_CALIFORNICATION"), (int*)m_aiCounterCultureBuildingMaster);
/* End LPlate - Californication */
 
Last edited:
Giving Boats to Land Units
This was inspired by CIV V and the code is totally based on @davidlallen's Dune code for sandriders. When a land based unit moves onto a water tile (presumably having some promo with bWaterWalking set to 1), its graphic is converted to a boat. You can set up different boats for different civs in the CIV4UnitArtStyleTypeInfos.xml by changing the LateArtDefineTag entries.
There's two sets of edits, both in CvUnit.cpp.

In void CvUnit::move(CvPlot* pPlot, bool bShow)
Spoiler :
Code:
/*// LPlate - adapting code from Dune to represent land units as boats
   // davidlallen: reuse late art tag for sandrider start

   TerrainTypes eNewTerrain = pPlot->getTerrainType();
   TerrainTypes eOldTerrain = pOldPlot->getTerrainType();
   if ((NO_TERRAIN != eNewTerrain) && (NO_TERRAIN != eOldTerrain))
       {
       if (GC.getTerrainInfo(eNewTerrain).isWormFriendly() != GC.getTerrainInfo(eOldTerrain).isWormFriendly())
           if (m_pUnitInfo->getLateArtDefineTag(0, (UnitArtStyleTypes) 0))
               if (isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_SANDRIDER")))
                   reloadEntity();
       }
   // davidlallen: reuse late art tag for sandrider end
// End LPlate - adapting code from Dune to represent land units as boats*/

// LPlate - Boats for Land Units
   TerrainTypes eNewTerrain = pPlot->getTerrainType();
   TerrainTypes eOldTerrain = pOldPlot->getTerrainType();
   if ((NO_TERRAIN != eNewTerrain) && (NO_TERRAIN != eOldTerrain))
   {
       if (pPlot->isWater() != pOldPlot->isWater())
       {
           if (m_pUnitInfo->getLateArtDefineTag(0, (UnitArtStyleTypes) 0))
           {
               reloadEntity();
           }
       }
   }
// End LPlate - Boats for Land Units

In const CvArtInfoUnit* CvUnit::getArtInfo(int i, EraTypes eEra) const
Spoiler :
Code:
// LPlate - Boats for Land Units based on davidlallen's Dune code, Original Dune Code
/*   // davidlallen: reuse late art tag for sandrider start
   // Yes, it is bad form to hardcode names into the sdk
   TerrainTypes eTerrain = plot()->getTerrainType();
   if (NO_TERRAIN != eTerrain)
       {
       if (GC.getTerrainInfo(eTerrain).isWormFriendly())
           if (isHasPromotion((PromotionTypes)GC.getInfoTypeForString("PROMOTION_SANDRIDER")))
               eEra = (EraTypes) (GC.getNumEraInfos() + 1);
       }
   // davidlallen: reuse late art tag for sandrider end*/
// End LPlate - Boats for Land Units based on davidlallen's Dune code, Original Dune Code

// LPlate - Boats for Land Units
   TerrainTypes eTerrain = plot()->getTerrainType();
   if (NO_TERRAIN != eTerrain)
   {
       if (plot()->isWater() && getDomainType() == DOMAIN_LAND)
       {
           eEra = (EraTypes) (GC.getNumEraInfos() + 1);
       }
   }
// End LPlate - Boats for Land Units
 
Giving Boats to Land Units
Hey man, just wanted to tell you that your code inspired me to create a similar concept of my own. :)

Since Civ4Col also knows the concept of "Professions" (additionally to the concept of Units), we will probably change Professions.
Changing Professions
in Civ4Col more or less automatically changes the graphics as well.
(Simple XML config of "UnitArtStyles" for Professions. - So that part of your code is not needed for our concept.)

So in my WTP concept it will change UnitArtStyle, Movement Rate, Combat Modifier, AI behaviour, ...
(All of that can be balanced / defined for each Profession specifically in Civ4Col.)

So thanks for the inspiration. :hug:
And definitely credits to your work. :thumbsup:
 
Last Ditch Defenders
This is code to create a temporary unit when the last unit defending a city (who isn't a last ditch defender) is killed. I planning to use it for civs that are adopting a more warlike culture in a fantasy setting, where essentially everyone is ready to wade into a fight. I've linked it to a tech, so it could be applied when a civ gets Conscription, to reflect the fact that there are militarily trained armed civilians in the city.
This differs from using python to spring up partisans around a city as it avoids losing the city immediately, allowing the defender a turn (or more if the attacker is down to one weakened unit) to reinforce.

The edits are surrounded bY "LPlate - War Spirit"
The main functional code goes in CvUnit.cpp in the void CvUnit::updateCombat(bool bQuick) function,
Spoiler :
define a few parameters as soon as pDefender is defined
Code:
// LPlate - War Spirit
   TeamTypes eDefenderTeam = pDefender->getTeam();
   PlayerTypes eDefenderPlayer = pDefender->getOwner();
   UnitTypes eDefenderType = pDefender->getUnitType();
// End LPlate - War Spirit
Functionality goes near the end of updateCombat
Code:
// LPlate - War Spirit
           bool bClearPath = true;
           if (GET_TEAM(eDefenderTeam).getLastDitchDefender())
           {
               if (pPlot->isCity())
               {


                   for (int iI = 0; iI < GC.getNumTechInfos(); iI++)
                   {
                       if ((UnitClassTypes)GC.getTechInfo((TechTypes)iI).getLastDitchDefenderUnitClass() != NO_UNITCLASS)
                       {
                           gDLL->getInterfaceIFace()->addMessage(GC.getGameINLINE().getActivePlayer(), true, GC.getEVENT_MESSAGE_TIME(), "Found last ditch defender 1", "AS2D_CITYCAPTURE", MESSAGE_TYPE_MAJOR_EVENT, ARTFILEMGR.getInterfaceArtInfo("WORLDBUILDER_CITY_EDIT")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), 1, 1, true, true);

                           if(!(GET_PLAYER(eDefenderPlayer).getTechLastDitchDefender((TechTypes)iI) == eDefenderType))
                           {
                               bClearPath = false;
                               gDLL->getInterfaceIFace()->addMessage(GC.getGameINLINE().getActivePlayer(), true, GC.getEVENT_MESSAGE_TIME(), "Not a clear path 1", "AS2D_CITYCAPTURE", MESSAGE_TYPE_MAJOR_EVENT, ARTFILEMGR.getInterfaceArtInfo("WORLDBUILDER_CITY_EDIT")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), 1, 1, true, true);
                               CvUnit* LastDitchUnit = GET_PLAYER(eDefenderPlayer).initUnit(GET_PLAYER(eDefenderPlayer).getTechLastDitchDefender((TechTypes)iI), pPlot->getX_INLINE(), pPlot->getY_INLINE(), UNITAI_CITY_DEFENSE);
                               LastDitchUnit->setDuration(3);
                           }
                       }
                   }
               }
           }
           if (bClearPath)
           {
               if (pPlot->getNumVisibleEnemyDefenders(this) == 0)
               {
                   getGroup()->groupMove(pPlot, true, ((bAdvance) ? this : NULL));
               }
           }
// End LPlate - War Spirit
// LPlate - Unmodified FFH Code - War Spirit
/*           if (pPlot->getNumVisibleEnemyDefenders(this) == 0)
           {
               getGroup()->groupMove(pPlot, true, ((bAdvance) ? this : NULL));
           }*/
// End LPlate - Unmodified FFH Code - War Spirit

           // This is is put before the plot advancement, the unit will always try to walk back
           // to the square that they came from, before advancing.
           getGroup()->clearMissionQueue();

The initial xml setup was adding in <LastDitchDefenderUnitClass> into CIV4TechnologiesSchema.xml and CIV4TechInfos.xml.
Spoiler :
CIV4TechnologiesSchema.xml
Code:
    <ElementType name="LastDitchDefenderUnitClass" content="textOnly"/>   <!-- LPlate - War Spirit -->
Code:
       <element type="LastDitchDefenderUnitClass" minOccurs="0"/>   <!-- LPlate - War Spirit -->
CvTechInfos.xml
Code:
           <LastDitchDefenderUnitClass>UNITCLASS_WAR_SPIRIT_DEFENDER</LastDitchDefenderUnitClass>
Text.xml
Code:
    <TEXT>
       <Tag>TXT_KEY_TECH_LAST_DITCH_DEFENCE</Tag>
       <English>[ICON_BULLET]Spawns a temporary [COLOR_UNIT_TEXT][LINK=literal]%s1_UnitName[\LINK][COLOR_REVERT] in cities when last defender is killed</English>
   </TEXT>

This is a similar entry to <FirstFreeUnitClass>, so I just copied and pasted the entries based on this for the DLL.
Spoiler :

CvInfos.h
Code:
// LPlate - War Spirit
   int getLastDitchDefenderUnitClass() const;       // Exposed to Python
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
   int m_iLastDitchDefenderUnitClass;
// End LPlate - War Spirit
CvInfos.cpp
Code:
// LPlate - War Spirit
m_iLastDitchDefenderUnitClass(NO_UNITCLASS),
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
int CvTechInfo::getLastDitchDefenderUnitClass() const
{
   return m_iLastDitchDefenderUnitClass;
}
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
   stream->Read(&m_iLastDitchDefenderUnitClass);
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
   stream->Write(m_iLastDitchDefenderUnitClass);
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
   pXML->GetChildXmlValByName(szTextVal, "LastDitchDefenderUnitClass");
   m_iLastDitchDefenderUnitClass = pXML->FindInInfoClass(szTextVal);
// End LPlate - War Spirit
CyInfoInterface1.cpp
Code:
// LPlate - War Spirit
       .def("getLastDitchDefenderUnitClass", &CvTechInfo::getLastDitchDefenderUnitClass, "int ()")
// End LPlate - War Spirit
CvGameTextMgr.h
Code:
// LPlate - War Spirit
   void buildLastDitchDefenderString( CvWStringBuffer& szBuffer, TechTypes eTech, bool bList = false, bool bPlayerContext = false );
// End LPlate - War Spirit
CvGameTextMgr.cpp
Code:
// LPlate - War Spirit
   //   Spawns a last ditch defender...
   buildLastDitchDefenderString(szBuffer, eTech, true, bPlayerContext);
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
   //   Spawns a last ditch defender...
void CvGameTextMgr::buildLastDitchDefenderString(CvWStringBuffer &szBuffer, TechTypes eTech, bool bList, bool bPlayerContext)
{
   UnitTypes eLastDitchDefender = NO_UNIT;
   if (GC.getGameINLINE().getActivePlayer() != NO_PLAYER)
   {
       eLastDitchDefender = GET_PLAYER(GC.getGameINLINE().getActivePlayer()).getTechLastDitchDefender(eTech);
   }
   else
   {
       if (GC.getTechInfo(eTech).getLastDitchDefenderUnitClass() != NO_UNITCLASS)
       {
           eLastDitchDefender = (UnitTypes)GC.getUnitClassInfo((UnitClassTypes)GC.getTechInfo(eTech).getLastDitchDefenderUnitClass()).getDefaultUnitIndex();
       }
   }

   if (eLastDitchDefender != NO_UNIT)
   {
       if (!bPlayerContext || (GC.getGameINLINE().countKnownTechNumTeams(eTech) == 0))
       {
           if (bList)
           {
               szBuffer.append(NEWLINE);
           }
           szBuffer.append(gDLL->getText("TXT_KEY_TECH_LAST_DITCH_DEFENCE", GC.getUnitInfo(eLastDitchDefender).getTextKeyWide()));
       }
   }
}
// End LPlate - War Spirit

CvPlayer.h
Code:
// LPlate - War Spirit
   UnitTypes getTechLastDitchDefender(TechTypes eTech) const;
// End LPlate - War Spirit
CvPlayer.cpp
Code:
// LPlate - War Spirit
UnitTypes CvPlayer::getTechLastDitchDefender(TechTypes eTech) const
{
   UnitClassTypes eUnitClass = (UnitClassTypes) GC.getTechInfo(eTech).getLastDitchDefenderUnitClass();
   if (eUnitClass == NO_UNITCLASS)
   {
       return NO_UNIT;
   }

   UnitTypes eUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(eUnitClass)));
   if (eUnit == NO_UNIT)
   {
       return NO_UNIT;
   }

   return eUnit;
}
// End LPlate - War Spirit

CvTeam.h
Code:
// LPlate - War Spirit
   int getLastDitchDefender() const;
   void changeLastDitchDefender(int iChange);
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
   int m_iLastDitchDefender;
// End LPlate - War Spirit
CvTeam.cpp
Code:
// LPlate - War Spirit
   m_iLastDitchDefender = 0;
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
int CvTeam::getLastDitchDefender() const
{
   return m_iLastDitchDefender;
}


void CvTeam::changeLastDitchDefender(int iChange)
{
   m_iLastDitchDefender += iChange;
   FAssert(getLastDitchDefender() >= 0);
}
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
           if (GC.getTechInfo(eIndex).getLastDitchDefenderUnitClass() != NO_UNITCLASS)
           {
               if (bNewValue)
               {
                   changeLastDitchDefender(1);
               }
               else
               {
                   changeLastDitchDefender(-1);
               }
           }
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
   pStream->Read(&m_iLastDitchDefender);
// End LPlate - War Spirit
Code:
// LPlate - War Spirit
   pStream->Write(m_iLastDitchDefender);
// End LPlate - War Spirit
 
Back
Top Bottom