Subversion Repositories RAND

Rev

Rev 992 | Rev 1033 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

// plot.cpp

#include "CvGameCoreDLL.h"
#include "CvPlot.h"
#include "CvCity.h"
#include "CvUnit.h"
#include "CvSelectionGroup.h"
#include "CvGlobals.h"
#include "CvArea.h"
#include "CvGameAI.h"
#include "CvDLLInterfaceIFaceBase.h"
#include "CvDLLSymbolIFaceBase.h"
#include "CvDLLEntityIFaceBase.h"
#include "CvDLLPlotBuilderIFaceBase.h"
#include "CvDLLEngineIFaceBase.h"
#include "CvDLLFlagEntityIFaceBase.h"
#include "CvMapInterfaceBase.h"
#include "CvViewport.h"
#include "CvPlayerAI.h"
#include "CvTeamAI.h"
#include "CvGameCoreUtils.h"
#include "CvRandom.h"
#include "CvDLLFAStarIFaceBase.h"
#include "CvInfos.h"
#include "FProfiler.h"
#include "CvArtFileMgr.h"
#include "CyArgsList.h"
#include "CvDLLPythonIFaceBase.h"
#include "CvEventReporter.h"
#include "CvInitCore.h"
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      11/30/08                                jdog5000      */
/*                                                                                              */
/* General AI                                                                                   */
/************************************************************************************************/
#include "FAStarNode.h"
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                       END                                                  */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess                       Start          04/14/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
#include "CyPlot.h"
#include <time.h>
#include <psapi.h>
#include "FDataStreamBuffer.h"
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/


#define STANDARD_MINIMAP_ALPHA          (0.6f)

//      Pseudo-value to indicate uninitialised in found values
#define INVALID_FOUND_VALUE     (0xFFFF)

typedef struct
{
        IDInfo  id;
        int             health;
} bestUnitInfo;

typedef struct
{
        int iValue;
        int iHealth;
} unitDefenderInfo;

static int      g_plotTypeZobristHashes[NUM_PLOT_TYPES];
static bool g_plotTypeZobristHashesSet = false;
int CvPlot::m_iGlobalCachePathEpoch = 0;
stdext::hash_map<int,int>* CvPlot::m_resultHashMap = NULL;
static const CvPlot* g_bestDefenderCachePlot = NULL;
static std::map<int,unitDefenderInfo>*  g_bestDefenderCache = NULL;
static CRITICAL_SECTION g_cBestDefenderCacheSection;

#ifdef SUPPORT_MULTITHREADED_PATHING
CRITICAL_SECTION CvPlot::m_resultHashAccessSection;
#endif
// Public Functions...

#pragma warning( disable : 4355 )
CvPlot::CvPlot() : m_GameObject(this),
m_Properties(this)
{
#ifdef SUPPORT_MULTITHREADED_PATHING
        InitializeCriticalSectionAndSpinCount(&m_resultHashAccessSection, 4000);
#endif
        if ( m_resultHashMap == NULL )
        {
                m_resultHashMap = new   stdext::hash_map<int,int>();
        }

        if ( g_bestDefenderCache == NULL )
        {
                InitializeCriticalSectionAndSpinCount(&g_cBestDefenderCacheSection,4000);
                g_bestDefenderCache = new std::map<int,unitDefenderInfo>();
        }

        m_aiYield = new short[NUM_YIELD_TYPES];

/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      08/21/09                                jdog5000      */
/*                                                                                              */
/* Efficiency                                                                                   */
/************************************************************************************************/
        // Plot danger cache
        m_abIsTeamBorderCache = new bool[MAX_TEAMS];
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                       END                                                  */
/************************************************************************************************/

        m_aiFoundValue = NULL;
        m_aiPlayerCityRadiusCount = NULL;
        m_aiPlotGroup = NULL;
        m_aiVisibilityCount = NULL;
        m_aiLastSeenTurn = NULL;
        m_aiDangerCount = NULL;
        m_aiStolenVisibilityCount = NULL;
        m_aiBlockadedCount = NULL;
        m_aiRevealedOwner = NULL;
        m_abRiverCrossing = NULL;
        m_abRevealed = NULL;
        m_aeRevealedImprovementType = NULL;
        m_aeRevealedRouteType = NULL;
        m_paiBuildProgress = NULL;
        m_apaiCultureRangeCities = NULL;
        m_apaiInvisibleVisibilityCount = NULL;
/************************************************************************************************/
/* Afforess                       Start          02/15/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        m_aiOccupationCultureRangeCities = NULL;
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/
        m_aiMountainLeaderCount = NULL; //      Koshling - mountain mod efficiency
        m_pFeatureSymbol = NULL;
        m_pPlotBuilder = NULL;
        m_pRouteSymbol = NULL;
        m_pRiverSymbol = NULL;
        m_pFlagSymbol = NULL;
        m_pFlagSymbolOffset = NULL;
        m_pCenterUnit = NULL;
        m_bInhibitCenterUnitCalculation = false;
        m_iGraphicsPageIndex = -1;

        m_szScriptData = NULL;
        //Afforess: use async rand so as to not pollute mp games
        m_zobristContribution = GC.getASyncRand().getInt();

        if ( !g_plotTypeZobristHashesSet )
        {
                for(int i = 0; i < NUM_PLOT_TYPES; i++)
                {
                        //Afforess: use async rand so as to not pollute mp games
                        g_plotTypeZobristHashes[i] = GC.getASyncRand().getInt();
                }

                g_plotTypeZobristHashesSet = true;
        }

        reset(0, 0, true);
}

void CvPlot::readZobristCache(CvTaggedSaveFormatWrapper *pStream)
{
        if (GC.getLoadedInitCore().getGameSaveSvnRev() >= 794)
        {
                static int _idHint, _saveSeq = -1;

                pStream->Read("CvPlot::g_plotTypeZobristHashes", _idHint, _saveSeq, NUM_PLOT_TYPES, g_plotTypeZobristHashes);
        }
}

void CvPlot::writeZobristCache(CvTaggedSaveFormatWrapper *pStream)
{
        static int _idHint, _saveSeq = -1;

        if (!g_plotTypeZobristHashesSet)
        {
                for (int i = 0; i < NUM_PLOT_TYPES; i++)
                {
                        //Afforess: use async rand so as to not pollute mp games
                        g_plotTypeZobristHashes[i] = GC.getASyncRand().getInt();
                }

                g_plotTypeZobristHashesSet = true;
        }

        pStream->Write("CvPlot::g_plotTypeZobristHashes", _idHint, _saveSeq, NUM_PLOT_TYPES, g_plotTypeZobristHashes);
}

CvPlot::~CvPlot()
{
        pageGraphicsOut();

        uninit();

        SAFE_DELETE_ARRAY(m_aiYield);

/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      08/21/09                                jdog5000      */
/*                                                                                              */
/* Efficiency                                                                                   */
/************************************************************************************************/
        // Plot danger cache
        SAFE_DELETE_ARRAY(m_abIsTeamBorderCache);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                       END                                                  */
/************************************************************************************************/
}

void CvPlot::init(int iX, int iY)
{
        //--------------------------------
        // Init saved data
        reset(iX, iY);

        //--------------------------------
        // Init non-saved data

        //--------------------------------
        // Init other game data
}


void CvPlot::uninit()
{
        SAFE_DELETE_ARRAY(m_szScriptData);

#ifdef MULTI_FEATURE_MOD
        for (int i=0; i<(int)m_aFeatures.size(); i++)
        {
                if (m_aFeatures[i].pSymbol)
                {
                        gDLL->getFeatureIFace()->destroy(m_aFeatures[i].pSymbol);
                }
        }
#else
        gDLL->getFeatureIFace()->destroy(m_pFeatureSymbol);
#endif
        if(m_pPlotBuilder)
        {
                gDLL->getPlotBuilderIFace()->destroy(m_pPlotBuilder);
        }
        gDLL->getRouteIFace()->destroy(m_pRouteSymbol);
        gDLL->getRiverIFace()->destroy(m_pRiverSymbol);
        gDLL->getFlagEntityIFace()->destroy(m_pFlagSymbol);
        gDLL->getFlagEntityIFace()->destroy(m_pFlagSymbolOffset);
        m_pCenterUnit = NULL;

        deleteAllSymbols();

        //      Don't use 'clear' - we want to release the memory so we need to
        //      ensure the capcity goes to 0
        std::vector<std::pair<PlayerTypes,int> >().swap(m_aiCulture);

        SAFE_DELETE_ARRAY(m_aiFoundValue);
        SAFE_DELETE_ARRAY(m_aiPlayerCityRadiusCount);
        SAFE_DELETE_ARRAY(m_aiPlotGroup);

        SAFE_DELETE_ARRAY(m_aiVisibilityCount);
        SAFE_DELETE_ARRAY(m_aiLastSeenTurn);
        SAFE_DELETE_ARRAY(m_aiDangerCount);
        SAFE_DELETE_ARRAY(m_aiStolenVisibilityCount);
        SAFE_DELETE_ARRAY(m_aiBlockadedCount);
        SAFE_DELETE_ARRAY(m_aiRevealedOwner);

        SAFE_DELETE_ARRAY(m_abRiverCrossing);
        SAFE_DELETE_ARRAY(m_abRevealed);

        SAFE_DELETE_ARRAY(m_aeRevealedImprovementType);
        SAFE_DELETE_ARRAY(m_aeRevealedRouteType);

        SAFE_DELETE_ARRAY(m_aiMountainLeaderCount);
/************************************************************************************************/
/* Afforess                       Start          02/15/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        SAFE_DELETE_ARRAY(m_aiOccupationCultureRangeCities);
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/

        SAFE_DELETE_ARRAY(m_paiBuildProgress);

        if (NULL != m_apaiCultureRangeCities)
        {
                for (int iI = 0; iI < MAX_PLAYERS; ++iI)
                {
                        SAFE_DELETE_ARRAY(m_apaiCultureRangeCities[iI]);
                }
                SAFE_DELETE_ARRAY(m_apaiCultureRangeCities);
        }

        if (NULL != m_apaiInvisibleVisibilityCount)
        {
                for (int iI = 0; iI < MAX_TEAMS; ++iI)
                {
                        SAFE_DELETE_ARRAY(m_apaiInvisibleVisibilityCount[iI]);
                }
                SAFE_DELETE_ARRAY(m_apaiInvisibleVisibilityCount);
        }

        m_units.clear();
}

// FUNCTION: reset()
// Initializes data members that are serialized.
void CvPlot::reset(int iX, int iY, bool bConstructorCall)
{
        int iI;

        //--------------------------------
        // Uninit class
        uninit();

/*********************************/
/***** Parallel Maps - Begin *****/
/*********************************/
        m_bNull = false;
/*******************************/
/***** Parallel Maps - End *****/
/*******************************/      

/************************************************************************************************/
/* DCM                    Start          05/31/10                                Johnny Smith       */
/*                                                                           Afforess           */
/* Battle Effects                                                                               */
/************************************************************************************************/
        // Dale - BE: Battle Effect START
        m_iBattleCountdown = 0;
        //Not saved intentionally
        m_eBattleEffect = NO_EFFECT;
        // Dale - BE: Battle Effect END
/************************************************************************************************/
/* DCM                                     END                                                  */
/************************************************************************************************/
        m_iX = iX;
        m_iY = iY;
        m_iArea = FFreeList::INVALID_INDEX;
        m_pPlotArea = NULL;
        m_iFeatureVariety = 0;
        m_iOwnershipDuration = 0;
        m_iImprovementDuration = 0;
        m_iUpgradeProgress = 0;
        m_iForceUnownedTimer = 0;
        m_iCityRadiusCount = 0;
        m_iRiverID = -1;
        m_iMinOriginalStartDist = -1;
        m_iReconCount = 0;
        m_iRiverCrossingCount = 0;

        m_bStartingPlot = false;
        m_bHills = false;
/************************************************************************************************/
/* Afforess                       Start          02/15/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        m_bDepletedMine = false;
        m_eClaimingOwner = NO_PLAYER;
        m_bCounted = false;
        m_eLandmarkType = NO_LANDMARK;
        m_szLandmarkMessage = "";
        m_szLandmarkName = "";
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/
        m_bNOfRiver = false;
        m_bWOfRiver = false;
        m_bIrrigated = false;
        m_bPotentialCityWork = false;
        m_bShowCitySymbols = false;
        m_bFlagDirty = false;
        m_bPlotLayoutDirty = false;
        m_bLayoutStateWorked = false;

        m_movementCharacteristicsHash = 0;
       
        m_eOwner = NO_PLAYER;
        m_ePlotType = PLOT_OCEAN;
        m_eTerrainType = NO_TERRAIN;
        m_eFeatureType = NO_FEATURE;
        m_eBonusType = NO_BONUS;
        m_eImprovementType = NO_IMPROVEMENT;
        m_eRouteType = NO_ROUTE;
        m_eRiverNSDirection = NO_CARDINALDIRECTION;
        m_eRiverWEDirection = NO_CARDINALDIRECTION;

#ifdef MULTI_FEATURE_MOD
        m_aFeatures.clear();
#endif

        m_plotCity.reset();
        m_workingCity.reset();
        m_workingCityOverride.reset();

        for (iI = 0; iI < NUM_YIELD_TYPES; ++iI)
        {
                m_aiYield[iI] = 0;
        }

/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      08/21/09                                jdog5000      */
/*                                                                                              */
/* Efficiency                                                                                   */
/************************************************************************************************/
        // Plot danger cache
        m_bIsActivePlayerNoDangerCache = false;
        m_bIsActivePlayerHasDangerCache = false;

        for (iI = 0; iI < MAX_TEAMS; iI++)
        {
                m_abIsTeamBorderCache[iI] = false;
        }
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                       END                                                  */
/************************************************************************************************/

        m_pathGenerationSeq = -1;

        m_Properties.clear();

        m_bPlotGroupsDirty = false;
}

typedef struct
{
        CvPlot* pPlot;
        int             iSeq;
} GraphicsPagingInfo;

static int iNumPagedInPlots = 0;
static int iPageTableSize = 0;
static int iRenderStartSeq = 0;
static int iCurrentSeq = 0;
static int iOldestSearchSeqHint = 0;
static GraphicsPagingInfo* pagingTable = NULL;

#define MAX_VALID_PAGING_INDEX 65534

int findFreePagingTableSlot()
{
        static int iSearchStartHintIndex = 0;

        for(int iI = 0; iI < iPageTableSize; iI++)
        {
                int iIndex = iSearchStartHintIndex++;

                if ( iSearchStartHintIndex >= iPageTableSize )
                {
                        iSearchStartHintIndex = 0;
                }

                if ( pagingTable[iIndex].pPlot == NULL )
                {
                        return iIndex;
                }
        }

        return -1;
}

int allocateNewPagingEntry()
{
        if ( iPageTableSize <= iNumPagedInPlots++ )
        {
                int iNewSize = std::max(64, iPageTableSize + std::min(iPageTableSize,512));

                GraphicsPagingInfo* newTable = new GraphicsPagingInfo[iNewSize];

                if ( iPageTableSize > 0 )
                {
                        memcpy(newTable, pagingTable, iPageTableSize*sizeof(GraphicsPagingInfo));
                        SAFE_DELETE_ARRAY(pagingTable);
                }
               
                pagingTable = newTable;
                iPageTableSize = iNewSize;
        }

        return findFreePagingTableSlot();
}

int findOldestEvictablePagingEntry()
{
        int iOldest = MAX_INT;
        int iResult = -1;

        for(int iI = 0; iI < iNumPagedInPlots; iI++)
        {
                if ( pagingTable[iI].pPlot != NULL && pagingTable[iI].iSeq < iRenderStartSeq )
                {
                        if ( pagingTable[iI].iSeq < iOldest )
                        {
                                iOldest = pagingTable[iI].iSeq;
                                iResult = iI;
                        }
                }
        }

        return iResult;
}

#define DEFAULT_MAX_WORKING_SET_THRESHOLD_BEFORE_EVICTION ((size_t)1024*(size_t)1024*(size_t)1024*(size_t)2)
#define DEFAULT_OS_MEMORY_ALLOWANCE ((size_t)1024*(size_t)1024*(size_t)512)

bool NeedToFreeMemory()
{
        PROCESS_MEMORY_COUNTERS pmc;
        static unsigned int uiMaxMem = 0xFFFFFFFF;

        if ( uiMaxMem == 0xFFFFFFFF )
        {
                MEMORYSTATUSEX memoryStatus;

                memoryStatus.dwLength = sizeof(memoryStatus);
                GlobalMemoryStatusEx(&memoryStatus);

                uiMaxMem = 1024*GC.getDefineINT("MAX_DESIRED_MEMORY_USED", DEFAULT_MAX_WORKING_SET_THRESHOLD_BEFORE_EVICTION/1024);

                DWORDLONG usableMemory = memoryStatus.ullTotalPhys - 1024*(DWORDLONG)GC.getDefineINT("OS_MEMORY_ALLOWANCE", DEFAULT_OS_MEMORY_ALLOWANCE/1024);
                if ( usableMemory < uiMaxMem )
                {
                        uiMaxMem = (unsigned int)usableMemory;
                }
        }

        GetProcessMemoryInfo( GetCurrentProcess(), &pmc, sizeof(pmc));
       
        if ( pmc.WorkingSetSize > uiMaxMem )
        {
                OutputDebugString(CvString::format("Found need to free memory: %d used vs %d target\n", pmc.WorkingSetSize, uiMaxMem).c_str());
                return true;
        }
        else
        {
                return false;
        }
}

static bool bFoundEvictable = true;

void CvPlot::EvictGraphicsIfNecessary()
{
        while(bFoundEvictable && NeedToFreeMemory())
        {
                int iEvictionIndex = findOldestEvictablePagingEntry();

                if ( iEvictionIndex == -1 )
                {
                        bFoundEvictable = false;
                        break;
                }

                pagingTable[iEvictionIndex].pPlot->setShouldHaveFullGraphics(false);
        }
}

void CvPlot::pageGraphicsOut()
{
        bFoundEvictable = true;

        if ( m_iGraphicsPageIndex != -1 )
        {
                pagingTable[m_iGraphicsPageIndex].pPlot = NULL;

                if ( --iNumPagedInPlots == 0 )
                {
                        SAFE_DELETE_ARRAY(pagingTable);

                        iPageTableSize = 0;
                }
        }

        m_iGraphicsPageIndex = -1;
}

void CvPlot::notePageRenderStart(int iRenderArea)
{
        iRenderStartSeq = std::max(0,iCurrentSeq-iRenderArea);
}

void    CvPlot::setShouldHaveFullGraphics(bool bShouldHaveFullGraphics)
{
        bool bChanged;

        if ( !GC.getGraphicalDetailPagingEnabled() )
        {
                bChanged = true;

                m_iGraphicsPageIndex = -1;

                if ( pagingTable != NULL )
                {
                        SAFE_DELETE_ARRAY(pagingTable);

                        iNumPagedInPlots = 0;
                        iPageTableSize = 0;
                }
        }
        else
        {
                //      A set to false is only ever used to switch out paged in graphics
                //      or when truning the entire mechanism on from it previously being off.
                //      In both of these cases we want to treat it as a change
                bChanged = (!bShouldHaveFullGraphics || ((m_iGraphicsPageIndex != -1) != bShouldHaveFullGraphics));

                if ( bShouldHaveFullGraphics )
                {
                        if ( m_iGraphicsPageIndex == -1 )
                        {
                                m_iGraphicsPageIndex = allocateNewPagingEntry();
                        }

                        GraphicsPagingInfo* pPagingInfo = &pagingTable[m_iGraphicsPageIndex];

                        pPagingInfo->iSeq = ++iCurrentSeq;
                        pPagingInfo->pPlot = this;

                        if ( iCurrentSeq == MAX_INT )
                        {
                                iCurrentSeq = 0;
                        }

                        EvictGraphicsIfNecessary();
                }
                else
                {
                        pageGraphicsOut();
                }
        }

        if ( bChanged )
        {
                setLayoutDirty(true);
                if ( getPlotCity() != NULL )
                {
                        getPlotCity()->setLayoutDirty(true);
                }

                if ( bShouldHaveFullGraphics )
                {
                        //gDLL->getEngineIFace()->RebuildPlot(getViewportX(), getViewportY(),false,true);
                        updateSymbols();
                        updateFeatureSymbol();
                        updateRiverSymbol();
                        updateRouteSymbol();
                }
                else
                {
                        destroyGraphics();
                }

                updateCenterUnit();
        }
}

bool    CvPlot::shouldHaveFullGraphics(void) const
{
        return (!GC.getGraphicalDetailPagingEnabled() || m_iGraphicsPageIndex != -1) && shouldHaveGraphics();
}

bool    CvPlot::shouldHaveGraphics(void) const
{
        return GC.IsGraphicsInitialized() && isInViewport(); // && isRevealed(GC.getGame().getActiveTeam(), false);
}

//////////////////////////////////////
// graphical only setup
//////////////////////////////////////
void CvPlot::setupGraphical()
{
        //MEMORY_TRACE_FUNCTION();
        PROFILE_FUNC();

        if ( !shouldHaveGraphics() )
        {
                return;
        }

        //updateSymbols();
       
        updateFeatureSymbol();
        updateRiverSymbol();
        updateMinimapColor();

        updateVisibility();
}

void CvPlot::updateGraphicEra()
{
        PROFILE_FUNC();

        if(m_pRouteSymbol != NULL)
                gDLL->getRouteIFace()->updateGraphicEra(m_pRouteSymbol);

        if(m_pFlagSymbol != NULL)
                gDLL->getFlagEntityIFace()->updateGraphicEra(m_pFlagSymbol);
}

void CvPlot::erase()
{
        CLLNode<IDInfo>* pUnitNode;
        CvCity* pCity;
        CvUnit* pLoopUnit;
        CLinkList<IDInfo> oldUnits;

        // kill units
        oldUnits.clear();

        pUnitNode = headUnitNode();

        while (pUnitNode != NULL)
        {
                oldUnits.insertAtEnd(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);
        }

        pUnitNode = oldUnits.head();

        while (pUnitNode != NULL)
        {
                pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = oldUnits.next(pUnitNode);

                if (pLoopUnit != NULL)
                {
                        pLoopUnit->kill(false, NO_PLAYER, true);
                }
        }

        // kill cities
        pCity = getPlotCity();
        if (pCity != NULL)
        {
                pCity->kill(false);
        }

        setBonusType(NO_BONUS);
        setImprovementType(NO_IMPROVEMENT);
        setRouteType(NO_ROUTE, false);
#ifdef MULTI_FEATURE_MOD
        removeAllFeatures();
#else
        setFeatureType(NO_FEATURE);
#endif

        // disable rivers
        setNOfRiver(false, NO_CARDINALDIRECTION);
        setWOfRiver(false, NO_CARDINALDIRECTION);
        setRiverID(-1);
}

float CvPlot::getPointX() const
{
        CvViewport* pCurrentViewport = GC.getCurrentViewport();

        FAssert(pCurrentViewport != NULL);

        return pCurrentViewport->plotXToPointX(pCurrentViewport->getViewportXFromMapX(getX_INLINE()));
}


float CvPlot::getPointY() const
{
        CvViewport* pCurrentViewport = GC.getCurrentViewport();

        FAssert(pCurrentViewport != NULL);

        return pCurrentViewport->plotYToPointY(pCurrentViewport->getViewportYFromMapY(getY_INLINE()));
}


NiPoint3 CvPlot::getPoint() const
{
        NiPoint3 pt3Point;

        memset(&pt3Point, 0, sizeof(pt3Point));

        if (this == NULL)
        {
                FAssert(false);
        }
        else
        {
                pt3Point.x = getPointX();
                pt3Point.y = getPointY();
                pt3Point.z = 0.0f;

                pt3Point.z = gDLL->getEngineIFace()->GetHeightmapZ(pt3Point);
        }

        return pt3Point;
}


float CvPlot::getSymbolSize() const
{
        if (isVisibleWorked())
        {
                if (isShowCitySymbols())
                {
                        return 1.6f;
                }
                else
                {
                        return 1.2f;
                }
        }
        else
        {
                if (isShowCitySymbols())
                {
                        return 1.2f;
                }
                else
                {
                        return 0.8f;
                }
        }
}


float CvPlot::getSymbolOffsetX(int iOffset) const
{
        return ((40.0f + (((float)iOffset) * 28.0f * getSymbolSize())) - (GC.getPLOT_SIZE() / 2.0f));
}


float CvPlot::getSymbolOffsetY(int iOffset) const
{
        return (-(GC.getPLOT_SIZE() / 2.0f) + 50.0f);
}


TeamTypes CvPlot::getTeam() const
{
        if (isOwned())
        {
                return GET_PLAYER(getOwnerINLINE()).getTeam();
        }
        else
        {
                return NO_TEAM;
        }
}


void CvPlot::doTurn()
{
        PROFILE_FUNC();

        if (getForceUnownedTimer() > 0)
        {
                changeForceUnownedTimer(-1);
        }

        if (isOwned())
        {
                changeOwnershipDuration(1);
        }

        if (getImprovementType() != NO_IMPROVEMENT)
        {
                changeImprovementDuration(1);
        }

        doFeature();

        doCulture();

        verifyUnitValidPlot();
/************************************************************************************************/
/* Afforess                       Start          02/15/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        doTerritoryClaiming();
       
        doResourceDepletion();
       
        if (getOwnerINLINE() != NO_PLAYER)
        {
                if (!GET_PLAYER(getOwnerINLINE()).isAlive())
                {
                        setOwner(NO_PLAYER, false, false);
                }
        }
       
/*      doImprovementSpawn();
       
void CvPlot::doImprovementSpawn()
{
        if (getImprovementType() != NO_IMPROVEMENT)
        {
                if (getOwnerINLINE() == NO_PLAYER)
                {
                        changeImprovementDecay(-1);
                        if (getImprovementDecay() == 0)
                        {
                                if (GC.getImprovementInfo(getImprovementType()).getImprovementPillage() != NO_IMPROVEMENT)
                                {
                                        int iTimer = 10;
                                        changeImprovementDecay(iTimer);
                                        setImprovementType((ImprovementTypes)GC.getImprovementInfo(getImprovementType()).getImprovementPillage());
                                }
                                else if (getImprovementType() != (ImprovementTypes)(GC.getDefineINT("RUINS_IMPROVEMENT")))
                                {
                                        int iTimer = 10;
                                        changeImprovementDecay(iTimer);
                                        setImprovementType((ImprovementTypes)(GC.getDefineINT("RUINS_IMPROVEMENT")));
                                }
                                else
                                {
                                        setImprovementType(NO_IMPROVEMENT);
                                }
                        }
                        return;
                }
        }
        if (getOwnerINLINE() != NO_PLAYER)
        {
                int iBestValue = 0;
                ImprovementTypes eBestImprovement = NO_IMPROVEMENT;
                for (int iI = 0; iI < GC.getNumImprovementInfos(); iI++)
                {
                        int iValue = GET_PLAYER(getOwnerINLINE()).getImprovementValue();
                        if (iValue > iBestValue)
                        {
                                iBestValue = iValue;
                                eBestImprovement = (ImprovementTypes)iI;
                        }
                }
                if (eBestImprovement != NO_IMPROVEMENT)
                {
                       
                               
/************************************************************************************************/

/* Afforess                          END                                                            */
/************************************************************************************************/
       
        /*
        if (!isOwned())
        {
                doImprovementUpgrade();
        }
        */


        // XXX
#ifdef _DEBUG
        {
                CLLNode<IDInfo>* pUnitNode;
                CvUnit* pLoopUnit;

                pUnitNode = headUnitNode();

                while (pUnitNode != NULL)
                {
                        pLoopUnit = ::getUnit(pUnitNode->m_data);
                        pUnitNode = nextUnitNode(pUnitNode);

                        FAssertMsg(pLoopUnit->atPlot(this), "pLoopUnit is expected to be at the current plot instance");
                }
        }
#endif
        // XXX
}


void CvPlot::doImprovement()
{
        PROFILE_FUNC();

        CvCity* pCity;
        CvWString szBuffer;
        int iI;

        FAssert(isBeingWorked() && isOwned());

        if (getImprovementType() != NO_IMPROVEMENT)
        {
                if (getBonusType() == NO_BONUS)
                {
                        FAssertMsg((0 < GC.getNumBonusInfos()), "GC.getNumBonusInfos() is not greater than zero but an array is being allocated in CvPlot::doImprovement");
                        for (iI = 0; iI < GC.getNumBonusInfos(); ++iI)
                        {
                                if (GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getBonusInfo((BonusTypes) iI).getTechReveal())))
                                {
/************************************************************************************************/
/* UNOFFICIAL_PATCH                       03/04/10                                jdog5000      */
/*                                                                                              */
/* Gamespeed scaling                                                                            */
/************************************************************************************************/
/* original bts code
                                        if (GC.getImprovementInfo(getImprovementType()).getImprovementBonusDiscoverRand(iI) > 0)
                                        {
                                                if (GC.getGameINLINE().getSorenRandNum(GC.getImprovementInfo(getImprovementType()).getImprovementBonusDiscoverRand(iI), "Bonus Discovery") == 0)
                                                {
*/

                                        //Afforess: check for valid terrains for this bonus before discovering it
                                        if (!canHaveBonus((BonusTypes)iI))
                                        {
                                                continue;
                                        }
                                        int iOdds = GC.getImprovementInfo(getImprovementType()).getImprovementBonusDiscoverRand(iI);
                                       
                                        if( iOdds > 0 )
                                        {
                                                iOdds *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getVictoryDelayPercent();
                                                iOdds /= 100;

                                                if( GC.getGameINLINE().getSorenRandNum(iOdds, "Bonus Discovery") == 0)
                                                {
/************************************************************************************************/
/* UNOFFICIAL_PATCH                        END                                                  */
/************************************************************************************************/
                                                        setBonusType((BonusTypes)iI);

                                                        pCity = GC.getMapINLINE().findCity(getX_INLINE(), getY_INLINE(), getOwnerINLINE(), NO_TEAM, false);

                                                        if (pCity != NULL && isInViewport())
                                                        {
                                                                MEMORY_TRACK_EXEMPT();

                                                                szBuffer = gDLL->getText("TXT_KEY_MISC_DISCOVERED_NEW_RESOURCE", GC.getBonusInfo((BonusTypes) iI).getTextKeyWide(), pCity->getNameKey());
                                                                AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_DISCOVERBONUS", MESSAGE_TYPE_MINOR_EVENT, GC.getBonusInfo((BonusTypes) iI).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getViewportX(), getViewportY(), true, true);
                                                        }

                                                        break;
                                                }
                                        }
                                }
                        }
                }
/************************************************************************************************/
/* Afforess                       Start          01/20/10                                               */
/*                                                                                              */
/*   Mine Depletion                                                                             */
/************************************************************************************************/
                if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_RESOURCE_DEPLETION))
                {
                        bool bObstruction = ((getBonusType(getTeam()) != NO_BONUS) && (GC.getImprovementInfo(getImprovementType()).isImprovementBonusMakesValid(getBonusType(getTeam()))));

                        if (!bObstruction)
                        {
                                int iDepletionOdds = GC.getImprovementInfo(getImprovementType()).getDepletionRand() * GC.getImprovementInfo(getImprovementType()).getDepletionRand();
                                iDepletionOdds *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getResearchPercent();
                                iDepletionOdds /= 100;
                                ImprovementTypes eDepletedMine;
                               
                                if( iDepletionOdds > 0 && isBeingWorked())
                                {
                                        eDepletedMine = findDepletedMine();
                                        if (eDepletedMine != NO_IMPROVEMENT)
                                        {
                                                if( GC.getGameINLINE().getSorenRandNum(iDepletionOdds, "Mine Depletion") == 0)
                                                {
                                                        {
                                                                MEMORY_TRACK_EXEMPT();

                                                                szBuffer = gDLL->getText("TXT_KEY_MISC_IMPROVEMENT_DEPLETED", GC.getImprovementInfo(getImprovementType()).getDescription());
                                                                AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_FIRSTTOTECH", MESSAGE_TYPE_MINOR_EVENT, GC.getImprovementInfo(getImprovementType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
                                                        }
                                                        setImprovementType(eDepletedMine);
                                                        setIsDepletedMine(true);
                                                        pCity = GC.getMapINLINE().findCity(getX_INLINE(), getY_INLINE(), getOwnerINLINE(), NO_TEAM, false);
                                                        GC.getGameINLINE().logMsg("Mine Depleted!");
                                                        if (pCity != NULL)
                                                        {      
                                                                pCity->AI_setAssignWorkDirty(true);
                                                        }
                                                }
                                        }
                                }
                        }
                }
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/
        }

        doImprovementUpgrade();
}

void CvPlot::doImprovementUpgrade()
{
        if (getImprovementType() != NO_IMPROVEMENT)
        {
/************************************************************************************************/
/* Afforess                       Start          05/23/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
/*
                ImprovementTypes eImprovementUpdrade = (ImprovementTypes)GC.getImprovementInfo(getImprovementType()).getImprovementUpgrade();
*/

                ImprovementTypes eImprovementUpdrade = GET_TEAM(getTeam()).getImprovementUpgrade(getImprovementType());
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/
                if (eImprovementUpdrade != NO_IMPROVEMENT)
                {
                        if (isBeingWorked() || GC.getImprovementInfo(eImprovementUpdrade).isOutsideBorders())
                        {
                                changeUpgradeProgressHundredths(GET_PLAYER(getOwnerINLINE()).getImprovementUpgradeRateTimes100(getImprovementType()));

                                if (getUpgradeProgressHundredths() >= 100*GC.getGameINLINE().getImprovementUpgradeTime(getImprovementType()))
                                {
                                        setImprovementType(eImprovementUpdrade);
                                }
                        }
                }
        }
}

void CvPlot::updateCulture(bool bBumpUnits, bool bUpdatePlotGroups)
{
        if (!isCity() && (!isActsAsCity() || getUnitPower(getOwnerINLINE()) == 0))
        {
                setOwner(calculateCulturalOwner(), bBumpUnits, bUpdatePlotGroups);
        }
}


void CvPlot::updateFog()
{
        //MEMORY_TRACE_FUNCTION();
        PROFILE_FUNC();

        if ( !shouldHaveGraphics() )
        {
                return;
        }

        FAssert(GC.getGameINLINE().getActiveTeam() != NO_TEAM);

        if (isRevealed(GC.getGameINLINE().getActiveTeam(), false))
        {
                if (gDLL->getInterfaceIFace()->isBareMapMode())
                {
                        gDLL->getEngineIFace()->LightenVisibility(getFOWIndex());
                }
                else
                {
                        int cityScreenFogEnabled = GC.getDefineINT("CITY_SCREEN_FOG_ENABLED");
                        if (cityScreenFogEnabled && gDLL->getInterfaceIFace()->isCityScreenUp() && (gDLL->getInterfaceIFace()->getHeadSelectedCity() != getWorkingCity()))
                        {
                                gDLL->getEngineIFace()->DarkenVisibility(getFOWIndex());
                        }
                        else if (isActiveVisible(false))
                        {
                                gDLL->getEngineIFace()->LightenVisibility(getFOWIndex());

                        }
                        else
                        {
                                gDLL->getEngineIFace()->DarkenVisibility(getFOWIndex());
                        }
                }
        }
        else
        {
                gDLL->getEngineIFace()->BlackenVisibility(getFOWIndex());
        }
}


void CvPlot::updateVisibility()
{
        //MEMORY_TRACE_FUNCTION();
        PROFILE("CvPlot::updateVisibility");

        if ( !shouldHaveGraphics() )
        {
                return;
        }

        setLayoutDirty(true);

        updateSymbolVisibility();
        updateFeatureSymbolVisibility();
        updateRouteSymbol();

        CvCity* pCity = getPlotCity();
        if (pCity != NULL)
        {
                pCity->updateVisibility();
        }
}


void CvPlot::updateSymbolDisplay()
{
        PROFILE_FUNC();

        CvSymbol* pLoopSymbol;
        int iLoop;

        if ( !shouldHaveFullGraphics() )
        {
                return;
        }

        for (iLoop = 0; iLoop < getNumSymbols(); iLoop++)
        {
                pLoopSymbol = getSymbol(iLoop);

                if (pLoopSymbol != NULL)
                {
                        if (isShowCitySymbols())
                        {
                                gDLL->getSymbolIFace()->setAlpha(pLoopSymbol, (isVisibleWorked()) ? 1.0f : 0.8f);
                        }
                        else
                        {
                                gDLL->getSymbolIFace()->setAlpha(pLoopSymbol, (isVisibleWorked()) ? 0.8f : 0.6f);
                        }
                        gDLL->getSymbolIFace()->setScale(pLoopSymbol, getSymbolSize());
                        gDLL->getSymbolIFace()->updatePosition(pLoopSymbol);
                }
        }
}


void CvPlot::updateSymbolVisibility()
{
        PROFILE_FUNC();

        CvSymbol* pLoopSymbol;
        int iLoop;

        if ( !shouldHaveFullGraphics() )
        {
                return;
        }

        if ( updateSymbolsInternal() )
        {
                updateSymbolDisplay();

                for (iLoop = 0; iLoop < getNumSymbols(); iLoop++)
                {
                        pLoopSymbol = getSymbol(iLoop);

                        if (pLoopSymbol != NULL)
                        {
                                gDLL->getSymbolIFace()->Hide(pLoopSymbol, false);
                        }
                }
        }
}


void CvPlot::updateSymbols()
{
        updateSymbolsInternal();
}

bool CvPlot::updateSymbolsInternal()
{
        MEMORY_TRACK_EXEMPT();
        PROFILE_FUNC();

        if ( !shouldHaveFullGraphics() )
        {
                return false;
        }

        deleteAllSymbols();

        if (isRevealed(GC.getGameINLINE().getActiveTeam(), true) &&
                  (isShowCitySymbols() ||
                   (gDLL->getInterfaceIFace()->isShowYields() && !(gDLL->getInterfaceIFace()->isCityScreenUp()))))
        {
                int yieldAmounts[NUM_YIELD_TYPES];
                int maxYield = 0;
                for (int iYieldType = 0; iYieldType < NUM_YIELD_TYPES; iYieldType++)
                {
                        int iYield = calculateYield(((YieldTypes)iYieldType), true);
                        yieldAmounts[iYieldType] = iYield;
                        if(iYield>maxYield)
                        {
                                maxYield = iYield;
                        }
                }

                if(maxYield>0)
                {
                        int maxYieldStack = GC.getDefineINT("MAX_YIELD_STACK");
                        int layers = maxYield /maxYieldStack + 1;
                       
                        CvSymbol *pSymbol= NULL;
                        for(int i=0;i<layers;i++)
                        {
                                pSymbol = addSymbol();
                                for (int iYieldType = 0; iYieldType < NUM_YIELD_TYPES; iYieldType++)
                                {
                                        int iYield = yieldAmounts[iYieldType] - (maxYieldStack * i);
                                        LIMIT_RANGE(0,iYield, maxYieldStack);
                                        if(yieldAmounts[iYieldType])
                                        {
                                                gDLL->getSymbolIFace()->setTypeYield(pSymbol,iYieldType,iYield);
                                        }
                                }
                        }
                        for(int i=0;i<getNumSymbols();i++)
                        {
                                SymbolTypes eSymbol  = (SymbolTypes)0;
                                pSymbol = getSymbol(i);
                                gDLL->getSymbolIFace()->init(pSymbol, gDLL->getSymbolIFace()->getID(pSymbol), i, eSymbol, this);
                        }
                }

                return true;
        }
        else
        {
                return false;
        }
}


void CvPlot::updateMinimapColor()
{
        PROFILE_FUNC();

        if ( !shouldHaveGraphics() )
        {
                return;
        }

        gDLL->getInterfaceIFace()->setMinimapColor(MINIMAPMODE_TERRITORY, getViewportX(),getViewportY(), plotMinimapColor(), STANDARD_MINIMAP_ALPHA);
}

bool CvPlot::unitHere(const CvUnit* pUnit) const
{
        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        pUnitNode = headUnitNode();

        while (pUnitNode != NULL)
        {
                pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);

                if ( pLoopUnit == pUnit )
                {
                        return true;
                }
        }

        return false;
}

void CvPlot::updateCenterUnit()
{
        PROFILE_FUNC();

        CvUnit* pOldCenterUnit = getCenterUnit();
        CvUnit* pNewCenterUnit;

        if ( m_bInhibitCenterUnitCalculation || !shouldHaveFullGraphics() )
        {
                //      Because we are inhibitting recalculation until all the adjusting code has run
                //      (rather than have it update multiple times) the currently cached center unit
                //      might become an invalid pointer in the interim.  To protect against this we unconditionally
                //      set the recorded center unit to NULL (without marking the plot dirty which would force a repaint).
                //      This makes the interim safe state, and the correct value will be calculated once the
                //      inhibitted section is exited
                m_pCenterUnit = NULL;
                return;
        }
        else
        {
                pNewCenterUnit = NULL;
        }

        if (!isActiveVisible(true))
        {
                pNewCenterUnit = NULL;
        }
        else
        {
                pNewCenterUnit = getSelectedUnit();

                if (pNewCenterUnit != NULL && !pNewCenterUnit->atPlot(this))
                {
                        pNewCenterUnit = NULL;
                }

                if (pNewCenterUnit == NULL)
                {
                        pNewCenterUnit = getBestDefender(GC.getGameINLINE().getActivePlayer(), NO_PLAYER, NULL, false, false, true);
                }

                if (pNewCenterUnit == NULL)
                {
                        pNewCenterUnit = getBestDefender(GC.getGameINLINE().getActivePlayer());
                }

                if (pNewCenterUnit == NULL)
                {
                        pNewCenterUnit = getBestDefender(NO_PLAYER, GC.getGameINLINE().getActivePlayer(), gDLL->getInterfaceIFace()->getHeadSelectedUnit(), true);
                }

                if (pNewCenterUnit == NULL)
                {
                        pNewCenterUnit = getBestDefender(NO_PLAYER, GC.getGameINLINE().getActivePlayer(), gDLL->getInterfaceIFace()->getHeadSelectedUnit());
                }

                if (pNewCenterUnit == NULL)
                {
                        pNewCenterUnit = getBestDefender(NO_PLAYER, GC.getGameINLINE().getActivePlayer());
                }
        }

        if ( pNewCenterUnit != pOldCenterUnit )
        {
#if 0
                OutputDebugString(CvString::format("Center unit change for plot (%d,%d) from %08lx to %08lx\n", m_iX, m_iY, pOldCenterUnit, pNewCenterUnit).c_str());
                if ( pOldCenterUnit != NULL )
                {
                        //      Does the old center unit still exist?
                        if ( unitHere(pOldCenterUnit) )
                        {
                                pOldCenterUnit->reloadEntity();
                        }
                        else
                        {
                                OutputDebugString("Old center unit no longer here\n");
                        }
                }
#endif

                if ( pNewCenterUnit != NULL )
                {
                        pNewCenterUnit->reloadEntity(true);
                }

                setCenterUnit(pNewCenterUnit);
        }
}


void CvPlot::verifyUnitValidPlot()
{
        PROFILE_FUNC();

        bool bAnyMoved = false;
        std::vector<CvUnit*> aUnits;
        CLLNode<IDInfo>* pUnitNode = headUnitNode();
        while (pUnitNode != NULL)
        {
                CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);
                if (NULL != pLoopUnit)
                {
                        aUnits.push_back(pLoopUnit);
                }
        }

        std::vector<CvUnit*>::iterator it = aUnits.begin();
        while (it != aUnits.end())
        {
                CvUnit* pLoopUnit = *it;
                bool bErased = false;

                if (pLoopUnit != NULL)
                {
                        if (pLoopUnit->atPlot(this))
                        {
                                if (!(pLoopUnit->isCargo()))
                                {
                                        if (!(pLoopUnit->isCombat()))
                                        {
                                                if (!isValidDomainForLocation(*pLoopUnit) || !(pLoopUnit->canEnterArea(getTeam(), area())))
                                                {
                                                        bAnyMoved = true;
                                                        if (!pLoopUnit->jumpToNearestValidPlot())
                                                        {
                                                                bErased = true;
                                                        }
                                                }
                                        }
                                }
                        }
                }

                if (bErased)
                {
                        it = aUnits.erase(it);
                }
                else
                {
                        ++it;
                }
        }

        if (isOwned())
        {
                it = aUnits.begin();
                while (it != aUnits.end())
                {
                        CvUnit* pLoopUnit = *it;
                        bool bErased = false;

                        if (pLoopUnit != NULL)
                        {
                                if (pLoopUnit->atPlot(this))
                                {
                                        if (!(pLoopUnit->isCombat()))
                                        {
                                                //if (pLoopUnit->getTeam() != getTeam() && (getTeam() == NO_TEAM || !GET_TEAM(getTeam()).isVassal(pLoopUnit->getTeam())))
                                                if (getTeam() != NO_TEAM &&
                                                        GET_PLAYER(pLoopUnit->getCombatOwner(getTeam(), this)).getTeam() != BARBARIAN_TEAM &&
                                                        pLoopUnit->getTeam() != getTeam() &&
                                                        !GET_TEAM(getTeam()).isVassal(pLoopUnit->getTeam()))
                                                {
                                                        if (isVisibleEnemyUnit(pLoopUnit))
                                                        {
                                                                if (!(pLoopUnit->isInvisible(getTeam(), false)))
                                                                {
                                                                        bAnyMoved = true;
                                                                        if (!pLoopUnit->jumpToNearestValidPlot())
                                                                        {
                                                                                bErased = true;
                                                                        }
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }

                        if (bErased)
                        {
                                it = aUnits.erase(it);
                        }
                        else
                        {
                                ++it;
                        }
                }
        }

        //      Check groups are not broken by only some of their constituent units
        //      having been moved
        if ( bAnyMoved )
        {
                it = aUnits.begin();
                while (it != aUnits.end())
                {
                        CvUnit* pLoopUnit = *it;

                        if (pLoopUnit != NULL)
                        {
                                CvSelectionGroup* pGroup = pLoopUnit->getGroup();

                                if ( pGroup != NULL && !pGroup->isMidMove() )
                                {
                                        CvUnit* pHeadUnit = pGroup->getHeadUnit();

                                        if ( pHeadUnit != NULL && pHeadUnit->plot() != pLoopUnit->plot() )
                                        {
                                                pLoopUnit->joinGroup(NULL);
                                        }
                                }
                        }

                        ++it;
                }
        }
}


void CvPlot::nukeExplosion(int iRange, CvUnit* pNukeUnit)
{
        CLLNode<IDInfo>* pUnitNode;
        CvCity* pLoopCity;
        CvUnit* pLoopUnit;
        CvPlot* pLoopPlot;
        CLinkList<IDInfo> oldUnits;
        CvWString szBuffer;
        int iNukeDamage;
        int iNukedPopulation;
        int iDX, iDY;
        int iI;

        GC.getGameINLINE().changeNukesExploded(1);

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

                        if (pLoopPlot != NULL)
                        {
                                // if we remove roads, don't remove them on the city... XXX

                                pLoopCity = pLoopPlot->getPlotCity();

                                if (pLoopCity == NULL)
                                {
                                        if (!(pLoopPlot->isWater()) && !(pLoopPlot->isImpassable()))
                                        {
                                                if (NO_FEATURE == pLoopPlot->getFeatureType() || !GC.getFeatureInfo(pLoopPlot->getFeatureType()).isNukeImmune())
                                                {
                                                        if (GC.getGameINLINE().getSorenRandNum(100, "Nuke Fallout") < GC.getDefineINT("NUKE_FALLOUT_PROB"))
                                                        {
                                                                pLoopPlot->setImprovementType(NO_IMPROVEMENT);
                                                                pLoopPlot->setFeatureType((FeatureTypes)(GC.getDefineINT("NUKE_FEATURE")));
                                                        }
                                                }
                                        }
                                }

                                oldUnits.clear();

                                pUnitNode = pLoopPlot->headUnitNode();

                                while (pUnitNode != NULL)
                                {
                                        oldUnits.insertAtEnd(pUnitNode->m_data);
                                        pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
                                }

                                pUnitNode = oldUnits.head();

                                while (pUnitNode != NULL)
                                {
                                        pLoopUnit = ::getUnit(pUnitNode->m_data);
                                        pUnitNode = oldUnits.next(pUnitNode);

                                        if (pLoopUnit != NULL)
                                        {
                                                // < M.A.D. Nukes Start >
                                                if (pLoopUnit != pNukeUnit && !pLoopUnit->isMADEnabled())
                                                //if (pLoopUnit != pNukeUnit)
                                                // < M.A.D. Nukes End   >
                                                {
                                                        if (!pLoopUnit->isNukeImmune() && !pLoopUnit->isDelayedDeath())
                                                        {
                                                                iNukeDamage = (GC.getDefineINT("NUKE_UNIT_DAMAGE_BASE") + GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("NUKE_UNIT_DAMAGE_RAND_1"), "Nuke Damage 1") + GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("NUKE_UNIT_DAMAGE_RAND_2"), "Nuke Damage 2"));

                                                                if (pLoopCity != NULL)
                                                                {
                                                                        iNukeDamage *= std::max(0, (pLoopCity->getNukeModifier() + 100));
                                                                        iNukeDamage /= 100;
                                                                }

                                                                if (pLoopUnit->canFight() || pLoopUnit->airBaseCombatStr() > 0)
                                                                {
                                                                        pLoopUnit->changeDamage(iNukeDamage, ((pNukeUnit != NULL) ? pNukeUnit->getOwnerINLINE() : NO_PLAYER));
                                                                }
                                                                else if (iNukeDamage >= GC.getDefineINT("NUKE_NON_COMBAT_DEATH_THRESHOLD"))
                                                                {
                                                                        pLoopUnit->kill(true, ((pNukeUnit != NULL) ? pNukeUnit->getOwnerINLINE() : NO_PLAYER));
                                                                }
                                                        }
                                                }
                                        }
                                }

                                if (pLoopCity != NULL)
                                {
                                        for (iI = 0; iI < GC.getNumBuildingInfos(); ++iI)
                                        {
                                                if (pLoopCity->getNumRealBuilding((BuildingTypes)iI) > 0)
                                                {
                                                        if (!(GC.getBuildingInfo((BuildingTypes) iI).isNukeImmune()))
                                                        {
                                                                if (GC.getGameINLINE().getSorenRandNum(100, "Building Nuked") < GC.getDefineINT("NUKE_BUILDING_DESTRUCTION_PROB"))
                                                                {
                                                                        pLoopCity->setNumRealBuilding(((BuildingTypes)iI), pLoopCity->getNumRealBuilding((BuildingTypes)iI) - 1);
                                                                }
                                                        }
                                                }
                                        }

                                        iNukedPopulation = ((pLoopCity->getPopulation() * (GC.getDefineINT("NUKE_POPULATION_DEATH_BASE") + GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("NUKE_POPULATION_DEATH_RAND_1"), "Population Nuked 1") + GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("NUKE_POPULATION_DEATH_RAND_2"), "Population Nuked 2"))) / 100);

                                        iNukedPopulation *= std::max(0, (pLoopCity->getNukeModifier() + 100));
                                        iNukedPopulation /= 100;

                                        pLoopCity->changePopulation(-(std::min((pLoopCity->getPopulation() - 1), iNukedPopulation)));
                                }
                        }
                }
        }

        CvEventReporter::getInstance().nukeExplosion(this, pNukeUnit);
}


bool CvPlot::isConnectedTo(const CvCity* pCity) const
{
        FAssert(isOwned());
        return ((getPlotGroup(getOwnerINLINE()) == pCity->plotGroup(getOwnerINLINE())) || (getPlotGroup(pCity->getOwnerINLINE()) == pCity->plotGroup(pCity->getOwnerINLINE())));
}


bool CvPlot::isConnectedToCapital(PlayerTypes ePlayer) const
{
        CvCity* pCapitalCity;

        if (ePlayer == NO_PLAYER)
        {
                ePlayer = getOwnerINLINE();
        }

        if (ePlayer != NO_PLAYER)
        {
                pCapitalCity = GET_PLAYER(ePlayer).getCapitalCity();

                if (pCapitalCity != NULL)
                {
                        return isConnectedTo(pCapitalCity);
                }
        }

        return false;
}


int CvPlot::getPlotGroupConnectedBonus(PlayerTypes ePlayer, BonusTypes eBonus) const
{
        CvPlotGroup* pPlotGroup;

        FAssertMsg(ePlayer != NO_PLAYER, "Player is not assigned a valid value");
        FAssertMsg(eBonus != NO_BONUS, "Bonus is not assigned a valid value");

        pPlotGroup = getPlotGroup(ePlayer);

        if (pPlotGroup != NULL)
        {
                return pPlotGroup->getNumBonuses(eBonus);
        }
        else
        {
                return 0;
        }
}


bool CvPlot::isPlotGroupConnectedBonus(PlayerTypes ePlayer, BonusTypes eBonus) const
{
        return (getPlotGroupConnectedBonus(ePlayer, eBonus) > 0);
}


bool CvPlot::isAdjacentPlotGroupConnectedBonus(PlayerTypes ePlayer, BonusTypes eBonus) const
{
        CvPlot* pAdjacentPlot;
        int iI;

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        if (pAdjacentPlot->isPlotGroupConnectedBonus(ePlayer, eBonus))
                        {
                                return true;
                        }
                }
        }

        return false;
}


void CvPlot::updatePlotGroupBonus(bool bAdd)
{
        PROFILE_FUNC();

        CvCity* pPlotCity;
        CvPlotGroup* pPlotGroup;
        BonusTypes eNonObsoleteBonus;
        int iI;

        if (!isOwned())
        {
                return;
        }

        pPlotGroup = getPlotGroup(getOwnerINLINE());

        if (pPlotGroup != NULL)
        {
                pPlotCity = getPlotCity();

                if (pPlotCity != NULL)
                {
                        PROFILE("CvPlot::updatePlotGroupBonus.PlotCity");

                        for (iI = 0; iI < GC.getNumBonusInfos(); ++iI)
                        {
                                if (!GET_TEAM(getTeam()).isBonusObsolete((BonusTypes)iI))
                                {
                                        pPlotGroup->changeNumBonuses(((BonusTypes)iI), (pPlotCity->getFreeBonus((BonusTypes)iI) * ((bAdd) ? 1 : -1)));
                                }
                        }

                        if (pPlotCity->isCapital())
                        {
                                PROFILE("CvPlot::updatePlotGroupBonus.Capital");

                                for (iI = 0; iI < GC.getNumBonusInfos(); ++iI)
                                {
                                        pPlotGroup->changeNumBonuses(((BonusTypes)iI), (GET_PLAYER(getOwnerINLINE()).getBonusExport((BonusTypes)iI) * ((bAdd) ? -1 : 1)));
                                        pPlotGroup->changeNumBonuses(((BonusTypes)iI), (GET_PLAYER(getOwnerINLINE()).getBonusImport((BonusTypes)iI) * ((bAdd) ? 1 : -1)));
                                }
                        }

                        if ( pPlotCity->getOwnerINLINE() == getOwnerINLINE() )
                        {
                                PROFILE("CvPlot::updatePlotGroupBonus.Buildings");

                                for (iI = 0; iI < GC.getNumBuildingInfos(); ++iI)
                                {
                                        if (GC.getBuildingInfo((BuildingTypes)iI).getFreeTradeRegionBuildingClass() != NO_BUILDINGCLASS)
                                        {
                                                if ( pPlotCity->getNumBuilding((BuildingTypes)iI) > 0 )
                                                {
                                                        BuildingTypes eFreeBuilding = (BuildingTypes)GC.getCivilizationInfo(GET_PLAYER(pPlotCity->getOwnerINLINE()).getCivilizationType()).getCivilizationBuildings(GC.getBuildingInfo((BuildingTypes)iI).getFreeTradeRegionBuildingClass());

                                                        pPlotGroup->changeNumFreeTradeRegionBuildings(eFreeBuilding, ((bAdd) ? 1 : -1));
                                                }
                                        }
                                }
                        }
                }

                eNonObsoleteBonus = getNonObsoleteBonusType(getTeam());

                if (eNonObsoleteBonus != NO_BONUS)
                {
                        PROFILE("CvPlot::updatePlotGroupBonus.NonObsoletePlotBonus");

                        if (GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getBonusInfo(eNonObsoleteBonus).getTechCityTrade())))
                        {
                                if (isCity(true, getTeam()) ||
                                        ((getImprovementType() != NO_IMPROVEMENT) && GC.getImprovementInfo(getImprovementType()).isImprovementBonusTrade(eNonObsoleteBonus)))
                                {
                                        if ((pPlotGroup != NULL) && isBonusNetwork(getTeam()))
                                        {
                                                pPlotGroup->changeNumBonuses(eNonObsoleteBonus, ((bAdd) ? 1 : -1));
                                        }
                                }
                        }
                }
        }
}


bool CvPlot::isAdjacentToArea(int iAreaID) const
{
        PROFILE_FUNC();

        CvPlot* pAdjacentPlot;
        int iI;

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        if (pAdjacentPlot->getArea() == iAreaID)
                        {
                                return true;
                        }
                }
        }

        return false;
}

bool CvPlot::isAdjacentToArea(const CvArea* pArea) const
{
        return isAdjacentToArea(pArea->getID());
}


bool CvPlot::shareAdjacentArea(const CvPlot* pPlot) const
{
        PROFILE_FUNC();

        int iCurrArea;
        int iLastArea;
        CvPlot* pAdjacentPlot;
        int iI;

        iLastArea = FFreeList::INVALID_INDEX;

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        iCurrArea = pAdjacentPlot->getArea();

                        if (iCurrArea != iLastArea)
                        {
                                if (pPlot->isAdjacentToArea(iCurrArea))
                                {
                                        return true;
                                }

                                iLastArea = iCurrArea;
                        }
                }
        }

        return false;
}


bool CvPlot::isAdjacentToLand() const
{
        PROFILE_FUNC();

        CvPlot* pAdjacentPlot;
        int iI;

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        if (!(pAdjacentPlot->isWater()))
                        {
                                return true;
                        }
                }
        }

        return false;
}


bool CvPlot::isCoastalLand(int iMinWaterSize) const
{
        PROFILE_FUNC();

        CvPlot* pAdjacentPlot;
        int iI;

        if (isWater())
        {
                return false;
        }

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        if (pAdjacentPlot->isWater())
                        {
                                if (pAdjacentPlot->area()->getNumTiles() >= iMinWaterSize)
                                {
                                        return true;
                                }
                        }
                }
        }

        return false;
}


bool CvPlot::isVisibleWorked() const
{
        if (isBeingWorked())
        {
                if ((getTeam() == GC.getGameINLINE().getActiveTeam()) || GC.getGameINLINE().isDebugMode())
                {
                        return true;
                }
        }

        return false;
}


bool CvPlot::isWithinTeamCityRadius(TeamTypes eTeam, PlayerTypes eIgnorePlayer) const
{
        PROFILE_FUNC();

        int iI;

        for (iI = 0; iI < MAX_PLAYERS; ++iI)
        {
                if (GET_PLAYER((PlayerTypes)iI).isAlive())
                {
                        if (GET_PLAYER((PlayerTypes)iI).getTeam() == eTeam)
                        {
                                if ((eIgnorePlayer == NO_PLAYER) || (((PlayerTypes)iI) != eIgnorePlayer))
                                {
                                        if (isPlayerCityRadius((PlayerTypes)iI))
                                        {
                                                return true;
                                        }
                                }
                        }
                }
        }

        return false;
}


bool CvPlot::isLake() const
{
        CvArea* pArea = area();
        if (pArea != NULL)
        {
                return pArea->isLake();
        }

        return false;
}


// XXX if this changes need to call updateIrrigated() and pCity->updateFreshWaterHealth()
// XXX precalculate this???
/************************************************************************************************/
/* Afforess                       Start          07/22/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
/*
bool CvPlot::isFreshWater() const
*/

bool CvPlot::isFreshWater(bool bIgnoreJungle) const
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/
{
        CvPlot* pLoopPlot;
        int iDX, iDY;

        if (isWater())
        {
                return false;
        }

        if (isImpassable())
        {
                return false;
        }

        if (isRiver())
        {
                return true;
        }

        for (iDX = -1; iDX <= 1; iDX++)
        {
                for (iDY = -1; iDY <= 1; iDY++)
                {
                        pLoopPlot       = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);

                        if (pLoopPlot != NULL)
                        {
                                if (pLoopPlot->isLake())
                                {
                                        return true;
                                }
#ifdef MULTI_FEATURE_MOD
                                for (int i=0; i<pLoopPlot->getNumFeatures(); i++)
                                {
                                        if (GC.getFeatureInfo(pLoopPlot->getFeatureByIndex(i)).isAddsFreshWater() && (!bIgnoreJungle || GC.getFeatureInfo(pLoopPlot->getFeatureByIndex(i)).getHealthPercent() >= 0))
                                        {
                                                return true;
                                        }
                                }
#else
                                if (pLoopPlot->getFeatureType() != NO_FEATURE)
                                {
                                        if (GC.getFeatureInfo(pLoopPlot->getFeatureType()).isAddsFreshWater() && (!bIgnoreJungle || GC.getFeatureInfo(pLoopPlot->getFeatureType()).getHealthPercent() >= 0))
                                        {
                                                return true;
                                        }
                                }
#endif
                                /************************************************************************************************/
                                /* Afforess                       Start          12/13/09                                                */
                                /*                                                                                              */
                                /*                                                                                              */
                                /************************************************************************************************/
                                if (pLoopPlot->isCity())
                                {
                                        CvCity* pCity = pLoopPlot->getPlotCity();
                                        if (pCity->hasFreshWater())
                                        {
                                                return true;
                                        }
                                }
                                /************************************************************************************************/
                                /* Afforess                          END                                                            */
                                /************************************************************************************************/
                        }
                }
        }

        return false;
}


bool CvPlot::isPotentialIrrigation() const
{
//===NM=====Mountain Mod===0X=====
        if ((isCity() && !(isHills() || isPeak())) || ((getImprovementType() != NO_IMPROVEMENT) && (GC.getImprovementInfo(getImprovementType()).isCarriesIrrigation())))
        {
                if ((getTeam() != NO_TEAM) && GET_TEAM(getTeam()).isIrrigation())
                {
                        return true;
                }
        }

        return false;
}


bool CvPlot::canHavePotentialIrrigation() const
{
        int iI;

//===NM=====Mountain Mod===0X=====
        if (isCity() && !(isHills() || isPeak()))
        {
                return true;
        }

        for (iI = 0; iI < GC.getNumImprovementInfos(); ++iI)
        {
                if (GC.getImprovementInfo((ImprovementTypes)iI).isCarriesIrrigation())
                {
                        if (canHaveImprovement(((ImprovementTypes)iI), NO_TEAM, true))
                        {
                                return true;
                        }
                }
        }

        return false;
}


bool CvPlot::isIrrigationAvailable(bool bIgnoreSelf) const
{
        CvPlot* pAdjacentPlot;
        int iI;

        if (!bIgnoreSelf && isIrrigated())
        {
                return true;
        }

        if (isFreshWater())
        {
                return true;
        }

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        if (pAdjacentPlot->isIrrigated())
                        {
                                return true;
                        }
                }
        }

        return false;
}


bool CvPlot::isRiverMask() const
{
        CvPlot* pPlot;

        if (isNOfRiver())
        {
                return true;
        }

        if (isWOfRiver())
        {
                return true;
        }

        pPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_EAST);
        if ((pPlot != NULL) && pPlot->isNOfRiver())
        {
                return true;
        }

        pPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_SOUTH);
        if ((pPlot != NULL) && pPlot->isWOfRiver())
        {
                return true;
        }

        return false;
}


bool CvPlot::isRiverCrossingFlowClockwise(DirectionTypes eDirection) const
{
        CvPlot *pPlot;
        switch(eDirection)
        {
        case DIRECTION_NORTH:
                pPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_NORTH);
                if (pPlot != NULL)
                {
                        return (pPlot->getRiverWEDirection() == CARDINALDIRECTION_EAST);
                }
                break;
        case DIRECTION_EAST:
                return (getRiverNSDirection() == CARDINALDIRECTION_SOUTH);
                break;
        case DIRECTION_SOUTH:
                return (getRiverWEDirection() == CARDINALDIRECTION_WEST);
                break;
        case DIRECTION_WEST:
                pPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_WEST);
                if(pPlot != NULL)
                {
                        return (pPlot->getRiverNSDirection() == CARDINALDIRECTION_NORTH);
                }
                break;
        default:
                FAssert(false);
                break;
        }

        return false;
}


bool CvPlot::isRiverSide() const
{
        CvPlot* pLoopPlot;
        int iI;

        for (iI = 0; iI < NUM_CARDINALDIRECTION_TYPES; ++iI)
        {
                pLoopPlot = plotCardinalDirection(getX_INLINE(), getY_INLINE(), ((CardinalDirectionTypes)iI));

                if (pLoopPlot != NULL)
                {
                        if (isRiverCrossing(directionXY(this, pLoopPlot)))
                        {
                                return true;
                        }
                }
        }

        return false;
}


bool CvPlot::isRiver() const
{
        return (getRiverCrossingCount() > 0);
}


bool CvPlot::isRiverConnection(DirectionTypes eDirection) const
{
        if (eDirection == NO_DIRECTION)
        {
                return false;
        }

        switch (eDirection)
        {
        case DIRECTION_NORTH:
                return (isRiverCrossing(DIRECTION_EAST) || isRiverCrossing(DIRECTION_WEST));
                break;

        case DIRECTION_NORTHEAST:
                return (isRiverCrossing(DIRECTION_NORTH) || isRiverCrossing(DIRECTION_EAST));
                break;

        case DIRECTION_EAST:
                return (isRiverCrossing(DIRECTION_NORTH) || isRiverCrossing(DIRECTION_SOUTH));
                break;

        case DIRECTION_SOUTHEAST:
                return (isRiverCrossing(DIRECTION_SOUTH) || isRiverCrossing(DIRECTION_EAST));
                break;

        case DIRECTION_SOUTH:
                return (isRiverCrossing(DIRECTION_EAST) || isRiverCrossing(DIRECTION_WEST));
                break;

        case DIRECTION_SOUTHWEST:
                return (isRiverCrossing(DIRECTION_SOUTH) || isRiverCrossing(DIRECTION_WEST));
                break;

        case DIRECTION_WEST:
                return (isRiverCrossing(DIRECTION_NORTH) || isRiverCrossing(DIRECTION_SOUTH));
                break;

        case DIRECTION_NORTHWEST:
                return (isRiverCrossing(DIRECTION_NORTH) || isRiverCrossing(DIRECTION_WEST));
                break;

        default:
                FAssert(false);
                break;
        }

        return false;
}


CvPlot* CvPlot::getNearestLandPlotInternal(int iDistance) const
{
        if (iDistance > GC.getMapINLINE().getGridHeightINLINE() && iDistance > GC.getMapINLINE().getGridWidthINLINE())
        {
                return NULL;
        }

        for (int iDX = -iDistance; iDX <= iDistance; iDX++)
        {
                for (int iDY = -iDistance; iDY <= iDistance; iDY++)
                {
                        if (abs(iDX) + abs(iDY) == iDistance)
                        {
                                CvPlot* pPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
                                if (pPlot != NULL)
                                {
                                        if (!pPlot->isWater())
                                        {
                                                return pPlot;
                                        }
                                }
                        }
                }
        }
        return getNearestLandPlotInternal(iDistance + 1);
}


int CvPlot::getNearestLandArea() const
{
        CvPlot* pPlot = getNearestLandPlot();
        return pPlot ? pPlot->getArea() : -1;
}


CvPlot* CvPlot::getNearestLandPlot() const
{
        return getNearestLandPlotInternal(0);
}


int CvPlot::seeFromLevel(TeamTypes eTeam) const
{
        int iLevel;

        FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

        iLevel = GC.getTerrainInfo(getTerrainType()).getSeeFromLevel();

        if (isPeak())
        {
                iLevel += GC.getPEAK_SEE_FROM_CHANGE();
        }

        if (isHills())
        {
                iLevel += GC.getHILLS_SEE_FROM_CHANGE();
        }

        if (isWater())
        {
                iLevel += GC.getSEAWATER_SEE_FROM_CHANGE();

                if (GET_TEAM(eTeam).isExtraWaterSeeFrom())
                {
                        iLevel++;
                }
        }

        return iLevel;
}


int CvPlot::seeThroughLevel() const
{
        int iLevel;

        FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

        iLevel = GC.getTerrainInfo(getTerrainType()).getSeeThroughLevel();

#ifdef MULTI_FEATURE_MOD
        for (int i=0; i<getNumFeatures(); i++)
        {
                iLevel += GC.getFeatureInfo(getFeatureByIndex(i)).getSeeThroughChange();
        }
#else
        if (getFeatureType() != NO_FEATURE)
        {
                iLevel += GC.getFeatureInfo(getFeatureType()).getSeeThroughChange();
        }
#endif

        if (isPeak())
        {
                iLevel += GC.getPEAK_SEE_THROUGH_CHANGE();
        }

        if (isHills())
        {
                iLevel += GC.getHILLS_SEE_THROUGH_CHANGE();
        }

        if (isWater())
        {
                iLevel += GC.getSEAWATER_SEE_FROM_CHANGE();
        }

        return iLevel;
}



void CvPlot::changeAdjacentSight(TeamTypes eTeam, int iRange, bool bIncrement, CvUnit* pUnit, bool bUpdatePlotGroups)
{
        bool bAerial = (pUnit != NULL && pUnit->getDomainType() == DOMAIN_AIR);

        DirectionTypes eFacingDirection = NO_DIRECTION;
        if (!bAerial && NULL != pUnit)
        {
                eFacingDirection = pUnit->getFacingDirection(true);
        }

        //fill invisible types
        std::vector<InvisibleTypes> aSeeInvisibleTypes;
        if (NULL != pUnit)
        {
                for(int i=0;i<pUnit->getNumSeeInvisibleTypes();i++)
                {
                        aSeeInvisibleTypes.push_back(pUnit->getSeeInvisibleType(i));
                }
        }

        if(aSeeInvisibleTypes.empty())
        {
                aSeeInvisibleTypes.push_back(NO_INVISIBLE);
        }

        //check one extra outer ring
        if (!bAerial)
        {
                iRange++;
        }

        for(int i=0;i<(int)aSeeInvisibleTypes.size();i++)
        {
                for (int dx = -iRange; dx <= iRange; dx++)
                {
                        for (int dy = -iRange; dy <= iRange; dy++)
                        {
                                //check if in facing direction
                                if (bAerial || shouldProcessDisplacementPlot(dx, dy, iRange - 1, eFacingDirection))
                                {
                                        bool outerRing = false;
                                        if ((abs(dx) == iRange) || (abs(dy) == iRange))
                                        {
                                                outerRing = true;
                                        }

                                        //check if anything blocking the plot
                                        if (bAerial || canSeeDisplacementPlot(eTeam, dx, dy, dx, dy, true, outerRing))
                                        {
                                                CvPlot* pPlot = plotXY(getX_INLINE(), getY_INLINE(), dx, dy);
                                                if (NULL != pPlot)
                                                {
                                                        pPlot->changeVisibilityCount(eTeam, ((bIncrement) ? 1 : -1), aSeeInvisibleTypes[i], bUpdatePlotGroups);
                                                }
                                        }
                                }
                               
                                if (eFacingDirection != NO_DIRECTION)
                                {
                                        if((abs(dx) <= 1) && (abs(dy) <= 1)) //always reveal adjacent plots when using line of sight
                                        {
                                                CvPlot* pPlot = plotXY(getX_INLINE(), getY_INLINE(), dx, dy);
                                                if (NULL != pPlot)
                                                {
                                                        pPlot->changeVisibilityCount(eTeam, 1, aSeeInvisibleTypes[i], bUpdatePlotGroups);
                                                        pPlot->changeVisibilityCount(eTeam, -1, aSeeInvisibleTypes[i], bUpdatePlotGroups);
                                                }
                                        }
                                }
                        }
                }
        }
}

bool CvPlot::canSeePlot(CvPlot *pPlot, TeamTypes eTeam, int iRange, DirectionTypes eFacingDirection) const
{
        iRange++;

        if (pPlot == NULL)
        {
                return false;
        }

        //find displacement
        int dx = pPlot->getX() - getX();
        int dy = pPlot->getY() - getY();
        dx = dxWrap(dx); //world wrap
        dy = dyWrap(dy);

        //check if in facing direction
        if (shouldProcessDisplacementPlot(dx, dy, iRange - 1, eFacingDirection))
        {
                bool outerRing = false;
                if ((abs(dx) == iRange) || (abs(dy) == iRange))
                {
                        outerRing = true;
                }

                //check if anything blocking the plot
                if (canSeeDisplacementPlot(eTeam, dx, dy, dx, dy, true, outerRing))
                {
                        return true;
                }
        }

        return false;
}

bool CvPlot::canSeeDisplacementPlot(TeamTypes eTeam, int dx, int dy, int originalDX, int originalDY, bool firstPlot, bool outerRing) const
{
        CvPlot *pPlot = plotXY(getX_INLINE(), getY_INLINE(), dx, dy);
        if (pPlot != NULL)
        {
                //base case is current plot
                if((dx == 0) && (dy == 0))
                {
                        return true;
                }

                //find closest of three points (1, 2, 3) to original line from Start (S) to End (E)
                //The diagonal is computed first as that guarantees a change in position
                // -------------
                // |   | 2 | S |
                // -------------
                // | E | 1 | 3 |
                // -------------

                int displacements[3][2] = {{dx - getSign(dx), dy - getSign(dy)}, {dx - getSign(dx), dy}, {dx, dy - getSign(dy)}};
                int allClosest[3];
                int closest = -1;
                for (int i=0;i<3;i++)
                {
                        //int tempClosest = abs(displacements[i][0] * originalDX - displacements[i][1] * originalDY); //more accurate, but less structured on a grid
                        allClosest[i] = abs(displacements[i][0] * dy - displacements[i][1] * dx); //cross product
                        if((closest == -1) || (allClosest[i] < closest))
                        {
                                closest = allClosest[i];
                        }
                }

                //iterate through all minimum plots to see if any of them are passable
                for(int i=0;i<3;i++)
                {
                        int nextDX = displacements[i][0];
                        int nextDY = displacements[i][1];
                        if((nextDX != dx) || (nextDY != dy)) //make sure we change plots
                        {
                                if(allClosest[i] == closest)
                                {
                                        if(canSeeDisplacementPlot(eTeam, nextDX, nextDY, originalDX, originalDY, false, false))
                                        {
                                                int fromLevel = seeFromLevel(eTeam);
                                                int throughLevel = pPlot->seeThroughLevel();
                                                if(outerRing) //check strictly higher level
                                                {
                                                        CvPlot *passThroughPlot = plotXY(getX_INLINE(), getY_INLINE(), nextDX, nextDY);
                                                        int passThroughLevel = passThroughPlot->seeThroughLevel();
                                                        if (fromLevel >= passThroughLevel)
                                                        {
                                                                if((fromLevel > passThroughLevel) || (pPlot->seeFromLevel(eTeam) > fromLevel)) //either we can see through to it or it is high enough to see from far
                                                                {
                                                                        return true;
                                                                }
                                                        }
                                                }
                                                else
                                                {
                                                        if(fromLevel >= throughLevel) //we can clearly see this level
                                                        {
                                                                return true;
                                                        }
                                                        else if(firstPlot) //we can also see it if it is the first plot that is too tall
                                                        {
                                                                return true;
                                                        }
                                                }
                                        }
                                }
                        }
                }
        }
       
        return false;
}

bool CvPlot::shouldProcessDisplacementPlot(int dx, int dy, int range, DirectionTypes eFacingDirection) const
{
        if(eFacingDirection == NO_DIRECTION)
        {
                return true;
        }
        else if((dx == 0) && (dy == 0)) //always process this plot
        {
                return true;
        }
        else
        {
                //                                                      N               NE              E               SE                      S               SW              W                       NW
                int displacements[8][2] = {{0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}};

                int directionX = displacements[eFacingDirection][0];
                int directionY = displacements[eFacingDirection][1];
               
                //compute angle off of direction
                int crossProduct = directionX * dy - directionY * dx; //cross product
                int dotProduct = directionX * dx + directionY * dy; //dot product

                float theta = atan2((float) crossProduct, (float) dotProduct);
                float spread = 60 * (float) M_PI / 180;
                if((abs(dx) <= 1) && (abs(dy) <= 1)) //close plots use wider spread
                {
                        spread = 90 * (float) M_PI / 180;
                }

                if((theta >= -spread / 2) && (theta <= spread / 2))
                {
                        return true;
                }
                else
                {
                        return false;
                }
       
                /*
                DirectionTypes leftDirection = GC.getTurnLeftDirection(eFacingDirection);
                DirectionTypes rightDirection = GC.getTurnRightDirection(eFacingDirection);

                //test which sides of the line equation (cross product)
                int leftSide = displacements[leftDirection][0] * dy - displacements[leftDirection][1] * dx;
                int rightSide = displacements[rightDirection][0] * dy - displacements[rightDirection][1] * dx;
                if((leftSide <= 0) && (rightSide >= 0))
                        return true;
                else
                        return false;
                */

        }
}

void CvPlot::updateSight(bool bIncrement, bool bUpdatePlotGroups)
{
        PROFILE_FUNC();

        CLLNode<IDInfo>* pUnitNode;
        CvCity* pCity;
//      CvCity* pHolyCity;
        CvUnit* pLoopUnit;
        int iLoop;
        int iI;

        pCity = getPlotCity();

        if (pCity != NULL)
        {
                // Religion - Disabled with new Espionage System
/*              for (iI = 0; iI < GC.getNumReligionInfos(); ++iI)
                {
                        if (pCity->isHasReligion((ReligionTypes)iI))
                        {
                                pHolyCity = GC.getGameINLINE().getHolyCity((ReligionTypes)iI);

                                if (pHolyCity != NULL)
                                {
                                        if (GET_PLAYER(pHolyCity->getOwnerINLINE()).getStateReligion() == iI)
                                        {
                                                changeAdjacentSight(pHolyCity->getTeam(), GC.getDefineINT("PLOT_VISIBILITY_RANGE"), bIncrement, NULL, bUpdatePlotGroups);
                                        }
                                }
                        }
                }*/


                // Vassal
                for (iI = 0; iI < MAX_TEAMS; ++iI)
                {
                        if (GET_TEAM(getTeam()).isVassal((TeamTypes)iI))
                        {
                                changeAdjacentSight((TeamTypes)iI, GC.getDefineINT("PLOT_VISIBILITY_RANGE"), bIncrement, NULL, bUpdatePlotGroups);
                        }
                }

                // EspionageEffect
                for (iI = 0; iI < MAX_CIV_TEAMS; ++iI)
                {
                        if (pCity->getEspionageVisibility((TeamTypes)iI))
                        {
                                // Passive Effect: enough EPs gives you visibility into someone's cities
                                changeAdjacentSight((TeamTypes)iI, GC.getDefineINT("PLOT_VISIBILITY_RANGE"), bIncrement, NULL, bUpdatePlotGroups);
                        }
                }
               
                /************************************************************************************************/
                /* Afforess                       Start          02/03/10                                              */
                /*                                                                                              */
                /* Embassy Allows Players to See Capitals                                                       */
                /************************************************************************************************/
                //Removed 3/7/10 due to bugs
                //fixed 12/5/12 by damgo and reinstated by ls612
                if (pCity->isCapital())
                {
                        TeamTypes pTeam = pCity->getTeam();
                        for (iI = 0; iI < MAX_CIV_TEAMS; ++iI)
                        {
                                if (GET_TEAM(pTeam).isHasEmbassy((TeamTypes)iI))
                                {
                                        changeAdjacentSight((TeamTypes)iI, GC.getDefineINT("PLOT_VISIBILITY_RANGE"), bIncrement, NULL, bUpdatePlotGroups);
                                }
                        }
                }
                /************************************************************************************************/
                /* Afforess                          END                                                            */
                /************************************************************************************************/
        }

        // Owned
        if (isOwned())
        {
                changeAdjacentSight(getTeam(), GC.getDefineINT("PLOT_VISIBILITY_RANGE"), bIncrement, NULL, bUpdatePlotGroups);
        }

        pUnitNode = headUnitNode();

        // Unit
        while (pUnitNode != NULL)
        {
                pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);
               
               
                changeAdjacentSight(pLoopUnit->getTeam(), pLoopUnit->visibilityRange(), bIncrement, pLoopUnit, bUpdatePlotGroups);
        }

        if (getReconCount() > 0)
        {
                int iRange = GC.getRECON_VISIBILITY_RANGE();
                for (iI = 0; iI < MAX_PLAYERS; ++iI)
                {
                        for(pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
                        {
                                if (pLoopUnit->getReconPlot() == this)
                                {
                                        changeAdjacentSight(pLoopUnit->getTeam(), iRange, bIncrement, pLoopUnit, bUpdatePlotGroups);
                                }
                        }
                }
        }
}


void CvPlot::updateSeeFromSight(bool bIncrement, bool bUpdatePlotGroups)
{
        PROFILE_FUNC();

        CvPlot* pLoopPlot;
        int iDX, iDY;

        int iRange = GC.getUNIT_VISIBILITY_RANGE() + 1;
        for (int iPromotion = 0; iPromotion < GC.getNumPromotionInfos(); ++iPromotion)
        {
                iRange += GC.getPromotionInfo((PromotionTypes)iPromotion).getVisibilityChange();
        }

        iRange = std::max(GC.getRECON_VISIBILITY_RANGE() + 1, iRange);
       
        for (iDX = -iRange; iDX <= iRange; iDX++)
        {
                for (iDY = -iRange; iDY <= iRange; iDY++)
                {
                        pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);

                        if (pLoopPlot != NULL)
                        {
                                pLoopPlot->updateSight(bIncrement, bUpdatePlotGroups);
                        }
                }
        }
}


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

        if (eBonus == NO_BONUS)
        {
                return true;
        }

        if (getBonusType() != NO_BONUS)
        {
                return false;
        }

/************************************************************************************************/
/* Afforess     Mountains Start          08/03/09                                                        */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
//      if (isPeak())
//      {
//              return false;
//      }

        if (!GC.getGameINLINE().isOption(GAMEOPTION_MOUNTAINS))
        {
                if (isPeak())
                {
                        return false;
                }
        }
/************************************************************************************************/
/* Afforess     Mountains End       END                                                              */
/************************************************************************************************/
#ifdef MULTI_FEATURE_MOD
        if (getNumFeatures() > 0)
        {
                if (!(GC.getBonusInfo(eBonus).isFeatureTerrain(getTerrainType())))
                {
                        return false;
                }
                bool bValid = false;
                for (int i=0; i<getNumFeatures(); i++)
                {
                        if (GC.getBonusInfo(eBonus).isFeature(getFeatureByIndex(i)))
                        {
                                bValid = true;
                                break;
                        }
                }
                if (!bValid)
                        return false;
        }
#else
        if (getFeatureType() != NO_FEATURE)
        {
                if (!(GC.getBonusInfo(eBonus).isFeature(getFeatureType())))
                {
                        return false;
                }

                if (!(GC.getBonusInfo(eBonus).isFeatureTerrain(getTerrainType())))
                {
                        return false;
                }
        }
#endif
        else
        {
                if (!(GC.getBonusInfo(eBonus).isTerrain(getTerrainType())))
                {
                        return false;
                }
        }

        if (isHills())
        {
                if (!(GC.getBonusInfo(eBonus).isHills()))
                {
                        return false;
                }
        }
/************************************************************************************************/
/* Afforess     Mountains Start          08/31/09                                                        */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        else if (isPeak())
        {
                if (GC.getGameINLINE().isOption(GAMEOPTION_MOUNTAINS))
                {
                        if (!(GC.getBonusInfo(eBonus).isPeaks()))
                        {
                                return false;
                        }
                }
        }
/************************************************************************************************/
/* Afforess     Mountains End       END                                                              */
/************************************************************************************************/
        else if (isFlatlands())
        {
                if (!(GC.getBonusInfo(eBonus).isFlatlands()))
                {
                        return false;
                }
        }

        if (GC.getBonusInfo(eBonus).isNoRiverSide())
        {
                if (isRiverSide())
                {
                        return false;
                }
        }

        if (GC.getBonusInfo(eBonus).getMinAreaSize() != -1)
        {
                if (area()->getNumTiles() < GC.getBonusInfo(eBonus).getMinAreaSize())
                {
                        return false;
                }
        }

        if (!bIgnoreLatitude)
        {
                if (getLatitude() > GC.getBonusInfo(eBonus).getMaxLatitude())
                {
                        return false;
                }

                if (getLatitude() < GC.getBonusInfo(eBonus).getMinLatitude())
                {
                        return false;
                }
        }

        if (!isPotentialCityWork())
        {
                return false;
        }

        return true;
}

/************************************************************************************************/
/* Afforess                       Start          05/24/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
bool CvPlot::canHaveImprovement(ImprovementTypes eImprovement, TeamTypes eTeam, bool bPotential, bool bOver) const
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/
{
        CvPlot* pLoopPlot;
        bool bValid;
        bool bIgnoreNatureYields = false;
        int iI;

        FAssertMsg(eImprovement != NO_IMPROVEMENT, "Improvement is not assigned a valid value");
        FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

        bValid = false;

        if (isCity())
        {
                return false;
        }
/************************************************************************************************/
/* Afforess     Mountains Start          09/18/09                                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        if (eTeam != NO_TEAM && isImpassable(eTeam))
/************************************************************************************************/
/* Afforess     Mountains End       END                                                                 */
/************************************************************************************************/
        {
                return false;
        }

        if (GC.getImprovementInfo(eImprovement).isWater() != isWater())
        {
                return false;
        }

#ifdef MULTI_FEATURE_MOD
        for (int i=0; i<getNumFeatures(); i++)
        {
                if (GC.getFeatureInfo(getFeatureByIndex(i)).isNoImprovement())
                {
                        return false;
                }
        }
#else
        if (getFeatureType() != NO_FEATURE)
        {
                if (GC.getFeatureInfo(getFeatureType()).isNoImprovement())
                {
                        return false;
                }
        }
#endif
/************************************************************************************************/
/* Afforess                       Start          05/24/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        if (!bOver)
        {
                if (eImprovement != NO_IMPROVEMENT && getImprovementType() == eImprovement)
                {
                        return false;
                }
        }
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/
        if ((getBonusType(eTeam) != NO_BONUS) && GC.getImprovementInfo(eImprovement).isImprovementBonusMakesValid(getBonusType(eTeam)))
        {
                return true;
        }

        if (GC.getImprovementInfo(eImprovement).isNoFreshWater() && isFreshWater())
        {
                return false;
        }

        if (GC.getImprovementInfo(eImprovement).isRequiresFlatlands() && !isFlatlands())
        {
                return false;
        }

#ifdef MULTI_FEATURE_MOD
        if (GC.getImprovementInfo(eImprovement).isRequiresFeature() && (getNumFeatures() == 0))
#else
        if (GC.getImprovementInfo(eImprovement).isRequiresFeature() && (getFeatureType() == NO_FEATURE))
#endif
        {
                return false;
        }

        if (GC.getImprovementInfo(eImprovement).isHillsMakesValid() && isHills())
        {
                bValid = true;
        }

/************************************************************************************************/
/* Afforess     Mountains Start          08/03/09                                                        */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        if (GC.getImprovementInfo(eImprovement).isPeakMakesValid() && isPeak())
        {
                bValid = true;
        }
/************************************************************************************************/
/* Afforess     Mountains End       END                                                              */
/************************************************************************************************/

        if (GC.getImprovementInfo(eImprovement).isFreshWaterMakesValid() && isFreshWater())
        {
                bValid = true;
        }

        if (GC.getImprovementInfo(eImprovement).isRiverSideMakesValid() && isRiverSide())
        {
                bValid = true;
        }

        if (GC.getImprovementInfo(eImprovement).getTerrainMakesValid(getTerrainType()))
        {
                bValid = true;
        }

#ifdef MULTI_FEATURE_MOD
        for (int i=0; i<getNumFeatures(); i++)
        {
                if (GC.getImprovementInfo(eImprovement).getFeatureMakesValid(getFeatureByIndex(i)))
                {
                        bValid = true;
                        break;
                }
        }
#else
        if ((getFeatureType() != NO_FEATURE) && GC.getImprovementInfo(eImprovement).getFeatureMakesValid(getFeatureType()))
        {
                bValid = true;
        }
#endif
       
/************************************************************************************************/
/* Afforess                       Start          05/22/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        //Desert has negative defense
        if (GC.getTerrainInfo(getTerrainType()).getDefenseModifier() < 0 && GC.getImprovementInfo(eImprovement).isRequiresIrrigation())
        {
                if ((eTeam != NO_TEAM && GET_TEAM(eTeam).isCanFarmDesert()) || (getTeam() != NO_TEAM && GET_TEAM(getTeam()).isCanFarmDesert()))
                {
                        bValid = true;
                        bIgnoreNatureYields = true;     //      This is an absolute override for desert
                }
        }
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/

        if (!bValid)
        {
                return false;
        }

        if (GC.getImprovementInfo(eImprovement).isRequiresRiverSide())
        {
                bValid = false;

                for (iI = 0; iI < NUM_CARDINALDIRECTION_TYPES; ++iI)
                {
                        pLoopPlot = plotCardinalDirection(getX_INLINE(), getY_INLINE(), ((CardinalDirectionTypes)iI));

                        if (pLoopPlot != NULL)
                        {
                                if (isRiverCrossing(directionXY(this, pLoopPlot)))
                                {
                                        if (pLoopPlot->getImprovementType() != eImprovement)
                                        {
                                                bValid = true;
                                                break;
                                        }
                                }
                        }
                }

                if (!bValid)
                {
                        return false;
                }
        }

        if ( !bIgnoreNatureYields )
        {
                for (iI = 0; iI < NUM_YIELD_TYPES; ++iI)
                {
                        if (calculateNatureYield(((YieldTypes)iI), eTeam) < GC.getImprovementInfo(eImprovement).getPrereqNatureYield(iI))
                        {
                                return false;
                        }
                }
        }

        if ((getTeam() == NO_TEAM) || !(GET_TEAM(getTeam()).isIgnoreIrrigation()))
        {
                if (!bPotential && GC.getImprovementInfo(eImprovement).isRequiresIrrigation() && !isIrrigationAvailable())
                {
                        return false;
                }
        }

        return true;
}


#ifdef CAN_BUILD_VALUE_CACHING

canBuildCache CvPlot::g_canBuildCache;
int CvPlot::canBuildCacheHits = 0;
int CvPlot::canBuildCacheReads = 0;

void CvPlot::ClearCanBuildCache(void)
{
        if ( g_canBuildCache.currentUseCounter > 0 )
        {
                OutputDebugString(CvString::format("Clear can build cache  - usage: %d reads with %d hits\n",canBuildCacheReads,canBuildCacheHits).c_str());

                g_canBuildCache.currentUseCounter = 0;

                for(int i = 0; i < CAN_BUILD_CACHE_SIZE; i++)
                {
                        g_canBuildCache.entries[i].iLastUseCount = 0;
                }
        }
}
#endif

bool CvPlot::hasCachedCanBuildEntry(int iX, int iY, BuildTypes eBuild, PlayerTypes ePlayer, struct canBuildCacheEntry*& entry)
{
        //      Check cache first
        int worstLRU = 0x7FFFFFFF;

        struct canBuildCacheEntry* worstLRUEntry = NULL;
        canBuildCacheReads++;

        //OutputDebugString(CvString::format("AI_yieldValue (%d,%d,%d) at seq %d\n", piYields[0], piYields[1], piYields[2], yieldValueCacheReads).c_str());
        //PROFILE_STACK_DUMP

        for(int i = 0; i < CAN_BUILD_CACHE_SIZE; i++)
        {
                entry = &g_canBuildCache.entries[i];
                if ( entry->iLastUseCount == 0 )
                {
                        worstLRUEntry = entry;
                        break;
                }

                if ( entry->iPlotX == iX &&
                         entry->iPlotY == iY &&
                         entry->eBuild == eBuild &&
                         entry->ePlayer == ePlayer )
                {
                        entry->iLastUseCount = ++g_canBuildCache.currentUseCounter;
                        canBuildCacheHits++;
#ifdef VERIFY_CAN_BUILD_CACHE_RESULTS
                        long lRealValue = canBuildFromPythonInternal(eBuild, ePlayer);
                       
                        if ( lRealValue != entry->lResult )
                        {
                                OutputDebugString(CvString::format("Cache entry %08lx verification failed, turn is %d\n", entry, GC.getGameINLINE().getGameTurn()).c_str());
                                FAssertMsg(false, "Can build value cache verification failure");
                        }
#endif
                        return true;
                }
                else if ( entry->iLastUseCount < worstLRU )
                {
                        worstLRU = entry->iLastUseCount;
                        worstLRUEntry = entry;
                }
        }

        entry = worstLRUEntry;

        return false;
}

long CvPlot::canBuildFromPython(BuildTypes eBuild, PlayerTypes ePlayer) const
{
        PROFILE_FUNC();

        PYTHON_ACCESS_LOCK_SCOPE

#ifdef CAN_BUILD_VALUE_CACHING
#ifdef _DEBUG
//      Uncomment this to perform functional verification
//#define VERIFY_CAN_BUILD_CACHE_RESULTS
#endif

        struct canBuildCacheEntry* entry;

        //      If this player does not own, and cannot build a unit that can perform this build
        //      no need to check with the Python.
        if ( !GET_PLAYER(ePlayer).canHaveBuilder(eBuild) )
        {
                return 0L;
        }

        if ( hasCachedCanBuildEntry(getX_INLINE(), getY_INLINE(), eBuild, ePlayer, entry) )
        {
                return entry->lResult;
        }
        else
        {
                int lResult = canBuildFromPythonInternal(eBuild, ePlayer);

                FAssertMsg(entry != NULL, "No can build cache entry found to replace");
                if ( entry != NULL )
                {
                        entry->iPlotX = getX_INLINE();
                        entry->iPlotY = getY_INLINE();
                        entry->eBuild = eBuild;
                        entry->ePlayer = ePlayer;
                        entry->lResult = lResult;
                        entry->iLastUseCount = ++g_canBuildCache.currentUseCounter;
                }

                return lResult;
        }
#else
        return canBuildFromPythonInternal(eBuild, ePlayer);
#endif
}

long CvPlot::canBuildFromPythonInternal(BuildTypes eBuild, PlayerTypes ePlayer) const
{
        PROFILE_FUNC();

        CyArgsList argsList;
        argsList.add(getX_INLINE());
        argsList.add(getY_INLINE());
        argsList.add((int)eBuild);
        argsList.add((int)ePlayer);
        long lResult=0;
        PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "canBuild", argsList.makeFunctionArgs(), &lResult);

        return lResult;
}

bool CvPlot::canBuild(BuildTypes eBuild, PlayerTypes ePlayer, bool bTestVisible, bool bIncludePythonOverrides) const
{
        PROFILE_FUNC();

        ImprovementTypes eImprovement;
        ImprovementTypes eFinalImprovementType;
        RouteTypes eRoute;
        bool bValid;

        if (eBuild == NO_BUILD)
        {
                return false;
        }

        //      Tech requirements are not checked here - they are checked in CvPlayer::canBuild() which will also be called
        //      when necessary (which is why the enabling tech is not checked here)

        if(bIncludePythonOverrides && GC.getUSE_CAN_BUILD_CALLBACK(eBuild))
        {
                long lResult = canBuildFromPython(eBuild, ePlayer);

                if (lResult >= 1)
                {
                        return true;
                }
                else if (lResult == 0)
                {
                        return false;
                }
        }

        bValid = false;

        eImprovement = ((ImprovementTypes)(GC.getBuildInfo(eBuild).getImprovement()));

        if (eImprovement != NO_IMPROVEMENT)
        {
                if (!canHaveImprovement(eImprovement, GET_PLAYER(ePlayer).getTeam(), bTestVisible))
                {
                        return false;
                }

                if (getImprovementType() != NO_IMPROVEMENT)
                {
                        if (GC.getImprovementInfo(getImprovementType()).isPermanent())
                        {
                                return false;
                        }

                        if (getImprovementType() == eImprovement)
                        {
                                return false;
                        }

                        eFinalImprovementType = finalImprovementUpgrade(getImprovementType());

                        if (eFinalImprovementType != NO_IMPROVEMENT)
                        {
                                if (eFinalImprovementType == finalImprovementUpgrade(eImprovement))
                                {
                                        return false;
                                }
                        }
                }

                if (!bTestVisible)
                {
                        if (GET_PLAYER(ePlayer).getTeam() != getTeam())
                        {
                                //outside borders can't be built in other's culture
                                if (GC.getImprovementInfo(eImprovement).isOutsideBorders())
                                {
                                        if (getTeam() != NO_TEAM)
                                        {
                                                return false;
                                        }
                                }
                                else //only buildable in own culture
                                {
                                        return false;
                                }
                        }
                }

                bValid = true;
        }

        eRoute = ((RouteTypes)(GC.getBuildInfo(eBuild).getRoute()));

        if (eRoute != NO_ROUTE)
        {
                if (getRouteType() != NO_ROUTE)
                {
                        if (GC.getRouteInfo(getRouteType()).getValue() >= GC.getRouteInfo(eRoute).getValue())
                        {
                                return false;
                        }
                        /************************************************************************************************/
                        /* Afforess                       Start          12/8/09                                                */
                        /*                                                                                              */
                        /*    The AI should not replace sea tunnels with non-sea tunnels                                */
                        /************************************************************************************************/
                        else if (GC.getRouteInfo(getRouteType()).isSeaTunnel() && (!GC.getRouteInfo(eRoute).isSeaTunnel()))
                        {
                                return false;
                        }
                        /************************************************************************************************/
                        /* Afforess                          END                                                            */
                        /************************************************************************************************/
                }

                if (!bTestVisible)
                {
                        if (GC.getRouteInfo(eRoute).getPrereqBonus() != NO_BONUS)
                        {
                                if (!isAdjacentPlotGroupConnectedBonus(ePlayer, ((BonusTypes)(GC.getRouteInfo(eRoute).getPrereqBonus()))))
                                {
                                        return false;
                                }
                        }

                        bool bFoundValid = true;
                        for (int i = 0; i < GC.getNUM_ROUTE_PREREQ_OR_BONUSES(); ++i)
                        {
                                if (NO_BONUS != GC.getRouteInfo(eRoute).getPrereqOrBonus(i))
                                {
                                        bFoundValid = false;

                                        if (isAdjacentPlotGroupConnectedBonus(ePlayer, ((BonusTypes)(GC.getRouteInfo(eRoute).getPrereqOrBonus(i)))))
                                        {
                                                bFoundValid = true;
                                                break;
                                        }
                                }
                        }

                        if (!bFoundValid)
                        {
                                return false;
                        }
                }

                bValid = true;
        }

#ifdef MULTI_FEATURE_MOD
        for (int i=0; i<getNumFeatures(); i++)
        {
                if (GC.getBuildInfo(eBuild).isFeatureRemove(getFeatureByIndex(i)))
                {
                        if (isOwned() && (GET_PLAYER(ePlayer).getTeam() != getTeam()) && !atWar(GET_PLAYER(ePlayer).getTeam(), getTeam()))
                        {
                                return false;
                        }

                        bValid = true;
                }
        }
#else
        if (getFeatureType() != NO_FEATURE)
        {
                if (GC.getBuildInfo(eBuild).isFeatureRemove(getFeatureType()))
                {
                        if (isOwned() && (GET_PLAYER(ePlayer).getTeam() != getTeam()) && !atWar(GET_PLAYER(ePlayer).getTeam(), getTeam()))
                        {
                                return false;
                        }

                        bValid = true;
                }
        }
#endif
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/13/09                                                        */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        TerrainTypes eTerrain;
        eTerrain = ((TerrainTypes)(GC.getBuildInfo(eBuild).getTerrainChange()));

        if (eTerrain != NO_TERRAIN)
        {
                if (getTerrainType() == eTerrain)
                {
                        return false;
                }

                bValid = true;
        }

        FeatureTypes eFeature;
        eFeature = ((FeatureTypes)(GC.getBuildInfo(eBuild).getFeatureChange()));

#ifdef MULTI_FEATURE_MOD
        if (getHasFeature(eFeature))
        {
                return false;
        }
#else
        if (eFeature != NO_FEATURE)
        {
                if (getFeatureType() == eFeature)
                {
                        return false;
                }
       
                bValid = true;
        }
#endif
/************************************************************************************************/
/* JOOYO_ADDON                          END                                                     */
/************************************************************************************************/

        return bValid;
}


int CvPlot::getBuildTime(BuildTypes eBuild) const
{
        int iTime;

        FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

        iTime = GC.getBuildInfo(eBuild).getTime();

        if (getFeatureType() != NO_FEATURE)
        {
                iTime += GC.getBuildInfo(eBuild).getFeatureTime(getFeatureType());
        }

/************************************************************************************************/
/* Afforess                       Start          07/18/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        if (isPeak())
        {
                iTime *= std::max(0, (GC.getDefineINT("PEAK_BUILD_TIME_MODIFIER") + 100));
                iTime /= 100;
        }
        if (GC.getBuildInfo(eBuild).getRoute() != NO_ROUTE)
        {
                if (getRouteType() != NO_ROUTE)
                {
                        if (GC.getDefineINT("ROUTES_UPGRADE"))
                        {
                                for (int iI = 0; iI < GC.getNumBuildInfos(); iI++)
                                {
                                        if (GC.getBuildInfo((BuildTypes)iI).getRoute() == getRouteType())
                                        {
                                                iTime = std::max(1, iTime - GC.getBuildInfo((BuildTypes)iI).getTime());
                                                break;
                                        }
                                }
                        }
                }
        }
               
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/
        iTime *= std::max(0, (GC.getTerrainInfo(getTerrainType()).getBuildModifier() + 100));
        iTime /= 100;

        iTime *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getBuildPercent();
        iTime /= 100;

        iTime *= GC.getEraInfo(GC.getGameINLINE().getStartEra()).getBuildPercent();
        iTime /= 100;

        return iTime;
}


// BUG - Partial Builds - start
int CvPlot::getBuildTurnsLeft(BuildTypes eBuild, PlayerTypes ePlayer) const
{
        int iWorkRate = getBuildTurnsLeft(eBuild, 0, 0);
        if (iWorkRate == 0)
        {
                return GET_PLAYER(ePlayer).getWorkRate(eBuild);
        }
        else
        {
                return iWorkRate;
        }
}
// BUG - Partial Builds - end

// BUG - Partial Builds - start
int CvPlot::getBuildTurnsLeft(BuildTypes eBuild, int iNowExtra, int iThenExtra, bool bIncludeUnits) const
// BUG - Partial Builds - end
{
        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        int iNowBuildRate;
        int iThenBuildRate;
        int iBuildLeft;
        int iTurnsLeft;

        iNowBuildRate = iNowExtra;
        iThenBuildRate = iThenExtra;

// BUG - Partial Builds - start
        if (bIncludeUnits)
        {
// BUG - Partial Builds - end
// RevolutionDCM - start BUG extra changes
                pUnitNode = headUnitNode();

                while (pUnitNode != NULL)
                {
                        pLoopUnit = ::getUnit(pUnitNode->m_data);
                        pUnitNode = nextUnitNode(pUnitNode);

                        if (pLoopUnit->getBuildType() == eBuild)
                        {
                                if (pLoopUnit->canMove())
                                {
                                        iNowBuildRate += pLoopUnit->workRate(false);
                                }
                                iThenBuildRate += pLoopUnit->workRate(true);
                        }
                }
// RevolutionDCM - end BUG extra changes
// BUG - Partial Builds - start
        }
// BUG - Partial Builds - end

        if (iThenBuildRate == 0)
        {
                //this means it will take forever under current circumstances
                return MAX_INT;
        }

        iBuildLeft = getBuildTime(eBuild);

        iBuildLeft -= getBuildProgress(eBuild);
        iBuildLeft -= iNowBuildRate;

        iBuildLeft = std::max(0, iBuildLeft);

        iTurnsLeft = (iBuildLeft / iThenBuildRate);

        if ((iTurnsLeft * iThenBuildRate) < iBuildLeft)
        {
                iTurnsLeft++;
        }

        iTurnsLeft++;

        return std::max(1, iTurnsLeft);
}


int CvPlot::getFeatureProduction(BuildTypes eBuild, TeamTypes eTeam, CvCity** ppCity) const
{

        if (getFeatureType() == NO_FEATURE)
        {
                return 0;
        }

        *ppCity = getWorkingCity();

        if (*ppCity == NULL)
        {
                *ppCity = GC.getMapINLINE().findCity(getX_INLINE(), getY_INLINE(), NO_PLAYER, eTeam, false);
        }

        if (*ppCity == NULL)
        {
                return 0;
        }

        int iProduction = 0;
#ifdef MULTI_FEATURE_MOD
        for (int i=0; i<getNumFeatures(); i++)
        {
                if (GET_TEAM(eTeam).isHasTech((TechTypes)GC.getBuildInfo(eBuild).getFeatureTech(getFeatureByIndex(i))))
                {
                        iProduction += GC.getBuildInfo(eBuild).getFeatureProduction(getFeatureByIndex(i));
                }
        }
#else
        iProduction = GC.getBuildInfo(eBuild).getFeatureProduction(getFeatureType());
#endif
        iProduction -= std::max(0, plotDistance(getX_INLINE(), getY_INLINE(), (*ppCity)->getX_INLINE(), (*ppCity)->getY_INLINE()) - 2) * 5;

        iProduction *= std::max(0, (GET_PLAYER((*ppCity)->getOwnerINLINE()).getFeatureProductionModifier() + 100));
        iProduction /= 100;

        iProduction *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getFeatureProductionPercent();
        iProduction /= 100;

        iProduction *= std::min((GC.getDefineINT("BASE_FEATURE_PRODUCTION_PERCENT") + (GC.getDefineINT("FEATURE_PRODUCTION_PERCENT_MULTIPLIER") * (*ppCity)->getPopulation())), 100);
        iProduction /= 100;

        if (getTeam() != eTeam)
        {
                iProduction *= GC.getDefineINT("DIFFERENT_TEAM_FEATURE_PRODUCTION_PERCENT");
                iProduction /= 100;
        }

/************************************************************************************************/
/* Afforess                       Start          05/25/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
#ifndef MULTI_FEATURE_MOD
        if (!GET_TEAM(eTeam).isHasTech((TechTypes)GC.getBuildInfo(eBuild).getFeatureTech(getFeatureType())))
                return 0;
#endif
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/

        return std::max(0, iProduction);
}

CvUnit* CvPlot::getBestDefender(PlayerTypes eOwner, PlayerTypes eAttackingPlayer, const CvUnit* pAttacker, bool bTestAtWar, bool bTestPotentialEnemy, bool bTestCanMove) const
{
        CvUnit* pBestUnit;

        PROFILE_FUNC();

/************************************************************************************************/
/* Afforess                       Start          06/15/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        if (isCity() && getPlotCity()->isInvaded())
        {
                return getWorstDefender(eOwner, eAttackingPlayer, pAttacker, bTestAtWar, bTestPotentialEnemy, bTestCanMove);
        }
/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/

        //      Heavily cache this routine as during large stack fights the question is asked over and over, with the
        //      same attacker cropping up repeatedly
        EnterCriticalSection(&g_cBestDefenderCacheSection);

        if ( g_bestDefenderCachePlot != this )
        {
                g_bestDefenderCachePlot = this;

                g_bestDefenderCache->clear();
        }

        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        int iBestValue = 0;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      02/21/10                                jdog5000      */
/*                                                                                              */
/* Lead From Behind                                                                             */
/************************************************************************************************/
// From Lead From Behind by UncutDragon
        int iBestUnitRank = -1;
        pBestUnit = NULL;

        pUnitNode = headUnitNode();

        while (pUnitNode != NULL)
        {
                pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);

                if ( pLoopUnit->plot() == NULL )
                {
                        //      It's not really here - it's in delayed death cycle
                        continue;
                }

                CvChecksum      checksum;
                int iValue = 0;

                if ( pAttacker != NULL )
                {
                        checksum.add(pAttacker->getID());
                }
                checksum.add((int)pLoopUnit->getOwnerINLINE());
                checksum.add(pLoopUnit->getID());
                checksum.add(bTestAtWar);
                checksum.add(bTestPotentialEnemy);
                checksum.add(bTestCanMove);
                checksum.add((int)eAttackingPlayer);
                checksum.add((int)eOwner);

                std::map<int,unitDefenderInfo>::const_iterator itr = g_bestDefenderCache->find(checksum.get());
                if ( itr != g_bestDefenderCache->end() && itr->second.iHealth == pLoopUnit->currHitPoints() )
                {
                        iValue = itr->second.iValue;
                }
                else if ( pLoopUnit->AI_getPredictedHitPoints() != 0 )
                {
                        if ((eOwner == NO_PLAYER) || (pLoopUnit->getOwnerINLINE() == eOwner))
                        {
                                if ((eAttackingPlayer == NO_PLAYER) || !(pLoopUnit->isInvisible(GET_PLAYER(eAttackingPlayer).getTeam(), false)))
                                {
                                        if (!bTestAtWar || eAttackingPlayer == NO_PLAYER || pLoopUnit->isEnemy(GET_PLAYER(eAttackingPlayer).getTeam(), this) || (NULL != pAttacker && pAttacker->isEnemy(GET_PLAYER(pLoopUnit->getOwnerINLINE()).getTeam(), this)))
                                        {
                                                if (!bTestPotentialEnemy || (eAttackingPlayer == NO_PLAYER) ||  pLoopUnit->isPotentialEnemy(GET_PLAYER(eAttackingPlayer).getTeam(), this) || (NULL != pAttacker && pAttacker->isPotentialEnemy(GET_PLAYER(pLoopUnit->getOwnerINLINE()).getTeam(), this)))
                                                {
                                                        if (!bTestCanMove || (pLoopUnit->canMove() && !(pLoopUnit->isCargo())))
                                                        {
                                                                if ((pAttacker == NULL) || (pAttacker->getDomainType() != DOMAIN_AIR) || (pLoopUnit->getDamage() < pAttacker->airCombatLimit()))
                                                                {
#if 0
                                                                        // UncutDragon
        /* original code
                                                                        if (pLoopUnit->isBetterDefenderThan(pBestUnit, pAttacker))
        */
                                                              // modified (added extra parameter)
                                                                        if (pLoopUnit->AI_getPredictedHitPoints() != 0 && pLoopUnit->isBetterDefenderThan(pBestUnit, pAttacker, &iBestUnitRank))
                                                                        // /UncutDragon
                                                                        {
                                                                                pBestUnit = pLoopUnit;
                                                                        }
#else
                                                                        iValue = pLoopUnit->defenderValue(pAttacker);

                                                                        if ( pBestUnit == NULL )
                                                                        {
                                                                                pBestUnit = pLoopUnit;
                                                                        }
#endif
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }

                        unitDefenderInfo info;

                        info.iHealth = pLoopUnit->currHitPoints();
                        info.iValue = iValue;


                        {
                                MEMORY_TRACK_EXEMPT();

                                (*g_bestDefenderCache)[checksum.get()] = info;
                        }
                }

                if ( iValue > iBestValue )
                {
                        pBestUnit = pLoopUnit;
                        iBestValue = iValue;
                }
        }
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                       END                                                  */
/************************************************************************************************/
        LeaveCriticalSection(&g_cBestDefenderCacheSection);

        return pBestUnit;
}

// returns a sum of the strength (adjusted by firepower) of all the units on a plot
int CvPlot::AI_sumStrength(PlayerTypes eOwner, PlayerTypes eAttackingPlayer, DomainTypes eDomainType, bool bDefensiveBonuses, bool bTestAtWar, bool bTestPotentialEnemy, int iRange) const
{
        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        int     strSum = 0;
        int iBaseCollateral = GC.getDefineINT("COLLATERAL_COMBAT_DAMAGE"); // K-Mod. (currently this number is "10")
        CvPlot* pLoopPlot;

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

                        if (pLoopPlot != NULL)
                        {
                                pUnitNode = pLoopPlot->headUnitNode();

                                while (pUnitNode != NULL)
                                {
                                        pLoopUnit = ::getUnit(pUnitNode->m_data);
                                        pUnitNode = nextUnitNode(pUnitNode);

                                        if ((eOwner == NO_PLAYER) || (pLoopUnit->getOwnerINLINE() == eOwner))
                                        {
                                                if ((eAttackingPlayer == NO_PLAYER) || !(pLoopUnit->isInvisible(GET_PLAYER(eAttackingPlayer).getTeam(), false)))
                                                {
                                                        if (!bTestAtWar || (eAttackingPlayer == NO_PLAYER) || atWar(GET_PLAYER(eAttackingPlayer).getTeam(), pLoopUnit->getTeam()))
                                                        {
                                                                if (!bTestPotentialEnemy || (eAttackingPlayer == NO_PLAYER) || pLoopUnit->isPotentialEnemy(GET_PLAYER(eAttackingPlayer).getTeam(), this))
                                                                {
                                                                        // we may want to be more sophisticated about domains
                                                                        // somewhere we need to check to see if this is a city, if so, only land units can defend here, etc
                                                                        if (eDomainType == NO_DOMAIN || (pLoopUnit->getDomainType() == eDomainType))
                                                                        {
                                                                                const CvPlot* pPlot = NULL;
                                                                               
                                                                                if (bDefensiveBonuses)
                                                                                        pPlot = this;

                                                                                strSum += pLoopUnit->currEffectiveStr(pPlot, NULL);

                                                                                // K-Mod assume that if we aren't counting defensive bonuses, then we should be counting collateral
                                                                                if (pLoopUnit->collateralDamage() > 0 && !bDefensiveBonuses)
                                                                                {
                                                                                        //int iPossibleTargets = std::min((pAttackedPlot->getNumVisibleEnemyDefenders(pLoopUnit) - 1), pLoopUnit->collateralDamageMaxUnits());
                                                                                        // unfortunately, we can't count how many targets there are...
                                                                                        int iPossibleTargets = pLoopUnit->collateralDamageMaxUnits();
                                                                                        if (iPossibleTargets > 0)
                                                                                        {
                                                                                                // collateral damage is not trivial to calculate. This estimate is pretty rough.
                                                                                                strSum += pLoopUnit->baseCombatStr() * iBaseCollateral * pLoopUnit->collateralDamage() * iPossibleTargets / 100;
                                                                                        }
                                                                                }
                                                                                // K-Mod end
                                                                        }
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                }
        }

        return strSum;
}


CvUnit* CvPlot::getSelectedUnit() const
{
        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;

        pUnitNode = headUnitNode();

        while (pUnitNode != NULL)
        {
                pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);

                if (pLoopUnit->IsSelected())
                {
                        return pLoopUnit;
                }
        }

        return NULL;
}

//ls612: Advanced Nuke Interception

//int CvPlot::getUnitNukeIntercept(PlayerTypes eOwner) const
//{
//      CLLNode<IDInfo>* pUnitNode;
//      CvUnit* pLoopUnit;
//      int iChance;
//      int iTempValue = 0;
//      iChance = 0;
//
//      pUnitNode = headUnitNode();
//
//      while (pUnitNode != NULL)
//      {
//              pLoopUnit = ::getUnit(pUnitNode->m_data);
//              pUnitNode = nextUnitNode(pUnitNode);
//
//              if (((eOwner == NO_PLAYER) || (pLoopUnit->getOwnerINLINE() == eOwner)) && (pLoopUnit->getUnitInfo().getNukeInterceptionProbability() > iTempValue))
//              {
//                      iChance = pLoopUnit->getUnitInfo().getNukeInterceptionProbability();
//                      iTempValue = iChance;
//              }
//      }
//
//      return iChance;
//}

int CvPlot::getUnitPower(PlayerTypes eOwner) const
{
        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        int iCount;

        iCount = 0;

        pUnitNode = headUnitNode();

        while (pUnitNode != NULL)
        {
                pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);

                if ((eOwner == NO_PLAYER) || (pLoopUnit->getOwnerINLINE() == eOwner))
                {
                        iCount += pLoopUnit->getUnitInfo().getPowerValue();
                }
        }

        return iCount;
}


int CvPlot::defenseModifier(TeamTypes eDefender, bool bIgnoreBuilding, bool bHelp) const
{
        CvCity* pCity;
        ImprovementTypes eImprovement;
        int iModifier;

        FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

#ifdef MULTI_FEATURE_MOD
        if (getNumFeatures() == 0)
                iModifier = GC.getTerrainInfo(getTerrainType()).getDefenseModifier();
        else
        {
                iModifier = 0;
                for (int i=0; i<getNumFeatures(); i++)
                {
                        iModifier += GC.getFeatureInfo(getFeatureByIndex(i)).getDefenseModifier();
                }
        }
#else
        iModifier = ((getFeatureType() == NO_FEATURE) ? GC.getTerrainInfo(getTerrainType()).getDefenseModifier() : GC.getFeatureInfo(getFeatureType()).getDefenseModifier());
#endif

        if (isHills())
        {
                iModifier += GC.getHILLS_EXTRA_DEFENSE();
        }

/************************************************************************************************/
/* Afforess     Mountains Start          08/03/09                                                        */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/

        if (GC.getGameINLINE().isOption(GAMEOPTION_MOUNTAINS))
        {
                if (isPeak())
                {
                        iModifier += GC.getPEAK_EXTRA_DEFENSE();
                }
        }
/************************************************************************************************/
/* Afforess     Mountains End       END                                                              */
/************************************************************************************************/

        if (bHelp)
        {
                eImprovement = getRevealedImprovementType(GC.getGameINLINE().getActiveTeam(), false);
        }
        else
        {
                eImprovement = getImprovementType();
        }

        if (eImprovement != NO_IMPROVEMENT)
        {
                if (eDefender != NO_TEAM && (getTeam() == NO_TEAM || GET_TEAM(eDefender).isFriendlyTerritory(getTeam())))
                {
                        iModifier += GC.getImprovementInfo(eImprovement).getDefenseModifier();
                }
        }

        if (!bHelp)
        {
                pCity = getPlotCity();

                if (pCity != NULL)
                {
                        iModifier += pCity->getDefenseModifier(bIgnoreBuilding);
                }
        }

        return iModifier;
}

void CvPlot::flushMovementCostCache(void)
{
#ifdef SUPPORT_MULTITHREADED_PATHING
        EnterCriticalSection(&m_resultHashAccessSection);
#endif
        if ( m_resultHashMap != NULL )
        {
                m_resultHashMap->clear();
        }

#ifdef SUPPORT_MULTITHREADED_PATHING
        LeaveCriticalSection(&m_resultHashAccessSection);
#endif
}

int CvPlot::movementCost(const CvUnit* pUnit, const CvPlot* pFromPlot) const
{
        //PROFILE_FUNC();

        int iRegularCost;
        int iRouteCost;
        int iRouteFlatCost;
        int iResult;

        int iResultKeyHash = -1;
       
        //      Don't use the cache for human players because that could cause values cached for unrevealed plots to be used on revealed
        //      ones and visa versa.  That not only results in incorrect path generation, but also to OOS errors in multiplayer
        //      due to the cached values being used at different times (so on one m,achine the plot may be first seen as revealed, while on
        //      another as unrevealed)
        if ( !pUnit->isHuman() ) //&& isRevealed(pUnit->getTeam()->getID()) - cleaner to use this condition directly but
                                                         // actually its only accessed in the logic below for humans so isHuman() is a cheaper proxy
        {
                iResultKeyHash = pFromPlot->getMovementCharacteristicsHash() ^
                                                 (getMovementCharacteristicsHash() << 1) ^ pUnit->getMovementCharacteristicsHash() ^
                                                 GET_PLAYER(pUnit->getOwnerINLINE()).getZobristValue();

                if ( pFromPlot->getOwnerINLINE() != NO_PLAYER )
                {
                        iResultKeyHash ^= GET_PLAYER(pFromPlot->getOwnerINLINE()).getZobristValue();
                }

                if ( getOwnerINLINE() != NO_PLAYER )
                {
                        iResultKeyHash ^= GET_PLAYER(getOwnerINLINE()).getZobristValue();
                }

                int iResult = -1;

#ifdef SUPPORT_MULTITHREADED_PATHING
                EnterCriticalSection(&m_resultHashAccessSection);
#endif
                stdext::hash_map<int,int>::const_iterator match = m_resultHashMap->find(iResultKeyHash);
                if ( match != m_resultHashMap->end() )
                {
                        iResult = match->second;
                }

#ifdef SUPPORT_MULTITHREADED_PATHING
                LeaveCriticalSection(&m_resultHashAccessSection);
#endif
                if ( iResult != -1 )
                {
                        return iResult;
                }
        }

        FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

        if (pUnit->flatMovementCost() || (pUnit->getDomainType() == DOMAIN_AIR))
        {
                iResult = GC.getMOVE_DENOMINATOR();
        }
        else if (pUnit->isHuman() && !isRevealed(pUnit->getTeam(), false))
        {
                iResult = pUnit->maxMoves();
        }
        else if (!pFromPlot->isValidDomainForLocation(*pUnit))
        {
                iResult = pUnit->maxMoves();
        }
        else if (!isValidDomainForAction(*pUnit))
        {
                iResult = GC.getMOVE_DENOMINATOR();
        }
        else
        {
                FAssert(pUnit->getDomainType() != DOMAIN_IMMOBILE);

                if (pUnit->ignoreTerrainCost())
                {
                        iRegularCost = GC.getMOVE_DENOMINATOR();
                }
                else
                {
#ifdef MULTI_FEATURE_MOD
                        if (getNumFeatures() == 0)
                        {
                                iRegularCost = GC.getTerrainInfo(getTerrainType()).getMovementCost();
                        }
                        else
                        {
                                iRegularCost = 0;
                                for (int i=0; i<getNumFeatures(); i++)
                                {
                                        iRegularCost = std::max(iRegularCost, GC.getFeatureInfo(getFeatureByIndex(i)).getMovementCost());
                                }
                        }
#else
                        iRegularCost = ((getFeatureType() == NO_FEATURE) ? GC.getTerrainInfo(getTerrainType()).getMovementCost() : GC.getFeatureInfo(getFeatureType()).getMovementCost());
#endif

                        if (isHills())
                        {
                                iRegularCost += GC.getHILLS_EXTRA_MOVEMENT();
                        }

        /************************************************************************************************/
        /* Afforess     Mountains Start          09/20/09                                                        */
        /*                                                                                              */
        /*                                                                                              */
        /************************************************************************************************/
                        if (isPeak())
                        {
                                if (!GET_TEAM(pUnit->getTeam()).isMoveFastPeaks())
                                {
                                        iRegularCost += GC.getPEAK_EXTRA_MOVEMENT();
                                }
                                else
                                {
                                        iRegularCost += 1;
                                }
                        }
        /************************************************************************************************/
        /* Afforess     Mountains End       END                                                              */
        /************************************************************************************************/

                        if (iRegularCost > 0)
                        {
                                iRegularCost = std::max(1, (iRegularCost - pUnit->getExtraMoveDiscount()));
                        //}

                        //if ( iRegularCost > 1 )
                        //{
                                if ( iRegularCost > pUnit->baseMoves() )
                                {
                                        iRegularCost = pUnit->baseMoves();
                                }

                                iRegularCost *= GC.getMOVE_DENOMINATOR();

#ifdef MULTI_FEATURE_MOD
                                if (((getNumFeatures() == 0) && pUnit->isTerrainDoubleMove(getTerrainType())) || (isHills() && pUnit->isHillsDoubleMove()))
                                {
                                        iRegularCost /= 2;
                                }
                                else
                                {
                                        for (int i=0; i<getNumFeatures(); i++)
                                        {
                                                if (pUnit->isFeatureDoubleMove(getFeatureByIndex(i)))
                                                {
                                                        iRegularCost /= 2;
                                                        break;
                                                }
                                        }
                                }
#else
                                if (((getFeatureType() == NO_FEATURE) ? pUnit->isTerrainDoubleMove(getTerrainType()) : pUnit->isFeatureDoubleMove(getFeatureType())) ||
                                        (isHills() && pUnit->isHillsDoubleMove()))
                                {
                                        iRegularCost /= 2;
                                }
#endif
                        }
                        //else
                        //{
                        //      iRegularCost = GC.getMOVE_DENOMINATOR();
                        //}
                }

                if (pFromPlot->isValidRoute(pUnit) && isValidRoute(pUnit) && ((GET_TEAM(pUnit->getTeam()).isBridgeBuilding() || !(pFromPlot->isRiverCrossing(directionXY(pFromPlot, this))))))
                {
                        RouteTypes fromRouteType = pFromPlot->getRouteType();
                        CvRouteInfo& fromRoute = GC.getRouteInfo(fromRouteType);
                        RouteTypes toRouteType = getRouteType();
                        CvRouteInfo& toRoute = GC.getRouteInfo(toRouteType);

                        iRouteCost = fromRoute.getMovementCost() + GET_TEAM(pUnit->getTeam()).getRouteChange(fromRouteType);
                        if ( toRouteType != fromRouteType )
                        {
                                int iToRouteCost = toRoute.getMovementCost() + GET_TEAM(pUnit->getTeam()).getRouteChange(toRouteType);

                                if ( iToRouteCost > iRouteCost )
                                {
                                        iRouteCost = iToRouteCost;
                                }
                        }

                        iRouteFlatCost = std::max(GC.getRouteInfo(pFromPlot->getRouteType()).getFlatMovementCost(),
                                                                          GC.getRouteInfo(getRouteType()).getFlatMovementCost()) * pUnit->baseMoves();

                        if ( iRouteFlatCost < iRouteCost )
                        {
                                iRouteCost = iRouteFlatCost;
                        }
                }
                else
                {
                        iRouteCost = MAX_INT;
                }

                iResult = std::max(1, std::min(iRegularCost, iRouteCost));
        }

        if ( !pUnit->isHuman() )
        {
                MEMORY_TRACK_EXEMPT();

#ifdef SUPPORT_MULTITHREADED_PATHING
                EnterCriticalSection(&m_resultHashAccessSection);
#endif
                (*m_resultHashMap)[iResultKeyHash] = iResult;

#ifdef SUPPORT_MULTITHREADED_PATHING
                LeaveCriticalSection(&m_resultHashAccessSection);
#endif
        }

        return iResult;
}

int CvPlot::getExtraMovePathCost() const
{
        return GC.getGameINLINE().getPlotExtraCost(getX_INLINE(), getY_INLINE());
}


void CvPlot::changeExtraMovePathCost(int iChange)
{
        GC.getGameINLINE().changePlotExtraCost(getX_INLINE(), getY_INLINE(), iChange);
}

//      Koshling - count of mountain leaders present per team maintained for efficiency of movement calculations
void CvPlot::changeMountainLeaderCount(TeamTypes eTeam, int iChange)
{
        FAssert(iChange > 0 || getHasMountainLeader(eTeam));

        if ( m_aiMountainLeaderCount == NULL )
        {
                m_aiMountainLeaderCount = new short[MAX_TEAMS];

                for(int i = 0; i < MAX_TEAMS; i++)
                {
                        m_aiMountainLeaderCount[i] = 0;
                }
        }

        m_aiMountainLeaderCount[eTeam] += iChange;

        if ( m_aiMountainLeaderCount[eTeam] == 0 )
        {
                bool bAnyMountainLeaderPresent = false;

                for(int i = 0; i < MAX_TEAMS; i++)
                {
                        if ( m_aiMountainLeaderCount[i] != 0 )
                        {
                                bAnyMountainLeaderPresent = true;
                                break;
                        }
                }

                if ( !bAnyMountainLeaderPresent )
                {
                        SAFE_DELETE_ARRAY(m_aiMountainLeaderCount);
                        m_aiMountainLeaderCount = NULL;
                }
        }
}

int     CvPlot::getHasMountainLeader(TeamTypes eTeam) const
{
        if ( m_aiMountainLeaderCount == NULL )
        {
                return 0;
        }
        else
        {
                return m_aiMountainLeaderCount[eTeam];
        }
}

bool CvPlot::isAdjacentOwned() const
{
        CvPlot* pAdjacentPlot;
        int iI;

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        if (pAdjacentPlot->isOwned())
                        {
                                return true;
                        }
                }
        }

        return false;
}


bool CvPlot::isAdjacentPlayer(PlayerTypes ePlayer, bool bLandOnly) const
{
        CvPlot* pAdjacentPlot;
        int iI;

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        if (pAdjacentPlot->getOwnerINLINE() == ePlayer)
                        {
                                if (!bLandOnly || !(pAdjacentPlot->isWater()))
                                {
                                        return true;
                                }
                        }
                }
        }

        return false;
}


bool CvPlot::isAdjacentTeam(TeamTypes eTeam, bool bLandOnly) const
{
        CvPlot* pAdjacentPlot;
        int iI;

        for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
        {
                pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pAdjacentPlot != NULL)
                {
                        if (pAdjacentPlot->getTeam() == eTeam)
                        {
                                if (!bLandOnly || !(pAdjacentPlot->isWater()))
                                {
                                        return true;
                                }
                        }
                }
        }

        return false;
}


bool CvPlot::isWithinCultureRange(PlayerTypes ePlayer, int* iFoundRange) const
{
        int iI;

        for (iI = 0; iI < GC.getNumCultureLevelInfos(); ++iI)
        {
                if (isCultureRangeCity(ePlayer, iI))
                {
                        if ( iFoundRange != NULL )
                        {
                                *iFoundRange = iI;
                        }
                        return true;
                }
        }

        return false;
}


int CvPlot::getNumCultureRangeCities(PlayerTypes ePlayer) const
{
        int iCount;
        int iI;

        iCount = 0;

        for (iI = 0; iI < GC.getNumCultureLevelInfos(); ++iI)
        {
                iCount += getCultureRangeCities(ePlayer, iI);
        }

        return iCount;
}

/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      01/10/10                                jdog5000      */
/*                                                                                              */
/* General AI                                                                                   */
/************************************************************************************************/
bool CvPlot::isHasPathToEnemyCity( TeamTypes eAttackerTeam, bool bIgnoreBarb )
{
        PROFILE_FUNC();

        int iI;
        CvCity* pLoopCity = NULL;
        int iLoop;

        FAssert(eAttackerTeam != NO_TEAM);

        if( (area()->getNumCities() - GET_TEAM(eAttackerTeam).countNumCitiesByArea(area())) == 0 )
        {
                return false;
        }

        // Imitate instatiation of irrigated finder, pIrrigatedFinder
        // Can't mimic step finder initialization because it requires creation from the exe
        std::vector<TeamTypes> teamVec;
        teamVec.push_back(eAttackerTeam);
        teamVec.push_back(NO_TEAM);
        FAStar* pTeamStepFinder = gDLL->getFAStarIFace()->create();
        gDLL->getFAStarIFace()->Initialize(pTeamStepFinder, GC.getMapINLINE().getGridWidthINLINE(), GC.getMapINLINE().getGridHeightINLINE(), GC.getMapINLINE().isWrapXINLINE(), GC.getMapINLINE().isWrapYINLINE(), stepDestValid, stepHeuristic, stepCost, teamStepValid, stepAdd, NULL, NULL);
        gDLL->getFAStarIFace()->SetData(pTeamStepFinder, &teamVec);

        bool bFound = false;

        // First check capitals
        for (iI = 0; !bFound && iI < MAX_CIV_PLAYERS; iI++)
        {
                if (GET_PLAYER((PlayerTypes)iI).isAlive() && GET_TEAM(eAttackerTeam).AI_getWarPlan(GET_PLAYER((PlayerTypes)iI).getTeam()) != NO_WARPLAN )
                {
                        if( !bIgnoreBarb || !(GET_PLAYER((PlayerTypes)iI).isBarbarian() || GET_PLAYER((PlayerTypes)iI).isMinorCiv()) )
                        {
                                pLoopCity = GET_PLAYER((PlayerTypes)iI).getCapitalCity();
                               
                                if( pLoopCity != NULL )
                                {
                                        if( (pLoopCity->area() == area()) )
                                        {
                                                bFound = gDLL->getFAStarIFace()->GeneratePath(pTeamStepFinder, getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE(), false, 0, true);

                                                if( bFound )
                                                {
                                                        break;
                                                }
                                        }
                                }
                        }
                }
        }

        // Check all other cities
        for (iI = 0; !bFound && iI < MAX_PLAYERS; iI++)
        {
                if (GET_PLAYER((PlayerTypes)iI).isAlive() && GET_TEAM(eAttackerTeam).AI_getWarPlan(GET_PLAYER((PlayerTypes)iI).getTeam()) != NO_WARPLAN )
                {
                        if( !bIgnoreBarb || !(GET_PLAYER((PlayerTypes)iI).isBarbarian() || GET_PLAYER((PlayerTypes)iI).isMinorCiv()) )
                        {
                                for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); !bFound && pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
                                {
                                        if( (pLoopCity->area() == area()) && !(pLoopCity->isCapital()) )
                                        {
                                                bFound = gDLL->getFAStarIFace()->GeneratePath(pTeamStepFinder, getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE(), false, 0, true);

                                                if( bFound )
                                                {
                                                        break;
                                                }
                                        }
                                }
                        }
                }
        }

        gDLL->getFAStarIFace()->destroy(pTeamStepFinder);

        return bFound;
}

bool CvPlot::isHasPathToPlayerCity( TeamTypes eMoveTeam, PlayerTypes eOtherPlayer )
{
        PROFILE_FUNC();

        CvCity* pLoopCity = NULL;
        int iLoop;

        FAssert(eMoveTeam != NO_TEAM);

        if( (area()->getCitiesPerPlayer(eOtherPlayer) == 0) )
        {
                return false;
        }

        // Imitate instatiation of irrigated finder, pIrrigatedFinder
        // Can't mimic step finder initialization because it requires creation from the exe
        std::vector<TeamTypes> teamVec;
        teamVec.push_back(eMoveTeam);
        teamVec.push_back(GET_PLAYER(eOtherPlayer).getTeam());
        FAStar* pTeamStepFinder = gDLL->getFAStarIFace()->create();
        gDLL->getFAStarIFace()->Initialize(pTeamStepFinder, GC.getMapINLINE().getGridWidthINLINE(), GC.getMapINLINE().getGridHeightINLINE(), GC.getMapINLINE().isWrapXINLINE(), GC.getMapINLINE().isWrapYINLINE(), stepDestValid, stepHeuristic, stepCost, teamStepValid, stepAdd, NULL, NULL);
        gDLL->getFAStarIFace()->SetData(pTeamStepFinder, &teamVec);

        bool bFound = false;

        for (pLoopCity = GET_PLAYER(eOtherPlayer).firstCity(&iLoop); !bFound && pLoopCity != NULL; pLoopCity = GET_PLAYER(eOtherPlayer).nextCity(&iLoop))
        {
                if( pLoopCity->area() == area() )
                {
                        bFound = gDLL->getFAStarIFace()->GeneratePath(pTeamStepFinder, getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE(), false, 0, true);

                        if( bFound )
                        {
                                break;
                        }
                }
        }

        gDLL->getFAStarIFace()->destroy(pTeamStepFinder);

        return bFound;
}

int CvPlot::calculatePathDistanceToPlot( TeamTypes eTeam, CvPlot* pTargetPlot )
{
        PROFILE_FUNC();

        FAssert(eTeam != NO_TEAM);

        if( pTargetPlot->area() != area() )
        {
                return 0;
        }

        // Imitate instatiation of irrigated finder, pIrrigatedFinder
        // Can't mimic step finder initialization because it requires creation from the exe
        std::vector<TeamTypes> teamVec;
        teamVec.push_back(eTeam);
        teamVec.push_back(NO_TEAM);
        FAStar* pTeamStepFinder = gDLL->getFAStarIFace()->create();
        gDLL->getFAStarIFace()->Initialize(pTeamStepFinder, GC.getMapINLINE().getGridWidthINLINE(), GC.getMapINLINE().getGridHeightINLINE(), GC.getMapINLINE().isWrapXINLINE(), GC.getMapINLINE().isWrapYINLINE(), stepDestValid, stepHeuristic, stepCost, teamStepValid, stepAdd, NULL, NULL);
        gDLL->getFAStarIFace()->SetData(pTeamStepFinder, &teamVec);
        FAStarNode* pNode;

        int iPathDistance = -1;
        gDLL->getFAStarIFace()->GeneratePath(pTeamStepFinder, getX_INLINE(), getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE(), false, 0, true);

        pNode = gDLL->getFAStarIFace()->GetLastNode(&GC.getStepFinder());

        if (pNode != NULL)
        {
                iPathDistance = pNode->m_iData1;
        }

        gDLL->getFAStarIFace()->destroy(pTeamStepFinder);

        return iPathDistance;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                       END                                                  */
/************************************************************************************************/

/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      08/21/09                                jdog5000      */
/*                                                                                              */
/* Efficiency                                                                                   */
/************************************************************************************************/
        // Plot danger cache
bool CvPlot::isActivePlayerNoDangerCache() const
{
        return m_bIsActivePlayerNoDangerCache;
}

bool CvPlot::isActivePlayerHasDangerCache() const
{
        return m_bIsActivePlayerHasDangerCache;
}

bool CvPlot::isTeamBorderCache( TeamTypes eTeam ) const
{
        return m_abIsTeamBorderCache[eTeam];
}

void CvPlot::setIsActivePlayerNoDangerCache( bool bNewValue )
{
        m_bIsActivePlayerNoDangerCache = bNewValue;
}

void CvPlot::setIsActivePlayerHasDangerCache( bool bNewValue )
{
        m_bIsActivePlayerHasDangerCache = bNewValue;
}

void CvPlot::setIsTeamBorderCache( TeamTypes eTeam, bool bNewValue )
{
        PROFILE_FUNC();
        m_abIsTeamBorderCache[eTeam] = bNewValue;
}

void CvPlot::invalidateIsTeamBorderCache()
{
        PROFILE_FUNC();

        for( int iI = 0; iI < MAX_TEAMS; iI++ )
        {
                m_abIsTeamBorderCache[iI] = false;
        }
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                       END                                                  */
/************************************************************************************************/


/*** Dexy - Fixed Borders START ****/
/* returns the city adjacent to this plot or NULL if none exists. more than one can't exist, because of the 2-tile spacing btwn cities limit. */
CvCity* CvPlot::getAdjacentCity() const
{
        int iDX, iDY;
        CvPlot* pLoopPlot;
        CvCity* pLoopCity;

        for (iDX = -1; iDX <= 1; iDX++)
        {
                for (iDY = -1; iDY <= 1; iDY++)
                {
                        pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
                        if (pLoopPlot != NULL)
                        {
                                pLoopCity = pLoopPlot->getPlotCity();
                                if (pLoopCity != NULL)
                                {
                                        return pLoopCity;
                                }
                        }
                }
        }

        return NULL;
}
/*** Dexy - Fixed Borders  END  ****/



PlayerTypes CvPlot::calculateCulturalOwner(bool bIgnoreClaimedTerritory) const
{
        PROFILE("CvPlot::calculateCulturalOwner()")

        CvCity* pLoopCity;
        CvCity* pBestCity;
        CvPlot* pLoopPlot;
        PlayerTypes eBestPlayer;
        bool bValid;
        int iCulture;
        int iBestCulture;
        int iBestCultureRange;
        int iPriority;
        int iBestPriority;
        int iI;

        if (isForceUnowned())
        {
                return NO_PLAYER;
        }

        iBestCulture = 0;
        iBestCultureRange = MAX_INT;
        eBestPlayer = NO_PLAYER;

        for (iI = 0; iI < MAX_PLAYERS; ++iI)
        {
                if (GET_PLAYER((PlayerTypes)iI).isAlive())
                {
                        iCulture = getCulture((PlayerTypes)iI);

                        if (iCulture > 0)
                        {
                                int iCultureRange;

                                // Super Forts begin *culture* - modified if statement
                                //      Koshling - modified back since C2C uses a unified city/fort culture state
                                //      inheritted from AND
                                //if (isWithinCultureRange((PlayerTypes)iI) || isWithinFortCultureRange((PlayerTypes)iI))
                                if (isWithinCultureRange((PlayerTypes)iI, &iCultureRange)) // Original Code
                                // Super Forts end
                                {
                                        //      Koshling - modified so that equal culture victory goes to the player with the closest cultuer source as
                                        //      first tie-breaker
                                        if (iCulture > iBestCulture ||
                                                (iCulture == iBestCulture &&
                                                 (iCultureRange < iBestCultureRange || (getOwnerINLINE() == iI && iCultureRange == iBestCultureRange))))
                                        {
                                                iBestCulture = iCulture;
                                                eBestPlayer = ((PlayerTypes)iI);
                                                iBestCultureRange = iCultureRange;
                                        }
                                }
                        }
                }
        }
/************************************************************************************************/
/* Afforess                       Start          02/15/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        bool bOwnerHasUnit;

        /* plots that are not forts and are adjacent to cities always belong to those cities' owners */
        if (GC.getGameINLINE().isOption(GAMEOPTION_MIN_CITY_BORDER))
        {
                if (!isActsAsCity())
                {
                        CvCity* adjacentCity = getAdjacentCity();
                        if (adjacentCity != NULL)
                        {
                                return adjacentCity->getOwner();
                        }
                }
        }
        //      Have to check for the current owner being alive for this to work correctly
        //      in the cultural re-assignment that takes place as he dies during processing of the capture
        //      of his last city
        if (getOwnerINLINE() != NO_PLAYER && GET_PLAYER(getOwnerINLINE()).isAlive())
        {
                if (GET_PLAYER(getOwnerINLINE()).hasFixedBorders())
                {
                        if (!bIgnoreClaimedTerritory)
                        {
                                //Can not steal land from other, more dominant fixed border civilization
                                bool bValidCulture = true;
                                if (eBestPlayer != NO_PLAYER && getOwnerINLINE() != eBestPlayer)
                                {
                                        bValidCulture = !GET_PLAYER(eBestPlayer).hasFixedBorders();
                                }

                                //      Koshling - changed Fixed Borders to not unconditionally hold territory, but only
                                //      to hold it against a natural owner with less than twice the FB player's culture
                                if (bValidCulture &&
                                        getCulture(getOwnerINLINE()) > iBestCulture / 2 &&
                                        (isWithinCultureRange(getOwnerINLINE()) || isWithinOccupationRange(getOwnerINLINE())))
                                {
                                        return getOwnerINLINE();
                                }

                                bOwnerHasUnit = false;

                                pUnitNode = headUnitNode();

                                while (pUnitNode != NULL)
                                {
                                        pLoopUnit = ::getUnit(pUnitNode->m_data);
                                        pUnitNode = nextUnitNode(pUnitNode);

                                        if (pLoopUnit->getTeam() == getTeam())
                                        {
                                                if (pLoopUnit->canClaimTerritory(NULL))
                                                {
                                                        bOwnerHasUnit = true;
                                                        break;
                                                }
                                        }
                                }

                                if (bOwnerHasUnit)
                                {
                                        return getOwnerINLINE();
                                }
                        }
                       
                        //This is a fort, outside of our normal borders, but we have fixed borders, and it has not been claimed
                        if (isActsAsCity())
                        {
                                return getOwnerINLINE();
                        }
                }
        }

/************************************************************************************************/
/* Afforess                          END                                                            */
/************************************************************************************************/

        if (!isCity())
        {
                if (eBestPlayer != NO_PLAYER)
                {
                        iBestPriority = MAX_INT;
                        pBestCity = NULL;

                        for (iI = 0; iI < NUM_CITY_PLOTS; ++iI)
                        {
                                pLoopPlot = plotCity(getX_INLINE(), getY_INLINE(), iI);

                                if (pLoopPlot != NULL)
                                {
                                        pLoopCity = pLoopPlot->getPlotCity();

                                        if (pLoopCity != NULL)
                                        {
                                                if (pLoopCity->getTeam() == GET_PLAYER(eBestPlayer).getTeam() || GET_TEAM(GET_PLAYER(eBestPlayer).getTeam()).isVassal(pLoopCity->getTeam()))
                                                {
                                                        if (getCulture(pLoopCity->getOwnerINLINE()) > 0)
                                                        {
                                                                if (isWithinCultureRange(pLoopCity->getOwnerINLINE()))
                                                                {
                                                                        iPriority = GC.getCityPlotPriority()[iI];

                                                                        if (pLoopCity->getTeam() == GET_PLAYER(eBestPlayer).getTeam())
                                                                        {
                                                                                iPriority += 5; // priority ranges from 0 to 4 -> give priority to Masters of a Vassal
                                                                        }

                                                                        if ((iPriority < iBestPriority) || ((iPriority == iBestPriority) && (pLoopCity->getOwnerINLINE() == eBestPlayer)))
                                                                        {
                                                                                iBestPriority = iPriority;
                                                                                pBestCity = pLoopCity;
                                                                        }
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }

                        if (pBestCity != NULL)
                        {
                                eBestPlayer = pBestCity->getOwnerINLINE();
                        }
                }
        }

        if (eBestPlayer == NO_PLAYER)
        {
                bValid = true;

                for (iI = 0; iI < NUM_CARDINALDIRECTION_TYPES; ++iI)
                {
                        pLoopPlot = plotCardinalDirection(getX_INLINE(), getY_INLINE(), ((CardinalDirectionTypes)iI));

                        if (pLoopPlot != NULL)
                        {
                                if (pLoopPlot->isOwned())
                                {
                                        if (eBestPlayer == NO_PLAYER)
                                        {
                                                eBestPlayer = pLoopPlot->getOwnerINLINE();
                                        }
                                        else if (eBestPlayer != pLoopPlot->getOwnerINLINE())
                                        {
                                                bValid = false;
                                                break;
                                        }
                                }
                                else
                                {
                                        bValid = false;
                                        break;
                                }
                        }
                }

                if (!bValid)
                {
                        eBestPlayer = NO_PLAYER;
                }
        }

        return eBestPlayer;
}


void CvPlot::plotAction(PlotUnitFunc func, int iData1, int iData2, PlayerTypes eOwner, TeamTypes eTeam)
{
        PROFILE_FUNC();

        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;

        pUnitNode = headUnitNode();

        while (pUnitNode != NULL)
        {
                pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);

                if ((eOwner == NO_PLAYER) || (pLoopUnit->getOwnerINLINE() == eOwner))
                {
                        if ((eTeam == NO_TEAM) || (pLoopUnit->getTeam() == eTeam))
                        {
                                func(pLoopUnit, iData1, iData2);
                        }
                }
        }
}

int CvPlot::getNumActiveDefenders(PlayerTypes eOwner) const
{
        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        int iCount = 0;

        pUnitNode = headUnitNode();

        while (pUnitNode != NULL)
        {
                pLoopUnit = ::getUnit(pUnitNode->m_data);
                pUnitNode = nextUnitNode(pUnitNode);

                if (pLoopUnit->isDead())
                {
                        continue;
                }

                if (eOwner == NO_PLAYER || pLoopUnit->getOwnerINLINE() == eOwner)
                {
                        if (pLoopUnit->canDefend())
                        {
                                bool bAutoDefender = pLoopUnit->getGroup()->getAutomateType() == AUTOMATE_NATIONAL_DEFENSE || pLoopUnit->getGroup()->getAutomateType() == AUTOMATE_CITY_DEFENSE;
                                if (bAutoDefender || pLoopUnit->AI_getUnitAIType() == UNITAI_CITY_DEFENSE || pLoopUnit->isWaiting())
                                {
                                        iCount++;
                                }
                        }
                }
        }
        return iCount;
}

int CvPlot::plotCount(ConstPlotUnitFunc funcA, int iData1A, int iData2A, PlayerTypes eOwner, TeamTypes eTeam, ConstPlotUnitFunc funcB, int iData1B, int iData2B, int iRange) const
{
        PROFILE_FUNC();

        CLLNode<IDInfo>* pUnitNode;
        CvUnit* pLoopUnit;
        int iCount;

        iCount