Rise of Mankind: A New Dawn Code
Brought to you by:
afforess,
n4538-e1347
// unit.cpp
#include "CvGameCoreDLL.h"
#include "CvUnit.h"
#include "CvArea.h"
#include "CvPlot.h"
#include "CvCity.h"
#include "CvGlobals.h"
#include "CvGameCoreUtils.h"
#include "CvGameAI.h"
#include "CvMap.h"
#include "CvViewport.h"
#include "CvPlayerAI.h"
#include "CvRandom.h"
#include "CvTeamAI.h"
#include "CvGameCoreUtils.h"
#include "CyUnit.h"
#include "CyArgsList.h"
#include "CyPlot.h"
#include "CvDLLEntityIFaceBase.h"
#include "CvDLLInterfaceIFaceBase.h"
#include "CvDLLEngineIFaceBase.h"
#include "CvEventReporter.h"
#include "CvDLLPythonIFaceBase.h"
#include "CvDLLFAStarIFaceBase.h"
#include "CvInfos.h"
#include "FProfiler.h"
#include "CvPopupInfo.h"
#include "CvInitCore.h"
#include "CvArtFileMgr.h"
// BUG - start
#include "CvBugOptions.h"
// BUG - end
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/24/10 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
#include "BetterBTSAI.h"
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
static CvEntity* g_dummyEntity = NULL;
static CvUnit* g_dummyUnit = NULL;
static int g_numEntities = 0;
static int g_dummyUsage = 0;
static bool g_bUseDummyEntities = false;
bool CvUnit::isDummyEntity(const CvEntity* entity)
{
return (entity == g_dummyEntity);
}
bool CvUnit::isRealEntity(const CvEntity* entity)
{
return (entity != NULL && entity != g_dummyEntity);
}
// Public Functions...
#pragma warning( disable : 4355 )
CvUnit::CvUnit(bool bIsDummy) : m_GameObject(this),
m_Properties(this)
{
m_aiExtraDomainModifier = new int[NUM_DOMAIN_TYPES];
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
m_paiTerrainProtected = NULL;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
m_pabHasPromotion = NULL;
m_paiTerrainDoubleMoveCount = NULL;
m_paiFeatureDoubleMoveCount = NULL;
m_paiExtraTerrainAttackPercent = NULL;
m_paiExtraTerrainDefensePercent = NULL;
m_paiExtraFeatureAttackPercent = NULL;
m_paiExtraFeatureDefensePercent = NULL;
m_paiExtraUnitCombatModifier = NULL;
m_iMaxMoveCacheTurn = -1;
if ( g_dummyUnit == NULL && !bIsDummy )
{
g_dummyUnit = new CvUnitAI(true);
if ( GC.getDefineINT("ENABLE_DYNAMIC_UNIT_ENTITIES") )
{
g_bUseDummyEntities = true;
}
}
if ( g_bUseDummyEntities )
{
if ( g_dummyEntity == NULL )
{
CvDLLEntity::createUnitEntity(this); // create and attach entity to unit
g_dummyEntity = getEntity();
}
else
{
setEntity(g_dummyEntity);
g_dummyUsage++;
}
}
else
{
CvDLLEntity::createUnitEntity(this); // create and attach entity to unit
}
bGraphicsSetup = false;
reset(0, NO_UNIT, NO_PLAYER, true);
}
CvUnit::~CvUnit()
{
if ( !isUsingDummyEntities() )
{
if (!gDLL->GetDone() && GC.IsGraphicsInitialized()) // don't need to remove entity when the app is shutting down, or crash can occur
{
gDLL->getEntityIFace()->RemoveUnitFromBattle(this);
CvDLLEntity::removeEntity(); // remove entity from engine
}
CvDLLEntity::destroyEntity(); // delete CvUnitEntity and detach from us
}
uninit();
SAFE_DELETE_ARRAY(m_aiExtraDomainModifier);
}
bool CvUnit::isUsingDummyEntities(void)
{
CvEntity* entity = getEntity();
return (entity != NULL && g_dummyEntity == entity);// || (m_eUnitType == 701);
}
void CvUnit::reloadEntity(bool bForceLoad)
{
bool bNeedsRealEntity = !g_bUseDummyEntities || bForceLoad || (plot() != NULL && plot()->isActiveVisible(false) && (plot()->getCenterUnit() == this || getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()));
//OutputDebugString(CvString::format("reloadEntity for %08lx\n", this).c_str());
if ( !IsSelected() )
{
BeginDLLSerialization();
if ( !isUsingDummyEntities() ) //&& !bNeedsRealEntity )
{
//destroy old entity
if (!gDLL->GetDone() && GC.IsGraphicsInitialized()) // don't need to remove entity when the app is shutting down, or crash can occur
{
gDLL->getEntityIFace()->RemoveUnitFromBattle(this);
CvDLLEntity::removeEntity(); // remove entity from engine
}
CvDLLEntity::destroyEntity(); // delete CvUnitEntity and detach from us
g_numEntities--;
setEntity(NULL);
}
else if ( isUsingDummyEntities() && bNeedsRealEntity )
{
g_dummyUsage--;
setEntity(NULL);
}
if ( getEntity() == NULL )
{
if ( g_bUseDummyEntities )
{
if ( bNeedsRealEntity )
{
//create new one
CvDLLEntity::createUnitEntity(this); // create and attach entity to unit
g_numEntities++;
bGraphicsSetup = false;
}
else
{
setEntity(g_dummyEntity);
g_dummyUsage++;
}
// Log every now and again in non final release builds
if ( g_numEntities%100 == 0 )
{
OutputDebugString(CvString::format("Dummy unit entity usage: %d, real %d\n", g_dummyUsage, g_numEntities).c_str());
}
}
else
{
//create new one
if ( plot() != NULL )
{
CvDLLEntity::createUnitEntity(this); // create and attach entity to unit
bGraphicsSetup = false;
}
}
}
if ( !bGraphicsSetup && bNeedsRealEntity && plot() != NULL )
{
setupGraphical();
bGraphicsSetup = true;
}
EndDLLSerialization();
}
else
{
OutputDebugString("Reload of selected unit\n");
}
}
void CvUnit::changeIdentity(UnitTypes eUnit)
{
reset(getID(), eUnit, getOwnerINLINE(), false, true);
// Same id is now a differnt unit - make sure no old cached path info
// gets used for it
CvPlot::NextCachePathEpoch();
}
void CvUnit::init(int iID, UnitTypes eUnit, UnitAITypes eUnitAI, PlayerTypes eOwner, int iX, int iY, DirectionTypes eFacingDirection, int iBirthmark)
{
CvWString szBuffer;
int iUnitName;
int iI, iJ;
FAssert(NO_UNIT != eUnit);
// If the current viewport is not yet initialized center it on the first unit created for the active player
if ( GC.getGameINLINE().getActivePlayer() == eOwner && GC.getCurrentViewport()->getState() == VIEWPORT_MODE_UNINITIALIZED && UNIT_BIRTHMARK_TEMP_UNIT != iBirthmark )
{
GC.getCurrentViewport()->setOffsetToShow(iX, iY);
}
//--------------------------------
// Init saved data
reset(iID, eUnit, eOwner);
if ( eOwner != NO_PLAYER && eUnitAI == UNITAI_SUBDUED_ANIMAL)
{
GET_PLAYER(eOwner).NoteAnimalSubdued();
}
// Koshling - moved this earlier to get unitAI set up so that
// constraint checking on the unitAI can work more uniformly
AI_init(eUnitAI, iBirthmark);
if(eFacingDirection == NO_DIRECTION)
m_eFacingDirection = DIRECTION_SOUTH;
else
m_eFacingDirection = eFacingDirection;
//--------------------------------
// Init containers
//--------------------------------
// Init pre-setup() data
setXY(iX, iY, false);
if ( getGroup() == NULL )
{
//TB OOS fix - POSSIBLE that this represents a fix but I consider it a longshot since they should really mean the same thing (-1)
::MessageBox(NULL,
getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID after set position in init\n" : "Unit with no group after set position in init\n",
"CvGameCoreDLL Diagnostics",
MB_OK);
}
if ( !isTempUnit() )
{
//--------------------------------
// Init non-saved data
setupGraphical();
//--------------------------------
// Init other game data
plot()->updateCenterUnit();
plot()->setFlagDirty(true);
iUnitName = GC.getGameINLINE().getUnitCreatedCount(getUnitType());
int iNumNames = m_pUnitInfo->getNumUnitNames();
if (iUnitName < iNumNames)
{
int iOffset = GC.getGameINLINE().getSorenRandNum(iNumNames, "Unit name selection");
for (iI = 0; iI < iNumNames; iI++)
{
int iIndex = (iI + iOffset) % iNumNames;
CvWString szName = gDLL->getText(m_pUnitInfo->getUnitNames(iIndex));
if (!GC.getGameINLINE().isGreatPersonBorn(szName))
{
setName(szName);
GC.getGameINLINE().addGreatPersonBornName(szName);
break;
}
}
}
setGameTurnCreated(GC.getGameINLINE().getGameTurn());
GC.getGameINLINE().incrementUnitCreatedCount(getUnitType());
GC.getGameINLINE().incrementUnitClassCreatedCount((UnitClassTypes)(m_pUnitInfo->getUnitClassType()));
GET_TEAM(getTeam()).changeUnitClassCount(((UnitClassTypes)(m_pUnitInfo->getUnitClassType())), 1);
GET_PLAYER(getOwnerINLINE()).changeUnitClassCount(((UnitClassTypes)(m_pUnitInfo->getUnitClassType())), 1);
GET_PLAYER(getOwnerINLINE()).changeExtraUnitCost(m_pUnitInfo->getExtraCost());
if (m_pUnitInfo->getNukeRange() != -1)
{
GET_PLAYER(getOwnerINLINE()).changeNumNukeUnits(1);
}
if (m_pUnitInfo->isMilitarySupport())
{
GET_PLAYER(getOwnerINLINE()).changeNumMilitaryUnits(1);
}
GET_PLAYER(getOwnerINLINE()).changeAssets(m_pUnitInfo->getAssetValue());
GET_PLAYER(getOwnerINLINE()).changeUnitPower(m_pUnitInfo->getPowerValue());
//doSetFreePromotions();
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if (m_pUnitInfo->getFreePromotions(iI))
{
setHasPromotion(((PromotionTypes)iI), true);
}
}
/************************************************************************************************/
/* Afforess Start 08/26/10 */
/* */
/* */
/************************************************************************************************/
/*
FAssertMsg((GC.getNumTraitInfos() > 0), "GC.getNumTraitInfos() is less than or equal to zero but is expected to be larger than zero in CvUnit::init");
for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
{
if (GET_PLAYER(getOwnerINLINE()).hasTrait((TraitTypes)iI))
{
for (iJ = 0; iJ < GC.getNumPromotionInfos(); iJ++)
{
if (GC.getTraitInfo((TraitTypes) iI).isFreePromotion(iJ))
{
if ((getUnitCombatType() != NO_UNITCOMBAT) && GC.getTraitInfo((TraitTypes) iI).isFreePromotionUnitCombat(getUnitCombatType()))
{
setHasPromotion(((PromotionTypes)iJ), true);
}
}
}
}
}
*/
if (getUnitCombatType() != NO_UNITCOMBAT)
{
for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
{
if (GET_PLAYER(getOwnerINLINE()).hasTrait((TraitTypes)iI))
{
for (iJ = 0; iJ < GC.getNumPromotionInfos(); iJ++)
{
if (GC.getTraitInfo((TraitTypes) iI).isFreePromotionUnitCombats(iJ, getUnitCombatType()))
{
setHasPromotion(((PromotionTypes)iJ), true);
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (NO_UNITCOMBAT != getUnitCombatType())
{
for (iJ = 0; iJ < GC.getNumPromotionInfos(); iJ++)
{
if (GET_PLAYER(getOwnerINLINE()).isFreePromotion(getUnitCombatType(), (PromotionTypes)iJ))
{
setHasPromotion(((PromotionTypes)iJ), true);
}
}
}
if (NO_UNITCLASS != getUnitClassType())
{
for (iJ = 0; iJ < GC.getNumPromotionInfos(); iJ++)
{
if (GET_PLAYER(getOwnerINLINE()).isFreePromotion(getUnitClassType(), (PromotionTypes)iJ))
{
setHasPromotion(((PromotionTypes)iJ), true);
}
}
}
if (getDomainType() == DOMAIN_LAND)
{
if (baseCombatStr() > 0)
{
if ((GC.getGameINLINE().getBestLandUnit() == NO_UNIT) || (baseCombatStr() > GC.getGameINLINE().getBestLandUnitCombat()))
{
GC.getGameINLINE().setBestLandUnit(getUnitType());
}
}
}
if (getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())
{
gDLL->getInterfaceIFace()->setDirty(GameData_DIRTY_BIT, true);
}
if (isWorldUnitClass((UnitClassTypes)(m_pUnitInfo->getUnitClassType())))
{
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
MEMORY_TRACK_EXEMPT();
if (GET_TEAM(getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
szBuffer = gDLL->getText("TXT_KEY_MISC_SOMEONE_CREATED_UNIT", GET_PLAYER(getOwnerINLINE()).getNameKey(), getNameKey());
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDER_UNIT_BUILD", MESSAGE_TYPE_MAJOR_EVENT, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_UNIT_TEXT"), getX_INLINE(), getY_INLINE(), true, true);
}
else
{
szBuffer = gDLL->getText("TXT_KEY_MISC_UNKNOWN_CREATED_UNIT", getNameKey());
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDER_UNIT_BUILD", MESSAGE_TYPE_MAJOR_EVENT, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_UNIT_TEXT"));
}
}
}
szBuffer = gDLL->getText("TXT_KEY_MISC_SOMEONE_CREATED_UNIT", GET_PLAYER(getOwnerINLINE()).getNameKey(), getNameKey());
GC.getGameINLINE().addReplayMessage(REPLAY_MESSAGE_MAJOR_EVENT, getOwnerINLINE(), szBuffer, getX_INLINE(), getY_INLINE(), (ColorTypes)GC.getInfoTypeForString("COLOR_UNIT_TEXT"));
}
CvEventReporter::getInstance().unitCreated(this);
}
}
void CvUnit::uninit()
{
}
// FUNCTION: reset()
// Initializes data members that are serialized.
void CvUnit::reset(int iID, UnitTypes eUnit, PlayerTypes eOwner, bool bConstructorCall, bool bIdentityChange)
{
int iI;
//--------------------------------
// Uninit class
uninit();
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - RB: Field Bombard START
m_iDCMBombRange = 0;
m_iDCMBombAccuracy = 0;
// Dale - RB: Field Bombard END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
// < M.A.D. Nukes Start >
m_bMADEnabled = false;
m_iMADTargetPlotX = INVALID_PLOT_COORD;
m_iMADTargetPlotY = INVALID_PLOT_COORD;
m_pMADTargetPlotOwner = NO_PLAYER;
// < M.A.D. Nukes End >
m_iID = iID;
if ( !bIdentityChange )
{
m_iGroupID = FFreeList::INVALID_INDEX;
}
m_iHotKeyNumber = -1;
m_iX = INVALID_PLOT_COORD;
m_iY = INVALID_PLOT_COORD;
m_iLastMoveTurn = 0;
m_iReconX = INVALID_PLOT_COORD;
m_iReconY = INVALID_PLOT_COORD;
m_iGameTurnCreated = 0;
m_iDamage = 0;
m_iMoves = 0;
m_iExperience = 0;
m_iLevel = 1;
m_iCargo = 0;
m_iAttackPlotX = INVALID_PLOT_COORD;
m_iAttackPlotY = INVALID_PLOT_COORD;
m_iCombatTimer = 0;
m_iCombatFirstStrikes = 0;
m_iFortifyTurns = 0;
m_iBlitzCount = 0;
m_iAmphibCount = 0;
m_iRiverCount = 0;
m_iEnemyRouteCount = 0;
m_iAlwaysHealCount = 0;
m_iHillsDoubleMoveCount = 0;
m_iImmuneToFirstStrikesCount = 0;
m_iOneUpCount = 0;
m_iExtraVisibilityRange = 0;
m_iExtraMoves = 0;
m_iExtraMoveDiscount = 0;
m_iExtraAirRange = 0;
m_iExtraIntercept = 0;
m_iExtraEvasion = 0;
m_iExtraFirstStrikes = 0;
m_iExtraChanceFirstStrikes = 0;
m_iExtraWithdrawal = 0;
m_iExtraStrength = 0;
m_iExtraCollateralDamage = 0;
m_iExtraBombardRate = 0;
m_iExtraEnemyHeal = 0;
m_iExtraNeutralHeal = 0;
m_iExtraFriendlyHeal = 0;
m_iSameTileHeal = 0;
m_iAdjacentTileHeal = 0;
m_iExtraCombatPercent = 0;
m_iExtraCityAttackPercent = 0;
m_iExtraCityDefensePercent = 0;
m_iExtraHillsAttackPercent = 0;
m_iExtraHillsDefensePercent = 0;
m_iRevoltProtection = 0;
m_iCollateralDamageProtection = 0;
m_iPillageChange = 0;
m_iUpgradeDiscount = 0;
m_iExperiencePercent = 0;
m_iKamikazePercent = 0;
m_eFacingDirection = DIRECTION_SOUTH;
m_iImmobileTimer = 0;
//Team Project (2)
/*****************************************************************************************************/
/** Author: TheLadiesOgre **/
/** Date: 21.09.2009 **/
/** ModComp: TLOTags **/
/** Reason Added: New Tag Definition **/
/** Notes: **/
/*****************************************************************************************************/
m_bCanRespawn = false;
m_bSurvivor = false;
/*****************************************************************************************************/
/** TheLadiesOgre; 21.09.2009; TLOTags **/
/*****************************************************************************************************/
m_bMadeAttack = false;
m_bMadeInterception = false;
m_bPromotionReady = false;
m_bDeathDelay = false;
m_bCombatFocus = false;
m_bInfoBarDirty = false;
m_bBlockading = false;
m_bAirCombat = false;
/************************************************************************************************/
/* Afforess Start 02/14/10 */
/* */
/* */
/************************************************************************************************/
m_iCanMovePeaksCount = 0;
// Koshling - enhanced mountaineering mode to differentiate between ability to move through
// mountains, and ability to lead a stack through mountains
m_iCanLeadThroughPeaksCount = 0;
if ( eUnit != NO_UNIT )
{
m_movementCharacteristicsHash = GC.getUnitInfo(eUnit).getZobristValue();
}
m_iSleepTimer = 0;
//@MOD Commanders: reset parameters
m_iOnslaughtCount = 0;
m_iExtraCommandRange = 0;
m_iExtraControlPoints = 0;
m_iControlPointsLeft = 0;
m_iCommanderID = -1;
m_iCommanderCacheTurn = -1;
m_eOriginalOwner = eOwner;
m_bCommander = false;
m_iZoneOfControlCount = 0;
m_bAutoPromoting = false;
m_bAutoUpgrading = false;
m_shadowUnit.reset();
m_eDesiredDiscoveryTech = NO_TECH;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
m_eOwner = eOwner;
m_eCapturingPlayer = NO_PLAYER;
m_eUnitType = eUnit;
m_pUnitInfo = (NO_UNIT != m_eUnitType) ? &GC.getUnitInfo(m_eUnitType) : NULL;
m_iBaseCombat = (NO_UNIT != m_eUnitType) ? m_pUnitInfo->getCombat() : 0;
m_eLeaderUnitType = NO_UNIT;
m_iCargoCapacity = (NO_UNIT != m_eUnitType) ? m_pUnitInfo->getCargoSpace() : 0;
// Thomas SG - AC: Advanced Cargo START
m_iTotalCargoCapacity = (NO_UNIT != m_eUnitType) ? m_pUnitInfo->getTotalCargoSpace() : 0;
// Thomas SG - AC: Advanced Cargo END
m_combatUnit.reset();
m_transportUnit.reset();
for (iI = 0; iI < NUM_DOMAIN_TYPES; iI++)
{
m_aiExtraDomainModifier[iI] = 0;
}
m_szName.clear();
m_szScriptData ="";
if (!bConstructorCall)
{
FAssertMsg((0 < GC.getNumPromotionInfos()), "GC.getNumPromotionInfos() is not greater than zero but an array is being allocated in CvUnit::reset");
m_pabHasPromotion = new bool[GC.getNumPromotionInfos()];
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
m_pabHasPromotion[iI] = false;
}
FAssertMsg((0 < GC.getNumTerrainInfos()), "GC.getNumTerrainInfos() is not greater than zero but a float array is being allocated in CvUnit::reset");
m_paiTerrainDoubleMoveCount = new int[GC.getNumTerrainInfos()];
m_paiExtraTerrainAttackPercent = new int[GC.getNumTerrainInfos()];
m_paiExtraTerrainDefensePercent = new int[GC.getNumTerrainInfos()];
/************************************************************************************************/
/* Afforess Start 06/14/10 */
/* */
/* */
/************************************************************************************************/
m_paiTerrainProtected = new int[GC.getNumTerrainInfos()];
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumTerrainInfos(); iI++)
{
m_paiTerrainDoubleMoveCount[iI] = 0;
m_paiExtraTerrainAttackPercent[iI] = 0;
m_paiExtraTerrainDefensePercent[iI] = 0;
/************************************************************************************************/
/* Afforess Start 06/14/10 */
/* */
/* */
/************************************************************************************************/
m_paiTerrainProtected[iI] = 0;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
FAssertMsg((0 < GC.getNumFeatureInfos()), "GC.getNumFeatureInfos() is not greater than zero but a float array is being allocated in CvUnit::reset");
m_paiFeatureDoubleMoveCount = new int[GC.getNumFeatureInfos()];
m_paiExtraFeatureDefensePercent = new int[GC.getNumFeatureInfos()];
m_paiExtraFeatureAttackPercent = new int[GC.getNumFeatureInfos()];
for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
{
m_paiFeatureDoubleMoveCount[iI] = 0;
m_paiExtraFeatureAttackPercent[iI] = 0;
m_paiExtraFeatureDefensePercent[iI] = 0;
}
FAssertMsg((0 < GC.getNumUnitCombatInfos()), "GC.getNumUnitCombatInfos() is not greater than zero but an array is being allocated in CvUnit::reset");
m_paiExtraUnitCombatModifier = new int[GC.getNumUnitCombatInfos()];
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
m_paiExtraUnitCombatModifier[iI] = 0;
}
m_unitCombatKeyedInfo.clear();
if ( !bIdentityChange )
{
AI_reset(NO_UNITAI, true);
}
}
m_Properties.clear();
}
//////////////////////////////////////
// graphical only setup
//////////////////////////////////////
void CvUnit::setupGraphical()
{
PROFILE_FUNC();
if (!GC.IsGraphicsInitialized())
{
return;
}
if (!isInViewport())
{
return;
}
if ( !isUsingDummyEntities() )
{
CvDLLEntity::setup();
}
if (getGroup()->getActivityType() == ACTIVITY_INTERCEPT)
{
airCircle(true);
}
}
void CvUnit::convert(CvUnit* pUnit)
{
PROFILE_FUNC();
CvPlot* pPlot = plot();
for (int iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
setHasPromotion(((PromotionTypes)iI), (pUnit->isHasPromotion((PromotionTypes)iI) || m_pUnitInfo->getFreePromotions(iI)));
}
setGameTurnCreated(pUnit->getGameTurnCreated());
setDamage(pUnit->getDamage());
setMoves(pUnit->getMoves());
/************************************************************************************************/
/* Afforess Start 06/06/10 */
/* */
/* */
/************************************************************************************************/
m_eOriginalOwner = pUnit->getOriginalOwner();
setAutoPromoting(pUnit->isAutoPromoting());
setAutoUpgrading(pUnit->isAutoUpgrading());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
setLevel(pUnit->getLevel());
int iOldModifier = std::max(1, 100 + GET_PLAYER(pUnit->getOwnerINLINE()).getLevelExperienceModifier());
int iOurModifier = std::max(1, 100 + GET_PLAYER(getOwnerINLINE()).getLevelExperienceModifier());
setExperience(std::max(0, (pUnit->getExperience() * iOurModifier) / iOldModifier));
setName(pUnit->getNameNoDesc());
// BUG - Unit Name - start
if (pUnit->isDescInName() && getBugOptionBOOL("MiscHover__UpdateUnitNameOnUpgrade", true, "BUG_UPDATE_UNIT_NAME_ON_UPGRADE"))
{
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
/*
CvWString szUnitType(pUnit->m_pUnitInfo->getDescription());
m_szName.replace(m_szName.find(szUnitType), szUnitType.length(), m_pUnitInfo->getDescription());
*/
CvWString szUnitType(pUnit->getDescription());
m_szName.replace(m_szName.find(szUnitType), szUnitType.length(), getDescription());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
//szUnitType.Format(L"%s", pUnit->m_pUnitInfo->getDescription());
}
// BUG - Unit Name - end
setLeaderUnitType(pUnit->getLeaderUnitType());
CvUnit* pTransportUnit = pUnit->getTransportUnit();
if (pTransportUnit != NULL)
{
pUnit->setTransportUnit(NULL);
setTransportUnit(pTransportUnit);
}
std::vector<CvUnit*> aCargoUnits;
pUnit->getCargoUnits(aCargoUnits);
for (uint i = 0; i < aCargoUnits.size(); ++i)
{
// Thomas SG - AC: Advanced Cargo START
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/30/09 Mongoose & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original BTS code
aCargoUnits[i]->setTransportUnit(this);
*/
// From Mongoose SDK
// Check cargo types and capacity when upgrading transports
int freeSpace = cargoSpaceAvailable(NO_SPECIALUNIT, aCargoUnits[i]->getDomainType());
{
for (int iK = 0; iK < aCargoUnits[i]->getNumSpecialUnitTypes(); iK++)
{
freeSpace = std::max(freeSpace,cargoSpaceAvailable(aCargoUnits[i]->getSpecialUnitType(iK), aCargoUnits[i]->getDomainType()));
}
if (freeSpace > 0)
{
aCargoUnits[i]->setTransportUnit(this);
}
else
{
aCargoUnits[i]->setTransportUnit(NULL);
aCargoUnits[i]->jumpToNearestValidPlot();
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
// Thomas SG - AC: Advanced Cargo END
}
pUnit->getGroup()->AI_setMissionAI(MISSIONAI_DELIBERATE_KILL, NULL, NULL);
pUnit->kill(true, NO_PLAYER, true);
}
void CvUnit::kill(bool bDelay, PlayerTypes ePlayer, bool bMessaged)
{
// If it's already dead (but delayed death in process) don't try to re-kill it
if ( m_bDeathDelay )
{
return;
}
killUnconditional(bDelay, ePlayer, bMessaged);
}
void CvUnit::killUnconditional(bool bDelay, PlayerTypes ePlayer, bool bMessaged)
{
PROFILE_FUNC();
CLLNode<IDInfo>* pUnitNode;
CvUnit* pTransportUnit;
CvUnit* pLoopUnit;
CvPlot* pPlot;
CvWString szBuffer;
PlayerTypes eOwner;
PlayerTypes eCapturingPlayer;
UnitTypes eCaptureUnitType;
pPlot = plot();
/*FAssertMsg(pPlot != NULL, "Plot is not assigned a valid value");*/
if (m_combatResult.bDeathMessaged)
{
bMessaged = true;
}
// < M.A.D. Nukes Start >
if(isMADEnabled())
{
setMADEnabled(false);
}
// < M.A.D. Nukes End >
if (pPlot != NULL)
{
std::vector<IDInfo> oldUnits;
oldUnits.clear();
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
oldUnits.push_back(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
}
for (uint i = 0; i < oldUnits.size(); i++)
{
pLoopUnit = ::getUnit(oldUnits[i]);
if (pLoopUnit != NULL)
{
if (pLoopUnit->getTransportUnit() == this)
{
//save old units because kill will clear the static list
std::vector<IDInfo> tempUnits = oldUnits;
if (pPlot->isValidDomainForLocation(*pLoopUnit))
{
pLoopUnit->setCapturingPlayer(NO_PLAYER);
}
/************************************************************************************************/
/* Afforess Start 09/08/10 */
/* */
/* */
/************************************************************************************************/
bool bSurvived = false;
CvPlot* pRescuePlot = NULL;
if (GC.getDefineINT("WAR_PRIZES"))
{
if (pPlot->isWater())
{
bool bAdjacentLand = false;
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()))
{
if (!pAdjacentPlot->isVisibleEnemyUnit(pLoopUnit))
{
pRescuePlot = pAdjacentPlot;
bAdjacentLand = true;
break;
}
}
}
}
if (bAdjacentLand)
{
if (GC.getGameINLINE().getSorenRandNum(10, "Unit Survives Drowning") <= 2 )
{
bSurvived = true;
}
}
}
}
if (bSurvived)
{
FAssertMsg(pRescuePlot != NULL, "pRescuePlot is expected to be a valid plot!");
pLoopUnit->setDamage(GC.getGameINLINE().getSorenRandNum(pLoopUnit->currHitPoints(), "Survival Damage"), NO_PLAYER);
pLoopUnit->move(pRescuePlot, false);
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_UNIT_SURVIVED_TRANSPORT_SINKING", pLoopUnit->getNameKey(), getNameKey());
AddDLLMessage(pLoopUnit->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, NULL, MESSAGE_TYPE_MINOR_EVENT);
}
else
{
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_UNIT_DROWNED", pLoopUnit->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), plot()->getX_INLINE(), plot()->getY_INLINE());
bMessaged = true;
pLoopUnit->kill(false, ePlayer, bMessaged);
oldUnits = tempUnits;
}
}
}
}
if (ePlayer != NO_PLAYER)
{
CvEventReporter::getInstance().unitKilled(this, ePlayer);
if ( (NO_UNIT != getLeaderUnitType())
|| (GC.getUnitClassInfo(getUnitClassType()).getMaxGlobalInstances() == 1) )
{
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive() && !bMessaged)
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_GENERAL_KILLED", getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_MAJOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), plot()->getX_INLINE(), plot()->getY_INLINE());
bMessaged = true;
}
}
}
}
//This is interrupting other messages and not coming up when it should be anyhow.
//if (!bMessaged)
//{
// MEMORY_TRACK_EXEMPT();
// szBuffer = gDLL->getText("TXT_KEY_MISC_UNIT_DEATH", getNameKey());
// AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), plot()->getX_INLINE(), plot()->getY_INLINE());
// m_combatResult.bDeathMessaged = false;
// bMessaged = true;
//}
if (bDelay)
{
startDelayedDeath();
return;
}
if (isMadeAttack() && nukeRange() != -1)
{
CvPlot* pTarget = getAttackPlot();
if (pTarget)
{
pTarget->nukeExplosion(nukeRange(), this);
setAttackPlot(NULL, false);
}
}
//Team Project (2)
/*****************************************************************************************************/
/** Author: TheLadiesOgre **/
/** Date: 21.09.2009 **/
/** ModComp: TLOTags **/
/** Reason Added: New Bool Flags **/
/** Notes: **/
/*****************************************************************************************************/
m_bDeathDelay = true;
if (isCanRespawn())
{
CvCity* pCapitalCity = GET_PLAYER(getOwnerINLINE()).getCapitalCity();
if ( pCapitalCity != NULL && pCapitalCity->plot() != plot())
{
setXY(pCapitalCity->getX_INLINE(), pCapitalCity->getY_INLINE(), false, false, false);
setDamage((9*GC.getMAX_HIT_POINTS())/10);
changeOneUpCount(-1);
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_BATTLEFIELD_EVAC", getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
m_bDeathDelay = false;
return;
}
}
if (isSurvivor())
{
setDamage(GC.getMAX_HIT_POINTS() - std::max(1,(getSurvivorChance() / 1000)));
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_UNIT_IS_HARDCORE", getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
m_bDeathDelay = false;
// Only applies to THIS combat - it might be attacked again the same turn
setSurvivor(false);
return;
}
/*****************************************************************************************************/
/** TheLadiesOgre; 21.09.2009; TLOTags **/
/*****************************************************************************************************/
finishMoves();
if (IsSelected())
{
if (gDLL->getInterfaceIFace()->getLengthSelectionList() == 1)
{
if (!(gDLL->getInterfaceIFace()->isFocused()) && !(gDLL->getInterfaceIFace()->isCitySelection()) && !(gDLL->getInterfaceIFace()->isDiploOrPopupWaiting()))
{
GC.getGameINLINE().updateSelectionList();
}
if (IsSelected())
{
gDLL->getInterfaceIFace()->setCycleSelectionCounter(1);
}
else
{
gDLL->getInterfaceIFace()->setDirty(SelectionCamera_DIRTY_BIT, true);
}
}
}
gDLL->getInterfaceIFace()->removeFromSelectionList(this);
// XXX this is NOT a hack, without it, the game crashes.
if ( !isUsingDummyEntities() && isInViewport() )
{
gDLL->getEntityIFace()->RemoveUnitFromBattle(this);
}
FAssertMsg(!isCombat(), "isCombat did not return false as expected");
pTransportUnit = getTransportUnit();
if (pTransportUnit != NULL)
{
setTransportUnit(NULL);
}
setReconPlot(NULL);
setBlockading(false);
FAssertMsg(getAttackPlot() == NULL, "The current unit instance's attack plot is expected to be NULL");
FAssertMsg(getCombatUnit() == NULL, "The current unit instance's combat unit is expected to be NULL");
}
GET_TEAM(getTeam()).changeUnitClassCount((UnitClassTypes)m_pUnitInfo->getUnitClassType(), -1);
GET_PLAYER(getOwnerINLINE()).changeUnitClassCount((UnitClassTypes)m_pUnitInfo->getUnitClassType(), -1);
GET_PLAYER(getOwnerINLINE()).changeExtraUnitCost(-(m_pUnitInfo->getExtraCost()));
if (m_pUnitInfo->getNukeRange() != -1)
{
GET_PLAYER(getOwnerINLINE()).changeNumNukeUnits(-1);
}
if (m_pUnitInfo->isMilitarySupport())
{
GET_PLAYER(getOwnerINLINE()).changeNumMilitaryUnits(-1);
}
GET_PLAYER(getOwnerINLINE()).changeAssets(-(m_pUnitInfo->getAssetValue()));
GET_PLAYER(getOwnerINLINE()).changeUnitPower(-(m_pUnitInfo->getPowerValue()));
/************************************************************************************************/
/* Afforess Start 04/16/10 */
/* */
/* Promotions affect iAsset and iPower values, so they must be removed on unit death */
/************************************************************************************************/
for (int iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
setHasPromotion(((PromotionTypes)iI), false);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pPlot != NULL)
{
OutputDebugString(CvString::format("Unit %S of player %S killed\n", getName().GetCString(),GET_PLAYER(getOwnerINLINE()).getCivilizationDescription(0)).c_str());
GET_PLAYER(getOwnerINLINE()).AI_changeNumAIUnits(AI_getUnitAIType(), -1);
AI_killed(); // Update AI counts for this unit
setXY(INVALID_PLOT_COORD, INVALID_PLOT_COORD, true);
joinGroup(NULL, false, false);
CvEventReporter::getInstance().unitLost(this);
eOwner = getOwnerINLINE();
eCapturingPlayer = getCapturingPlayer();
eCaptureUnitType = ((eCapturingPlayer != NO_PLAYER) ? getCaptureUnitType(GET_PLAYER(eCapturingPlayer).getCivilizationType()) : NO_UNIT);
// BUG - Unit Captured Event - start
PlayerTypes eFromPlayer = getOwner();
UnitTypes eCapturedUnitType = getUnitType();
// BUG - Unit Captured Event - end
if ((eCapturingPlayer != NO_PLAYER) && (eCaptureUnitType != NO_UNIT) && !(GET_PLAYER(eCapturingPlayer).isBarbarian()))
{
if (GET_PLAYER(eCapturingPlayer).isHuman() || GET_PLAYER(eCapturingPlayer).AI_captureUnit(eCaptureUnitType, pPlot) || 0 == GC.getDefineINT("AI_CAN_DISBAND_UNITS"))
{
CvUnit* pkCapturedUnit = GET_PLAYER(eCapturingPlayer).initUnit(eCaptureUnitType, pPlot->getX_INLINE(), pPlot->getY_INLINE(), NO_UNITAI, NO_DIRECTION, GC.getGameINLINE().getSorenRandNum(10000, "AI Unit Birthmark"));
if (pkCapturedUnit != NULL)
{
// BUG - Unit Captured Event - start
CvEventReporter::getInstance().unitCaptured(eFromPlayer, eCapturedUnitType, pkCapturedUnit);
// BUG - Unit Captured Event - end
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_CAPTURED_UNIT", GC.getUnitInfo(eCaptureUnitType).getTextKeyWide());
AddDLLMessage(eCapturingPlayer, true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_UNITCAPTURE", MESSAGE_TYPE_INFO, pkCapturedUnit->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
// Add a captured mission
if ( !pkCapturedUnit->isUsingDummyEntities() )
{
CvMissionDefinition kMission;
kMission.setMissionTime(GC.getMissionInfo(MISSION_CAPTURED).getTime() * gDLL->getSecsPerTurn());
kMission.setUnit(BATTLE_UNIT_ATTACKER, pkCapturedUnit);
kMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kMission.setPlot(pPlot);
kMission.setMissionType(MISSION_CAPTURED);
addMission(&kMission);
}
pkCapturedUnit->finishMoves();
if (!GET_PLAYER(eCapturingPlayer).isHuman())
{
CvPlot* pPlot = pkCapturedUnit->plot();
if (pPlot && !pPlot->isCity(false))
{
if (GET_PLAYER(eCapturingPlayer).AI_getPlotDanger(pPlot) && GC.getDefineINT("AI_CAN_DISBAND_UNITS"))
{
pkCapturedUnit->kill(false, NO_PLAYER, true);
}
}
}
}
}
}
GET_PLAYER(getOwnerINLINE()).deleteUnit(getID());
}
}
void CvUnit::NotifyEntity(MissionTypes eMission)
{
if ( !isUsingDummyEntities() && isInViewport() )
{
gDLL->getEntityIFace()->NotifyEntity(getUnitEntity(), eMission);
}
}
void CvUnit::doTurn()
{
PROFILE("CvUnit::doTurn()")
FAssertMsg(!isDead(), "isDead did not return false as expected");
FAssertMsg(getGroup() != NULL, "getGroup() is not expected to be equal with NULL");
testPromotionReady();
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* */
/************************************************************************************************/
m_iControlPointsLeft = controlPoints(); //restore control points for commander
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
m_iCommanderID = -1; //reset used commander for combat units
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isBlockading())
{
collectBlockadeGold();
}
if (isSpy() && isIntruding() && !isCargo())
{
TeamTypes eTeam = plot()->getTeam();
if (NO_TEAM != eTeam)
{
if (GET_TEAM(getTeam()).isOpenBorders(eTeam))
{
testSpyIntercepted(plot()->getOwnerINLINE(), GC.getDefineINT("ESPIONAGE_SPY_NO_INTRUDE_INTERCEPT_MOD"));
}
else
{
testSpyIntercepted(plot()->getOwnerINLINE(), GC.getDefineINT("ESPIONAGE_SPY_INTERCEPT_MOD"));
}
}
}
if (baseCombatStr() > 0)
{
#ifdef MULTI_FEATURE_MOD
for (int i=0; i<plot()->getNumFeatures(); i++)
{
FeatureTypes eFeature = plot()->getFeatureByIndex(i);
if (0 != GC.getFeatureInfo(eFeature).getTurnDamage())
{
changeDamage(GC.getFeatureInfo(eFeature).getTurnDamage(), NO_PLAYER);
}
}
#else
FeatureTypes eFeature = plot()->getFeatureType();
if (NO_FEATURE != eFeature)
{
if (0 != GC.getFeatureInfo(eFeature).getTurnDamage())
{
changeDamage(GC.getFeatureInfo(eFeature).getTurnDamage(), NO_PLAYER);
}
}
#endif
/************************************************************************************************/
/* Afforess Start 05/17/10 */
/* */
/* */
/************************************************************************************************/
if (plot()->getTerrainTurnDamage(this) != 0)
{
changeDamage(plot()->getTerrainTurnDamage(this), NO_PLAYER);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
if (hasMoved())
{
if (isAlwaysHeal())
{
doHeal();
}
}
else
{
if (isHurt())
{
doHeal();
}
if (!isCargo())
{
changeFortifyTurns(1);
}
}
//Team Project (2)
/*****************************************************************************************************/
/** Author: TheLadiesOgre **/
/** Date: 29.09.2009 **/
/** ModComp: TLOTags **/
/** Reason Added: New Bool Flags **/
/** Notes: **/
/*****************************************************************************************************/
if (isCanRespawn())
{
setCanRespawn(false);
}
if (isSurvivor())
{
setSurvivor(false);
}
/*****************************************************************************************************/
/** TheLadiesOgre; 29.09.2009; TLOTags **/
/*****************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* */
/************************************************************************************************/
if (isSpy() && m_iSleepTimer > 0)
{
if (getFortifyTurns() == GC.getDefineINT("MAX_FORTIFY_TURNS"))
{
getGroup()->setActivityType(ACTIVITY_AWAKE);
m_iSleepTimer = 0;
}
}
if (getDesiredDiscoveryTech() != NO_TECH && canDiscover(NULL))
{
if (getDesiredDiscoveryTech() == getDiscoveryTech())
{
getGroup()->setActivityType(ACTIVITY_AWAKE);
setDesiredDiscoveryTech(NO_TECH);
discover();
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
changeImmobileTimer(-1);
setMadeAttack(false);
setMadeInterception(false);
setReconPlot(NULL);
setMoves(0);
//Afforess earned XP start
if (canAcquirePromotionAny() && GET_PLAYER(getOwnerINLINE()).getFractionalXPEarnedInCity() > 0)
{
CvCity* pCity = plot()->getPlotCity();
if (pCity != NULL && pCity->getOwnerINLINE() == getOwnerINLINE())
{
float fXP = (float)GET_PLAYER(getOwnerINLINE()).getFractionalXPEarnedInCity();
//Normal game speed train percent != 100, try to use it instead of 100 to scale by as the base modifier
int iNormal = GC.getInfoTypeForString("GAMESPEED_NORMAL");
fXP *= (iNormal == -1 ? 100 : GC.getGameSpeedInfo((GameSpeedTypes)iNormal).getTrainPercent());
fXP /= GC.getGameSpeedInfo((GameSpeedTypes)GC.getGameINLINE().getGameSpeedType()).getTrainPercent();
setExperience100(getExperience100() + std::max(1, (int)fXP));
}
}
//Afforess earned xp end
// < M.A.D. Nukes Start >
if(isMADEnabled())
{
doMADNukes(false);
finishMoves();
}
// < M.A.D. Nukes End >
}
void CvUnit::updateAirStrike(CvPlot* pPlot, bool bQuick, bool bFinish)
{
bool bVisible = false;
if (!bFinish)
{
if (isFighting())
{
return;
}
if (!bQuick)
{
bVisible = isCombatVisible(NULL);
}
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - NB: A-Bomb START
if(canNuke(pPlot)){
kill(true, NO_PLAYER, true);
return;
}
// Dale - NB: A-Bomb END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
if (!airStrike(pPlot))
{
return;
}
if (bVisible)
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRSTRIKE);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
setCombatTimer(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime());
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
kAirMission.setMissionTime(getCombatTimer() * gDLL->getSecsPerTurn());
if (pPlot->isActiveVisible(false))
{
addMission(&kAirMission);
}
return;
}
}
CvUnit *pDefender = getCombatUnit();
if (pDefender != NULL)
{
pDefender->setCombatUnit(NULL);
}
setCombatUnit(NULL);
setAttackPlot(NULL, false);
getGroup()->clearMissionQueue();
if (isSuicide() && !isDead())
{
kill(true);
}
}
void CvUnit::resolveAirCombat(CvUnit* pInterceptor, CvPlot* pPlot, CvAirMissionDefinition& kBattle)
{
CvWString szBuffer;
int iTheirStrength = (DOMAIN_AIR == pInterceptor->getDomainType() ? pInterceptor->airCurrCombatStr(this) : pInterceptor->currCombatStr(NULL, NULL));
int iOurStrength = (DOMAIN_AIR == getDomainType() ? airCurrCombatStr(pInterceptor) : currCombatStr(NULL, NULL));
int iTotalStrength = iOurStrength + iTheirStrength;
if (0 == iTotalStrength)
{
FAssert(false);
return;
}
/********************************************************************************/
/* BETTER_BTS_AI_MOD 10/19/08 Roland J & jdog5000 */
/* */
/* Combat mechanics */
/********************************************************************************/
/*
int iOurOdds = (100 * iOurStrength) / std::max(1, iTotalStrength);
int iOurRoundDamage = (pInterceptor->currInterceptionProbability() * GC.getDefineINT("MAX_INTERCEPTION_DAMAGE")) / 100;
int iTheirRoundDamage = (currInterceptionProbability() * GC.getDefineINT("MAX_INTERCEPTION_DAMAGE")) / 100;
if (getDomainType() == DOMAIN_AIR)
{
iTheirRoundDamage = std::max(GC.getDefineINT("MIN_INTERCEPTION_DAMAGE"), iTheirRoundDamage);
}
//original BTS code
int iTheirDamage = 0;
int iOurDamage = 0;
for (int iRound = 0; iRound < GC.getDefineINT("INTERCEPTION_MAX_ROUNDS"); ++iRound)
*/
// For air v air, more rounds and factor in strength for per round damage
int iOurOdds = (100 * iOurStrength) / std::max(1, iTotalStrength);
int iMaxRounds = 0;
int iOurRoundDamage = 0;
int iTheirRoundDamage = 0;
// Air v air is more like standard combat
// Round damage in this case will now depend on strength and interception probability
if( GC.getBBAI_AIR_COMBAT() && (DOMAIN_AIR == pInterceptor->getDomainType() && DOMAIN_AIR == getDomainType()) )
{
int iBaseDamage = GC.getDefineINT("AIR_COMBAT_DAMAGE");
int iOurFirepower = ((airMaxCombatStr(pInterceptor) + iOurStrength + 1) / 2);
int iTheirFirepower = ((pInterceptor->airMaxCombatStr(this) + iTheirStrength + 1) / 2);
int iStrengthFactor = ((iOurFirepower + iTheirFirepower + 1) / 2);
int iTheirInterception = std::max(pInterceptor->maxInterceptionProbability(),2*GC.getDefineINT("MIN_INTERCEPTION_DAMAGE"));
int iOurInterception = std::max(maxInterceptionProbability(),2*GC.getDefineINT("MIN_INTERCEPTION_DAMAGE"));
iOurRoundDamage = std::max(1, ((iBaseDamage * (iTheirFirepower + iStrengthFactor) * iTheirInterception) / ((iOurFirepower + iStrengthFactor) * 100)));
iTheirRoundDamage = std::max(1, ((iBaseDamage * (iOurFirepower + iStrengthFactor) * iOurInterception) / ((iTheirFirepower + iStrengthFactor) * 100)));
iMaxRounds = 2*GC.getDefineINT("INTERCEPTION_MAX_ROUNDS") - 1;
}
else
{
iOurRoundDamage = (pInterceptor->currInterceptionProbability() * GC.getDefineINT("MAX_INTERCEPTION_DAMAGE")) / 100;
iTheirRoundDamage = (currInterceptionProbability() * GC.getDefineINT("MAX_INTERCEPTION_DAMAGE")) / 100;
if (getDomainType() == DOMAIN_AIR)
{
iTheirRoundDamage = std::max(GC.getDefineINT("MIN_INTERCEPTION_DAMAGE"), iTheirRoundDamage);
}
iMaxRounds = GC.getDefineINT("INTERCEPTION_MAX_ROUNDS");
}
int iTheirDamage = 0;
int iOurDamage = 0;
for (int iRound = 0; iRound < iMaxRounds; ++iRound)
/********************************************************************************/
/* BETTER_BTS_AI_MOD END */
/********************************************************************************/
{
if (GC.getGameINLINE().getSorenRandNum(100, "Air combat") < iOurOdds)
{
if (DOMAIN_AIR == pInterceptor->getDomainType())
{
iTheirDamage += iTheirRoundDamage;
pInterceptor->changeDamage(iTheirRoundDamage, getOwnerINLINE());
if (pInterceptor->isDead())
{
break;
}
}
}
else
{
iOurDamage += iOurRoundDamage;
changeDamage(iOurRoundDamage, pInterceptor->getOwnerINLINE());
if (isDead())
{
break;
}
}
}
if (isDead())
{
if (iTheirRoundDamage > 0)
{
int iExperience = attackXPValue();
iExperience = (iExperience * iOurStrength) / std::max(1, iTheirStrength);
iExperience = range(iExperience, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));
pInterceptor->changeExperience(iExperience, maxXPValue(), true, pPlot->getOwnerINLINE() == pInterceptor->getOwnerINLINE(), !isBarbarian());
}
}
else if (pInterceptor->isDead())
{
int iExperience = pInterceptor->defenseXPValue();
iExperience = (iExperience * iTheirStrength) / std::max(1, iOurStrength);
iExperience = range(iExperience, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));
changeExperience(iExperience, pInterceptor->maxXPValue(), true, pPlot->getOwnerINLINE() == getOwnerINLINE(), !pInterceptor->isBarbarian());
}
else if (iOurDamage > 0)
{
if (iTheirRoundDamage > 0)
{
pInterceptor->changeExperience(GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL"), maxXPValue(), true, pPlot->getOwnerINLINE() == pInterceptor->getOwnerINLINE(), !isBarbarian());
}
}
else if (iTheirDamage > 0)
{
changeExperience(GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL"), pInterceptor->maxXPValue(), true, pPlot->getOwnerINLINE() == getOwnerINLINE(), !pInterceptor->isBarbarian());
}
kBattle.setDamage(BATTLE_UNIT_ATTACKER, iOurDamage);
kBattle.setDamage(BATTLE_UNIT_DEFENDER, iTheirDamage);
}
void CvUnit::updateAirCombat(bool bQuick)
{
CvUnit* pInterceptor = NULL;
bool bFinish = false;
FAssert(getDomainType() == DOMAIN_AIR || getDropRange() > 0);
if (getCombatTimer() > 0)
{
changeCombatTimer(-1);
if (getCombatTimer() > 0)
{
return;
}
else
{
bFinish = true;
}
}
CvPlot* pPlot = getAttackPlot();
if (pPlot == NULL)
{
return;
}
if (bFinish)
{
pInterceptor = getCombatUnit();
}
else
{
pInterceptor = bestInterceptor(pPlot);
}
if (pInterceptor == NULL)
{
setAttackPlot(NULL, false);
setCombatUnit(NULL);
getGroup()->clearMissionQueue();
return;
}
//check if quick combat
bool bVisible = false;
if (!bQuick)
{
bVisible = isCombatVisible(pInterceptor);
}
//if not finished and not fighting yet, set up combat damage and mission
if (!bFinish)
{
if (!isFighting())
{
if (plot()->isFighting() || pPlot->isFighting())
{
return;
}
setMadeAttack(true);
setCombatUnit(pInterceptor, true);
pInterceptor->setCombatUnit(this, false);
}
FAssertMsg(pInterceptor != NULL, "Defender is not assigned a valid value");
FAssertMsg(plot()->isFighting(), "Current unit instance plot is not fighting as expected");
FAssertMsg(pInterceptor->plot()->isFighting(), "pPlot is not fighting as expected");
CvAirMissionDefinition kAirMission;
if (DOMAIN_AIR != getDomainType())
{
kAirMission.setMissionType(MISSION_PARADROP);
}
else
{
kAirMission.setMissionType(MISSION_AIRSTRIKE);
}
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, pInterceptor);
resolveAirCombat(pInterceptor, pPlot, kAirMission);
if (!bVisible)
{
bFinish = true;
}
else
{
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime() * gDLL->getSecsPerTurn());
setCombatTimer(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime());
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
if (pPlot->isActiveVisible(false))
{
addMission(&kAirMission);
}
}
changeMoves(GC.getMOVE_DENOMINATOR());
if (DOMAIN_AIR != pInterceptor->getDomainType())
{
pInterceptor->setMadeInterception(true);
}
if (isDead())
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_SHOT_DOWN_ENEMY", pInterceptor->getNameKey(), getNameKey(), getVisualCivAdjective(pInterceptor->getTeam()));
AddDLLMessage(pInterceptor->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_SHOT_DOWN", getNameKey(), pInterceptor->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, pInterceptor->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
else if (kAirMission.getDamage(BATTLE_UNIT_ATTACKER) > 0)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_HURT_ENEMY_AIR", pInterceptor->getNameKey(), getNameKey(), -(kAirMission.getDamage(BATTLE_UNIT_ATTACKER)), getVisualCivAdjective(pInterceptor->getTeam()));
AddDLLMessage(pInterceptor->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIR_UNIT_HURT", getNameKey(), pInterceptor->getNameKey(), -(kAirMission.getDamage(BATTLE_UNIT_ATTACKER)));
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, pInterceptor->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
if (pInterceptor->isDead())
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_SHOT_DOWN_ENEMY", getNameKey(), pInterceptor->getNameKey(), pInterceptor->getVisualCivAdjective(getTeam()));
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, pInterceptor->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_SHOT_DOWN", pInterceptor->getNameKey(), getNameKey());
AddDLLMessage(pInterceptor->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
else if (kAirMission.getDamage(BATTLE_UNIT_DEFENDER) > 0)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_DAMAGED_ENEMY_AIR", getNameKey(), pInterceptor->getNameKey(), -(kAirMission.getDamage(BATTLE_UNIT_DEFENDER)), pInterceptor->getVisualCivAdjective(getTeam()));
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, pInterceptor->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_AIR_UNIT_DAMAGED", pInterceptor->getNameKey(), getNameKey(), -(kAirMission.getDamage(BATTLE_UNIT_DEFENDER)));
AddDLLMessage(pInterceptor->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
if (0 == kAirMission.getDamage(BATTLE_UNIT_ATTACKER) + kAirMission.getDamage(BATTLE_UNIT_DEFENDER))
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ABORTED_ENEMY_AIR", pInterceptor->getNameKey(), getNameKey(), getVisualCivAdjective(getTeam()));
AddDLLMessage(pInterceptor->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, pInterceptor->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_AIR_UNIT_ABORTED", getNameKey(), pInterceptor->getNameKey());
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
}
if (bFinish)
{
setAttackPlot(NULL, false);
setCombatUnit(NULL);
pInterceptor->setCombatUnit(NULL);
if (!isDead() && isSuicide())
{
kill(true);
}
}
}
void CvUnit::resolveCombat(CvUnit* pDefender, CvPlot* pPlot, CvBattleDefinition& kBattle)
{
PROFILE_FUNC();
MEMORY_TRACK();
CombatDetails cdAttackerDetails;
CombatDetails cdDefenderDetails;
AI_setPredictedHitPoints(-1);
pDefender->AI_setPredictedHitPoints(-1);
int iAttackerStrength = currCombatStr(NULL, NULL, &cdAttackerDetails);
int iAttackerFirepower = currFirepower(NULL, NULL);
int iDefenderStrength;
int iAttackerDamage;
int iDefenderDamage;
int iDefenderOdds;
/************************************************************************************************/
/* Afforess Start 03/15/10 Coded By: KillMePlease */
/* */
/* Occasional Promotions */
/************************************************************************************************/
bool bAttackerWithdrawn = false;
bool bAttackerHasLostNoHP = true;
int iAttackerInitialDamage = getDamage();
int iDefenderInitialDamage = pDefender->getDamage();
int iInitialDefXP = pDefender->getExperience100();
int iInitialAttXP = getExperience100();
int iInitialAttGGXP = GET_PLAYER(getOwnerINLINE()).getCombatExperience();
int iInitialDefGGXP = GET_PLAYER(pDefender->getOwnerINLINE()).getCombatExperience();
bool bDynamicXP = GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
getDefenderCombatValues(*pDefender, pPlot, iAttackerStrength, iAttackerFirepower, iDefenderOdds, iDefenderStrength, iAttackerDamage, iDefenderDamage, &cdDefenderDetails, pDefender);
int iAttackerKillOdds = iDefenderOdds * (100 - withdrawalProbability()) / 100;
if (isHuman() || pDefender->isHuman())
{
//Added ST
CyArgsList pyArgsCD;
pyArgsCD.add(gDLL->getPythonIFace()->makePythonObject(&cdAttackerDetails));
pyArgsCD.add(gDLL->getPythonIFace()->makePythonObject(&cdDefenderDetails));
pyArgsCD.add(getCombatOdds(this, pDefender));
CvEventReporter::getInstance().genericEvent("combatLogCalc", pyArgsCD.makeFunctionArgs());
}
collateralCombat(pPlot, pDefender);
/************************************************************************************************/
/* Afforess Start 02/22/10 Coded by: KillMePlease */
/* */
/* Defender Withdraw */
/************************************************************************************************/
int iCloseCombatRoundNum = -1;
bool bTryMobileWithdraw = false; //if unit will be trying to withdraw from a plot it occupies
if (pPlot->getNumDefenders(pDefender->getOwner()) == 1 && pDefender->baseMoves() > baseMoves()) //must be faster than attacker
{
bTryMobileWithdraw = true;
}
int iWinningOdds = getCombatOdds(this, pDefender);
bool bDefenderSkirmish = false; //iWinningOdds > 60;
m_combatResult.bDefenderWithdrawn = false;
m_combatResult.pPlot = NULL;
m_combatResult.iAttacksCount++;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
while (true)
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */
/* */
/* Lead From Behind */
/************************************************************************************************/
// From Lead From Behind by UncutDragon
/* original code
if (GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("COMBAT_DIE_SIDES"), "Combat") < iDefenderOdds)
*/ // modified
if (GC.getGameINLINE().getSorenRandNum(GC.getCOMBAT_DIE_SIDES(), "Combat") < iDefenderOdds)
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
if (getCombatFirstStrikes() == 0)
{
if (getDamage() + iAttackerDamage >= maxHitPoints() && GC.getGameINLINE().getSorenRandNum(100, "Withdrawal") < withdrawalProbability())
{
flankingStrikeCombat(pPlot, iAttackerStrength, iAttackerFirepower, iAttackerKillOdds, iDefenderDamage, pDefender);
/************************************************************************************************/
/* Afforess Start 03/15/10 Coded By: KillMePlease */
/* */
/* Occasional Promotions */
/************************************************************************************************/
bAttackerWithdrawn = true;
/** Great Generals From Barbarian Combat Start **/
if (!bDynamicXP)
changeExperience(GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL"), pDefender->maxXPValue(), true, pPlot->getOwnerINLINE() == getOwnerINLINE(), (!pDefender->isBarbarian() || GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_GENERALS)));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// BUG - Combat Events - start
CvEventReporter::getInstance().combatRetreat(this, pDefender);
// BUG - Combat Events - end
break;
}
changeDamage(iAttackerDamage, pDefender->getOwnerINLINE());
/************************************************************************************************/
/* Afforess Start 03/15/10 Coded By: KillMePlease */
/* */
/* Occasional Promotions */
/************************************************************************************************/
bAttackerHasLostNoHP = false;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pDefender->getCombatFirstStrikes() > 0 && pDefender->isRanged())
{
kBattle.addFirstStrikes(BATTLE_UNIT_DEFENDER, 1);
kBattle.addDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_RANGED, iAttackerDamage);
}
cdAttackerDetails.iCurrHitPoints = currHitPoints();
if (isHuman() || pDefender->isHuman())
{
CyArgsList pyArgs;
pyArgs.add(gDLL->getPythonIFace()->makePythonObject(&cdAttackerDetails));
pyArgs.add(gDLL->getPythonIFace()->makePythonObject(&cdDefenderDetails));
pyArgs.add(1);
pyArgs.add(iAttackerDamage);
CvEventReporter::getInstance().genericEvent("combatLogHit", pyArgs.makeFunctionArgs());
}
}
}
else
{
if (pDefender->getCombatFirstStrikes() == 0)
{
/************************************************************************************************/
/* Afforess Start 02/22/10 Coded by: KillMePlease */
/* */
/* Defender Withdraw */
/************************************************************************************************/
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_DEFENDER_WITHDRAW))
{
iCloseCombatRoundNum++;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (std::min(GC.getMAX_HIT_POINTS(), pDefender->getDamage() + iDefenderDamage) > combatLimit())
{
if (!bDynamicXP)
changeExperience(GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL"), pDefender->maxXPValue(), true, pPlot->getOwnerINLINE() == getOwnerINLINE(), !pDefender->isBarbarian());
pDefender->setDamage(combatLimit(), getOwnerINLINE());
// BUG - Combat Events - start
CvEventReporter::getInstance().combatWithdrawal(this, pDefender);
// BUG - Combat Events - end
break;
}
/************************************************************************************************/
/* Afforess Start 02/22/10 Coded by: KillMePlease */
/* */
/* Defender Withdraw */
/************************************************************************************************/
else if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_DEFENDER_WITHDRAW) && (
(pDefender->getDamage() + iDefenderDamage >= maxHitPoints() || bDefenderSkirmish) &&
GC.getGameINLINE().getSorenRandNum(100, "Withdrawal") < pDefender->withdrawalProbability()
&& !isSuicide() && iCloseCombatRoundNum > 0) ) //can not to escape at close combat round 1
{
//attacker got experience
int iExperience = pDefender->attackXPValue();
iExperience = ((iExperience * iDefenderStrength) / iAttackerStrength);
iExperience = range(iExperience, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));
changeExperience(iExperience, pDefender->maxXPValue(), true, pPlot->getOwnerINLINE() == getOwnerINLINE(), (!pDefender->isBarbarian() || GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_GENERALS)));
//if (pPlot->getNumDefenders(pDefender->getOwner()) == 1 && pDefender->baseMoves() > baseMoves()) //must be faster to flee a battle
{
bool bEnemy = true;
for (int iPlot = 0; iPlot < NUM_DIRECTION_TYPES; iPlot++)
{
CvPlot* pAdjacentPlot = plotDirection(pDefender->plot()->getX_INLINE(), pDefender->plot()->getY_INLINE(), ((DirectionTypes)iPlot));
if (pAdjacentPlot != NULL)
{
if (pDefender->canMoveInto(pAdjacentPlot))
{
//Check that this tile is safe (ie, no attackers next to it)
for (int iPlot2 = 0; iPlot2 < NUM_DIRECTION_TYPES; iPlot2++)
{
CvPlot* pAdjacentPlot2 = plotDirection(pAdjacentPlot->getX_INLINE(), pAdjacentPlot->getY_INLINE(), ((DirectionTypes)iPlot2));
if (pAdjacentPlot2 != NULL)
{
CLLNode<IDInfo>* pUnitNode;
pUnitNode = pAdjacentPlot2->headUnitNode();
CvUnit* pLoopUnit;
bEnemy = false;
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pAdjacentPlot2->nextUnitNode(pUnitNode);
if (GET_TEAM(pLoopUnit->getTeam()).isAtWar(pDefender->getTeam()))
{
bEnemy = true;
break;
}
}
if (bEnemy)
{
break;
}
}
}
if (!bEnemy)
{
m_combatResult.pPlot = pAdjacentPlot;
m_combatResult.bDefenderWithdrawn = true;
if (!bDynamicXP)
pDefender->changeExperience(GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL"), pDefender->maxXPValue(),
true, pPlot->getOwnerINLINE() == pDefender->getOwnerINLINE(), (!isBarbarian() || GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_GENERALS)));
doDynamicXP(pDefender, pPlot, iAttackerInitialDamage, iWinningOdds, iDefenderInitialDamage, iInitialAttXP, iInitialDefXP, iInitialAttGGXP, iInitialDefGGXP, false, false);
return;
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
pDefender->changeDamage(iDefenderDamage, getOwnerINLINE());
if (getCombatFirstStrikes() > 0 && isRanged())
{
kBattle.addFirstStrikes(BATTLE_UNIT_ATTACKER, 1);
kBattle.addDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_RANGED, iDefenderDamage);
}
cdDefenderDetails.iCurrHitPoints=pDefender->currHitPoints();
if (isHuman() || pDefender->isHuman())
{
CyArgsList pyArgs;
pyArgs.add(gDLL->getPythonIFace()->makePythonObject(&cdAttackerDetails));
pyArgs.add(gDLL->getPythonIFace()->makePythonObject(&cdDefenderDetails));
pyArgs.add(0);
pyArgs.add(iDefenderDamage);
CvEventReporter::getInstance().genericEvent("combatLogHit", pyArgs.makeFunctionArgs());
}
}
}
if (getCombatFirstStrikes() > 0)
{
changeCombatFirstStrikes(-1);
}
if (pDefender->getCombatFirstStrikes() > 0)
{
pDefender->changeCombatFirstStrikes(-1);
}
if (isDead() || pDefender->isDead())
{
if (isDead())
{
int iExperience = defenseXPValue();
iExperience = ((iExperience * iAttackerStrength) / iDefenderStrength);
iExperience = range(iExperience, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));
/*************************************************************************************************/
/** Great Generals From Barbarian Combat Start **/
/** Oct 19 2009 **/
/** **/
/*************************************************************************************************/
if (!bDynamicXP)
pDefender->changeExperience(iExperience, maxXPValue(), true, pPlot->getOwnerINLINE() == pDefender->getOwnerINLINE(), (!isBarbarian() || GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_GENERALS)));
/*************************************************************************************************/
/** Great Generals From Barbarian Combat End **/
/*************************************************************************************************/
// Koshling - add rolling history of combat results to allow the AI to adapt
// to what it sees happening
pPlot->area()->recordCombatDeath(getOwnerINLINE(), getUnitType(), pDefender->getUnitType());
}
else
{
flankingStrikeCombat(pPlot, iAttackerStrength, iAttackerFirepower, iAttackerKillOdds, iDefenderDamage, pDefender);
int iExperience = pDefender->attackXPValue();
iExperience = ((iExperience * iDefenderStrength) / iAttackerStrength);
iExperience = range(iExperience, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));
/*************************************************************************************************/
/** Great Generals From Barbarian Combat Start **/
/** Oct 19 2009 **/
/** **/
/*************************************************************************************************/
if (!bDynamicXP)
{
changeExperience(iExperience, pDefender->maxXPValue(), true, pPlot->getOwnerINLINE() == getOwnerINLINE(), (!pDefender->isBarbarian() || GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_GENERALS)));
}
/*************************************************************************************************/
/** Great Generals From Barbarian Combat End **/
/*************************************************************************************************/
// Koshling - add rolling history of combat results to allow the AI to adapt
// to what it sees happening
pPlot->area()->recordCombatDeath(pDefender->getOwnerINLINE(), pDefender->getUnitType(), getUnitType());
}
break;
}
}
/************************************************************************************************/
/* Afforess Start 05/6/10 */
/* */
/* */
/************************************************************************************************/
bool bPromotion = false;
bool bDefPromotion = false;
doBattleFieldPromotions(pDefender, cdDefenderDetails, pPlot, bAttackerHasLostNoHP, bAttackerWithdrawn, iAttackerInitialDamage, iWinningOdds, iInitialAttXP, iInitialAttGGXP, iDefenderInitialDamage, iInitialDefXP, iInitialDefGGXP, bPromotion, bDefPromotion);
doDynamicXP(pDefender, pPlot, iAttackerInitialDamage, iWinningOdds, iDefenderInitialDamage, iInitialAttXP, iInitialDefXP, iInitialAttGGXP, iInitialDefGGXP, bPromotion, bDefPromotion);
doCommerceAttacks(pDefender, pPlot);
doCommerceAttacks(this, pPlot);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
void CvUnit::updateCombat(bool bQuick)
{
PROFILE_FUNC();
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - SA: Stack Attack START
if (GC.isDCM_STACK_ATTACK())
{
updateStackCombat(bQuick);
return;
}
// Dale - SA: Stack Attack END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
CvWString szBuffer;
bool bFinish = false;
bool bVisible = false;
if (getCombatTimer() > 0)
{
changeCombatTimer(-1);
if (getCombatTimer() > 0)
{
return;
}
else
{
bFinish = true;
}
}
CvPlot* pPlot = getAttackPlot();
if (pPlot == NULL)
{
return;
}
if (getDomainType() == DOMAIN_AIR)
{
updateAirStrike(pPlot, bQuick, bFinish);
return;
}
CvUnit* pDefender = NULL;
if (bFinish)
{
pDefender = getCombatUnit();
}
else
{
pDefender = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
}
if (pDefender == NULL)
{
setAttackPlot(NULL, false);
setCombatUnit(NULL);
getGroup()->groupMove(pPlot, true, ((canAdvance(pPlot, 0)) ? this : NULL));
getGroup()->clearMissionQueue();
return;
}
//check if quick combat
if (!bQuick)
{
bVisible = isCombatVisible(pDefender);
}
//FAssertMsg((pPlot == pDefender->plot()), "There is not expected to be a defender or the defender's plot is expected to be pPlot (the attack plot)");
//if not finished and not fighting yet, set up combat damage and mission
if (!bFinish)
{
if (!isFighting())
{
PROFILE("CvUnit::updateCombat.StartFight");
if (plot()->isFighting() || pPlot->isFighting())
{
return;
}
setMadeAttack(true);
//rotate to face plot
DirectionTypes newDirection = estimateDirection(this->plot(), pDefender->plot());
if (newDirection != NO_DIRECTION)
{
setFacingDirection(newDirection);
}
//rotate enemy to face us
newDirection = estimateDirection(pDefender->plot(), this->plot());
if (newDirection != NO_DIRECTION)
{
pDefender->setFacingDirection(newDirection);
}
setCombatUnit(pDefender, true);
pDefender->setCombatUnit(this, false);
pDefender->getGroup()->clearMissionQueue();
bool bFocused = (bVisible && isCombatFocus() && gDLL->getInterfaceIFace()->isCombatFocus() && plot()->isInViewport() && pDefender->isInViewport());
if (bFocused)
{
DirectionTypes directionType = directionXY(plot(), pPlot);
// N NE E SE S SW W NW
NiPoint2 directions[8] = {NiPoint2(0, 1), NiPoint2(1, 1), NiPoint2(1, 0), NiPoint2(1, -1), NiPoint2(0, -1), NiPoint2(-1, -1), NiPoint2(-1, 0), NiPoint2(-1, 1)};
NiPoint3 attackDirection = NiPoint3(directions[directionType].x, directions[directionType].y, 0);
float plotSize = GC.getPLOT_SIZE();
NiPoint3 lookAtPoint(plot()->getPoint().x + plotSize / 2 * attackDirection.x, plot()->getPoint().y + plotSize / 2 * attackDirection.y, (plot()->getPoint().z + pPlot->getPoint().z) / 2);
attackDirection.Unitize();
gDLL->getInterfaceIFace()->lookAt(lookAtPoint, (((getOwnerINLINE() != GC.getGameINLINE().getActivePlayer()) || gDLL->getGraphicOption(GRAPHICOPTION_NO_COMBAT_ZOOM)) ? CAMERALOOKAT_BATTLE : CAMERALOOKAT_BATTLE_ZOOM_IN), attackDirection);
}
else
{
MEMORY_TRACK_EXEMPT();
PlayerTypes eAttacker = getVisualOwner(pDefender->getTeam());
CvWString szMessage;
if (BARBARIAN_PLAYER != eAttacker)
{
szMessage = gDLL->getText("TXT_KEY_MISC_YOU_UNITS_UNDER_ATTACK", GET_PLAYER(getOwnerINLINE()).getNameKey());
}
else
{
szMessage = gDLL->getText("TXT_KEY_MISC_YOU_UNITS_UNDER_ATTACK_UNKNOWN");
}
AddDLLMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szMessage, "AS2D_COMBAT", MESSAGE_TYPE_DISPLAY_ONLY, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true);
}
}
//OutputDebugString("UI interaction - unit attacked 1\n");
GET_PLAYER(pDefender->getOwnerINLINE()).setTurnHadUIInteraction(true);
FAssertMsg(pDefender != NULL, "Defender is not assigned a valid value");
FAssertMsg(plot()->isFighting(), "Current unit instance plot is not fighting as expected");
FAssertMsg(pPlot->isFighting(), "pPlot is not fighting as expected");
if (!pDefender->canDefend())
{
if (!bVisible)
{
bFinish = true;
}
else
{
if ( !pDefender->isUsingDummyEntities() )
{
CvMissionDefinition kMission;
kMission.setMissionTime(getCombatTimer() * gDLL->getSecsPerTurn());
kMission.setMissionType(MISSION_SURRENDER);
kMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kMission.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
kMission.setPlot(pPlot);
addMission(&kMission);
}
// Surrender mission
setCombatTimer(GC.getMissionInfo(MISSION_SURRENDER).getTime());
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
}
// Kill them!
pDefender->setDamage(GC.getMAX_HIT_POINTS());
}
else
{
PROFILE("CvUnit::updateCombat.CanDefend");
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
//USE commanders here (so their command points will be decreased) for attacker and defender:
this->tryUseCommander();
pDefender->tryUseCommander();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
CvBattleDefinition kBattle;
kBattle.setUnit(BATTLE_UNIT_ATTACKER, this);
kBattle.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
kBattle.setDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_BEGIN, getDamage());
kBattle.setDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_BEGIN, pDefender->getDamage());
// Koshling - save pre-combat helath so we can use health loss as
// a basis for more granular war weariness
setupPreCombatDamage();
pDefender->setupPreCombatDamage();
resolveCombat(pDefender, pPlot, kBattle);
if (!bVisible)
{
if ( !isHuman() )
{
bFinish = true;
}
else
{
// Hack to make quick offensive option not switch away from
// the stack. It appears to be a bug in the main game engine
// in that it ALWAYS switches away unles you compleet the combat
// in a timer update call rather than directly here, so fake up
// a pseudo-combat round to perform delayed completion (but without
// animation, so no battle setup) via the unit timer
setCombatTimer(1);
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
}
}
else
{
kBattle.setDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_END, getDamage());
kBattle.setDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_END, pDefender->getDamage());
kBattle.setAdvanceSquare(canAdvance(pPlot, pDefender->isDead() ? 0 : 1));
if (isRanged() && pDefender->isRanged())
{
kBattle.setDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_END));
kBattle.setDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_END));
}
else
{
kBattle.addDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_BEGIN));
kBattle.addDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_BEGIN));
}
int iTurns = planBattle( kBattle);
kBattle.setMissionTime(iTurns * gDLL->getSecsPerTurn());
setCombatTimer(iTurns);
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
if (pPlot->isActiveVisible(false) && !pDefender->isUsingDummyEntities() )
{
ExecuteMove(0.5f, true);
addMission(&kBattle);
}
}
}
}
if (bFinish)
{
PROFILE("CvUnit::updateCombat.Finish");
if (bVisible)
{
if (isCombatFocus() && gDLL->getInterfaceIFace()->isCombatFocus())
{
if (getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())
{
gDLL->getInterfaceIFace()->releaseLockedCamera();
}
}
}
//end the combat mission if this code executes first
if ( !isUsingDummyEntities() && isInViewport())
{
gDLL->getEntityIFace()->RemoveUnitFromBattle(this);
}
if ( !pDefender->isUsingDummyEntities() && pDefender->isInViewport())
{
gDLL->getEntityIFace()->RemoveUnitFromBattle(pDefender);
}
setAttackPlot(NULL, false);
setCombatUnit(NULL);
pDefender->setCombatUnit(NULL);
NotifyEntity(MISSION_DAMAGE);
pDefender->NotifyEntity(MISSION_DAMAGE);
if (isDead())
{
if (isBarbarian())
{
GET_PLAYER(pDefender->getOwnerINLINE()).changeWinsVsBarbs(1);
}
if (!m_pUnitInfo->isHiddenNationality() && !pDefender->getUnitInfo().isHiddenNationality())
{
int attackerPreCombatDamage = getPreCombatDamage();
int defenderPreCombatDamage = pDefender->getPreCombatDamage();
int attackerWarWearinessChangeTimes100 = std::max(1, (100*GC.getDefineINT("WW_UNIT_KILLED_ATTACKING")*(maxHitPoints() - attackerPreCombatDamage))/maxHitPoints());
GET_TEAM(getTeam()).changeWarWearinessTimes100(pDefender->getTeam(), *pPlot, attackerWarWearinessChangeTimes100);
int defenderWarWearinessChangeTimes100 = (100*GC.getDefineINT("WW_KILLED_UNIT_DEFENDING")*(pDefender->getDamage() - pDefender->getPreCombatDamage()))/pDefender->maxHitPoints();
GET_TEAM(pDefender->getTeam()).changeWarWearinessTimes100(getTeam(), *pPlot, defenderWarWearinessChangeTimes100);
GET_TEAM(pDefender->getTeam()).AI_changeWarSuccess(getTeam(), GC.getDefineINT("WAR_SUCCESS_DEFENDING"));
}
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_DIED_ATTACKING", getNameKey(), pDefender->getNameKey());
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
float fInfluenceRatio = 0.0;
if (GC.isIDW_ENABLED())
{
fInfluenceRatio = pDefender->doVictoryInfluence(this, false, false);
/*** Dexy - Fixed Borders START ****/
if (fInfluenceRatio > 0.0f)
/*** Dexy - Fixed Borders END ****/
{
CvWString szTempBuffer;
szTempBuffer.Format(L" Influence: -%.1f%%", fInfluenceRatio);
szBuffer += szTempBuffer;
}
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_KILLED_ENEMY_UNIT", pDefender->getNameKey(), getNameKey(), getVisualCivAdjective(pDefender->getTeam()));
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
if (GC.isIDW_ENABLED())
{
/*** Dexy - Fixed Borders START ****/
if (fInfluenceRatio > 0.0f)
/*** Dexy - Fixed Borders END ****/
{
CvWString szTempBuffer;
szTempBuffer.Format(L" Influence: +%.1f%%", fInfluenceRatio);
szBuffer += szTempBuffer;
}
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/27/09 */
/* */
/* */
/************************************************************************************************/
pDefender->combatWon(this, false);
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
// report event to Python, along with some other key state
CvEventReporter::getInstance().combatResult(pDefender, this);
getUnitInfo().getKillOutcomeList()->execute(*pDefender, getOwnerINLINE(), getUnitType());
}
else if (pDefender->isDead())
{
if (pDefender->isBarbarian())
{
GET_PLAYER(getOwnerINLINE()).changeWinsVsBarbs(1);
}
if (!m_pUnitInfo->isHiddenNationality() && !pDefender->getUnitInfo().isHiddenNationality())
{
int attackerPreCombatDamage = getPreCombatDamage();
int defenderPreCombatDamage = pDefender->getPreCombatDamage();
int defenderWarWearinessChangeTimes100 = std::max(1, (100*GC.getDefineINT("WW_UNIT_KILLED_DEFENDING")*(pDefender->maxHitPoints() - defenderPreCombatDamage))/pDefender->maxHitPoints());
GET_TEAM(pDefender->getTeam()).changeWarWearinessTimes100(getTeam(), *pPlot, defenderWarWearinessChangeTimes100);
int attackerWarWearinessChangeTimes100 = (100*GC.getDefineINT("WW_KILLED_UNIT_ATTACKING")*(getDamage() - getPreCombatDamage()))/maxHitPoints();
GET_TEAM(getTeam()).changeWarWearinessTimes100(pDefender->getTeam(), *pPlot, attackerWarWearinessChangeTimes100);
GET_TEAM(getTeam()).AI_changeWarSuccess(pDefender->getTeam(), GC.getDefineINT("WAR_SUCCESS_ATTACKING"));
}
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_DESTROYED_ENEMY", getNameKey(), pDefender->getNameKey());
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
float fInfluenceRatio = 0.0;
if (GC.isIDW_ENABLED())
{
fInfluenceRatio = doVictoryInfluence(pDefender, true, false);
/*** Dexy - Fixed Borders START ****/
if (fInfluenceRatio > 0.0f)
/*** Dexy - Fixed Borders END ****/
{
CvWString szTempBuffer;
szTempBuffer.Format(L" Influence: +%.1f%%", fInfluenceRatio);
szBuffer += szTempBuffer;
}
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
if (getVisualOwner(pDefender->getTeam()) != getOwnerINLINE())
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WAS_DESTROYED_UNKNOWN", pDefender->getNameKey(), getNameKey());
}
else
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WAS_DESTROYED", pDefender->getNameKey(), getNameKey(), getVisualCivAdjective(pDefender->getTeam()));
}
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
if (GC.isIDW_ENABLED())
{
/*** Dexy - Fixed Borders START ****/
if (fInfluenceRatio > 0.0f)
/*** Dexy - Fixed Borders END ****/
{
CvWString szTempBuffer;
szTempBuffer.Format(L" Influence: -%.1f%%", fInfluenceRatio);
szBuffer += szTempBuffer;
}
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer,GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/27/09 */
/* */
/* */
/************************************************************************************************/
combatWon(pDefender, true);
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
// report event to Python, along with some other key state
CvEventReporter::getInstance().combatResult(this, pDefender);
CvUnitInfo* pDefenderUnitInfo = &(pDefender->getUnitInfo());
UnitTypes eDefenderUnitType = pDefender->getUnitType();
PlayerTypes eDefenderUnitPlayer = pDefender->getOwnerINLINE();
bool bAdvance = false;
if (isSuicide())
{
kill(true);
pDefender->kill(false, NO_PLAYER, true);
pDefender = NULL;
}
else
{
bAdvance = canAdvance(pPlot, ((pDefender->canDefend() && !pDefender->isDead()) ? 1 : 0));
if (bAdvance)
{
if (!isNoCapture())
{
pDefender->setCapturingPlayer(getOwnerINLINE());
}
}
pDefender->killUnconditional(false, NO_PLAYER, true);
pDefender = NULL;
if (!bAdvance)
{
changeMoves(std::max(GC.getMOVE_DENOMINATOR(), pPlot->movementCost(this, plot())));
checkRemoveSelectionAfterAttack();
}
}
/****************************************************************************************/
/* REVOLUTIONDCM 09/06/09 jdog5000 */
/** */
/** */
/****************************************************************************************/
// Fix rare crash bug
if( getGroup() != NULL )
{
if (pPlot->getNumVisibleEnemyDefenders(this) == 0)
{
PROFILE("CvUnit::updateCombat.Advance");
getGroup()->groupMove(pPlot, true, ((bAdvance) ? this : NULL));
}
// This is is put before the plot advancement, the unit will always try to walk back
// to the square that they came from, before advancing.
getGroup()->clearMissionQueue();
}
pDefenderUnitInfo->getKillOutcomeList()->execute(*this, eDefenderUnitPlayer, eDefenderUnitType);
/****************************************************************************************/
/* REVOLUTIONDCM END jdog5000 */
/****************************************************************************************/
}
/************************************************************************************************/
/* Afforess Start 02/22/10 Coded By: KillMePlease */
/* */
/* Defender Withdraw */
/************************************************************************************************/
else if (m_combatResult.bDefenderWithdrawn)
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_UNIT_WITHDRAW", pDefender->getNameKey(), getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_OUR_WITHDRAWL", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WITHDRAW", pDefender->getNameKey(), getNameKey());
AddDLLMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_THEIR_WITHDRAWL", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
if (pPlot->isCity())
{
m_combatResult.pPlot = NULL;
}
if (m_combatResult.pPlot != NULL)
{
//defender escapes to a safe plot
pDefender->move(m_combatResult.pPlot, true);
changeMoves(std::max(GC.getMOVE_DENOMINATOR(), pPlot->movementCost(this, plot())));
checkRemoveSelectionAfterAttack();
getGroup()->clearMissionQueue();
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
else
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WITHDRAW", getNameKey(), pDefender->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_OUR_WITHDRAWL", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_UNIT_WITHDRAW", getNameKey(), pDefender->getNameKey());
AddMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_THEIR_WITHDRAWL", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
changeMoves(std::max(GC.getMOVE_DENOMINATOR(), pPlot->movementCost(this, plot())));
checkRemoveSelectionAfterAttack();
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
if (GC.isIDW_ENABLED())
{
if (!canMove() || !isBlitz())
{
if (IsSelected())
{
if (gDLL->getInterfaceIFace()->getLengthSelectionList() > 1)
{
gDLL->getInterfaceIFace()->removeFromSelectionList(this);
}
}
}
}
// ------ END InfluenceDrivenWar -------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
getGroup()->clearMissionQueue();
}
}
}
void CvUnit::checkRemoveSelectionAfterAttack()
{
if (!canMove() || !isBlitz())
{
if (IsSelected())
{
if (gDLL->getInterfaceIFace()->getLengthSelectionList() > 1)
{
gDLL->getInterfaceIFace()->removeFromSelectionList(this);
}
}
}
}
bool CvUnit::isActionRecommended(int iAction)
{
CvCity* pWorkingCity;
CvPlot* pPlot;
ImprovementTypes eImprovement;
ImprovementTypes eFinalImprovement;
BuildTypes eBuild;
RouteTypes eRoute;
BonusTypes eBonus;
int iIndex;
if (getOwnerINLINE() != GC.getGameINLINE().getActivePlayer())
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_NO_UNIT_RECOMMENDATIONS))
{
return false;
}
{
PYTHON_ACCESS_LOCK_SCOPE
CyUnit* pyUnit = new CyUnit(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyUnit)); // pass in unit class
argsList.add(iAction);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "isActionRecommended", argsList.makeFunctionArgs(), &lResult);
delete pyUnit; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return true;
}
}
pPlot = gDLL->getInterfaceIFace()->getGotoPlot();
if (pPlot == NULL)
{
if (gDLL->shiftKey())
{
pPlot = getGroup()->lastMissionPlot();
}
}
if (pPlot == NULL)
{
pPlot = plot();
}
// BUFFY - Don't Recommend Actions in Fog of War - start
#ifdef _BUFFY
// from HOF Mod - Denniz
if (!pPlot->isVisible(GC.getGameINLINE().getActiveTeam(), false))
{
return false;
}
#endif
// BUFFY - Don't Recommend Actions in Fog of War - end
if (GC.getActionInfo(iAction).getMissionType() == MISSION_FORTIFY)
{
if (pPlot->isCity(true, getTeam()))
{
if (canDefend(pPlot))
{
if (pPlot->getNumDefenders(getOwnerINLINE()) < ((atPlot(pPlot)) ? 2 : 1))
{
return true;
}
}
}
}
// BUG - Sentry Actions - start
#ifdef _MOD_SENTRY
if ((GC.getActionInfo(iAction).getMissionType() == MISSION_HEAL) || (GC.getActionInfo(iAction).getMissionType() == MISSION_SENTRY_WHILE_HEAL))
#else
if (GC.getActionInfo(iAction).getMissionType() == MISSION_HEAL)
#endif
// BUG - Sentry Actions - end
{
if (isHurt())
{
if (!hasMoved())
{
if ((pPlot->getTeam() == getTeam()) || (healTurns(pPlot) < 4))
{
return true;
}
}
}
}
if (GC.getActionInfo(iAction).getMissionType() == MISSION_FOUND)
{
if (canFound(pPlot))
{
if (pPlot->isBestAdjacentFound(getOwnerINLINE()))
{
return true;
}
}
}
if (GC.getActionInfo(iAction).getMissionType() == MISSION_BUILD)
{
if (pPlot->getOwnerINLINE() == getOwnerINLINE())
{
eBuild = ((BuildTypes)(GC.getActionInfo(iAction).getMissionData()));
FAssert(eBuild != NO_BUILD);
FAssertMsg(eBuild < GC.getNumBuildInfos(), "Invalid Build");
if (canBuild(pPlot, eBuild))
{
eImprovement = ((ImprovementTypes)(GC.getBuildInfo(eBuild).getImprovement()));
eRoute = ((RouteTypes)(GC.getBuildInfo(eBuild).getRoute()));
eBonus = pPlot->getBonusType(getTeam());
pWorkingCity = pPlot->getWorkingCity();
if (pPlot->getImprovementType() == NO_IMPROVEMENT)
{
if (pWorkingCity != NULL)
{
iIndex = pWorkingCity->getCityPlotIndex(pPlot);
if (iIndex != -1)
{
if (pWorkingCity->AI_getBestBuild(iIndex) == eBuild)
{
return true;
}
}
}
if (eImprovement != NO_IMPROVEMENT)
{
if (eBonus != NO_BONUS)
{
if (GC.getImprovementInfo(eImprovement).isImprovementBonusTrade(eBonus))
{
return true;
}
}
if (pPlot->getImprovementType() == NO_IMPROVEMENT)
{
if (!(pPlot->isIrrigated()) && pPlot->isIrrigationAvailable(true))
{
if (GC.getImprovementInfo(eImprovement).isCarriesIrrigation())
{
return true;
}
}
if (pWorkingCity != NULL)
{
if (GC.getImprovementInfo(eImprovement).getYieldChange(YIELD_FOOD) > 0)
{
return true;
}
if (pPlot->isHills())
{
if (GC.getImprovementInfo(eImprovement).getYieldChange(YIELD_PRODUCTION) > 0)
{
return true;
}
}
else
{
if (GC.getImprovementInfo(eImprovement).getYieldChange(YIELD_COMMERCE) > 0)
{
return true;
}
}
}
}
}
}
if (eRoute != NO_ROUTE)
{
if (!(pPlot->isRoute()))
{
if (eBonus != NO_BONUS)
{
return true;
}
if (pWorkingCity != NULL)
{
if (pPlot->isRiver())
{
return true;
}
}
}
eFinalImprovement = eImprovement;
if (eFinalImprovement == NO_IMPROVEMENT)
{
eFinalImprovement = pPlot->getImprovementType();
}
if (eFinalImprovement != NO_IMPROVEMENT)
{
if ((GC.getImprovementInfo(eFinalImprovement).getRouteYieldChanges(eRoute, YIELD_FOOD) > 0) ||
(GC.getImprovementInfo(eFinalImprovement).getRouteYieldChanges(eRoute, YIELD_PRODUCTION) > 0) ||
(GC.getImprovementInfo(eFinalImprovement).getRouteYieldChanges(eRoute, YIELD_COMMERCE) > 0))
{
return true;
}
}
}
}
}
}
if (GC.getActionInfo(iAction).getCommandType() == COMMAND_PROMOTION)
{
return true;
}
return false;
}
int CvUnit::defenderValue(const CvUnit* pAttacker) const
{
int iValue = 0;
TeamTypes eAttackerTeam = NO_TEAM;
if (NULL != pAttacker)
{
eAttackerTeam = pAttacker->getTeam();
}
if (canCoexistWithEnemyUnit(eAttackerTeam))
{
return 0;
}
if (!canDefend())
{
return 1;
}
if (pAttacker)
{
if (isTargetOf(*pAttacker))
{
iValue += 10000;
}
if (!pAttacker->canAttack(*this))
{
return 2;
}
}
iValue += currCombatStr(plot(), pAttacker);
if (::isWorldUnitClass(getUnitClassType()))
{
iValue /= 2;
}
if (NULL == pAttacker)
{
if (collateralDamage() > 0)
{
iValue *= 100;
iValue /= (100 + collateralDamage());
}
if (currInterceptionProbability() > 0)
{
iValue *= 100;
iValue /= (100 + currInterceptionProbability());
}
}
else
{
if (!(pAttacker->immuneToFirstStrikes()))
{
// UncutDragon
/* original code
iOurDefense *= ((((firstStrikes() * 2) + chanceFirstStrikes()) * ((GC.getDefineINT("COMBAT_DAMAGE") * 2) / 5)) + 100);
*/ // modified
iValue *= ((((firstStrikes() * 2) + chanceFirstStrikes()) * ((GC.getCOMBAT_DAMAGE() * 2) / 5)) + 100);
// /UncutDragon
iValue /= 100;
}
if (immuneToFirstStrikes())
{
// UncutDragon
/* original code
iOurDefense *= ((((pAttacker->firstStrikes() * 2) + pAttacker->chanceFirstStrikes()) * ((GC.getDefineINT("COMBAT_DAMAGE") * 2) / 5)) + 100);
*/ // modified
iValue *= ((((pAttacker->firstStrikes() * 2) + pAttacker->chanceFirstStrikes()) * ((GC.getCOMBAT_DAMAGE() * 2) / 5)) + 100);
// /UncutDragon
iValue /= 100;
}
}
int iAssetValue = std::max(1, getUnitInfo().getAssetValue());
int iCargoAssetValue = 0;
std::vector<CvUnit*> aCargoUnits;
getCargoUnits(aCargoUnits);
for (uint i = 0; i < aCargoUnits.size(); ++i)
{
iCargoAssetValue += aCargoUnits[i]->getUnitInfo().getAssetValue();
}
iValue = iValue * iAssetValue / std::max(1, iAssetValue + iCargoAssetValue);
if (NO_UNIT == getLeaderUnitType())
{
++iValue;
}
return iValue + 3;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */
/* */
/* Efficiency, Lead From Behind */
/************************************************************************************************/
// From Lead From Behind by UncutDragon
/* original code
bool CvUnit::isBetterDefenderThan(const CvUnit* pDefender, const CvUnit* pAttacker) const
*/ // modified (with extra parameter)
bool CvUnit::isBetterDefenderThan(const CvUnit* pDefender, const CvUnit* pAttacker, int* pBestDefenderRank) const
{
int iOurDefense;
int iTheirDefense;
if (pDefender == NULL)
{
return true;
}
TeamTypes eAttackerTeam = NO_TEAM;
if (NULL != pAttacker)
{
eAttackerTeam = pAttacker->getTeam();
}
if (canCoexistWithEnemyUnit(eAttackerTeam))
{
return false;
}
if (!canDefend())
{
return false;
}
if (canDefend() && !(pDefender->canDefend()))
{
return true;
}
if (pAttacker)
{
if (isTargetOf(*pAttacker) && !pDefender->isTargetOf(*pAttacker))
{
return true;
}
if (!isTargetOf(*pAttacker) && pDefender->isTargetOf(*pAttacker))
{
return false;
}
if (pAttacker->canAttack(*pDefender) && !pAttacker->canAttack(*this))
{
return false;
}
if (pAttacker->canAttack(*this) && !pAttacker->canAttack(*pDefender))
{
return true;
}
}
// UncutDragon
// To cut down on changes to existing code, we just short-circuit the method
// and this point and call our own version instead
if (GC.getLFBEnable())
return LFBisBetterDefenderThan(pDefender, pAttacker, pBestDefenderRank);
// /UncutDragon
iOurDefense = currCombatStr(plot(), pAttacker);
if (::isWorldUnitClass(getUnitClassType()))
{
iOurDefense /= 2;
}
if (NULL == pAttacker)
{
if (pDefender->collateralDamage() > 0)
{
iOurDefense *= (100 + pDefender->collateralDamage());
iOurDefense /= 100;
}
if (pDefender->currInterceptionProbability() > 0)
{
iOurDefense *= (100 + pDefender->currInterceptionProbability());
iOurDefense /= 100;
}
}
else
{
if (!(pAttacker->immuneToFirstStrikes()))
{
// UncutDragon
/* original code
iOurDefense *= ((((firstStrikes() * 2) + chanceFirstStrikes()) * ((GC.getDefineINT("COMBAT_DAMAGE") * 2) / 5)) + 100);
*/ // modified
iOurDefense *= ((((firstStrikes() * 2) + chanceFirstStrikes()) * ((GC.getCOMBAT_DAMAGE() * 2) / 5)) + 100);
// /UncutDragon
iOurDefense /= 100;
}
if (immuneToFirstStrikes())
{
// UncutDragon
/* original code
iOurDefense *= ((((pAttacker->firstStrikes() * 2) + pAttacker->chanceFirstStrikes()) * ((GC.getDefineINT("COMBAT_DAMAGE") * 2) / 5)) + 100);
*/ // modified
iOurDefense *= ((((pAttacker->firstStrikes() * 2) + pAttacker->chanceFirstStrikes()) * ((GC.getCOMBAT_DAMAGE() * 2) / 5)) + 100);
// /UncutDragon
iOurDefense /= 100;
}
}
int iAssetValue = std::max(1, getUnitInfo().getAssetValue());
int iCargoAssetValue = 0;
std::vector<CvUnit*> aCargoUnits;
getCargoUnits(aCargoUnits);
for (uint i = 0; i < aCargoUnits.size(); ++i)
{
iCargoAssetValue += aCargoUnits[i]->getUnitInfo().getAssetValue();
}
iOurDefense = iOurDefense * iAssetValue / std::max(1, iAssetValue + iCargoAssetValue);
iTheirDefense = pDefender->currCombatStr(plot(), pAttacker);
if (::isWorldUnitClass(pDefender->getUnitClassType()))
{
iTheirDefense /= 2;
}
if (NULL == pAttacker)
{
if (collateralDamage() > 0)
{
iTheirDefense *= (100 + collateralDamage());
iTheirDefense /= 100;
}
if (currInterceptionProbability() > 0)
{
iTheirDefense *= (100 + currInterceptionProbability());
iTheirDefense /= 100;
}
}
else
{
if (!(pAttacker->immuneToFirstStrikes()))
{
// UncutDragon
/* original code
iTheirDefense *= ((((pDefender->firstStrikes() * 2) + pDefender->chanceFirstStrikes()) * ((GC.getDefineINT("COMBAT_DAMAGE") * 2) / 5)) + 100);
*/ // modified
iTheirDefense *= ((((pDefender->firstStrikes() * 2) + pDefender->chanceFirstStrikes()) * ((GC.getCOMBAT_DAMAGE() * 2) / 5)) + 100);
// /UncutDragon
iTheirDefense /= 100;
}
if (pDefender->immuneToFirstStrikes())
{
// UncutDragon
/* original code
iTheirDefense *= ((((pAttacker->firstStrikes() * 2) + pAttacker->chanceFirstStrikes()) * ((GC.getDefineINT("COMBAT_DAMAGE") * 2) / 5)) + 100);
*/ // modified
iTheirDefense *= ((((pAttacker->firstStrikes() * 2) + pAttacker->chanceFirstStrikes()) * ((GC.getCOMBAT_DAMAGE() * 2) / 5)) + 100);
// /UncutDragon
iTheirDefense /= 100;
}
}
iAssetValue = std::max(1, pDefender->getUnitInfo().getAssetValue());
iCargoAssetValue = 0;
pDefender->getCargoUnits(aCargoUnits);
for (uint i = 0; i < aCargoUnits.size(); ++i)
{
iCargoAssetValue += aCargoUnits[i]->getUnitInfo().getAssetValue();
}
iTheirDefense = iTheirDefense * iAssetValue / std::max(1, iAssetValue + iCargoAssetValue);
if (iOurDefense == iTheirDefense)
{
if (NO_UNIT == getLeaderUnitType() && NO_UNIT != pDefender->getLeaderUnitType())
{
++iOurDefense;
}
else if (NO_UNIT != getLeaderUnitType() && NO_UNIT == pDefender->getLeaderUnitType())
{
++iTheirDefense;
}
else if (isBeforeUnitCycle(this, pDefender))
{
++iOurDefense;
}
}
return (iOurDefense > iTheirDefense);
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
bool CvUnit::canDoCommand(CommandTypes eCommand, int iData1, int iData2, bool bTestVisible, bool bTestBusy)
{
CvUnit* pUnit;
if (bTestBusy && getGroup()->isBusy())
{
return false;
}
switch (eCommand)
{
case COMMAND_PROMOTION:
if (canPromote((PromotionTypes)iData1, iData2))
{
return true;
}
break;
case COMMAND_UPGRADE:
if (canUpgrade(((UnitTypes)iData1), bTestVisible))
{
return true;
}
break;
case COMMAND_AUTOMATE:
if (canAutomate((AutomateTypes)iData1))
{
return true;
}
break;
case COMMAND_WAKE:
if (!isAutomated() && isWaiting())
{
return true;
}
break;
case COMMAND_CANCEL:
case COMMAND_CANCEL_ALL:
if (!isAutomated() && (getGroup()->getLengthMissionQueue() > 0))
{
return true;
}
// < M.A.D. Nukes Start >
if(isMADEnabled() && getUnitInfo().getUnitAIType(UNITAI_ICBM))
{
return true;
}
// < M.A.D. Nukes End >
break;
case COMMAND_STOP_AUTOMATION:
if (isAutomated())
{
return true;
}
break;
case COMMAND_DELETE:
if (canScrap())
{
return true;
}
break;
case COMMAND_GIFT:
if (canGift(bTestVisible))
{
return true;
}
break;
case COMMAND_LOAD:
if (canLoad(plot()))
{
return true;
}
break;
case COMMAND_LOAD_UNIT:
pUnit = ::getUnit(IDInfo(((PlayerTypes)iData1), iData2));
if (pUnit != NULL)
{
if (canLoadUnit(pUnit, plot()))
{
return true;
}
}
break;
case COMMAND_UNLOAD:
if (canUnload())
{
return true;
}
break;
case COMMAND_UNLOAD_ALL:
if (canUnloadAll())
{
return true;
}
break;
case COMMAND_HOTKEY:
if (isGroupHead())
{
return true;
}
break;
/************************************************************************************************/
/* Great Diplomat MOD START */
/************************************************************************************************/
case COMMAND_COLONIZE_BARBARIANS:
if (canColonizeBarbarians(plot()))
{
return true;
}
break;
case COMMAND_FORCE_PEACE:
if (canForcePeace())
{
return true;
}
break;
case COMMAND_UPGRADE_IMPROVEMENTS:
if (canUpgradeImprovements(plot(), eCommand))
{
return true;
}
break;
/************************************************************************************************/
/* Great Diplomat MOD END */
/************************************************************************************************/
default:
FAssert(false);
break;
}
return false;
}
void CvUnit::doCommand(CommandTypes eCommand, int iData1, int iData2)
{
CvUnit* pUnit;
bool bCycle;
bCycle = false;
FAssert(getOwnerINLINE() != NO_PLAYER);
if (canDoCommand(eCommand, iData1, iData2))
{
switch (eCommand)
{
case COMMAND_PROMOTION:
promote((PromotionTypes)iData1, iData2);
break;
case COMMAND_UPGRADE:
upgrade((UnitTypes)iData1);
bCycle = true;
break;
case COMMAND_AUTOMATE:
automate((AutomateTypes)iData1);
bCycle = true;
break;
case COMMAND_WAKE:
getGroup()->setActivityType(ACTIVITY_AWAKE);
break;
case COMMAND_CANCEL:
// < M.A.D. Nukes Start >
if(isMADEnabled() && getUnitInfo().getUnitAIType(UNITAI_ICBM))
{
clearMADTargetPlot();
setMoves(0);
}
getGroup()->popMission();
// < M.A.D. Nukes End >
break;
case COMMAND_CANCEL_ALL:
getGroup()->clearMissionQueue();
break;
case COMMAND_STOP_AUTOMATION:
getGroup()->setAutomateType(NO_AUTOMATE);
break;
case COMMAND_DELETE:
scrap();
bCycle = true;
break;
case COMMAND_GIFT:
gift();
bCycle = true;
break;
case COMMAND_LOAD:
load();
bCycle = true;
break;
case COMMAND_LOAD_UNIT:
pUnit = ::getUnit(IDInfo(((PlayerTypes)iData1), iData2));
if (pUnit != NULL)
{
loadUnit(pUnit);
bCycle = true;
}
break;
case COMMAND_UNLOAD:
unload();
bCycle = true;
break;
case COMMAND_UNLOAD_ALL:
unloadAll();
bCycle = true;
break;
case COMMAND_HOTKEY:
setHotKeyNumber(iData1);
break;
/************************************************************************************************/
/* Great Diplomat MOD START */
/************************************************************************************************/
case COMMAND_COLONIZE_BARBARIANS:
colonizeBarbarians();
bCycle = true;
break;
case COMMAND_FORCE_PEACE:
tryForcePeace();
bCycle = true;
break;
case COMMAND_UPGRADE_IMPROVEMENTS:
upgradeImprovements(plot(), eCommand);
bCycle = true;
break;
/************************************************************************************************/
/* Great Diplomat MOD END */
/************************************************************************************************/
default:
FAssert(false);
break;
}
}
if (bCycle)
{
if (IsSelected())
{
gDLL->getInterfaceIFace()->setCycleSelectionCounter(1);
}
}
getGroup()->doDelayedDeath();
}
#ifdef USE_OLD_PATH_GENERATOR
FAStarNode* CvUnit::getPathLastNode() const
{
return getGroup()->getPathLastNode();
}
int CvUnit::getPathMovementRemaining() const
{
return getGroup()->movesLeft();
}
#else
int CvUnit::getPathMovementRemaining() const
{
return getGroup()->getPath().movementRemaining();
}
#endif
CvPlot* CvUnit::getPathEndTurnPlot() const
{
return getGroup()->getPathEndTurnPlot();
}
bool CvUnit::generatePath(const CvPlot* pToPlot, int iFlags, bool bReuse, int* piPathTurns, int iMaxTurns, int iOptimizationLimit) const
{
return getGroup()->generatePath(plot(), pToPlot, iFlags, bReuse, piPathTurns, iMaxTurns, iOptimizationLimit);
}
bool CvUnit::canEnterTerritory(TeamTypes eTeam, bool bIgnoreRightOfPassage) const
{
if (GET_TEAM(getTeam()).isFriendlyTerritory(eTeam))
{
return true;
}
if (eTeam == NO_TEAM)
{
return true;
}
if (isEnemy(eTeam))
{
return true;
}
if (isRivalTerritory())
{
return true;
}
if (alwaysInvisible())
{
return true;
}
if (m_pUnitInfo->isHiddenNationality())
{
return true;
}
if (!bIgnoreRightOfPassage)
{
if (GET_TEAM(getTeam()).isOpenBorders(eTeam))
{
return true;
}
/************************************************************************************************/
/* Afforess Start 02/14/10 */
/* */
/* */
/************************************************************************************************/
if (GET_TEAM(getTeam()).isLimitedBorders(eTeam))
{
if (isOnlyDefensive() || !canFight())
{
return true;
}
}
}
if (!GET_TEAM(eTeam).isAlive())
{
return true;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return false;
}
bool CvUnit::canEnterArea(TeamTypes eTeam, const CvArea* pArea, bool bIgnoreRightOfPassage) const
{
if (!canEnterTerritory(eTeam, bIgnoreRightOfPassage))
{
return false;
}
if (isBarbarian() && DOMAIN_LAND == getDomainType())
{
if (eTeam != NO_TEAM && eTeam != getTeam())
{
if (pArea && pArea->isBorderObstacle(eTeam))
{
return false;
}
}
}
return true;
}
// Returns the ID of the team to declare war against
TeamTypes CvUnit::getDeclareWarMove(const CvPlot* pPlot) const
{
CvUnit* pUnit;
TeamTypes eRevealedTeam;
FAssert(isHuman());
if (getDomainType() != DOMAIN_AIR)
{
eRevealedTeam = pPlot->getRevealedTeam(getTeam(), false);
if (eRevealedTeam != NO_TEAM)
{
if (!canEnterArea(eRevealedTeam, pPlot->area()) || (getDomainType() == DOMAIN_SEA && !canCargoEnterArea(eRevealedTeam, pPlot->area(), false) && getGroup()->isAmphibPlot(pPlot)))
{
if (GET_TEAM(getTeam()).canDeclareWar(pPlot->getTeam()))
{
return eRevealedTeam;
}
}
}
else
{
if (pPlot->isActiveVisible(false))
{
if (canMoveInto(pPlot, true, true, true))
{
pUnit = pPlot->plotCheck(PUF_canDeclareWar, getOwnerINLINE(), isAlwaysHostile(pPlot), NO_PLAYER, NO_TEAM, PUF_isVisible, getOwnerINLINE());
if (pUnit != NULL)
{
return pUnit->getTeam();
}
}
}
}
}
return NO_TEAM;
}
bool CvUnit::willRevealByMove(const CvPlot* pPlot) const
{
int iRange = visibilityRange(pPlot) + 1;
for (int i = -iRange; i <= iRange; ++i)
{
for (int j = -iRange; j <= iRange; ++j)
{
CvPlot* pLoopPlot = ::plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), i, j);
if (NULL != pLoopPlot)
{
if (!pLoopPlot->isRevealed(getTeam(), false) && pPlot->canSeePlot(pLoopPlot, getTeam(), visibilityRange(), NO_DIRECTION))
{
return true;
}
}
}
}
return false;
}
/************************************************************************************************/
/* Afforess Start 06/17/10 */
/* */
/* */
/************************************************************************************************/
bool CvUnit::canMoveInto(const CvPlot* pPlot, bool bAttack, bool bDeclareWar, bool bIgnoreLoad, bool bIgnoreTileLimit, bool bIgnoreLocation, bool bIgnoreAttack) const
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
bool bFailWithAttack = false;
bool bFailWithoutAttack = false;
PROFILE_FUNC();
FAssertMsg(pPlot != NULL, "Plot is not assigned a valid value");
/************************************************************************************************/
/* Afforess Start 06/22/10 */
/* */
/* */
/************************************************************************************************/
static int iHill = -1;
static int iPeak = -1;
if ( iHill == -1 )
{
iHill = GC.getInfoTypeForString("TERRAIN_HILL");
}
if ( iPeak == -1 )
{
iPeak = GC.getInfoTypeForString("TERRAIN_PEAK");
}
if (!bIgnoreLocation)
{
if (atPlot(pPlot))
{
return false;
}
}
//ls612: This code was sticky tape for the earlier AI shortcomings in this area, It was bad,
//It wasted a ton of time, It was in the wrong file, and it didn't even do anything!
//if (!isHuman())
//{
// if (plot()->getTerrainTurnDamage() <= 0)
// {
// if (getDamage() > 50)
// {
// if (pPlot->getTerrainTurnDamage() > 0)
// {
// return false;
// }
// }
// }
//}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// Cannot move around in unrevealed land freely
if (m_pUnitInfo->isNoRevealMap() && willRevealByMove(pPlot))
{
return false;
}
if (GC.getUSE_SPIES_NO_ENTER_BORDERS())
{
if (isSpy() && NO_PLAYER != pPlot->getOwnerINLINE())
{
if (!GET_PLAYER(getOwnerINLINE()).canSpiesEnterBorders(pPlot->getOwnerINLINE()))
{
return false;
}
}
}
CvArea *pPlotArea = pPlot->area();
TeamTypes ePlotTeam = pPlot->getTeam();
bool bCanEnterArea = canEnterArea(ePlotTeam, pPlotArea);
if (bCanEnterArea)
{
if (pPlot->getFeatureType() != NO_FEATURE)
{
if (m_pUnitInfo->getFeatureImpassable(pPlot->getFeatureType()))
{
TechTypes eTech = (TechTypes)m_pUnitInfo->getFeaturePassableTech(pPlot->getFeatureType());
if (NO_TECH == eTech || !GET_TEAM(getTeam()).isHasTech(eTech))
{
if (DOMAIN_SEA != getDomainType() || pPlot->getTeam() != getTeam()) // sea units can enter impassable in own cultural borders
{
return false;
}
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 09/17/09 TC01 & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
else
*/
// always check terrain also
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
/************************************************************************************************/
/* Afforess Start 02/14/10 */
/* */
/* */
/************************************************************************************************/
bool bPeak = false;
bool bHill = false;
if (m_pUnitInfo->getTerrainImpassable(iHill))
bHill = true;
if (m_pUnitInfo->getTerrainImpassable(iPeak))
bPeak = true;
if (m_pUnitInfo->getTerrainImpassable(pPlot->getTerrainType()) || (pPlot->isPeak() && bPeak) || (pPlot->isHills() && bHill))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
TechTypes eTech = (TechTypes)m_pUnitInfo->getTerrainPassableTech(pPlot->getTerrainType());
if (NO_TECH == eTech || !GET_TEAM(getTeam()).isHasTech(eTech))
{
if (DOMAIN_SEA != getDomainType() || pPlot->getTeam() != getTeam()) // sea units can enter impassable in own cultural borders
{
if (bIgnoreLoad || !canLoad(pPlot))
{
return false;
}
}
}
}
}
}
switch (getDomainType())
{
case DOMAIN_SEA:
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 07/07/09 */
/* */
/* */
/************************************************************************************************/
if (!pPlot->isWater() && !canMoveAllTerrain() && !pPlot->isCanMoveSeaUnits())
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
if (!pPlot->isFriendlyCity(*this, true) || !pPlot->isCoastalLand())
{
return false;
}
}
break;
case DOMAIN_AIR:
if (!bAttack)
{
bool bValid = false;
if (pPlot->isFriendlyCity(*this, true))
{
bValid = true;
if (m_pUnitInfo->getAirUnitCap() > 0)
{
if (pPlot->airUnitSpaceAvailable(getTeam()) <= 0)
{
bValid = false;
}
}
}
if (!bValid)
{
if (bIgnoreLoad || !canLoad(pPlot))
{
if ( !bIgnoreAttack )
{
return false;
}
else
{
bFailWithoutAttack = true;
}
}
}
/************************************************************************************************/
/* Afforess Start 03/7/10 */
/* */
/* Rebase Limit */
/************************************************************************************************/
if (!bFailWithoutAttack && !GET_TEAM(getTeam()).isRebaseAnywhere())
{
if ((GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_AIRLIFT_RANGE)))
{
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), getX_INLINE(), getY_INLINE()) > (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_AIRLIFT_RANGE)))
{
if ( !bIgnoreAttack )
{
return false;
}
else
{
bFailWithoutAttack = true;
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
break;
case DOMAIN_LAND:
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 07/07/09 */
/* */
/* */
/************************************************************************************************/
if (pPlot->isWater() && !canMoveAllTerrain() && !pPlot->isCanMoveLandUnits())
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
if (!pPlot->isCity() || 0 == GC.getLAND_UNITS_CAN_ATTACK_WATER_CITIES())
{
if (bIgnoreLoad || !isHuman() || plot()->isWater() || !canLoad(pPlot))
{
return false;
}
}
}
break;
case DOMAIN_IMMOBILE:
return false;
break;
default:
FAssert(false);
break;
}
/************************************************************************************************/
/* Afforess Route Restricter Start 08/03/09 */
/* */
/* */
/************************************************************************************************/
if (m_pUnitInfo->getPassableRouteNeeded(0) || m_pUnitInfo->getPassableRouteNeeded(1))
{
if (!(m_pUnitInfo->getPassableRouteNeeded(pPlot->getRouteType()) && pPlot->isRoute()))
{
return false;
}
}
/************************************************************************************************/
/* Afforess Route Restricter End END */
/************************************************************************************************/
//ls612: For units that can't enter non-Owned Cities
if (m_pUnitInfo->isNoNonOwnedEntry() && pPlot->isCity() && (pPlot->getOwnerINLINE() != getOwnerINLINE()))
{
return false;
}
if (isAnimal())
{
if (pPlot->isOwned() && pPlot->getOwnerINLINE() != BARBARIAN_PLAYER)
{
return false;
}
if (!bAttack && !bFailWithoutAttack)
{
if (pPlot->getBonusType() != NO_BONUS)
{
if ( !bIgnoreAttack )
{
return false;
}
else
{
bFailWithoutAttack = true;
}
}
if (pPlot->getImprovementType() != NO_IMPROVEMENT)
{
if ( !bIgnoreAttack )
{
return false;
}
else
{
bFailWithoutAttack = true;
}
}
if (pPlot->getNumUnits() > 0)
{
if ( !bIgnoreAttack )
{
return false;
}
else
{
bFailWithoutAttack = true;
}
}
}
}
if (!bAttack && !bFailWithoutAttack)
{
if (isNoCapture())
{
if (pPlot->isEnemyCity(*this))
{
if ( !bIgnoreAttack )
{
return false;
}
else
{
bFailWithoutAttack = true;
}
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 07/23/09 jdog5000 */
/* */
/* Consistency */
/************************************************************************************************/
/* original bts code
if (bAttack)
{
if (isMadeAttack() && !isBlitz())
{
return false;
}
}
*/
// The following change makes capturing an undefended city like a attack action, it
// cannot be done after another attack or a paradrop
/*
if (bAttack || (pPlot->isEnemyCity(*this) && !canCoexistWithEnemyUnit(NO_TEAM)) )
{
if (isMadeAttack() && !isBlitz())
{
return false;
}
}
*/
// The following change makes it possible to capture defenseless units after having
// made a previous attack or paradrop
if( bAttack )
{
if (isMadeAttack() && !isBlitz() && (pPlot->getNumVisibleEnemyDefenders(this) > 0))
{
if ( !bIgnoreAttack || bFailWithoutAttack )
{
return false;
}
else
{
bFailWithAttack = true;
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
if (getDomainType() == DOMAIN_AIR)
{
if (bAttack && !bFailWithAttack)
{
if (!canAirStrike(pPlot))
{
if ( !bIgnoreAttack || bFailWithoutAttack )
{
return false;
}
else
{
bFailWithAttack = true;
}
}
}
}
else
{
if (canAttack())
{
if (!isHuman() || (pPlot->isVisible(getTeam(), false)))
{
if ( bIgnoreAttack )
{
if ( !bFailWithoutAttack )
{
if (!canCoexistWithEnemyUnit(NO_TEAM))
{
if (pPlot->isVisibleEnemyUnit(this))
{
//FAssertMsg(isHuman() || (!bDeclareWar || (pPlot->isVisibleOtherUnit(getOwnerINLINE()) != bAttack)), "hopefully not an issue, but tracking how often this is the case when we dont want to really declare war");
if (!bDeclareWar || (pPlot->isVisibleOtherUnit(getOwnerINLINE())))
{
if ( bFailWithAttack )
{
return false;
}
bFailWithoutAttack = true;
}
}
}
}
if ( !bFailWithAttack )
{
if (!pPlot->isVisibleEnemyUnit(this))
{
//FAssertMsg(isHuman() || (!bDeclareWar || (pPlot->isVisibleOtherUnit(getOwnerINLINE()) != bAttack)), "hopefully not an issue, but tracking how often this is the case when we dont want to really declare war");
if (!bDeclareWar || (!pPlot->isVisibleOtherUnit(getOwnerINLINE()) && !(pPlot->getPlotCity() && !isNoCapture())))
{
if ( bFailWithoutAttack )
{
return false;
}
bFailWithAttack = true;
}
}
}
}
else if (bAttack || !canCoexistWithEnemyUnit(NO_TEAM))
{
if (pPlot->isVisibleEnemyUnit(this) != bAttack)
//if ((pPlot->isVisibleEnemyUnit(this) || pPlot->isEnemyCity(*this)) != bAttack)
{
//FAssertMsg(isHuman() || (!bDeclareWar || (pPlot->isVisibleOtherUnit(getOwnerINLINE()) != bAttack)), "hopefully not an issue, but tracking how often this is the case when we dont want to really declare war");
if (!bDeclareWar || (pPlot->isVisibleOtherUnit(getOwnerINLINE()) != bAttack && !(bAttack && pPlot->getPlotCity() && !isNoCapture())))
{
return false;
}
}
}
}
if (bAttack && !bFailWithAttack)
{
CvUnit* pDefender = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
if (NULL != pDefender)
{
if (!canAttack(*pDefender))
{
if ( !bIgnoreAttack || bFailWithoutAttack )
{
return false;
}
else
{
bFailWithAttack = true;
}
}
}
}
}
else
{
if (bAttack && !bFailWithAttack)
{
if ( !bIgnoreAttack || bFailWithoutAttack )
{
return false;
}
else
{
bFailWithAttack = true;
}
}
if (!canCoexistWithEnemyUnit(NO_TEAM))
{
if (!isHuman() || pPlot->isVisible(getTeam(), false))
{
if (pPlot->isEnemyCity(*this))
{
return false;
}
if (pPlot->isVisibleEnemyUnit(this))
{
return false;
}
}
}
}
if (isHuman())
{
ePlotTeam = pPlot->getRevealedTeam(getTeam(), false);
bCanEnterArea = canEnterArea(ePlotTeam, pPlotArea);
}
if (!bCanEnterArea)
{
FAssert(ePlotTeam != NO_TEAM);
if (!(GET_TEAM(getTeam()).canDeclareWar(ePlotTeam)))
{
return false;
}
if (isHuman())
{
if (!bDeclareWar)
{
return false;
}
}
else
{
if (GET_TEAM(getTeam()).AI_isSneakAttackReady(ePlotTeam))
{
if (!(getGroup()->AI_isDeclareWar(pPlot)))
{
return false;
}
}
else
{
return false;
}
}
}
}
/************************************************************************************************/
/* Afforess Start 08/18/10 */
/* */
/* */
/************************************************************************************************/
bool bValid = false;
if (pPlot->isImpassable(getTeam()))
{
//Check our current tile
if (plot()->isPeak())
{
// Can this unit move through peaks regardless?
if ( isCanMovePeaks() )
{
bValid = true;
}
else
{
// If not we need a peak leader to be present
bValid = plot()->getHasMountainLeader(getTeam());
}
}
if (pPlot->isPeak())
{
//Check the impassible tile
if (!bValid)
{
// Can this unit move through peaks regardless?
if ( isCanMovePeaks() )
{
bValid = true;
}
else
{
// If not we need a peak leader to be present
bValid = pPlot->getHasMountainLeader(getTeam());
}
}
}
if (!bValid)
{
if (!canMoveImpassable())
{
return false;
}
}
}
if (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_MAX_UNITS_PER_TILES) > 0)
{
if (!bIgnoreTileLimit)
{
if (!getUnitInfo().isOnlyDefensive() && baseCombatStr() > 0)
{
if (getDomainType() == DOMAIN_LAND && !pPlot->isWater() || getDomainType() == DOMAIN_SEA && pPlot->isWater() || getDomainType() == DOMAIN_AIR)
{
int iCount = 0;
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
//Check our current tile
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTeam() == getTeam())
{
//Ignore workers, Missionaries, etc...
if (!pLoopUnit->getUnitInfo().isOnlyDefensive() && pLoopUnit->baseCombatStr() > 0)
{
//No counting cargo for ships, or harbors
if (pLoopUnit->getDomainType() == getDomainType())
{
iCount++;
}
}
}
}//Unit is already on the tile, ignore it in the count
if (bIgnoreLocation)
{
iCount--;
}
if (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_MAX_UNITS_PER_TILES) <= iCount)
{
return false;
}
}
}
}
}
if ( !bIgnoreLocation )
{
// ZOCs don't apply into cities of the unit owner
if ( pPlot->getPlotCity() == NULL || pPlot->getPlotCity()->getTeam() != getTeam() )
{
//Fort ZOC
PlayerTypes eDefender = plot()->controlsAdjacentFort(getTeam());
if (eDefender != NO_PLAYER)
{
const CvPlot* pZoneOfControl = plot()->isInFortControl(true, eDefender, getOwnerINLINE());
const CvPlot* pForwardZoneOfControl = pPlot->isInFortControl(true, eDefender, getOwnerINLINE());
if (pZoneOfControl != NULL && pForwardZoneOfControl != NULL)
{
if (pZoneOfControl == pPlot->isInFortControl(true, eDefender, getOwnerINLINE(), pZoneOfControl))
{
return false;
}
}
}
//City ZoC
if (plot()->isInCityZoneOfControl(getOwnerINLINE()) && pPlot->isInCityZoneOfControl(getOwnerINLINE()))
{
return false;
}
}
}
//City Minimum Defense Level
if (!bIgnoreLocation && pPlot->getPlotCity() != NULL && !isSpy())
{
if (GET_TEAM(getTeam()).isAtWar(pPlot->getTeam()))
{
if ( !pPlot->getPlotCity()->isDirectAttackable() )
{
return false;
}
}
}
//Promotion ZoC
if (!bIgnoreLocation && plot()->isInUnitZoneOfControl(getOwnerINLINE()) && pPlot->isInUnitZoneOfControl(getOwnerINLINE()))
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (GC.getUSE_UNIT_CANNOT_MOVE_INTO_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
// Python Override
CyArgsList argsList;
argsList.add(getOwnerINLINE()); // Player ID
argsList.add(getID()); // Unit ID
argsList.add(pPlot->getX()); // Plot X
argsList.add(pPlot->getY()); // Plot Y
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "unitCannotMoveInto", argsList.makeFunctionArgs(), &lResult);
if (lResult != 0)
{
return false;
}
}
return true;
}
bool CvUnit::canMoveOrAttackInto(const CvPlot* pPlot, bool bDeclareWar) const
{
return canMoveInto(pPlot, false, bDeclareWar, false, false, stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), getX_INLINE(), getY_INLINE()) != 1, true);
}
bool CvUnit::canMoveThrough(const CvPlot* pPlot, bool bDeclareWar) const
{
return canMoveInto(pPlot, false, bDeclareWar, true);
}
void CvUnit::attack(CvPlot* pPlot, bool bQuick)
{
PROFILE_FUNC();
FAssert(canMoveInto(pPlot, true));
FAssert(getCombatTimer() == 0);
/************************************************************************************************/
/* Afforess Start 02/22/10 Coded by: KillMePlease */
/* */
/* Defender Withdraw */
/************************************************************************************************/
m_combatResult.iAttacksCount = 0;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
setAttackPlot(pPlot, false);
updateCombat(bQuick);
}
void CvUnit::fightInterceptor(const CvPlot* pPlot, bool bQuick)
{
FAssert(getCombatTimer() == 0);
setAttackPlot(pPlot, true);
updateAirCombat(bQuick);
}
void CvUnit::attackForDamage(CvUnit *pDefender, int attackerDamageChange, int defenderDamageChange)
{
FAssert(getCombatTimer() == 0);
FAssert(pDefender != NULL);
FAssert(!isFighting());
if(pDefender == NULL)
{
return;
}
setAttackPlot(pDefender->plot(), false);
CvPlot* pPlot = getAttackPlot();
if (pPlot == NULL)
{
return;
}
//rotate to face plot
DirectionTypes newDirection = estimateDirection(this->plot(), pDefender->plot());
if(newDirection != NO_DIRECTION)
{
setFacingDirection(newDirection);
}
//rotate enemy to face us
newDirection = estimateDirection(pDefender->plot(), this->plot());
if(newDirection != NO_DIRECTION)
{
pDefender->setFacingDirection(newDirection);
}
//check if quick combat
bool bVisible = isCombatVisible(pDefender);
//if not finished and not fighting yet, set up combat damage and mission
if (!isFighting())
{
if (plot()->isFighting() || pPlot->isFighting())
{
return;
}
setCombatUnit(pDefender, true);
pDefender->setCombatUnit(this, false);
pDefender->getGroup()->clearMissionQueue();
bool bFocused = (bVisible && isCombatFocus() && gDLL->getInterfaceIFace()->isCombatFocus() && plot()->isInViewport() && pDefender->isInViewport());
if (bFocused)
{
DirectionTypes directionType = directionXY(plot(), pPlot);
// N NE E SE S SW W NW
NiPoint2 directions[8] = {NiPoint2(0, 1), NiPoint2(1, 1), NiPoint2(1, 0), NiPoint2(1, -1), NiPoint2(0, -1), NiPoint2(-1, -1), NiPoint2(-1, 0), NiPoint2(-1, 1)};
NiPoint3 attackDirection = NiPoint3(directions[directionType].x, directions[directionType].y, 0);
float plotSize = GC.getPLOT_SIZE();
NiPoint3 lookAtPoint(plot()->getPoint().x + plotSize / 2 * attackDirection.x, plot()->getPoint().y + plotSize / 2 * attackDirection.y, (plot()->getPoint().z + pPlot->getPoint().z) / 2);
attackDirection.Unitize();
gDLL->getInterfaceIFace()->lookAt(lookAtPoint, (((getOwnerINLINE() != GC.getGameINLINE().getActivePlayer()) || gDLL->getGraphicOption(GRAPHICOPTION_NO_COMBAT_ZOOM)) ? CAMERALOOKAT_BATTLE : CAMERALOOKAT_BATTLE_ZOOM_IN), attackDirection);
}
else
{
MEMORY_TRACK_EXEMPT();
PlayerTypes eAttacker = getVisualOwner(pDefender->getTeam());
CvWString szMessage;
if (BARBARIAN_PLAYER != eAttacker)
{
szMessage = gDLL->getText("TXT_KEY_MISC_YOU_UNITS_UNDER_ATTACK", GET_PLAYER(getOwnerINLINE()).getNameKey());
}
else
{
szMessage = gDLL->getText("TXT_KEY_MISC_YOU_UNITS_UNDER_ATTACK_UNKNOWN");
}
AddDLLMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szMessage, "AS2D_COMBAT", MESSAGE_TYPE_DISPLAY_ONLY, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true);
}
}
//OutputDebugString("UI interaction - unit attacked 2\n");
GET_PLAYER(pDefender->getOwnerINLINE()).setTurnHadUIInteraction(true);
FAssertMsg(plot()->isFighting(), "Current unit instance plot is not fighting as expected");
FAssertMsg(pPlot->isFighting(), "pPlot is not fighting as expected");
//setup battle object
CvBattleDefinition kBattle;
kBattle.setUnit(BATTLE_UNIT_ATTACKER, this);
kBattle.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
kBattle.setDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_BEGIN, getDamage());
kBattle.setDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_BEGIN, pDefender->getDamage());
changeDamage(attackerDamageChange, pDefender->getOwnerINLINE());
pDefender->changeDamage(defenderDamageChange, getOwnerINLINE());
if (bVisible)
{
kBattle.setDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_END, getDamage());
kBattle.setDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_END, pDefender->getDamage());
kBattle.setAdvanceSquare(canAdvance(pPlot, pDefender->isDead() ? 0 : 1));
kBattle.addDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_BEGIN));
kBattle.addDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_BEGIN));
int iTurns = planBattle( kBattle);
kBattle.setMissionTime(iTurns * gDLL->getSecsPerTurn());
setCombatTimer(iTurns);
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
if (pPlot->isActiveVisible(false) && !pDefender->isUsingDummyEntities())
{
ExecuteMove(0.5f, true);
addMission(&kBattle);
}
}
else
{
setCombatTimer(1);
}
}
void CvUnit::move(CvPlot* pPlot, bool bShow)
{
PROFILE_FUNC();
FAssert(canMoveOrAttackInto(pPlot) || isMadeAttack());
CvPlot* pOldPlot = plot();
changeMoves(pPlot->movementCost(this, plot()));
setXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true, bShow && pPlot->isVisibleToWatchingHuman(), bShow);
//change feature
FeatureTypes featureType = pPlot->getFeatureType();
if(featureType != NO_FEATURE)
{
CvString featureString(GC.getFeatureInfo(featureType).getOnUnitChangeTo());
if(!featureString.IsEmpty())
{
FeatureTypes newFeatureType = (FeatureTypes) GC.getInfoTypeForString(featureString);
pPlot->setFeatureType(newFeatureType);
}
}
if (getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())
{
if (!(pPlot->isOwned()))
{
//spawn birds if trees present - JW
if (featureType != NO_FEATURE)
{
if (GC.getASyncRand().get(100) < GC.getFeatureInfo(featureType).getEffectProbability())
{
EffectTypes eEffect = (EffectTypes)GC.getInfoTypeForString(GC.getFeatureInfo(featureType).getEffectType());
gDLL->getEngineIFace()->TriggerEffect(eEffect, pPlot->getPoint(), (float)(GC.getASyncRand().get(360)));
gDLL->getInterfaceIFace()->playGeneralSound("AS3D_UN_BIRDS_SCATTER", pPlot->getPoint());
}
}
}
}
CvEventReporter::getInstance().unitMove(pPlot, this, pOldPlot);
}
// false if unit is killed
/************************************************************************************************/
/* Afforess Start 06/13/10 */
/* */
/* */
/************************************************************************************************/
bool CvUnit::jumpToNearestValidPlot(bool bKill)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
CvCity* pNearestCity;
CvPlot* pLoopPlot;
CvPlot* pBestPlot;
int iValue;
int iBestValue;
int iI;
FAssertMsg(!isAttacking(), "isAttacking did not return false as expected");
FAssertMsg(!isFighting(), "isFighting did not return false as expected");
// If the jump is due to being in an incorrect doamin it implies there WILL be an area change, so the relevant nearest
// city cannot possibly be in the same area, hence we need to search all
pNearestCity = GC.getMapINLINE().findCity(getX_INLINE(), getY_INLINE(), getOwnerINLINE(), NO_TEAM, plot()->isValidDomainForAction(*this));
iBestValue = MAX_INT;
pBestPlot = NULL;
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if (pLoopPlot->isValidDomainForLocation(*this))
{
if (canMoveInto(pLoopPlot))
{
if (canEnterArea(pLoopPlot->getTeam(), pLoopPlot->area()) && !isEnemy(pLoopPlot->getTeam(), pLoopPlot))
{
FAssertMsg(!atPlot(pLoopPlot), "atPlot(pLoopPlot) did not return false as expected");
if ((getDomainType() != DOMAIN_AIR) || pLoopPlot->isFriendlyCity(*this, true))
{
if (pLoopPlot->isRevealed(getTeam(), false))
{
iValue = (plotDistance(getX_INLINE(), getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()) * 2);
if (pNearestCity != NULL)
{
iValue += plotDistance(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), pNearestCity->getX_INLINE(), pNearestCity->getY_INLINE());
// Try to at least favour the same landmass as the nearest city
if (pLoopPlot->area() != pNearestCity->area())
{
iValue *= 3;
}
}
if (getDomainType() == DOMAIN_SEA && !plot()->isWater())
{
if (!pLoopPlot->isWater() || !pLoopPlot->isAdjacentToArea(area()))
{
iValue *= 3;
}
}
else
{
if (pLoopPlot->area() != area())
{
iValue *= 3;
}
}
/************************************************************************************************/
/* Afforess Start 06/20/10 */
/* */
/* */
/************************************************************************************************/
iValue *= std::max(1, (pLoopPlot->getTerrainTurnDamage(this) / 2));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iValue < iBestValue)
{
iBestValue = iValue;
pBestPlot = pLoopPlot;
}
}
}
}
}
}
}
bool bValid = true;
if (pBestPlot != NULL)
{
setXY(pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE());
}
/************************************************************************************************/
/* Afforess Start 06/13/10 */
/* */
/* */
/************************************************************************************************/
else if (bKill)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
kill(false);
bValid = false;
}
/************************************************************************************************/
/* Afforess Start 06/13/10 */
/* */
/* */
/************************************************************************************************/
else
{
bValid = false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return bValid;
}
bool CvUnit::canAutomate(AutomateTypes eAutomate) const
{
if (eAutomate == NO_AUTOMATE)
{
return false;
}
if (!isGroupHead())
{
return false;
}
/************************************************************************************************/
/* Afforess Start 02/14/10 */
/* */
/* Clicking on the Automate button with an Inquisitor causes a CTD */
/************************************************************************************************/
if (m_pUnitInfo->isInquisitor())
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
switch (eAutomate)
{
case AUTOMATE_BUILD:
if ((AI_getUnitAIType() != UNITAI_WORKER) && (AI_getUnitAIType() != UNITAI_WORKER_SEA))
{
return false;
}
break;
case AUTOMATE_NETWORK:
if ((AI_getUnitAIType() != UNITAI_WORKER) || !canBuildRoute())
{
return false;
}
break;
case AUTOMATE_CITY:
if (AI_getUnitAIType() != UNITAI_WORKER)
{
return false;
}
break;
case AUTOMATE_EXPLORE:
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 04/25/10 jdog5000 */
/* */
/* Player Interface */
/************************************************************************************************/
if ( !canFight() )
{
// Enable exploration for air units
if((getDomainType() != DOMAIN_SEA) && (getDomainType() != DOMAIN_AIR))
{
if( !(alwaysInvisible()) || !(isSpy()) )
{
return false;
}
}
}
if( (getDomainType() == DOMAIN_IMMOBILE) )
{
return false;
}
if( getDomainType() == DOMAIN_AIR && !canRecon(NULL) )
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_EXPLORE))
{
return false;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case AUTOMATE_RELIGION:
if (AI_getUnitAIType() != UNITAI_MISSIONARY)
{
return false;
}
/************************************************************************************************/
/* Afforess Start 09/16/10 */
/* */
/* Advanced Automations */
/************************************************************************************************/
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_SPREAD))
{
return false;
}
break;
case AUTOMATE_PILLAGE:
if (!getUnitInfo().isPillage())
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_PILLAGE))
{
return false;
}
break;
case AUTOMATE_HUNT:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_HUNT))
{
return false;
}
if (!canAttack())
{
return false;
}
break;
case AUTOMATE_CITY_DEFENSE:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_DEFENSE))
{
return false;
}
if (!canAttack())
{
return false;
}
break;
case AUTOMATE_NATIONAL_DEFENSE:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_NATIONAL_DEFENSE))
{
return false;
}
if (!canAttack())
{
return false;
}
break;
case AUTOMATE_BORDER_PATROL:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_PATROL))
{
return false;
}
if (!canAttack())
{
return false;
}
break;
case AUTOMATE_PIRATE:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_PIRATE))
{
return false;
}
if (getDomainType() != DOMAIN_SEA)
{
return false;
}
if (!canAttack())
{
return false;
}
if (!m_pUnitInfo->isHiddenNationality() || !m_pUnitInfo->isAlwaysHostile())
{
return false;
}
break;
case AUTOMATE_HURRY:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_CARAVAN))
{
return false;
}
if (m_pUnitInfo->getBaseHurry() <= 0)
{
return false;
}
//Do not give ability to great people
if (m_pUnitInfo->getProductionCost() < 0)
{
return false;
}
break;
case AUTOMATE_AIRSTRIKE:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_AIR))
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (!canAirAttack())
{
return false;
}
//Jets and Fighters can intercept, modders, if you have fighters with 0 interception, feel free to get rid of this check
if (m_pUnitInfo->getInterceptionProbability() <= 0)
{
return false;
}
break;
case AUTOMATE_AIRBOMB:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_AIR))
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (airBombBaseRate() == 0)
{
return false;
}
if (canAutomate(AUTOMATE_AIRSTRIKE))
{
return false;
}
case AUTOMATE_AIR_RECON:
if (!canRecon(NULL))
{
return false;
}
break;
case AUTOMATE_UPGRADING:
if (GC.getUnitInfo(getUnitType()).getUpgradeUnitClassTypes().size() == 0)
{
return false;
}
if (isAutoUpgrading())
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_UPGRADE))
{
return false;
}
break;
case AUTOMATE_CANCEL_UPGRADING:
if (GC.getUnitInfo(getUnitType()).getUpgradeUnitClassTypes().size() == 0)
{
return false;
}
if (!isAutoUpgrading())
{
return false;
}
break;
case AUTOMATE_PROMOTIONS:
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_PROMOTE))
{
return false;
}
if (!canAcquirePromotionAny())
{
return false;
}
if (isAutoPromoting())
{
return false;
}
break;
case AUTOMATE_CANCEL_PROMOTIONS:
if (!canAcquirePromotionAny())
{
return false;
}
if (!isAutoPromoting())
{
return false;
}
break;
case AUTOMATE_SHADOW:
if (!canShadow())
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_PROTECT))
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
default:
FAssert(false);
break;
}
return true;
}
void CvUnit::automate(AutomateTypes eAutomate)
{
if (!canAutomate(eAutomate))
{
return;
}
/************************************************************************************************/
/* Afforess Start 08/20/10 */
/* */
/* Advanced Automations */
/************************************************************************************************/
CvUnit* pLoopUnit = NULL;
CLLNode<IDInfo>* pUnitNode = NULL;
if (eAutomate == AUTOMATE_UPGRADING || eAutomate == AUTOMATE_CANCEL_UPGRADING)
{
pUnitNode = getGroup()->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = getGroup()->nextUnitNode(pUnitNode);
pLoopUnit->setAutoUpgrading((eAutomate == AUTOMATE_UPGRADING));
}
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
}
return;
}
if (eAutomate == AUTOMATE_PROMOTIONS || eAutomate == AUTOMATE_CANCEL_PROMOTIONS)
{
pUnitNode = getGroup()->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = getGroup()->nextUnitNode(pUnitNode);
pLoopUnit->setAutoPromoting((eAutomate == AUTOMATE_PROMOTIONS));
}
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
}
return;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
getGroup()->setAutomateType(eAutomate);
}
bool CvUnit::canScrap() const
{
if (plot()->isFighting())
{
return false;
}
return true;
}
void CvUnit::scrap()
{
if (!canScrap())
{
return;
}
if( gUnitLogLevel > 2 )
{
CvWString szString;
getUnitAIString(szString, AI_getUnitAIType());
logBBAI(" %S scraps %S", GET_PLAYER(getOwnerINLINE()).getCivilizationDescription(0), getName(0).GetCString());
}
/************************************************************************************************/
/* Afforess Start 01/13/10 */
/* */
/* Gold when Disbanding */
/************************************************************************************************/
if (GC.getDefineINT("DISBANDING_EARNS_GOLD") && plot()->getOwnerINLINE() == getOwnerINLINE())
{
int iCost = (getUnitInfo().getProductionCost() * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent()) / 100;
GET_PLAYER(getOwnerINLINE()).changeGold(iCost / std::max(1, (GC.getDefineINT("UNIT_GOLD_DISBAND_DIVISOR"))));
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
getGroup()->AI_setMissionAI(MISSIONAI_DELIBERATE_KILL, NULL, NULL);
kill(true, NO_PLAYER, true);
}
bool CvUnit::canGift(bool bTestVisible, bool bTestTransport)
{
CvPlot* pPlot = plot();
CvUnit* pTransport = getTransportUnit();
if (!(pPlot->isOwned()))
{
return false;
}
if (pPlot->getOwnerINLINE() == getOwnerINLINE())
{
return false;
}
if (pPlot->isVisibleEnemyUnit(this))
{
return false;
}
if (pPlot->isVisibleEnemyUnit(pPlot->getOwnerINLINE()))
{
return false;
}
if (!pPlot->isValidDomainForLocation(*this) && NULL == pTransport)
{
return false;
}
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (m_pUnitInfo->getCorporationSpreads(iCorp) > 0)
{
return false;
}
}
if (bTestTransport)
{
if (pTransport && pTransport->getTeam() != pPlot->getTeam())
{
return false;
}
}
if (!bTestVisible)
{
if (GET_TEAM(pPlot->getTeam()).isUnitClassMaxedOut(getUnitClassType(), GET_TEAM(pPlot->getTeam()).getUnitClassMaking(getUnitClassType())))
{
return false;
}
if (GET_PLAYER(pPlot->getOwnerINLINE()).isUnitClassMaxedOut(getUnitClassType(), GET_PLAYER(pPlot->getOwnerINLINE()).getUnitClassMaking(getUnitClassType())))
{
return false;
}
if (!(GET_PLAYER(pPlot->getOwnerINLINE()).AI_acceptUnit(this)))
{
return false;
}
}
return !atWar(pPlot->getTeam(), getTeam());
}
void CvUnit::gift(bool bTestTransport)
{
CvUnit* pGiftUnit;
CvWString szBuffer;
PlayerTypes eOwner;
if (!canGift(false, bTestTransport))
{
return;
}
std::vector<CvUnit*> aCargoUnits;
getCargoUnits(aCargoUnits);
for (uint i = 0; i < aCargoUnits.size(); ++i)
{
aCargoUnits[i]->gift(false);
}
FAssertMsg(plot()->getOwnerINLINE() != NO_PLAYER, "plot()->getOwnerINLINE() is not expected to be equal with NO_PLAYER");
pGiftUnit = GET_PLAYER(plot()->getOwnerINLINE()).initUnit(getUnitType(), getX_INLINE(), getY_INLINE(), AI_getUnitAIType(), NO_DIRECTION, GC.getGameINLINE().getSorenRandNum(10000, "AI Unit Birthmark"));
FAssertMsg(pGiftUnit != NULL, "GiftUnit is not assigned a valid value");
eOwner = getOwnerINLINE();
pGiftUnit->convert(this);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/03/09 jdog5000 */
/* */
/* General AI */
/************************************************************************************************/
//GET_PLAYER(pGiftUnit->getOwnerINLINE()).AI_changePeacetimeGrantValue(eOwner, (pGiftUnit->getUnitInfo().getProductionCost() / 5));
if( pGiftUnit->isCombat() )
{
GET_PLAYER(pGiftUnit->getOwnerINLINE()).AI_changePeacetimeGrantValue(eOwner, (pGiftUnit->getUnitInfo().getProductionCost() * 3 * GC.getGameINLINE().AI_combatValue(pGiftUnit->getUnitType()))/100);
}
else
{
GET_PLAYER(pGiftUnit->getOwnerINLINE()).AI_changePeacetimeGrantValue(eOwner, (pGiftUnit->getUnitInfo().getProductionCost()));
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_GIFTED_UNIT_TO_YOU", GET_PLAYER(eOwner).getNameKey(), pGiftUnit->getNameKey());
AddDLLMessage(pGiftUnit->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_UNITGIFTED", MESSAGE_TYPE_INFO, pGiftUnit->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), pGiftUnit->getX_INLINE(), pGiftUnit->getY_INLINE(), true, true);
}
// Python Event
CvEventReporter::getInstance().unitGifted(pGiftUnit, getOwnerINLINE(), plot());
}
bool CvUnit::canLoadUnit(const CvUnit* pUnit, const CvPlot* pPlot) const
{
FAssert(pUnit != NULL);
FAssert(pPlot != NULL);
if (pUnit == this)
{
return false;
}
if (pUnit->getTeam() != getTeam())
{
return false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/23/10 Mongoose & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// From Mongoose SDK
if (isCargo() && getTransportUnit() == pUnit)
{
return false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
if (getCargo() > 0)
{
return false;
}
if (pUnit->isCargo())
{
return false;
}
// Thomas SG - AC: Advanced Cargo START
//if (!(pUnit->cargoSpaceAvailable(getSpecialUnitType(), getDomainType())))
//{
// return false;
//}
// Thomas SG - AC: Advanced Cargo END
if (!(pUnit->atPlot(pPlot)))
{
return false;
}
if (!m_pUnitInfo->isHiddenNationality() && pUnit->getUnitInfo().isHiddenNationality())
{
return false;
}
// Thomas SG - AC: Advanced Cargo START
{
if (getNumSpecialUnitTypes() > 0)
{
bool bDomainSpace = false;
if (pUnit->cargoSpaceAvailable(NO_SPECIALUNIT, getDomainType()) > 0)
{
bDomainSpace = true;
}
bool set = false;
for (int i = 0; i < getNumSpecialUnitTypes() && set == false; i++)
{
if (bDomainSpace || pUnit->cargoSpaceAvailable(getSpecialUnitType(i), getDomainType()) > 0)
{
if (GC.getSpecialUnitInfo(getSpecialUnitType(i)).isCityLoad())
{
if (pPlot->isCity(true, getTeam()))
{
set = true;
}
}
else
{
set = true;
}
}
}
if (set == false)
{
return false;
}
}
if (getNumSpecialUnitTypes() == 0)
{
if (pUnit->cargoSpaceAvailable(NO_SPECIALUNIT, getDomainType()) == 0)
{
return false;
}
}
if (getNumSpecialUnitTypes() > 0)
{
if (GC.getSpecialUnitInfo(getSpecialUnitType(0)).isCityLoad())
{
if (!pPlot->isCity(true, getTeam()))
{
return false;
}
}
}
}
// Thomas SG - AC: Advanced Cargo END
return true;
}
void CvUnit::loadUnit(CvUnit* pUnit)
{
if (!canLoadUnit(pUnit, plot()))
{
return;
}
setTransportUnit(pUnit);
}
bool CvUnit::shouldLoadOnMove(const CvPlot* pPlot) const
{
if (isCargo())
{
return false;
}
switch (getDomainType())
{
case DOMAIN_LAND:
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/30/09 Mongoose & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if (pPlot->isWater())
*/
// From Mongoose SDK
/************************************************************************************************/
/* Afforess Start 08/18/10 */
/* */
/* */
/************************************************************************************************/
/*
if (pPlot->isWater() && !canMoveAllTerrain())
*/
if ((pPlot->isWater() && !canMoveAllTerrain()) && !pPlot->isCanMoveLandUnits())
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
return true;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
break;
case DOMAIN_AIR:
if (!pPlot->isFriendlyCity(*this, true))
{
return true;
}
if (m_pUnitInfo->getAirUnitCap() > 0)
{
if (pPlot->airUnitSpaceAvailable(getTeam()) <= 0)
{
return true;
}
}
break;
default:
break;
}
if (m_pUnitInfo->getTerrainImpassable(pPlot->getTerrainType()))
{
TechTypes eTech = (TechTypes)m_pUnitInfo->getTerrainPassableTech(pPlot->getTerrainType());
if (NO_TECH == eTech || !GET_TEAM(getTeam()).isHasTech(eTech))
{
return true;
}
}
return false;
}
bool CvUnit::canLoad(const CvPlot* pPlot) const
{
PROFILE_FUNC();
FAssert(pPlot != NULL);
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (canLoadUnit(pLoopUnit, pPlot))
{
return true;
}
}
return false;
}
void CvUnit::load()
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pPlot;
int iPass;
if (!canLoad(plot()))
{
return;
}
pPlot = plot();
for (iPass = 0; iPass < 2; iPass++)
{
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (canLoadUnit(pLoopUnit, pPlot))
{
if ((iPass == 0) ? (pLoopUnit->getOwnerINLINE() == getOwnerINLINE()) : (pLoopUnit->getTeam() == getTeam()))
{
setTransportUnit(pLoopUnit);
break;
}
}
}
if (isCargo())
{
break;
}
}
}
bool CvUnit::canUnload() const
{
CvPlot& kPlot = *(plot());
if (getTransportUnit() == NULL)
{
return false;
}
if (!kPlot.isValidDomainForLocation(*this))
{
return false;
}
if (getDomainType() == DOMAIN_AIR)
{
if (kPlot.isFriendlyCity(*this, true))
{
int iNumAirUnits = kPlot.countNumAirUnits(getTeam());
CvCity* pCity = kPlot.getPlotCity();
if (NULL != pCity)
{
if (iNumAirUnits >= pCity->getAirUnitCapacity(getTeam()))
{
return false;
}
}
else
{
if (iNumAirUnits >= GC.getDefineINT("CITY_AIR_UNIT_CAPACITY"))
{
return false;
}
}
}
}
return true;
}
void CvUnit::unload()
{
if (!canUnload())
{
return;
}
setTransportUnit(NULL);
}
bool CvUnit::canUnloadAll() const
{
if (getCargo() == 0)
{
return false;
}
return true;
}
void CvUnit::unloadAll()
{
if (!canUnloadAll())
{
return;
}
std::vector<CvUnit*> aCargoUnits;
getCargoUnits(aCargoUnits);
for (uint i = 0; i < aCargoUnits.size(); ++i)
{
CvUnit* pCargo = aCargoUnits[i];
if (pCargo->canUnload())
{
pCargo->setTransportUnit(NULL);
}
else
{
FAssert(isHuman() || pCargo->getDomainType() == DOMAIN_AIR);
pCargo->getGroup()->setActivityType(ACTIVITY_AWAKE);
}
}
}
bool CvUnit::canHold(const CvPlot* pPlot) const
{
return true;
}
bool CvUnit::canSleep(const CvPlot* pPlot) const
{
if (isFortifyable())
{
return false;
}
if (isWaiting())
{
return false;
}
return true;
}
bool CvUnit::canFortify(const CvPlot* pPlot) const
{
if (!isFortifyable())
{
return false;
}
if (isWaiting())
{
return false;
}
return true;
}
bool CvUnit::canAirPatrol(const CvPlot* pPlot) const
{
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (!canAirDefend(pPlot))
{
return false;
}
if (isWaiting())
{
return false;
}
return true;
}
bool CvUnit::canSeaPatrol(const CvPlot* pPlot) const
{
if (!pPlot->isWater())
{
return false;
}
if (getDomainType() != DOMAIN_SEA)
{
return false;
}
if (!canFight() || isOnlyDefensive())
{
return false;
}
if (isWaiting())
{
return false;
}
return true;
}
void CvUnit::airCircle(bool bStart)
{
if (!GC.IsGraphicsInitialized())
{
return;
}
if (!isInViewport())
{
return;
}
if ((getDomainType() != DOMAIN_AIR) || (maxInterceptionProbability() == 0))
{
return;
}
//cancel previos missions
if ( !isUsingDummyEntities() && isInViewport() )
{
gDLL->getEntityIFace()->RemoveUnitFromBattle( this );
if (bStart)
{
CvAirMissionDefinition kDefinition;
kDefinition.setPlot(plot());
kDefinition.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefinition.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kDefinition.setMissionType(MISSION_AIRPATROL);
kDefinition.setMissionTime(1.0f); // patrol is indefinite - time is ignored
addMission( &kDefinition );
}
}
}
bool CvUnit::canHeal(const CvPlot* pPlot) const
{
if (!isHurt())
{
return false;
}
if (isWaiting())
{
return false;
}
/*************************************************************************************************/
/* UNOFFICIAL_PATCH 06/30/10 LunarMongoose */
/* */
/* Bugfix */
/*************************************************************************************************/
/* original bts code
if (healRate(pPlot) <= 0)
{
return false;
}
*/
// Mongoose FeatureDamageFix
if (healTurns(pPlot) == MAX_INT)
{
return false;
}
/*************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/*************************************************************************************************/
return true;
}
bool CvUnit::canSentry(const CvPlot* pPlot) const
{
if (!canDefend(pPlot))
{
return false;
}
if (isWaiting())
{
return false;
}
return true;
}
int CvUnit::healRate(const CvPlot* pPlot) const
{
PROFILE_FUNC();
CLLNode<IDInfo>* pUnitNode;
CvCity* pCity;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iTotalHeal;
int iHeal;
int iBestHeal;
int iI;
pCity = pPlot->getPlotCity();
iTotalHeal = 0;
if (pPlot->isCity(true, getTeam()))
{
iTotalHeal += GC.getDefineINT("CITY_HEAL_RATE") + (GET_TEAM(getTeam()).isFriendlyTerritory(pPlot->getTeam()) ? getExtraFriendlyHeal() : getExtraNeutralHeal());
if (pCity && !pCity->isOccupation())
{
iTotalHeal += pCity->getHealRate();
}
}
else
{
if (!GET_TEAM(getTeam()).isFriendlyTerritory(pPlot->getTeam()))
{
if (isEnemy(pPlot->getTeam(), pPlot))
{
iTotalHeal += (GC.getDefineINT("ENEMY_HEAL_RATE") + getExtraEnemyHeal());
}
else
{
iTotalHeal += (GC.getDefineINT("NEUTRAL_HEAL_RATE") + getExtraNeutralHeal());
}
}
else
{
iTotalHeal += (GC.getDefineINT("FRIENDLY_HEAL_RATE") + getExtraFriendlyHeal());
}
}
// XXX optimize this (save it?)
iBestHeal = 0;
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTeam() == getTeam()) // XXX what about alliances?
{
iHeal = pLoopUnit->getSameTileHeal();
if (iHeal > iBestHeal)
{
iBestHeal = iHeal;
}
}
}
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlot->area())
{
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTeam() == getTeam()) // XXX what about alliances?
{
iHeal = pLoopUnit->getAdjacentTileHeal();
if (iHeal > iBestHeal)
{
iBestHeal = iHeal;
}
}
}
}
}
}
iTotalHeal += iBestHeal;
// XXX
return iTotalHeal;
}
int CvUnit::healTurns(const CvPlot* pPlot) const
{
int iHeal;
int iTurns;
if (!isHurt())
{
return 0;
}
iHeal = healRate(pPlot);
/*************************************************************************************************/
/* UNOFFICIAL_PATCH 06/02/10 LunarMongoose */
/* */
/* Bugfix */
/*************************************************************************************************/
// Mongoose FeatureDamageFix
#ifdef MULTI_FEATURE_MOD
for (int i=0; i<pPlot->getNumFeatures(); i++)
{
iHeal -= GC.getFeatureInfo(pPlot->getFeatureByIndex(i)).getTurnDamage();
}
#else
FeatureTypes eFeature = pPlot->getFeatureType();
if (eFeature != NO_FEATURE)
{
iHeal -= GC.getFeatureInfo(eFeature).getTurnDamage();
}
#endif
/*************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/*************************************************************************************************/
if (iHeal > 0)
{
iTurns = (getDamage() / iHeal);
if ((getDamage() % iHeal) != 0)
{
iTurns++;
}
return iTurns;
}
else
{
return MAX_INT;
}
}
void CvUnit::doHeal()
{
changeDamage(-(healRate(plot())));
}
bool CvUnit::canAirlift(const CvPlot* pPlot) const
{
CvCity* pCity;
if (getDomainType() != DOMAIN_LAND)
{
return false;
}
if (hasMoved())
{
return false;
}
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (pCity->getCurrAirlift() >= pCity->getMaxAirlift())
{
return false;
}
if (pCity->getTeam() != getTeam())
{
return false;
}
return true;
}
bool CvUnit::canAirliftAt(const CvPlot* pPlot, int iX, int iY) const
{
CvPlot* pTargetPlot;
CvCity* pTargetCity;
if (!canAirlift(pPlot))
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (!canMoveInto(pTargetPlot))
{
return false;
}
pTargetCity = pTargetPlot->getPlotCity();
if (pTargetCity == NULL)
{
return false;
}
if (pTargetCity->isAirliftTargeted())
{
return false;
}
if (pTargetCity->getTeam() != getTeam() && !GET_TEAM(pTargetCity->getTeam()).isVassal(getTeam()))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 03/7/10 */
/* */
/* Airlift Range */
/************************************************************************************************/
if (!GET_TEAM(getTeam()).isRebaseAnywhere())
{
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_AIRLIFT_RANGE))
{
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iX, iY) > (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_AIRLIFT_RANGE)))
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return true;
}
bool CvUnit::airlift(int iX, int iY)
{
CvCity* pCity;
CvCity* pTargetCity;
CvPlot* pTargetPlot;
if (!canAirliftAt(plot(), iX, iY))
{
return false;
}
pCity = plot()->getPlotCity();
FAssert(pCity != NULL);
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
FAssert(pTargetPlot != NULL);
pTargetCity = pTargetPlot->getPlotCity();
FAssert(pTargetCity != NULL);
FAssert(pCity != pTargetCity);
pCity->changeCurrAirlift(1);
if (pTargetCity->getMaxAirlift() == 0)
{
pTargetCity->setAirliftTargeted(true);
}
finishMoves();
setXY(pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE());
return true;
}
bool CvUnit::isNukeVictim(const CvPlot* pPlot, TeamTypes eTeam) const
{
CvPlot* pLoopPlot;
int iDX, iDY;
if (!(GET_TEAM(eTeam).isAlive()))
{
return false;
}
if (eTeam == getTeam())
{
return false;
}
for (iDX = -(nukeRange()); iDX <= nukeRange(); iDX++)
{
for (iDY = -(nukeRange()); iDY <= nukeRange(); iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getTeam() == eTeam)
{
return true;
}
if (pLoopPlot->plotCheck(PUF_isCombatTeam, eTeam, getTeam()) != NULL)
{
return true;
}
}
}
}
return false;
}
bool CvUnit::canNuke(const CvPlot* pPlot) const
{
if (nukeRange() == -1)
{
return false;
}
return true;
}
/************************************************************************************************/
/* Afforess Start 09/09/10 */
/* */
/* M.A.D Nukes */
/************************************************************************************************/
bool CvUnit::canNukeAt(const CvPlot* pPlot, int iX, int iY, bool bTestAtWar) const
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
CvPlot* pTargetPlot;
int iI;
if (!canNuke(pPlot))
{
return false;
}
int iDistance = plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iX, iY);
if (iDistance <= nukeRange())
{
return false;
}
if (airRange() > 0 && iDistance > airRange())
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
// < M.A.D. Nukes Start >
if (bTestAtWar)
{
for (iI = 0; iI < MAX_TEAMS; iI++)
{
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - NB: A-Bomb START
if (!((TeamTypes)iI == GET_TEAM(getTeam()).getID()))
{
if (isNukeVictim(pTargetPlot, ((TeamTypes)iI)))
{
if (!isEnemy((TeamTypes)iI))
{
return false;
}
}
}
// Dale - NB: A-Bomb END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
}
}
// < M.A.D. Nukes End >
return true;
}
// < M.A.D. Nukes Start >
bool CvUnit::setMADTargetPlot(int iX, int iY)
{
CvPlot* pPlot;
CvCity* pCity;
CvWString szBuffer;
if(!GC.getMapINLINE().isPlot(iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
pCity = pPlot->getPlotCity();
if(pCity == NULL)
{
return false;
}
if(/*!(atWar(getTeam(), pCity->getTeam())) &&*/ (!isMADEnabled() || pPlot != getMADTargetPlot()))
{
if(isMADEnabled() && getMADTargetPlot() != NULL)
{
GET_PLAYER(getOwnerINLINE()).changeMADOutgoing(-1);
GET_PLAYER(getMADTargetPlotOwner()).changeMADIncoming(-1);
getMADTargetPlot()->getPlotCity()->changeMADIncoming(-1);
}
setMADEnabled(true); // Dale - MAD: turn MAD on for unit
setMADTargetPlot(pPlot); // Dale - MAD: set target
setMADTargetPlotOwner(pCity->getOwnerINLINE());
//getGroup()->setActivityType(ACTIVITY_SLEEP);
finishMoves();
GET_PLAYER(getOwnerINLINE()).changeMADOutgoing(1);
GET_PLAYER(pCity->getOwnerINLINE()).changeMADIncoming(1);
getMADTargetPlot()->getPlotCity()->changeMADIncoming(1);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_NUKE_TARGET_SET_ON", getNameKey(), pCity->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_NUKE_EXPLODES", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("Someone has targetted %s1 with a nuke!", pCity->getNameKey());
AddDLLMessage(pCity->getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_NUKE_EXPLODES", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
finishMoves();
return true;
}
return false;
}
bool CvUnit::clearMADTargetPlot()
{
CvWString szBuffer;
if(!isMADEnabled())
{
return false;
}
setMADEnabled(false);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_NUKE_TARGET_RESET", getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, NULL, MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
}
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
return true;
}
// < M.A.D. Nukes End >
bool CvUnit::nuke(int iX, int iY)
{
CvPlot* pPlot;
CvWString szBuffer;
bool abTeamsAffected[MAX_TEAMS];
TeamTypes eBestTeam;
int iBestInterception;
int iI, iJ, iK;
/************************************************************************************************/
/* Afforess Start 09/09/10 */
/* */
/* M.A.D Nukes */
/************************************************************************************************/
/*
if (!canNukeAt(plot(), iX, iY))
*/
if (!canNukeAt(plot(), iX, iY, !isMADEnabled()))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
// < M.A.D. Nukes Start >
if(GET_PLAYER(getOwnerINLINE()).isEnabledMAD())
{
if(!isHuman() && !isMADEnabled())
{
if(GET_PLAYER(getOwnerINLINE()).getMADDeterrent() > 0)
{
if(GET_PLAYER(getOwnerINLINE()).getMADIncoming() >= GET_PLAYER(getOwnerINLINE()).getMADOutgoing())
{
GET_PLAYER(getOwnerINLINE()).changeMADDeterrent(-1);
return false;
}
}
}
}
// Dale - MAD: check validity of target before blowing it up
if(isMADEnabled())
{
CvCity* pCity = getMADTargetPlot()->getPlotCity();
if(pCity == NULL || pCity->getOwnerINLINE() != getMADTargetPlotOwner())
{
setMADEnabled(false);
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_NUKE_TARGET_FAILED");
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NUKE_EXPLODES", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), true, true);
return false;
}
}
// < M.A.D. Nukes End >
for (iI = 0; iI < MAX_TEAMS; iI++)
{
abTeamsAffected[iI] = isNukeVictim(pPlot, ((TeamTypes)iI));
}
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (abTeamsAffected[iI])
{
if (!isEnemy((TeamTypes)iI))
{
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - NB: A-Bomb START
if (!((TeamTypes)iI == GET_TEAM(getTeam()).getID()))
{
GET_TEAM(getTeam()).declareWar(((TeamTypes)iI), false, WARPLAN_TOTAL);
}
// Dale - NB: A-Bomb END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
}
}
}
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - NB: A-Bomb START
if(airBaseCombatStr() != 0)
{
if (interceptTest(pPlot))
{
return true;
}
}
// Dale - NB: A-Bomb END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
iBestInterception = 0;
eBestTeam = NO_TEAM;
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (abTeamsAffected[iI])
{
if (GET_TEAM((TeamTypes)iI).getNukeInterception() > iBestInterception)
{
iBestInterception = GET_TEAM((TeamTypes)iI).getNukeInterception();
eBestTeam = ((TeamTypes)iI);
}
}
}
iBestInterception *= (100 - m_pUnitInfo->getEvasionProbability());
iBestInterception /= 100;
setReconPlot(pPlot);
if (GC.getGameINLINE().getSorenRandNum(100, "Nuke") < iBestInterception)
{
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_NUKE_INTERCEPTED", GET_PLAYER(getOwnerINLINE()).getNameKey(), getNameKey(), GET_TEAM(eBestTeam).getName().GetCString());
AddDLLMessage(((PlayerTypes)iI), (((PlayerTypes)iI) == getOwnerINLINE()), GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NUKE_INTERCEPTED", MESSAGE_TYPE_MAJOR_EVENT, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
}
if (pPlot->isActiveVisible(false))
{
// Nuke entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_NUKE).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_NUKE);
kDefiniton.setPlot(pPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, this);
// Add the intercepted mission (defender is not NULL)
addMission(&kDefiniton);
}
kill(true, NO_PLAYER, true);
return true; // Intercepted!!! (XXX need special event for this...)
}
// < M.A.D. Nukes Start >
// MAD: the guts of MAD. Here is what happens:
// 1. Ascertain the teams affected.
// 2. Ascertain players in the teams affected.
// 3. Set the MAD trigger to true for the players affected to the agressor.
if (GET_PLAYER(getOwnerINLINE()).isEnabledMAD())
{
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (iI != getTeam())
{
if (abTeamsAffected[iI])
{
for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
if (GET_PLAYER((PlayerTypes)iJ).isAlive())
{
if (GET_PLAYER((PlayerTypes)iJ).getTeam() == ((TeamTypes)iI))
{
GET_PLAYER((PlayerTypes)iJ).setMADTrigger(getOwnerINLINE(), true);
}
}
}
}
}
}
}
}
// < M.A.D. Nukes Start >
if (pPlot->isActiveVisible(false))
{
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - NB: A-Bomb START
if(airBaseCombatStr() != 0)
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionTime(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime() * gDLL->getSecsPerTurn());
kAirMission.setMissionType(MISSION_AIRSTRIKE);
kAirMission.setPlot(pPlot);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
addMission(&kAirMission);
}
else
{
// Nuke entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_NUKE).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_NUKE);
kDefiniton.setPlot(pPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, NULL);
// Add the non-intercepted mission (defender is NULL)
addMission(&kDefiniton);
}
// Dale - NB: A-Bomb END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
}
setMadeAttack(true);
setAttackPlot(pPlot, false);
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (abTeamsAffected[iI])
{
GET_TEAM((TeamTypes)iI).changeWarWeariness(getTeam(), 100 * GC.getDefineINT("WW_HIT_BY_NUKE"));
GET_TEAM(getTeam()).changeWarWeariness(((TeamTypes)iI), 100 * GC.getDefineINT("WW_ATTACKED_WITH_NUKE"));
GET_TEAM(getTeam()).AI_changeWarSuccess(((TeamTypes)iI), GC.getDefineINT("WAR_SUCCESS_NUKE"));
}
}
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (iI != getTeam())
{
if (abTeamsAffected[iI])
{
for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
if (GET_PLAYER((PlayerTypes)iJ).isAlive())
{
if (GET_PLAYER((PlayerTypes)iJ).getTeam() == ((TeamTypes)iI))
{
GET_PLAYER((PlayerTypes)iJ).AI_changeMemoryCount(getOwnerINLINE(), MEMORY_NUKED_US, 1);
}
}
}
}
else
{
for (iJ = 0; iJ < MAX_TEAMS; iJ++)
{
if (GET_TEAM((TeamTypes)iJ).isAlive())
{
if (abTeamsAffected[iJ])
{
if (GET_TEAM((TeamTypes)iI).isHasMet((TeamTypes)iJ))
{
if (GET_TEAM((TeamTypes)iI).AI_getAttitude((TeamTypes)iJ) >= ATTITUDE_CAUTIOUS)
{
for (iK = 0; iK < MAX_PLAYERS; iK++)
{
if (GET_PLAYER((PlayerTypes)iK).isAlive())
{
if (GET_PLAYER((PlayerTypes)iK).getTeam() == ((TeamTypes)iI))
{
GET_PLAYER((PlayerTypes)iK).AI_changeMemoryCount(getOwnerINLINE(), MEMORY_NUKED_FRIEND, 1);
}
}
}
break;
}
}
}
}
}
}
}
}
}
// XXX some AI should declare war here...
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_NUKE_LAUNCHED", GET_PLAYER(getOwnerINLINE()).getNameKey(), getNameKey());
AddDLLMessage(((PlayerTypes)iI), (((PlayerTypes)iI) == getOwnerINLINE()), GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NUKE_EXPLODES", MESSAGE_TYPE_MAJOR_EVENT, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
}
if (isSuicide())
{
kill(true);
}
// < M.A.D. Nukes Start >
GC.getGameINLINE().setLastNukeStrikePlot(pPlot);
// < M.A.D. Nukes End >
return true;
}
bool CvUnit::canRecon(const CvPlot* pPlot) const
{
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (airRange() == 0)
{
return false;
}
if (m_pUnitInfo->isSuicide())
{
return false;
}
return true;
}
bool CvUnit::canReconAt(const CvPlot* pPlot, int iX, int iY) const
{
if (!canRecon(pPlot))
{
return false;
}
int iDistance = plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iX, iY);
if (iDistance > airRange() || 0 == iDistance)
{
return false;
}
return true;
}
bool CvUnit::recon(int iX, int iY)
{
CvPlot* pPlot;
if (!canReconAt(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
setReconPlot(pPlot);
finishMoves();
/************************************************************************************************/
/* Afforess Start 09/13/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP))
{
setExperience100(getExperience100() + 5, -1);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pPlot->isActiveVisible(false))
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRBOMB);
kAirMission.setMissionType(MISSION_RECON);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo((MissionTypes)MISSION_RECON).getTime() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
return true;
}
bool CvUnit::canParadrop(const CvPlot* pPlot) const
{
if (getDropRange() <= 0)
{
return false;
}
if (hasMoved())
{
return false;
}
if (!pPlot->isFriendlyCity(*this, true))
{
return false;
}
return true;
}
bool CvUnit::canParadropAt(const CvPlot* pPlot, int iX, int iY) const
{
if (!canParadrop(pPlot))
{
return false;
}
CvPlot* pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (NULL == pTargetPlot || pTargetPlot == pPlot)
{
return false;
}
if (!pTargetPlot->isVisible(getTeam(), false))
{
return false;
}
if (!canMoveInto(pTargetPlot, false, false, true))
{
return false;
}
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iX, iY) > getDropRange())
{
return false;
}
if (!canCoexistWithEnemyUnit(NO_TEAM))
{
if (pTargetPlot->isEnemyCity(*this))
{
return false;
}
if (pTargetPlot->isVisibleEnemyUnit(this))
{
return false;
}
}
return true;
}
bool CvUnit::paradrop(int iX, int iY)
{
if (!canParadropAt(plot(), iX, iY))
{
return false;
}
CvPlot* pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
changeMoves(GC.getMOVE_DENOMINATOR() / 2);
setMadeAttack(true);
setXY(pPlot->getX_INLINE(), pPlot->getY_INLINE());
//check if intercepted
if(interceptTest(pPlot))
{
return true;
}
/************************************************************************************************/
/* Afforess Start 09/13/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP))
{
setExperience100(getExperience100() + 5, -1);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
//play paradrop animation by itself
if (pPlot->isActiveVisible(false))
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_PARADROP);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo((MissionTypes)MISSION_PARADROP).getTime() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
return true;
}
bool CvUnit::canAirBomb(const CvPlot* pPlot) const
{
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - AB: Bombing START
if (GC.isDCM_AIR_BOMBING())
{
if (isHuman())
{
return false;
}
}
// Dale - AB: Bombing END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (airBombBaseRate() == 0)
{
return false;
}
if (isMadeAttack())
{
return false;
}
return true;
}
/************************************************************************************************/
/* Great Diplomat MOD START Stolenrays */
/************************************************************************************************/
bool CvUnit::canBribeBarbarian(const CvPlot* pPlot, bool bTestVisible) const
{
CvUnit* pLoopUnit;
if (!m_pUnitInfo->isBribeBarbarian())
{
return false;
}
if (isBarbarian())
{
return false;
}
// Can't buy units if they are not in a legal plot
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (NULL != pLoopUnit)
{
if (pLoopUnit->isBarbarian())
{
if (pLoopUnit->getTeam() != getTeam())
{
return true;
}
}
}
}
return false;
}
bool CvUnit::BribeBarbarian()
{
// FAssertMsg(pUnit != NULL, "City is not assigned a valid value");
int iPass;
CvPlot* pPlot = plot();
if (!canBribeBarbarian(pPlot))
{
return false;
}
for (iPass = 0; iPass < 2; iPass++)
{
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
CvUnit* pLoopUnit;
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->isBarbarian())
{
if (iPass == 0)
{
CvWString szBuffer;
pLoopUnit->kill(true);
szBuffer = gDLL->getText("TXT_KEY_MISC_BRIBE_BARBARIAN", pLoopUnit->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
}
}
}
if (pPlot->isActiveVisible(false))
{
// Bribe Barbarian entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_BRIBE_BARBARIAN).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_BRIBE_BARBARIAN);
kDefiniton.setPlot(pPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, NULL);
gDLL->getEntityIFace()->AddMission(&kDefiniton);
}
kill(true);
return true;
}
bool CvUnit::canGoodwill(const CvPlot* pPlot, bool bTestVisible) const
{
CvCity* pCity;
if (!(m_pUnitInfo->isGoodwill()))
{
return false;
}
if (pPlot->getTeam() == getTeam())
{
return false;
}
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
return true;
}
bool CvUnit::Goodwill()
{
CvCity* pCity;
CvPlot* pPlot;
CvWString szBuffer;
if (!canGoodwill(plot()))
{
return false;
}
pCity = plot()->getPlotCity();
FAssertMsg(pCity != NULL, "City is not assigned a valid value");
int iAttitudeModifier = GC.getDefineINT("GOODWILL_ATTITUDE");
szBuffer = gDLL->getText("TXT_KEY_MISC_GOODWILL", GET_PLAYER(pCity->getOwnerINLINE()).getNameKey(), GET_TEAM(getTeam()).getName().GetCString(), iAttitudeModifier);
AddMessage(pCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WELOVEKING", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_GOODWILL", GET_PLAYER(pCity->getOwnerINLINE()).getNameKey(), iAttitudeModifier);
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WELOVEKING", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE());
GET_PLAYER(pCity->getOwnerINLINE()).AI_changeAttitudeExtra(getOwnerINLINE(), GC.getDefineINT("GOODWILL_ATTITUDE"));
pPlot = plot();
if (pPlot->isActiveVisible(false))
{
// Goodwill entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_GOODWILL).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_GOODWILL);
kDefiniton.setPlot(pPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, NULL);
gDLL->getEntityIFace()->AddMission(&kDefiniton);
}
kill(true);
return true;
}
bool CvUnit::canColonizeBarbarians(const CvPlot* pPlot) const
{
CvCity* pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (isBarbarian())
{
return false;
}
if (!m_pUnitInfo->isBarbariansColonisator())
{
return false;
}
if (getOwnerINLINE() == pCity->getOwnerINLINE())
{
return false;
}
if (!pCity->isBarbarian())
{
return false;
}
return true;
}
void CvUnit::colonizeBarbarians()
{
CvPlot* pPlot = plot();
CvCity* pCity = pPlot->getPlotCity();
if (!(canColonizeBarbarians(pPlot)))
{
return;
}
pPlot->setOwner(getOwnerINLINE(), false, true);
GET_PLAYER(getOwnerINLINE()).AI_makeAssignWorkDirty();
kill(true);
}
bool CvUnit::canForcePeace() const
{
if (isBarbarian())
{
return false;
}
if (!(m_pUnitInfo->canForcePeace()))
{
return false;
}
bool bValid = false;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (canForcePeaceWith((PlayerTypes)iI))
{
bValid = true;
break;
}
}
}
return bValid;
}
bool CvUnit::canForcePeaceWith(PlayerTypes ePlayer) const
{
TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam();
if (getOwnerINLINE() == ePlayer || getTeam() == eTeam)
{
return false;
}
if (GET_TEAM(eTeam).isBarbarian())
{
return false;
}
if (!atWar(getTeam(), eTeam))
{
return false;
}
if (!GET_TEAM(getTeam()).canChangeWarPeace(eTeam))
{
return false;
}
return true;
}
void CvUnit::tryForcePeace()
{
if (!canForcePeace())
{
return;
}
if (isHuman())// The non human behaviour is coded in UnitAI
{
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_UNIT_FORCE_PEACE);
if (NULL != pInfo)
{
pInfo->setData1(getID());
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE());
}
}
}
void CvUnit::applyForcePeace(PlayerTypes ePlayer)
{
if (!canForcePeaceWith(ePlayer))
{
return;
}
GET_PLAYER(getOwnerINLINE()).forcePeace(ePlayer);
CvWString szBuffer;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if ((PlayerTypes)iI == getOwnerINLINE() || (PlayerTypes)iI == ePlayer || GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isHasMet(getTeam()) || GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isHasMet(GET_PLAYER(ePlayer).getTeam()))
{
szBuffer = gDLL->getText("TXT_KEY_MISC_SOMEUNIT_FORCE_PEACE", getNameKey(), GET_PLAYER(ePlayer).getCivilizationDescriptionKey(), GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey());
AddMessage((PlayerTypes)iI, false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_THEIRMAKEPEACE", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_HIGHLIGHT_TEXT"));
}
}
}
kill(true);
}
void CvUnit::upgradeImprovements(const CvPlot* pPlot, CommandTypes eCommand)
{
if (!canUpgradeImprovements(pPlot, eCommand))
{
return;
}
CvCity* pCity = pPlot->getPlotCity();
CvWString szTempBuffer;
CvPlot* pLoopPlot;
int iI, iJ, iK;
int aiPotential1[NUM_YIELD_TYPES];
int aiPotential2[NUM_YIELD_TYPES];
for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
aiPotential1[iJ] = 0;
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = pCity->getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
aiPotential1[iJ] += pLoopPlot->calculateYield((YieldTypes)iJ);
}
}
}
if (GC.getCommandInfo(eCommand).isUpgradeImprovementRateArrayValid())
{
int numImprovementInfos = GC.getNumImprovementInfos();
for (iJ = 0; iJ < numImprovementInfos; ++iJ)
{
if (GC.getCommandInfo(eCommand).getUpgradeImprovementRate(iJ) > 0)
{
ImprovementTypes eFromImprovement = (ImprovementTypes)iJ;
ImprovementTypes eToImprovement = NO_IMPROVEMENT;
for (iK = 0; iK < GC.getCommandInfo(eCommand).getUpgradeImprovementRate(iJ); ++iK)
{
eToImprovement = (ImprovementTypes)GC.getImprovementInfo(eFromImprovement).getImprovementUpgrade();
if (eToImprovement != NO_IMPROVEMENT)
{
eFromImprovement = eToImprovement;
}
else
{
break;
}
}
if (eToImprovement == NO_IMPROVEMENT)
{
eToImprovement = eFromImprovement;
}
if ((ImprovementTypes)iJ != eToImprovement)
{
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = pCity->getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getImprovementType() == (ImprovementTypes)iJ)
{
pLoopPlot->setImprovementType(eToImprovement);
}
}
}
}
}
}
}
for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
aiPotential2[iJ] = 0;
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = pCity->getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
aiPotential2[iJ] += pLoopPlot->calculateYield((YieldTypes)iJ);
}
}
aiPotential2[iJ] -= aiPotential1[iJ];
if (aiPotential2[iJ] != 0)
{
if (szTempBuffer.empty())
{
szTempBuffer.Format(L"+%d%c", aiPotential2[iJ], GC.getYieldInfo((YieldTypes)iJ).getChar());
szTempBuffer.append(szTempBuffer);
}
else
{
szTempBuffer.Format(L", +%d%c", aiPotential2[iJ], GC.getYieldInfo((YieldTypes)iJ).getChar());
szTempBuffer.append(szTempBuffer);
}
}
}
if (GET_PLAYER(getOwnerINLINE()).isHuman())
{
int iPopulationModifier = GC.getDefineINT("SOCIAL_REFORM_POPULATION_MODIFIER");
CvWString szBuffer;
szBuffer = gDLL->getText("TXT_KEY_MISC_ACTION_UPGRADE_IMPROVEMENTS", GC.getCommandInfo(eCommand).getDescription(), pCity->getNameKey(), szTempBuffer.GetCString(), iPopulationModifier);
AddMessage(getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_WELOVEKING", MESSAGE_TYPE_MINOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"));
}
pCity->changePopulation(GC.getDefineINT("SOCIAL_REFORM_POPULATION_MODIFIER"));
pCity->AI_setAssignWorkDirty(true);
pCity->setInfoDirty(true);
kill(true);
}
bool CvUnit::canUpgradeImprovements(const CvPlot* pPlot, CommandTypes eCommand) const
{
if (pPlot == NULL)
{
return false;
}
if (!m_pUnitInfo->isUpgradeImprovements())
{
return false;
}
CvCity* pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (pCity->getTeam() != getTeam())
{
return false;
}
CvPlot* pLoopPlot;
int iI, iJ, iK;
bool bContinue;
bool bValid = false;
if (GC.getCommandInfo(eCommand).isUpgradeImprovementRateArrayValid())
{
int numImprovementInfos = GC.getNumImprovementInfos();
for (iJ = 0; iJ < numImprovementInfos; ++iJ)
{
if (GC.getCommandInfo(eCommand).getUpgradeImprovementRate(iJ) > 0)
{
ImprovementTypes eFromImprovement = (ImprovementTypes)iJ;
bContinue = false;
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = pCity->getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getImprovementType() == eFromImprovement)
{
bContinue = true;
break;
}
}
}
if (bContinue)
{
ImprovementTypes eToImprovement = NO_IMPROVEMENT;
for (iK = 0; iK < GC.getCommandInfo(eCommand).getUpgradeImprovementRate(iJ); ++iK)
{
eToImprovement = (ImprovementTypes)GC.getImprovementInfo(eFromImprovement).getImprovementUpgrade();
if (eToImprovement != NO_IMPROVEMENT)
{
eFromImprovement = eToImprovement;
}
else
{
break;
}
}
if (eToImprovement == NO_IMPROVEMENT)
{
eToImprovement = eFromImprovement;
}
if ((ImprovementTypes)iJ != eToImprovement)
{
bValid = true;
}
}
if (bValid)
{
break;
}
}
}
}
if (!bValid)
{
return false;
}
return true;
}
/************************************************************************************************/
/* Great Diplomat MOD END */
/************************************************************************************************/
bool CvUnit::canAirBombAt(const CvPlot* pPlot, int iX, int iY) const
{
CvCity* pCity;
CvPlot* pTargetPlot;
if (!canAirBomb(pPlot))
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > airRange())
{
return false;
}
if (pTargetPlot->isOwned())
{
if (!potentialWarAction(pTargetPlot))
{
return false;
}
}
pCity = pTargetPlot->getPlotCity();
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - AB: Bombing START
if (pTargetPlot->getImprovementType() != NO_IMPROVEMENT)
{
if (GC.getImprovementInfo(pTargetPlot->getImprovementType()).isActsAsCity() && pCity == NULL)
{
if (GC.getUnitInfo(getUnitType()).getDCMAirBomb4())
{
int iCount = 0;
CvUnit* pUnit = NULL;
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
CvUnit* pLoopUnit;
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getDomainType() == DOMAIN_SEA && atWar(pLoopUnit->getTeam(), getTeam()))
{
iCount++;
}
}
if (iCount > 0)
{
return true;
}
}
}
}
else if (pCity != NULL)
{
if (GC.isDCM_AIR_BOMBING())
{
int iI, iLoop;
CvUnit* pLoopUnit;
for (iI = 0; iI < MAX_PLAYERS; ++iI)
{
if (atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), getTeam()))
{
for(pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->plot() == pTargetPlot)
{
if (pLoopUnit->getDomainType() == DOMAIN_SEA)
{
return true;
}
}
}
}
}
if (pCity->isBombardable(this))
{
return true;
}
return false;
} else {
if (!(pCity->isBombardable(this)))
{
return false;
}
}
// Dale - AB: Bombing END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
}
else
{
if (pTargetPlot->getImprovementType() == NO_IMPROVEMENT)
{
return false;
}
if (GC.getImprovementInfo(pTargetPlot->getImprovementType()).isPermanent())
{
return false;
}
if (GC.getImprovementInfo(pTargetPlot->getImprovementType()).getAirBombDefense() == -1)
{
return false;
}
}
return true;
}
bool CvUnit::airBomb(int iX, int iY)
{
CvCity* pCity;
CvPlot* pPlot;
CvWString szBuffer;
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - AB: Bombing START
bool bNoTarget = true;
int iMis0, iMis1, iMis2, iMis3, iMis4, iMis5;
iMis0 = iMis1 = iMis2 = iMis3 = iMis4 = iMis5 = 0;
int iI, iCount = 0;
CvUnit* pUnit = NULL;
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
// Dale - AB: Bombing END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
if (!canAirBombAt(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (!isEnemy(pPlot->getTeam()))
{
getGroup()->groupDeclareWar(pPlot, true);
}
if (!isEnemy(pPlot->getTeam()))
{
return false;
}
if (interceptTest(pPlot))
{
return true;
}
/************************************************************************************************/
/* DCM Start 05/31/10 Johnny Smith */
/* Afforess */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
pCity = pPlot->getPlotCity();
// RevolutionDCM start - AB Bombing<->BTS interaction bug fix
if (pPlot->getImprovementType() != NO_IMPROVEMENT)
{
if (!GC.isDCM_AIR_BOMBING())
{
// RevolutionDCM start - vanilla airbomb behaviour
if (GC.getGameINLINE().getSorenRandNum(airBombCurrRate(), "Air Bomb - Offense") >=
GC.getGameINLINE().getSorenRandNum(GC.getImprovementInfo(pPlot->getImprovementType()).getAirBombDefense(), "Air Bomb - Defense"))
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_DESTROYED_IMP", getNameKey(), GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGE", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
if (pPlot->isOwned())
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_IMP_WAS_DESTROYED", GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide(), getNameKey(), getVisualCivAdjective(pPlot->getTeam()));
AddDLLMessage(pPlot->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
pPlot->setImprovementType((ImprovementTypes)(GC.getImprovementInfo(pPlot->getImprovementType()).getImprovementPillage()));
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_FAIL_DESTROY_IMP", getNameKey(), GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BOMB_FAILS", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
// RevolutionDCM end - vanilla airbomb behaviour
} else
// RevolutionDCM end - AB Bombing<->BTS interaction bug fix
{
// Dale - AB: AI Bombing START
if (GC.getImprovementInfo(pPlot->getImprovementType()).isActsAsCity() && pCity == NULL)
{
if (GC.getUnitInfo(getUnitType()).getDCMAirBomb4())
{
iMis4 = 10;
iCount = 0;
pUnit = NULL;
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getDomainType() == DOMAIN_SEA)
{
iCount++;
}
}
if (iCount > 0)
{
airBomb4(iX, iY);
}
}
}
// Dale - AB: AI Bombing END
}
} else
{
// RevolutionDCM start - AB Bombing<->BTS interaction bug fix
if (pCity != NULL)
{
if (!GC.isDCM_AIR_BOMBING())
{
// RevolutionDCM start - vanilla airbomb behaviour
pCity->changeDefenseModifier(-airBombCurrRate());
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_DEFENSES_REDUCED_TO", pCity->getNameKey(), pCity->getDefenseModifier(false), getNameKey());
AddDLLMessage(pCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_DEFENSES_REDUCED_TO", getNameKey(), pCity->getNameKey(), pCity->getDefenseModifier(false));
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE());
// RevolutionDCM end - vanilla airbomb behaviour
} else
{
// Dale - AB: AI Bombing START
if (GC.getUnitInfo(getUnitType()).getDCMAirBomb1())
{
iMis1 = 10;
iCount = 0;
for (int iDX = -2; iDX <= 2; iDX++)
{
for (int iDY = 2; iDY <= 2; iDY++)
{
pLoopPlot = plotXY(plot()->getX_INLINE(), plot()->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
pUnit = NULL;
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getOwnerINLINE() == getOwnerINLINE())
{
iCount++;
}
}
}
}
}
iMis1 *= (iCount * 2);
}
if (GC.getUnitInfo(getUnitType()).getDCMAirBomb2())
{
iMis2 = 10;
iCount = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getDCMAirbombMission() == 2)
{
if (pCity->getNumRealBuilding((BuildingTypes)iI))
{
iCount++;
}
}
}
iMis2 *= iCount;
}
if (GC.getUnitInfo(getUnitType()).getDCMAirBomb3())
{
iMis3 = 10;
iCount = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getDCMAirbombMission() == 3)
{
if (pCity->getNumRealBuilding((BuildingTypes)iI))
{
iCount++;
}
}
}
iMis3 *= (iCount * 2);
}
if (GC.getUnitInfo(getUnitType()).getDCMAirBomb4())
{
iMis4 = 10;
iCount = 0;
pUnit = NULL;
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getDomainType() == DOMAIN_SEA)
{
iCount++;
}
}
iMis4 *= (iCount * 4);
}
if (GC.getUnitInfo(getUnitType()).getDCMAirBomb5())
{
iMis5 = 10;
iMis5 *= GC.getGame().getSorenRandNum(20, "Strat Bombing");
}
int temp = iMis1;
iMis0 = 1;
if (iMis2 > temp)
{
temp = iMis2;
iMis0 = 2;
}
if (iMis3 > temp)
{
temp = iMis3;
iMis0 = 3;
}
if (iMis4 > temp)
{
temp = iMis4;
iMis0 = 4;
}
if (iMis5 > temp)
{
temp = iMis5;
iMis0 = 5;
}
switch(iMis0)
{
case 1:
if (airBomb1(iX, iY))
{
bNoTarget = false;
}
break;
case 2:
if (airBomb2(iX, iY))
{
bNoTarget = false;
}
break;
case 3:
if (airBomb3(iX, iY))
{
bNoTarget = false;
}
break;
case 4:
if (airBomb4(iX, iY))
{
bNoTarget = false;
}
break;
case 5:
if (airBomb5(iX, iY))
{
bNoTarget = false;
}
break;
}
if(bNoTarget)
{
if(pCity->getPopulation() > 1)
{
if(GC.getGameINLINE().getSorenRandNum(5, "Airbomb population") < 2)
{
pCity->changePopulation(-1);
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB_POP");
AddDLLMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB_POP");
AddDLLMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
}
}
// Dale - AB: AI Bombing END
}
// RevolutionDCM end - AB Bombing<->BTS interaction bug fix
}
}
/************************************************************************************************/
/* Afforess Start 09/13/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP))
{
setExperience100(getExperience100() + getRandomMinExperienceTimes100(), -1);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
setReconPlot(pPlot);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
if (pPlot->isActiveVisible(false))
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRBOMB);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo((MissionTypes)MISSION_AIRBOMB).getTime() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
if (isSuicide())
{
kill(true);
}
return true;
}
CvCity* CvUnit::bombardTarget(const CvPlot* pPlot) const
{
int iBestValue = MAX_INT;
CvCity* pBestCity = NULL;
for (int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
CvPlot* pLoopPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
CvCity* pLoopCity = pLoopPlot->getPlotCity();
if (pLoopCity != NULL)
{
if (pLoopCity->isBombardable(this))
{
int iValue = pLoopCity->getDefenseDamage();
// always prefer cities we are at war with
if (isEnemy(pLoopCity->getTeam(), pPlot))
{
iValue *= 128;
}
if (iValue < iBestValue)
{
iBestValue = iValue;
pBestCity = pLoopCity;
}
}
}
}
}
return pBestCity;
}
bool CvUnit::canBombard(const CvPlot* pPlot, bool bIgnoreHasAttacked) const
{
if (bombardRate() <= 0)
{
return false;
}
if (!bIgnoreHasAttacked && isMadeAttack())
{
return false;
}
if (isCargo())
{
return false;
}
if (bombardTarget(pPlot) == NULL)
{
return false;
}
return true;
}
bool CvUnit::bombard()
{
CvPlot* pPlot = plot();
if (!canBombard(pPlot))
{
return false;
}
CvCity* pBombardCity = bombardTarget(pPlot);
FAssertMsg(pBombardCity != NULL, "BombardCity is not assigned a valid value");
// Dale - RB: Bug Fix (RevolutionDCM - just checks for a null value)
if (pBombardCity != NULL)
{
CvPlot* pTargetPlot = pBombardCity->plot();
if (!isEnemy(pTargetPlot->getTeam()))
{
getGroup()->groupDeclareWar(pTargetPlot, true);
}
if (!isEnemy(pTargetPlot->getTeam()))
{
return false;
}
int iBombardModifier = 0;
if (!ignoreBuildingDefense())
{
iBombardModifier -= pBombardCity->getBuildingBombardDefense();
}
pBombardCity->changeDefenseModifier(-(bombardRate() * std::max(0, 100 + iBombardModifier)) / 100);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_DEFENSES_IN_CITY_REDUCED_TO", pBombardCity->getNameKey(), pBombardCity->getDefenseModifier(false), GET_PLAYER(getOwnerINLINE()).getNameKey());
AddDLLMessage(pBombardCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pBombardCity->getX_INLINE(), pBombardCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_REDUCE_CITY_DEFENSES", getNameKey(), pBombardCity->getNameKey(), pBombardCity->getDefenseModifier(false));
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pBombardCity->getX_INLINE(), pBombardCity->getY_INLINE());
}
/************************************************************************************************/
/* Afforess Start 07/22/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP))
{
setExperience100(getExperience100() + getRandomMinExperienceTimes100(), -1);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pPlot->isActiveVisible(false))
{
CvUnit *pDefender = pBombardCity->plot()->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
if ( pDefender != NULL && !pDefender->isUsingDummyEntities() )
{
// Bombard entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_BOMBARD).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_BOMBARD);
kDefiniton.setPlot(pBombardCity->plot());
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
addMission(&kDefiniton);
}
}
}
return true;
}
bool CvUnit::canPillage(const CvPlot* pPlot) const
{
if (!(m_pUnitInfo->isPillage()))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 06/01/10 */
/* */
/* */
/************************************************************************************************/
if (pPlot == NULL)
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_NO_FRIENDLY_PILLAGING))
{
if (pPlot->getTeam() == getTeam())
{
return false;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/23/10 Mongoose & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// From Mongoose SDK
if (isCargo())
{
return false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
if (pPlot->isCity())
{
return false;
}
if (pPlot->getImprovementType() == NO_IMPROVEMENT)
{
if (!(pPlot->isRoute()))
{
return false;
}
}
else
{
if (GC.getImprovementInfo(pPlot->getImprovementType()).isPermanent())
{
return false;
}
}
if (pPlot->isOwned())
{
if (!potentialWarAction(pPlot))
{
if ((pPlot->getImprovementType() == NO_IMPROVEMENT) || (pPlot->getOwnerINLINE() != getOwnerINLINE()))
{
return false;
}
}
}
if (!(pPlot->isValidDomainForAction(*this)))
{
return false;
}
return true;
}
bool CvUnit::pillage()
{
CvWString szBuffer;
int iPillageGold;
long lPillageGold;
ImprovementTypes eTempImprovement = NO_IMPROVEMENT;
RouteTypes eTempRoute = NO_ROUTE;
CvPlot* pPlot = plot();
if (!canPillage(pPlot))
{
return false;
}
if (pPlot->isOwned())
{
// we should not be calling this without declaring war first, so do not declare war here
if (!isEnemy(pPlot->getTeam(), pPlot))
{
if ((pPlot->getImprovementType() == NO_IMPROVEMENT) || (pPlot->getOwnerINLINE() != getOwnerINLINE()))
{
return false;
}
}
}
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 07/07/09 */
/* */
/* */
/************************************************************************************************/
/*
if (getDomainType() == DOMAIN_LAND && pPlot->isCanUseRouteLandUnits() && pPlot->isWater())
{
return false;
}
if (getDomainType() == DOMAIN_SEA && pPlot->isCanUseRouteSeaUnits() && !pPlot->isWater())
{
return false;
}
*/
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
if (pPlot->isWater())
{
// UncutDragon
/* original code
CvUnit* pInterceptor = bestSeaPillageInterceptor(this, GC.getDefineINT("COMBAT_DIE_SIDES") / 2);
*/ // modified
CvUnit* pInterceptor = bestSeaPillageInterceptor(this, GC.getCOMBAT_DIE_SIDES() / 2);
// /UncutDragon
if (NULL != pInterceptor)
{
setMadeAttack(false);
int iWithdrawal = withdrawalProbability();
changeExtraWithdrawal(-iWithdrawal); // no withdrawal since we are really the defender
attack(pInterceptor->plot(), false);
changeExtraWithdrawal(iWithdrawal);
return false;
}
}
if (pPlot->getImprovementType() != NO_IMPROVEMENT)
{
eTempImprovement = pPlot->getImprovementType();
if (pPlot->getTeam() != getTeam())
{
PYTHON_ACCESS_LOCK_SCOPE
// Use python to determine pillage amounts...
lPillageGold = 0;
CyPlot* pyPlot = new CyPlot(pPlot);
CyUnit* pyUnit = new CyUnit(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyPlot)); // pass in plot class
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyUnit)); // pass in unit class
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "doPillageGold", argsList.makeFunctionArgs(),&lPillageGold);
delete pyPlot; // python fxn must not hold on to this pointer
delete pyUnit; // python fxn must not hold on to this pointer
iPillageGold = (int)lPillageGold;
if (iPillageGold > 0)
{
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
float fInfluenceRatio = 0.0f;
if (GC.isIDW_ENABLED() && GC.isIDW_PILLAGE_INFLUENCE_ENABLED())
{
if (atWar(pPlot->getTeam(), getTeam()))
{
fInfluenceRatio = doPillageInfluence();
}
}
// ------ END InfluenceDrivenWar -------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
GET_PLAYER(getOwnerINLINE()).changeGold(iPillageGold);
szBuffer = gDLL->getText("TXT_KEY_MISC_PLUNDERED_GOLD_FROM_IMP", iPillageGold, GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
if (fInfluenceRatio > 0.0f)
{
CvWString szInfluence;
szInfluence.Format(L" %s: +%.1f%%", gDLL->getText("TXT_KEY_TILE_INFLUENCE").GetCString(), fInfluenceRatio);
szBuffer += szInfluence;
}
// ------ END InfluenceDrivenWar -------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
if (pPlot->isOwned())
{
szBuffer = gDLL->getText("TXT_KEY_MISC_IMP_DESTROYED", GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide(), getNameKey(), getVisualCivAdjective(pPlot->getTeam()));
AddDLLMessage(pPlot->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
if (fInfluenceRatio > 0.0f)
{
CvWString szInfluence;
szInfluence.Format(L" %s: -%.1f%%", gDLL->getText("TXT_KEY_TILE_INFLUENCE").GetCString(), fInfluenceRatio);
szBuffer += szInfluence;
}
// ------ END InfluenceDrivenWar -------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
// A pillage implies a source of danger even if we can't see it
GET_PLAYER(pPlot->getOwnerINLINE()).addPlotDangerSource(pPlot, 100);
}
}
}
pPlot->setImprovementType((ImprovementTypes)(GC.getImprovementInfo(pPlot->getImprovementType()).getImprovementPillage()));
}
/*****************************************************************************************************/
/** TheLadiesOgre; 01.10.2009; TLOTags **/
/*****************************************************************************************************/
else if (pPlot->isRoute())
{
eTempRoute = pPlot->getRouteType();
pPlot->setRouteType(NO_ROUTE, true); // XXX downgrade rail???
/************************************************************************************************/
/* Afforess Start 09/13/10 */
/* */
/* Alert Player of Pillaged Routes */
/************************************************************************************************/
if (pPlot->isOwned())
{
// A pillage implies a source of danger even if we can't see it
GET_PLAYER(pPlot->getOwnerINLINE()).addPlotDangerSource(pPlot, 100);
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_IMP_DESTROYED", GC.getRouteInfo(eTempRoute).getTextKeyWide(), getNameKey(), getVisualCivAdjective(pPlot->getTeam()));
AddDLLMessage(pPlot->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
changeMoves(GC.getMOVE_DENOMINATOR());
/************************************************************************************************/
/* Afforess Start 09/13/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP))
{
setExperience100(getExperience100() + getRandomMinExperienceTimes100(), -1);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pPlot->isActiveVisible(false))
{
// Pillage entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_PILLAGE).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_PILLAGE);
kDefiniton.setPlot(pPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, NULL);
addMission(&kDefiniton);
}
if (eTempImprovement != NO_IMPROVEMENT || eTempRoute != NO_ROUTE)
{
CvEventReporter::getInstance().unitPillage(this, eTempImprovement, eTempRoute, getOwnerINLINE());
}
return true;
}
bool CvUnit::canPlunder(const CvPlot* pPlot, bool bTestVisible) const
{
if (getDomainType() != DOMAIN_SEA)
{
return false;
}
if (!(m_pUnitInfo->isPillage()))
{
return false;
}
if (!pPlot->isWater())
{
return false;
}
if (pPlot->isFreshWater())
{
return false;
}
if (!pPlot->isValidDomainForAction(*this))
{
return false;
}
if (!bTestVisible)
{
if (pPlot->getTeam() == getTeam())
{
return false;
}
}
return true;
}
bool CvUnit::plunder()
{
CvPlot* pPlot = plot();
if (!canPlunder(pPlot))
{
return false;
}
setBlockading(true);
finishMoves();
return true;
}
void CvUnit::updatePlunder(int iChange, bool bUpdatePlotGroups)
{
PROFILE_FUNC();
int iBlockadeRange = GC.getDefineINT("SHIP_BLOCKADE_RANGE");
bool bOldTradeNet;
bool bChanged = false;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 06/01/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
//gDLL->getFAStarIFace()->ForceReset(&GC.getStepFinder());
bool bValid = false;
if (bUpdatePlotGroups)
{
CvPlot::setDeferredPlotGroupRecalculationMode(true);
}
for (int i = -iBlockadeRange; i <= iBlockadeRange; ++i)
{
for (int j = -iBlockadeRange; j <= iBlockadeRange; ++j)
{
CvPlot* pLoopPlot = ::plotXY(getX_INLINE(), getY_INLINE(), i, j);
if (NULL != pLoopPlot && pLoopPlot->isWater() && pLoopPlot->area() == area())
{
int iPathDist = GC.getMapINLINE().calculatePathDistance(plot(),pLoopPlot);
// BBAI NOTES: There are rare issues where the path finder will return incorrect results
// for unknown reasons. Seems to find a suboptimal path sometimes in partially repeatable
// circumstances. The fix below is a hack to address the permanent one or two tile blockades which
// would appear randomly, it should cause extra blockade clearing only very rarely.
/*
if( iPathDist > iBlockadeRange )
{
// No blockading on other side of an isthmus
continue;
}
*/
if( (iPathDist >= 0) && (iPathDist <= iBlockadeRange + 2) )
{
for (int iTeam = 0; iTeam < MAX_TEAMS; ++iTeam)
{
if (isEnemy((TeamTypes)iTeam))
{
bValid = (iPathDist <= iBlockadeRange);
if( !bValid && (iChange == -1 && pLoopPlot->getBlockadedCount((TeamTypes)iTeam) > 0) )
{
bValid = true;
}
if( bValid )
{
bOldTradeNet = pLoopPlot->isTradeNetwork((TeamTypes)iTeam);
pLoopPlot->changeBlockadedCount((TeamTypes)iTeam, iChange);
if (bOldTradeNet != pLoopPlot->isTradeNetwork((TeamTypes)iTeam))
{
bChanged = true;
if (bUpdatePlotGroups)
{
pLoopPlot->updatePlotGroup();
}
}
}
}
}
}
}
}
}
if (bChanged)
{
gDLL->getInterfaceIFace()->setDirty(BlockadedPlots_DIRTY_BIT, true);
if (bUpdatePlotGroups)
{
CvPlot::setDeferredPlotGroupRecalculationMode(false);
//GC.getGameINLINE().updatePlotGroups();
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
int CvUnit::sabotageCost(const CvPlot* pPlot) const
{
return GC.getDefineINT("BASE_SPY_SABOTAGE_COST");
}
// XXX compare with destroy prob...
int CvUnit::sabotageProb(const CvPlot* pPlot, ProbabilityTypes eProbStyle) const
{
CvPlot* pLoopPlot;
int iDefenseCount;
int iCounterSpyCount;
int iProb;
int iI;
iProb = 0; // XXX
if (pPlot->isOwned())
{
iDefenseCount = pPlot->plotCount(PUF_canDefend, -1, -1, NO_PLAYER, pPlot->getTeam());
iCounterSpyCount = pPlot->plotCount(PUF_isCounterSpy, -1, -1, NO_PLAYER, pPlot->getTeam());
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
iCounterSpyCount += pLoopPlot->plotCount(PUF_isCounterSpy, -1, -1, NO_PLAYER, pPlot->getTeam());
}
}
}
else
{
iDefenseCount = 0;
iCounterSpyCount = 0;
}
if (eProbStyle == PROBABILITY_HIGH)
{
iCounterSpyCount = 0;
}
iProb += (40 / (iDefenseCount + 1)); // XXX
if (eProbStyle != PROBABILITY_LOW)
{
iProb += (50 / (iCounterSpyCount + 1)); // XXX
}
return iProb;
}
bool CvUnit::canSabotage(const CvPlot* pPlot, bool bTestVisible) const
{
if (!(m_pUnitInfo->isSabotage()))
{
return false;
}
if (pPlot->getTeam() == getTeam())
{
return false;
}
if (pPlot->isCity())
{
return false;
}
if (pPlot->getImprovementType() == NO_IMPROVEMENT)
{
return false;
}
if (!bTestVisible)
{
if (GET_PLAYER(getOwnerINLINE()).getGold() < sabotageCost(pPlot))
{
return false;
}
}
return true;
}
bool CvUnit::sabotage()
{
CvCity* pNearestCity;
CvPlot* pPlot;
CvWString szBuffer;
bool bCaught;
if (!canSabotage(plot()))
{
return false;
}
pPlot = plot();
bCaught = (GC.getGameINLINE().getSorenRandNum(100, "Spy: Sabotage") > sabotageProb(pPlot));
GET_PLAYER(getOwnerINLINE()).changeGold(-(sabotageCost(pPlot)));
if (!bCaught)
{
pPlot->setImprovementType((ImprovementTypes)(GC.getImprovementInfo(pPlot->getImprovementType()).getImprovementPillage()));
finishMoves();
pNearestCity = GC.getMapINLINE().findCity(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pPlot->getOwnerINLINE(), NO_TEAM, false);
if (pNearestCity != NULL)
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_SPY_SABOTAGED", getNameKey(), pNearestCity->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_SABOTAGE", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
if (pPlot->isOwned())
{
szBuffer = gDLL->getText("TXT_KEY_MISC_SABOTAGE_NEAR", pNearestCity->getNameKey());
AddDLLMessage(pPlot->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_SABOTAGE", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
}
if (pPlot->isActiveVisible(false))
{
NotifyEntity(MISSION_SABOTAGE);
}
}
else
{
if (pPlot->isOwned())
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_SPY_CAUGHT_AND_KILLED", GET_PLAYER(getOwnerINLINE()).getCivilizationAdjective(), getNameKey());
AddDLLMessage(pPlot->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSE", MESSAGE_TYPE_INFO);
}
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_SPY_CAUGHT", getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSED", MESSAGE_TYPE_INFO);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_SURRENDER);
}
if (pPlot->isOwned())
{
if (!isEnemy(pPlot->getTeam(), pPlot))
{
GET_PLAYER(pPlot->getOwnerINLINE()).AI_changeMemoryCount(getOwnerINLINE(), MEMORY_SPY_CAUGHT, 1);
}
}
kill(true, pPlot->getOwnerINLINE(), true);
}
return true;
}
int CvUnit::destroyCost(const CvPlot* pPlot) const
{
CvCity* pCity;
bool bLimited;
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return 0;
}
bLimited = false;
if (pCity->isProductionUnit())
{
bLimited = isLimitedUnitClass((UnitClassTypes)(GC.getUnitInfo(pCity->getProductionUnit()).getUnitClassType()));
}
else if (pCity->isProductionBuilding())
{
bLimited = isLimitedWonderClass((BuildingClassTypes)(GC.getBuildingInfo(pCity->getProductionBuilding()).getBuildingClassType()));
}
else if (pCity->isProductionProject())
{
bLimited = isLimitedProject(pCity->getProductionProject());
}
return (GC.getDefineINT("BASE_SPY_DESTROY_COST") + (pCity->getProduction() * ((bLimited) ? GC.getDefineINT("SPY_DESTROY_COST_MULTIPLIER_LIMITED") : GC.getDefineINT("SPY_DESTROY_COST_MULTIPLIER"))));
}
int CvUnit::destroyProb(const CvPlot* pPlot, ProbabilityTypes eProbStyle) const
{
CvCity* pCity;
CvPlot* pLoopPlot;
int iDefenseCount;
int iCounterSpyCount;
int iProb;
int iI;
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return 0;
}
iProb = 0; // XXX
iDefenseCount = pPlot->plotCount(PUF_canDefend, -1, -1, NO_PLAYER, pPlot->getTeam());
iCounterSpyCount = pPlot->plotCount(PUF_isCounterSpy, -1, -1, NO_PLAYER, pPlot->getTeam());
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
iCounterSpyCount += pLoopPlot->plotCount(PUF_isCounterSpy, -1, -1, NO_PLAYER, pPlot->getTeam());
}
}
if (eProbStyle == PROBABILITY_HIGH)
{
iCounterSpyCount = 0;
}
iProb += (25 / (iDefenseCount + 1)); // XXX
if (eProbStyle != PROBABILITY_LOW)
{
iProb += (50 / (iCounterSpyCount + 1)); // XXX
}
iProb += std::min(25, pCity->getProductionTurnsLeft()); // XXX
return iProb;
}
bool CvUnit::canDestroy(const CvPlot* pPlot, bool bTestVisible) const
{
CvCity* pCity;
if (!(m_pUnitInfo->isDestroy()))
{
return false;
}
if (pPlot->getTeam() == getTeam())
{
return false;
}
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (pCity->getProduction() == 0)
{
return false;
}
if (!bTestVisible)
{
if (GET_PLAYER(getOwnerINLINE()).getGold() < destroyCost(pPlot))
{
return false;
}
}
return true;
}
bool CvUnit::destroy()
{
CvCity* pCity;
CvWString szBuffer;
bool bCaught;
if (!canDestroy(plot()))
{
return false;
}
bCaught = (GC.getGameINLINE().getSorenRandNum(100, "Spy: Destroy") > destroyProb(plot()));
pCity = plot()->getPlotCity();
FAssertMsg(pCity != NULL, "City is not assigned a valid value");
GET_PLAYER(getOwnerINLINE()).changeGold(-(destroyCost(plot())));
if (!bCaught)
{
pCity->setProduction(pCity->getProduction() / 2);
finishMoves();
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_SPY_DESTROYED_PRODUCTION", getNameKey(), pCity->getProductionNameKey(), pCity->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_DESTROY", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_CITY_PRODUCTION_DESTROYED", pCity->getProductionNameKey(), pCity->getNameKey());
AddDLLMessage(pCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_DESTROY", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_DESTROY);
}
}
else
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_SPY_CAUGHT_AND_KILLED", GET_PLAYER(getOwnerINLINE()).getCivilizationAdjective(), getNameKey());
AddDLLMessage(pCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSE", MESSAGE_TYPE_INFO);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_SPY_CAUGHT", getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSED", MESSAGE_TYPE_INFO);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_SURRENDER);
}
if (!isEnemy(pCity->getTeam()))
{
GET_PLAYER(pCity->getOwnerINLINE()).AI_changeMemoryCount(getOwnerINLINE(), MEMORY_SPY_CAUGHT, 1);
}
kill(true, pCity->getOwnerINLINE(), true);
}
return true;
}
int CvUnit::stealPlansCost(const CvPlot* pPlot) const
{
CvCity* pCity;
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return 0;
}
return (GC.getDefineINT("BASE_SPY_STEAL_PLANS_COST") + ((GET_TEAM(pCity->getTeam()).getTotalLand() + GET_TEAM(pCity->getTeam()).getTotalPopulation()) * GC.getDefineINT("SPY_STEAL_PLANS_COST_MULTIPLIER")));
}
// XXX compare with destroy prob...
int CvUnit::stealPlansProb(const CvPlot* pPlot, ProbabilityTypes eProbStyle) const
{
CvCity* pCity;
CvPlot* pLoopPlot;
int iDefenseCount;
int iCounterSpyCount;
int iProb;
int iI;
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return 0;
}
iProb = ((pCity->isGovernmentCenter()) ? 20 : 0); // XXX
iDefenseCount = pPlot->plotCount(PUF_canDefend, -1, -1, NO_PLAYER, pPlot->getTeam());
iCounterSpyCount = pPlot->plotCount(PUF_isCounterSpy, -1, -1, NO_PLAYER, pPlot->getTeam());
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
iCounterSpyCount += pLoopPlot->plotCount(PUF_isCounterSpy, -1, -1, NO_PLAYER, pPlot->getTeam());
}
}
if (eProbStyle == PROBABILITY_HIGH)
{
iCounterSpyCount = 0;
}
iProb += (20 / (iDefenseCount + 1)); // XXX
if (eProbStyle != PROBABILITY_LOW)
{
iProb += (50 / (iCounterSpyCount + 1)); // XXX
}
return iProb;
}
bool CvUnit::canStealPlans(const CvPlot* pPlot, bool bTestVisible) const
{
CvCity* pCity;
if (!(m_pUnitInfo->isStealPlans()))
{
return false;
}
if (pPlot->getTeam() == getTeam())
{
return false;
}
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (!bTestVisible)
{
if (GET_PLAYER(getOwnerINLINE()).getGold() < stealPlansCost(pPlot))
{
return false;
}
}
return true;
}
bool CvUnit::stealPlans()
{
CvCity* pCity;
CvWString szBuffer;
bool bCaught;
if (!canStealPlans(plot()))
{
return false;
}
bCaught = (GC.getGameINLINE().getSorenRandNum(100, "Spy: Steal Plans") > stealPlansProb(plot()));
pCity = plot()->getPlotCity();
FAssertMsg(pCity != NULL, "City is not assigned a valid value");
GET_PLAYER(getOwnerINLINE()).changeGold(-(stealPlansCost(plot())));
if (!bCaught)
{
GET_TEAM(getTeam()).changeStolenVisibilityTimer(pCity->getTeam(), 2);
finishMoves();
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_SPY_STOLE_PLANS", getNameKey(), pCity->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_STEALPLANS", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_PLANS_STOLEN", pCity->getNameKey());
AddDLLMessage(pCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_STEALPLANS", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_STEAL_PLANS);
}
}
else
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_SPY_CAUGHT_AND_KILLED", GET_PLAYER(getOwnerINLINE()).getCivilizationAdjective(), getNameKey());
AddDLLMessage(pCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSE", MESSAGE_TYPE_INFO);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_SPY_CAUGHT", getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSED", MESSAGE_TYPE_INFO);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_SURRENDER);
}
if (!isEnemy(pCity->getTeam()))
{
GET_PLAYER(pCity->getOwnerINLINE()).AI_changeMemoryCount(getOwnerINLINE(), MEMORY_SPY_CAUGHT, 1);
}
kill(true, pCity->getOwnerINLINE(), true);
}
return true;
}
bool CvUnit::canFound(const CvPlot* pPlot, bool bTestVisible) const
{
if (!isFound())
{
return false;
}
if (!(GET_PLAYER(getOwnerINLINE()).canFound(pPlot->getX_INLINE(), pPlot->getY_INLINE(), bTestVisible)))
{
return false;
}
return true;
}
bool CvUnit::found()
{
if (!canFound(plot()))
{
return false;
}
if (GC.getGameINLINE().getActivePlayer() == getOwnerINLINE())
{
GC.getCurrentViewport()->bringIntoView(getX_INLINE(), getY_INLINE());
//gDLL->getInterfaceIFace()->lookAt(plot()->getPoint(), CAMERALOOKAT_NORMAL);
}
GET_PLAYER(getOwnerINLINE()).found(getX_INLINE(), getY_INLINE());
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_FOUND);
}
// For the AI we need to run the turn for the new city to get production set
if ( !GET_PLAYER(getOwnerINLINE()).isHuman() )
{
CvCity* pCity = plot()->getPlotCity();
FAssert(pCity!=NULL);
if (pCity != NULL)//TB Debug note: unknown as to why, after the found statement above, this does not work in some cases to find the new city.
{
CvPipeline* pPipeline = GET_PLAYER(getOwnerINLINE()).createCityTurnPipeline();
pPipeline->Begin();
pPipeline->EnqueueWorkItem(new CvCityTurnPipelineWorkItem(pCity));
pPipeline->End();
delete pPipeline;
}
}
kill(true, NO_PLAYER, true);
return true;
}
bool CvUnit::canSpread(const CvPlot* pPlot, ReligionTypes eReligion, bool bTestVisible) const
{
PROFILE_FUNC();
CvCity* pCity;
/************************************************************************************************/
/* UNOFFICIAL_PATCH 08/19/09 jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
/* orginal bts code
if (GC.getUSE_USE_CANNOT_SPREAD_RELIGION_CALLBACK())
{
CyArgsList argsList;
argsList.add(getOwnerINLINE());
argsList.add(getID());
argsList.add((int) eReligion);
argsList.add(pPlot->getX());
argsList.add(pPlot->getY());
long lResult=0;
PYTHON_CALL_FUNCTION(__FUNCTION__, PYGameModule, "cannotSpreadReligion", argsList.makeFunctionArgs(), &lResult);
if (lResult > 0)
{
return false;
}
}
*/
// UP efficiency: Moved below faster calls
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
if (eReligion == NO_RELIGION)
{
return false;
}
if (m_pUnitInfo->getReligionSpreads(eReligion) <= 0)
{
return false;
}
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (pCity->isHasReligion(eReligion))
{
return false;
}
if (!canEnterArea(pPlot->getTeam(), pPlot->area()))
{
return false;
}
if (!bTestVisible)
{
if (pCity->getTeam() != getTeam())
{
if (GET_PLAYER(pCity->getOwnerINLINE()).isNoNonStateReligionSpread())
{
if (eReligion != GET_PLAYER(pCity->getOwnerINLINE()).getStateReligion())
{
return false;
}
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 08/19/09 jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
if (GC.getUSE_USE_CANNOT_SPREAD_RELIGION_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyArgsList argsList;
argsList.add(getOwnerINLINE());
argsList.add(getID());
argsList.add((int) eReligion);
argsList.add(pPlot->getX());
argsList.add(pPlot->getY());
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "cannotSpreadReligion", argsList.makeFunctionArgs(), &lResult);
if (lResult > 0)
{
return false;
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
//TB Prophet Mod start
if (AI_getUnitAIType() != UNITAI_MISSIONARY)
{
return false;
}
return true;
}
bool CvUnit::spread(ReligionTypes eReligion)
{
CvCity* pCity;
CvWString szBuffer;
int iSpreadProb;
if (!canSpread(plot(), eReligion))
{
return false;
}
pCity = plot()->getPlotCity();
if (pCity != NULL)
{
iSpreadProb = m_pUnitInfo->getReligionSpreads(eReligion);
if (pCity->getTeam() != getTeam())
{
iSpreadProb /= 2;
}
bool bSuccess;
iSpreadProb += (((GC.getNumReligionInfos() - pCity->getReligionCount()) * (100 - iSpreadProb)) / GC.getNumReligionInfos());
if (GC.getGameINLINE().getSorenRandNum(100, "Unit Spread Religion") < iSpreadProb)
{
//TB Prophet Mod start
#ifdef C2C_BUILD
//FfH: Modified by Kael 10/04/2008
if (GC.getGameINLINE().isReligionFounded(eReligion))
{
pCity->setHasReligion(eReligion, true, true, false);
}
else
{
pCity->setHasReligion(eReligion, true, true, false);
GC.getGameINLINE().setHolyCity(eReligion, pCity, true);
GC.getGameINLINE().setReligionSlotTaken(eReligion, true);
}
//FfH: End Modify
#else
//TB Prophet Mod end
pCity->setHasReligion(eReligion, true, true, false);
#endif
bSuccess = true;
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_RELIGION_FAILED_TO_SPREAD", getNameKey(), GC.getReligionInfo(eReligion).getChar(), pCity->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NOSPREAD", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE());
bSuccess = false;
}
// Python Event
CvEventReporter::getInstance().unitSpreadReligionAttempt(this, eReligion, bSuccess);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_SPREAD);
}
kill(true, NO_PLAYER, true);
return true;
}
bool CvUnit::canSpreadCorporation(const CvPlot* pPlot, CorporationTypes eCorporation, bool bTestVisible) const
{
if (NO_CORPORATION == eCorporation)
{
return false;
}
if (!GET_PLAYER(getOwnerINLINE()).isActiveCorporation(eCorporation))
{
return false;
}
if (m_pUnitInfo->getCorporationSpreads(eCorporation) <= 0)
{
return false;
}
CvCity* pCity = pPlot->getPlotCity();
if (NULL == pCity)
{
return false;
}
if (pCity->isHasCorporation(eCorporation))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 01/17/10 */
/* */
/* Blocks obsolete Corps from spreading */
/************************************************************************************************/
if (GC.getCorporationInfo(eCorporation).getObsoleteTech() != NO_TECH)
{
if (GET_TEAM(GET_PLAYER(pCity->getOwnerINLINE()).getTeam()).isHasTech((TechTypes)GC.getCorporationInfo(eCorporation).getObsoleteTech()))
{
return false;
}
}
if (GC.getGame().isOption(GAMEOPTION_REALISTIC_CORPORATIONS))
{
if (!GC.getGame().isModderGameOption(MODDERGAMEOPTION_NO_AUTO_CORPORATION_FOUNDING))
{
return false;
}
}
if (!GC.getGameINLINE().canEverSpread(eCorporation))
{
return false;
}
if (!bTestVisible)
{
for (int iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
if (GC.getCorporationInfo(eCorporation).getPrereqBuildingClass(iI) > 0)
{
if (GET_PLAYER(pCity->getOwnerINLINE()).getBuildingClassCount((BuildingClassTypes)iI) < GC.getCorporationInfo(eCorporation).getPrereqBuildingClass(iI))
{
return false;
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!canEnterArea(pPlot->getTeam(), pPlot->area()))
{
return false;
}
if (!bTestVisible)
{
if (!GET_PLAYER(pCity->getOwnerINLINE()).isActiveCorporation(eCorporation))
{
return false;
}
for (int iCorporation = 0; iCorporation < GC.getNumCorporationInfos(); ++iCorporation)
{
if (pCity->isHeadquarters((CorporationTypes)iCorporation))
{
if (GC.getGameINLINE().isCompetingCorporation((CorporationTypes)iCorporation, eCorporation))
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess Start 02/17/10 */
/* */
/* Some corporations don't require any resources... */
/************************************************************************************************/
bool bValid = false;
bool bRequiresBonus = false;
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(i);
if (NO_BONUS != eBonus)
{
bRequiresBonus = true;
if (pCity->hasBonus(eBonus))
{
bValid = true;
break;
}
}
}
if (!bValid && bRequiresBonus)
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (GET_PLAYER(getOwnerINLINE()).getGold() < spreadCorporationCost(eCorporation, pCity))
{
return false;
}
}
return true;
}
int CvUnit::spreadCorporationCost(CorporationTypes eCorporation, CvCity* pCity) const
{
int iCost = std::max(0, GC.getCorporationInfo(eCorporation).getSpreadCost() * (100 + GET_PLAYER(getOwnerINLINE()).calculateInflationRate()));
iCost /= 100;
if (NULL != pCity)
{
if (getTeam() != pCity->getTeam() && !GET_TEAM(pCity->getTeam()).isVassal(getTeam()))
{
iCost *= GC.getDefineINT("CORPORATION_FOREIGN_SPREAD_COST_PERCENT");
iCost /= 100;
}
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (iCorp != eCorporation)
{
if (pCity->isActiveCorporation((CorporationTypes)iCorp))
{
if (GC.getGameINLINE().isCompetingCorporation(eCorporation, (CorporationTypes)iCorp))
{
iCost *= 100 + GC.getCorporationInfo((CorporationTypes)iCorp).getSpreadFactor();
iCost /= 100;
}
}
}
}
}
return iCost;
}
bool CvUnit::spreadCorporation(CorporationTypes eCorporation)
{
int iSpreadProb;
if (!canSpreadCorporation(plot(), eCorporation))
{
return false;
}
CvCity* pCity = plot()->getPlotCity();
if (NULL != pCity)
{
GET_PLAYER(getOwnerINLINE()).changeGold(-spreadCorporationCost(eCorporation, pCity));
iSpreadProb = m_pUnitInfo->getCorporationSpreads(eCorporation);
if (pCity->getTeam() != getTeam())
{
iSpreadProb /= 2;
}
iSpreadProb += (((GC.getNumCorporationInfos() - pCity->getCorporationCount()) * (100 - iSpreadProb)) / GC.getNumCorporationInfos());
if (GC.getGameINLINE().getSorenRandNum(100, "Unit Spread Corporation") < iSpreadProb)
{
pCity->setHasCorporation(eCorporation, true, true, false);
}
else
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_CORPORATION_FAILED_TO_SPREAD", getNameKey(), GC.getCorporationInfo(eCorporation).getChar(), pCity->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NOSPREAD", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE());
}
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_SPREAD_CORPORATION);
}
kill(true, NO_PLAYER, true);
return true;
}
bool CvUnit::canJoin(const CvPlot* pPlot, SpecialistTypes eSpecialist) const
{
CvCity* pCity;
/************************************************************************************************/
/* Afforess Start 06/05/10 */
/* */
/* */
/************************************************************************************************/
if (isCommander())
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (eSpecialist == NO_SPECIALIST)
{
return false;
}
if (!(m_pUnitInfo->getGreatPeoples(eSpecialist)))
{
return false;
}
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (!(pCity->canJoin()))
{
return false;
}
if (pCity->getTeam() != getTeam())
{
return false;
}
if (isDelayedDeath())
{
return false;
}
return true;
}
bool CvUnit::join(SpecialistTypes eSpecialist)
{
CvCity* pCity;
if (!canJoin(plot(), eSpecialist))
{
return false;
}
pCity = plot()->getPlotCity();
if (pCity != NULL)
{
pCity->changeFreeSpecialistCount(eSpecialist, 1, true);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_JOIN);
}
getGroup()->AI_setMissionAI(MISSIONAI_DELIBERATE_KILL, NULL, NULL);
kill(true, NO_PLAYER, true);
return true;
}
bool CvUnit::canConstruct(const CvPlot* pPlot, BuildingTypes eBuilding, bool bTestVisible) const
{
CvCity* pCity;
if (eBuilding == NO_BUILDING)
{
return false;
}
/************************************************************************************************/
/* Afforess Start 06/05/10 */
/* */
/* */
/************************************************************************************************/
if (isCommander())
{
return false;
}
if (GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce() > NO_RELIGION)
{
if (GC.getBuildingInfo(eBuilding).getProductionCost() == -1)
{
if (GC.getGameINLINE().getBuildingClassCreatedCount((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType()) > 0)
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (getTeam() != pCity->getTeam())
{
return false;
}
if (pCity->getNumRealBuilding(eBuilding) > 0)
{
return false;
}
if (!(m_pUnitInfo->getForceBuildings(eBuilding)))
{
if (!(m_pUnitInfo->getBuildings(eBuilding)))
{
return false;
}
if (!(pCity->canConstruct(eBuilding, false, bTestVisible, true)))
{
return false;
}
}
if (isDelayedDeath())
{
return false;
}
return true;
}
bool CvUnit::construct(BuildingTypes eBuilding)
{
CvCity* pCity;
if (!canConstruct(plot(), eBuilding))
{
return false;
}
pCity = plot()->getPlotCity();
if (pCity != NULL)
{
pCity->setNumRealBuilding(eBuilding, pCity->getNumRealBuilding(eBuilding) + 1);
CvEventReporter::getInstance().buildingBuilt(pCity, eBuilding);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_CONSTRUCT);
}
GET_PLAYER(getOwnerINLINE()).NoteUnitConstructed(eBuilding);
getGroup()->AI_setMissionAI(MISSIONAI_DELIBERATE_KILL, NULL, NULL);
kill(true, NO_PLAYER, true);
return true;
}
TechTypes CvUnit::getDiscoveryTech() const
{
return ::getDiscoveryTech(getUnitType(), getOwnerINLINE());
}
int CvUnit::getDiscoverResearch(TechTypes eTech) const
{
int iResearch;
iResearch = (m_pUnitInfo->getBaseDiscover() + (m_pUnitInfo->getDiscoverMultiplier() * GET_TEAM(getTeam()).getTotalPopulation()));
iResearch *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getUnitDiscoverPercent();
iResearch /= 100;
if (eTech != NO_TECH)
{
iResearch = std::min(GET_TEAM(getTeam()).getResearchLeft(eTech), iResearch);
}
return std::max(0, iResearch);
}
bool CvUnit::canDiscover(const CvPlot* pPlot) const
{
TechTypes eTech;
eTech = getDiscoveryTech();
if (eTech == NO_TECH)
{
return false;
}
if (getDiscoverResearch(eTech) == 0)
{
return false;
}
if (isDelayedDeath())
{
return false;
}
return true;
}
bool CvUnit::discover()
{
TechTypes eDiscoveryTech;
if (!canDiscover(plot()))
{
return false;
}
eDiscoveryTech = getDiscoveryTech();
FAssertMsg(eDiscoveryTech != NO_TECH, "DiscoveryTech is not assigned a valid value");
GET_TEAM(getTeam()).changeResearchProgress(eDiscoveryTech, getDiscoverResearch(eDiscoveryTech), getOwnerINLINE());
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_DISCOVER);
}
kill(true, NO_PLAYER, true);
return true;
}
int CvUnit::getMaxHurryProduction(CvCity* pCity) const
{
int iProduction;
iProduction = (m_pUnitInfo->getBaseHurry() + (m_pUnitInfo->getHurryMultiplier() * pCity->getPopulation()));
iProduction *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getUnitHurryPercent();
iProduction /= 100;
return std::max(0, iProduction);
}
int CvUnit::getHurryProduction(const CvPlot* pPlot) const
{
CvCity* pCity;
int iProduction;
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return 0;
}
iProduction = getMaxHurryProduction(pCity);
iProduction = std::min(pCity->productionLeft(), iProduction);
return std::max(0, iProduction);
}
bool CvUnit::canHurry(const CvPlot* pPlot, bool bTestVisible) const
{
if (isDelayedDeath())
{
return false;
}
CvCity* pCity;
if (getHurryProduction(pPlot) == 0)
{
return false;
}
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (pCity->getProductionTurnsLeft() == 1)
{
return false;
}
/************************************************************************************************/
/* Afforess Start 04/23/10 */
/* */
/* */
/************************************************************************************************/
if (getTeam() != pCity->getTeam())
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!bTestVisible)
{
if (!(pCity->isProductionBuilding()))
{
return false;
}
}
return true;
}
bool CvUnit::hurry()
{
CvCity* pCity;
if (!canHurry(plot()))
{
return false;
}
pCity = plot()->getPlotCity();
if (pCity != NULL)
{
pCity->changeProduction(getHurryProduction(plot()));
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_HURRY);
}
kill(true, NO_PLAYER, true);
return true;
}
int CvUnit::getTradeGold(const CvPlot* pPlot) const
{
CvCity* pCapitalCity;
CvCity* pCity;
int iGold;
pCity = pPlot->getPlotCity();
pCapitalCity = GET_PLAYER(getOwnerINLINE()).getCapitalCity();
if (pCity == NULL)
{
return 0;
}
iGold = (m_pUnitInfo->getBaseTrade() + (m_pUnitInfo->getTradeMultiplier() * ((pCapitalCity != NULL) ? pCity->calculateTradeProfit(pCapitalCity) : 0)));
iGold *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getUnitTradePercent();
iGold /= 100;
/************************************************************************************************/
/* Afforess Start 03/9/10 */
/* */
/* */
/************************************************************************************************/
iGold *= (GET_TEAM(getTeam()).getTradeMissionModifier() + 100);
iGold /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return std::max(0, iGold);
}
bool CvUnit::canTrade(const CvPlot* pPlot, bool bTestVisible) const
{
if (isDelayedDeath())
{
return false;
}
CvCity* pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (getTradeGold(pPlot) == 0)
{
return false;
}
if (!canEnterArea(pPlot->getTeam(), pPlot->area()))
{
return false;
}
if (!bTestVisible)
{
if (pCity->getTeam() == getTeam())
{
return false;
}
}
return true;
}
bool CvUnit::trade()
{
if (!canTrade(plot()))
{
return false;
}
GET_PLAYER(getOwnerINLINE()).changeGold(getTradeGold(plot()));
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_TRADE);
}
kill(true, NO_PLAYER, true);
return true;
}
int CvUnit::getGreatWorkCulture(const CvPlot* pPlot) const
{
int iCulture;
iCulture = m_pUnitInfo->getGreatWorkCulture();
iCulture *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getUnitGreatWorkPercent();
iCulture /= 100;
return std::max(0, iCulture);
}
bool CvUnit::canGreatWork(const CvPlot* pPlot) const
{
if (isDelayedDeath())
{
return false;
}
CvCity* pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (pCity->getOwnerINLINE() != getOwnerINLINE())
{
return false;
}
if (getGreatWorkCulture(pPlot) == 0)
{
return false;
}
return true;
}
bool CvUnit::greatWork()
{
if (!canGreatWork(plot()))
{
return false;
}
CvCity* pCity = plot()->getPlotCity();
if (pCity != NULL)
{
pCity->setCultureUpdateTimer(0);
pCity->setOccupationTimer(0);
int iCultureToAdd = 100 * getGreatWorkCulture(plot());
int iNumTurnsApplied = (GC.getDefineINT("GREAT_WORKS_CULTURE_TURNS") * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getUnitGreatWorkPercent()) / 100;
for (int i = 0; i < iNumTurnsApplied; ++i)
{
pCity->changeCultureTimes100(getOwnerINLINE(), iCultureToAdd / iNumTurnsApplied, true, true);
}
if (iNumTurnsApplied > 0)
{
pCity->changeCultureTimes100(getOwnerINLINE(), iCultureToAdd % iNumTurnsApplied, false, true);
}
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_GREAT_WORK);
}
kill(true, NO_PLAYER, true);
return true;
}
bool CvUnit::doOutcomeMission(MissionTypes eMission)
{
CvOutcomeMission* pOutcomeMission = getUnitInfo().getOutcomeMissionByMission(eMission);
if (!pOutcomeMission)
{
// Outcome missions on unit combats
for (int iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
if (hasCombatType((UnitCombatTypes)iI))
{
UnitCombatTypes eCombat = (UnitCombatTypes)iI;
pOutcomeMission = GC.getUnitCombatInfo(eCombat).getOutcomeMissionByMission(eMission);
if (pOutcomeMission)
{
break;
}
}
}
}
if (!pOutcomeMission)
{
return false;
}
if (!pOutcomeMission->isPossible(this))
{
return false;
}
pOutcomeMission->execute(this);
if (plot()->isActiveVisible(false))
{
NotifyEntity(eMission);
}
if (pOutcomeMission->isKill())
{
getGroup()->AI_setMissionAI(MISSIONAI_DELIBERATE_KILL, NULL, NULL);
kill(true);
}
return true;
}
int CvUnit::getEspionagePoints(const CvPlot* pPlot) const
{
int iEspionagePoints;
iEspionagePoints = m_pUnitInfo->getEspionagePoints();
iEspionagePoints *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getUnitGreatWorkPercent();
iEspionagePoints /= 100;
return std::max(0, iEspionagePoints);
}
bool CvUnit::canInfiltrate(const CvPlot* pPlot, bool bTestVisible) const
{
if (isDelayedDeath())
{
return false;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE))
{
return false;
}
if (getEspionagePoints(NULL) == 0)
{
return false;
}
CvCity* pCity = pPlot->getPlotCity();
if (pCity == NULL || pCity->isBarbarian())
{
return false;
}
if (!bTestVisible)
{
if (NULL != pCity && pCity->getTeam() == getTeam())
{
return false;
}
}
return true;
}
bool CvUnit::infiltrate()
{
if (!canInfiltrate(plot()))
{
return false;
}
int iPoints = getEspionagePoints(NULL);
GET_TEAM(getTeam()).changeEspionagePointsAgainstTeam(GET_PLAYER(plot()->getOwnerINLINE()).getTeam(), iPoints);
GET_TEAM(getTeam()).changeEspionagePointsEver(iPoints);
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_INFILTRATE);
}
kill(true, NO_PLAYER, true);
return true;
}
bool CvUnit::canEspionage(const CvPlot* pPlot, bool bTestVisible) const
{
if (isDelayedDeath())
{
return false;
}
if (!isSpy())
{
return false;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE))
{
return false;
}
PlayerTypes ePlotOwner = pPlot->getOwnerINLINE();
if (NO_PLAYER == ePlotOwner)
{
return false;
}
CvPlayer& kTarget = GET_PLAYER(ePlotOwner);
if (kTarget.isBarbarian())
{
return false;
}
if (kTarget.getTeam() == getTeam())
{
return false;
}
if (GET_TEAM(getTeam()).isVassal(kTarget.getTeam()))
{
return false;
}
if (!bTestVisible)
{
if (isMadeAttack())
{
return false;
}
if (hasMoved())
{
return false;
}
if (kTarget.getTeam() != getTeam() && !isInvisible(kTarget.getTeam(), false))
{
return false;
}
}
return true;
}
//TSHEEP start
bool CvUnit::awardSpyExperience(TeamTypes eTargetTeam, int iModifier)
{
if (GC.isSS_ENABLED())
{
int iDifficulty = (getSpyInterceptPercent(eTargetTeam) * (100 + iModifier))/100;
if (iDifficulty < 1)
changeExperience(1);
else if (iDifficulty < 10)
changeExperience(2);
else if (iDifficulty < 25)
changeExperience(3);
else if (iDifficulty < 50)
changeExperience(4);
else if (iDifficulty < 75)
changeExperience(5);
else if (iDifficulty >= 75)
changeExperience(6);
// Koshling - testing promotion readiness here is uneccessary since CvUnit::doTurn
// will do it. It is alo now dangerous to do it here (or indeed anywhere but controlled
// places) becaue it is not thread-safe and needs to run strictly on the main thread
//testPromotionReady();
return true;
} else return false;
}
//TSHEEP End
/************************************************************************************************/
/* RevolutionDCM 04/19/09 Glider1 */
/************************************************************************************************/
//RevolutionDCM - Super Spies
bool CvUnit::canAssassin(const CvPlot* pPlot, bool bTestVisible) const
{
if (isDelayedDeath())
{
return false;
}
if (!isSpy())
{
return false;
}
CvCity* pCity = pPlot->getPlotCity();
if (NULL == pCity)
{
return false;
}
int numGreatPeople = pCity->getNumGreatPeople();
if (numGreatPeople <= 0)
{
return false;
}
CvPlayer& kTarget = GET_PLAYER(pCity->getOwnerINLINE());
if (kTarget.getTeam() == getTeam())
{
return false;
}
if (kTarget.isBarbarian())
{
return false;
}
if (GET_TEAM(getTeam()).isVassal(kTarget.getTeam()))
{
return false;
}
if (!bTestVisible)
{
if (isMadeAttack())
{
return false;
}
if (hasMoved())
{
return false;
}
if (kTarget.getTeam() != getTeam() && !isInvisible(kTarget.getTeam(), false))
{
return false;
}
}
return true;
}
bool CvUnit::canBribe(const CvPlot* pPlot, bool bTestVisible) const
{
if (isDelayedDeath())
{
return false;
}
if (!isSpy())
{
return false;
}
if(pPlot->plotCount(PUF_isOtherTeam, getOwnerINLINE(), -1, NO_PLAYER, NO_TEAM, PUF_isVisible, getOwnerINLINE()) < 1)
{
return false;
}
if (pPlot->plotCount(PUF_isUnitAIType, UNITAI_WORKER, -1) < 1)
{
return false;
}
CvUnit* pTargetUnit;
pTargetUnit = pPlot->plotCheck(PUF_isOtherTeam, getOwnerINLINE(), -1, NO_PLAYER, NO_TEAM, PUF_isVisible, getOwnerINLINE());
CvPlayer& kTarget = GET_PLAYER(pTargetUnit->getOwnerINLINE());
if (kTarget.getTeam() == getTeam())
{
return false;
}
if (kTarget.isBarbarian())
{
return false;
}
if (GET_TEAM(getTeam()).isVassal(kTarget.getTeam()))
{
return false;
}
if (!bTestVisible)
{
if (isMadeAttack())
{
return false;
}
if (hasMoved())
{
return false;
}
if (kTarget.getTeam() != getTeam() && !isInvisible(kTarget.getTeam(), false))
{
return false;
}
}
return true;
}
// RevolutionDCM end
/************************************************************************************************/
/* RevolutionDCM END Glider1 */
/************************************************************************************************/
bool CvUnit::espionage(EspionageMissionTypes eMission, int iData)
{
if (!canEspionage(plot()))
{
return false;
}
PlayerTypes eTargetPlayer = plot()->getOwnerINLINE();
if (NO_ESPIONAGEMISSION == eMission)
{
FAssert(GET_PLAYER(getOwnerINLINE()).isHuman());
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_DOESPIONAGE);
if (NULL != pInfo)
{
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE(), true);
}
}
else if (GC.getEspionageMissionInfo(eMission).isTwoPhases() && -1 == iData)
{
FAssert(GET_PLAYER(getOwnerINLINE()).isHuman());
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_DOESPIONAGE_TARGET);
if (NULL != pInfo)
{
pInfo->setData1(eMission);
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE(), true);
}
}
else
{
if (testSpyIntercepted(eTargetPlayer, GC.getEspionageMissionInfo(eMission).getDifficultyMod()))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 01/31/10 */
/* */
/* */
/************************************************************************************************/
bool bCaught = testSpyIntercepted(eTargetPlayer, GC.getDefineINT("ESPIONAGE_SPY_MISSION_ESCAPE_MOD"));
if (GET_PLAYER(getOwnerINLINE()).doEspionageMission(eMission, eTargetPlayer, plot(), iData, this, (bCaught && !isAlwaysHeal())))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
// If it died in the mission (e.g. - nuke and blew itself up) then nothing else
// needs doing
if ( !isDelayedDeath() )
{
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_ESPIONAGE);
}
if (!bCaught)
{
setFortifyTurns(0);
setMadeAttack(true);
finishMoves();
CvCity* pCapital = GET_PLAYER(getOwnerINLINE()).getCapitalCity();
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/*Spy actions that aren't in a city don't cause the spy to be sent back */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_ESPIONAGE))
{
if (!plot()->isCity())
{
pCapital = NULL;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (NULL != pCapital)
{
if ( !pCapital->isInViewport() )
{
GC.getCurrentViewport()->bringIntoView(pCapital->getX_INLINE(), pCapital->getY_INLINE(), NULL, true, true);
}
setXY(pCapital->getX_INLINE(), pCapital->getY_INLINE(), false, false, false);
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_ESPIONAGE_SPY_SUCCESS", getNameKey(), pCapital->getNameKey());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), pCapital->getX_INLINE(), pCapital->getY_INLINE(), true, true);
}
//TSHEEP Give spies xp for successful missions
awardSpyExperience(GET_PLAYER(eTargetPlayer).getTeam(),GC.getEspionageMissionInfo(eMission).getDifficultyMod());
//TSHEEP end
}
}
return true;
}
}
return false;
}
bool CvUnit::testSpyIntercepted(PlayerTypes eTargetPlayer, int iModifier)
{
CvPlayer& kTargetPlayer = GET_PLAYER(eTargetPlayer);
if (kTargetPlayer.isBarbarian())
{
return false;
}
if (GC.getGameINLINE().getSorenRandNum(10000, "Spy Interception") >= getSpyInterceptPercent(kTargetPlayer.getTeam()) * (100 + iModifier))
{
return false;
}
CvString szFormatNoReveal;
CvString szFormatReveal;
if (GET_TEAM(kTargetPlayer.getTeam()).getCounterespionageModAgainstTeam(getTeam()) > 0)
{
szFormatNoReveal = "TXT_KEY_SPY_INTERCEPTED_MISSION";
szFormatReveal = "TXT_KEY_SPY_INTERCEPTED_MISSION_REVEAL";
}
else if (plot()->isEspionageCounterSpy(kTargetPlayer.getTeam()))
{
szFormatNoReveal = "TXT_KEY_SPY_INTERCEPTED_SPY";
szFormatReveal = "TXT_KEY_SPY_INTERCEPTED_SPY_REVEAL";
}
else
{
szFormatNoReveal = "TXT_KEY_SPY_INTERCEPTED";
szFormatReveal = "TXT_KEY_SPY_INTERCEPTED_REVEAL";
}
CvWString szCityName = kTargetPlayer.getCivilizationShortDescription();
CvCity* pClosestCity = GC.getMapINLINE().findCity(getX_INLINE(), getY_INLINE(), eTargetPlayer, kTargetPlayer.getTeam(), true, false);
if (pClosestCity != NULL)
{
szCityName = pClosestCity->getName();
}
CvWString szBuffer;
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText(szFormatReveal.GetCString(), GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey(), getNameKey(), kTargetPlayer.getCivilizationAdjectiveKey(), szCityName.GetCString());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
}
//TSHEEP Enable Loyalty Promotion
//if (GC.getGameINLINE().getSorenRandNum(100, "Spy Reveal identity") < GC.getDefineINT("ESPIONAGE_SPY_REVEAL_IDENTITY_PERCENT"))
if (GC.getGameINLINE().getSorenRandNum(100, "Spy Reveal identity") < GC.getDefineINT("ESPIONAGE_SPY_REVEAL_IDENTITY_PERCENT") && !isAlwaysHeal())//TSHEEP End
{
if (!isEnemy(kTargetPlayer.getTeam()))
{
GET_PLAYER(eTargetPlayer).AI_changeMemoryCount(getOwnerINLINE(), MEMORY_SPY_CAUGHT, 1);
}
MEMORY_TRACK_EXEMPT();
AddDLLMessage(eTargetPlayer, true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSE", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText(szFormatNoReveal.GetCString(), getNameKey(), kTargetPlayer.getCivilizationAdjectiveKey(), szCityName.GetCString());
AddDLLMessage(eTargetPlayer, true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSE", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_SURRENDER);
}
//TSHEEP - Give xp to spy who catches spy
CvUnit* pCounterUnit;
pCounterUnit = plot()->plotCheck(PUF_isCounterSpy, -1, -1, NO_PLAYER, kTargetPlayer.getTeam());
if(NULL != pCounterUnit)
{
pCounterUnit->changeExperience(1);
//RevolutionDCM - just ensure that promotion readiness is tested
// Koshling - testing promotion readiness here is uneccessary since CvUnit::doTurn
// will do it. It is alo now dangerous to do it here (or indeed anywhere but controlled
// places) becaue it is not thread-safe and needs to run strictly on the main thread
//pCounterUnit->testPromotionReady();
}
//TSHEEP End
//TSHEEP Implement Escape Promotion
if(GC.getGameINLINE().getSorenRandNum(100, "Spy Reveal identity") < withdrawalProbability())
{
setFortifyTurns(0);
setMadeAttack(true);
finishMoves();
CvCity* pCapital = GET_PLAYER(getOwnerINLINE()).getCapitalCity();
if (NULL != pCapital)
{
setXY(pCapital->getX_INLINE(), pCapital->getY_INLINE(), false, false, false);
}
{
MEMORY_TRACK_EXEMPT();
szFormatReveal = "TXT_KEY_SPY_ESCAPED_REVEAL";
szFormatNoReveal = "TXT_KEY_SPY_ESCAPED";
szBuffer = gDLL->getText(szFormatReveal.GetCString(), GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey(), getNameKey(), kTargetPlayer.getCivilizationAdjectiveKey(), szCityName.GetCString());
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
szBuffer = gDLL->getText(szFormatNoReveal.GetCString(), getNameKey(), kTargetPlayer.getCivilizationAdjectiveKey(), szCityName.GetCString());
AddDLLMessage(eTargetPlayer, true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_EXPOSE", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
}
changeExperience(1);
// Koshling - testing promotion readiness here is uneccessary since CvUnit::doTurn
// will do it. It is alo now dangerous to do it here (or indeed anywhere but controlled
// places) becaue it is not thread-safe and needs to run strictly on the main thread
//testPromotionReady();
return true;
}
//TSHEEP End
kill(true, NO_PLAYER, true);
return true;
}
int CvUnit::getSpyInterceptPercent(TeamTypes eTargetTeam) const
{
FAssert(isSpy());
FAssert(getTeam() != eTargetTeam);
int iSuccess = 0;
int iTargetPoints = GET_TEAM(eTargetTeam).getEspionagePointsEver();
int iOurPoints = GET_TEAM(getTeam()).getEspionagePointsEver();
iSuccess += (GC.getDefineINT("ESPIONAGE_INTERCEPT_SPENDING_MAX") * iTargetPoints) / std::max(1, iTargetPoints + iOurPoints);
//TSHEEP - add evasion attribute to spy chances
if(getExtraEvasion())
{
iSuccess -= getExtraEvasion();
}
//TSHEEP end
if (plot()->isEspionageCounterSpy(eTargetTeam))
{
iSuccess += GC.getDefineINT("ESPIONAGE_INTERCEPT_COUNTERSPY");
//TSHEEP - Add intercept attribute of any enemy spies present to chances
if(plot()->plotCheck(PUF_isCounterSpy, -1, -1, NO_PLAYER, eTargetTeam))
{
CvUnit* pCounterUnit = plot()->plotCheck(PUF_isCounterSpy, -1, -1, NO_PLAYER, eTargetTeam);
if(pCounterUnit->getExtraIntercept())
iSuccess += pCounterUnit->getExtraIntercept();
}
//TSHEEP end
}
if (GET_TEAM(eTargetTeam).getCounterespionageModAgainstTeam(getTeam()) > 0)
{
iSuccess += GC.getDefineINT("ESPIONAGE_INTERCEPT_COUNTERESPIONAGE_MISSION");
}
//TSHEEP - This check was always returning true since there is always at least one friendly spy in the tile
//if (0 == getFortifyTurns() || plot()->plotCount(PUF_isSpy, -1, -1, NO_PLAYER, getTeam()) > 0)
if (0 == getFortifyTurns() || plot()->plotCount(PUF_isSpy, -1, -1, NO_PLAYER, getTeam()) > 1)//TSHEEP - End
{
iSuccess += GC.getDefineINT("ESPIONAGE_INTERCEPT_RECENT_MISSION");
}
return std::min(100, std::max(0, iSuccess));
}
bool CvUnit::isIntruding() const
{
TeamTypes eLocalTeam = plot()->getTeam();
if (NO_TEAM == eLocalTeam || eLocalTeam == getTeam())
{
return false;
}
// UNOFFICIAL_PATCH Start
// * Vassal's spies no longer caught in master's territory
//if (GET_TEAM(eLocalTeam).isVassal(getTeam()))
if (GET_TEAM(eLocalTeam).isVassal(getTeam()) || GET_TEAM(getTeam()).isVassal(eLocalTeam))
// UNOFFICIAL_PATCH End
{
return false;
}
return true;
}
bool CvUnit::canGoldenAge(const CvPlot* pPlot, bool bTestVisible) const
{
if (!isGoldenAge())
{
return false;
}
if (!bTestVisible)
{
if (GET_PLAYER(getOwnerINLINE()).unitsRequiredForGoldenAge() > GET_PLAYER(getOwnerINLINE()).unitsGoldenAgeReady())
{
return false;
}
}
return true;
}
bool CvUnit::goldenAge()
{
if (!canGoldenAge(plot()))
{
return false;
}
GET_PLAYER(getOwnerINLINE()).killGoldenAgeUnits(this);
GET_PLAYER(getOwnerINLINE()).changeGoldenAgeTurns(GET_PLAYER(getOwnerINLINE()).getGoldenAgeLength());
GET_PLAYER(getOwnerINLINE()).changeNumUnitGoldenAges(1);
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_GOLDEN_AGE);
}
kill(true, NO_PLAYER, true);
return true;
}
bool CvUnit::canBuild(const CvPlot* pPlot, BuildTypes eBuild, bool bTestVisible) const
{
FAssertMsg(eBuild < GC.getNumBuildInfos(), "Index out of bounds");
if (!(m_pUnitInfo->getBuilds(eBuild)))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* */
/************************************************************************************************/
if (getGroup()->isAutomated())
{
if (!GET_PLAYER(getOwnerINLINE()).isAutomatedCanBuild(eBuild))
{
return false;
}
if (plot()->getWorkingCity() != NULL)
{
if (!plot()->getWorkingCity()->isAutomatedCanBuild(eBuild))
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!(GET_PLAYER(getOwnerINLINE()).canBuild(pPlot, eBuild, false, bTestVisible)))
{
return false;
}
if (!pPlot->isValidDomainForAction(*this))
{
return false;
}
return true;
}
// Returns true if build finished...
bool CvUnit::build(BuildTypes eBuild)
{
bool bFinished;
FAssertMsg(eBuild < GC.getNumBuildInfos(), "Invalid Build");
if (!canBuild(plot(), eBuild))
{
return false;
}
// Note: notify entity must come before changeBuildProgress - because once the unit is done building,
// that function will notify the entity to stop building.
NotifyEntity((MissionTypes)GC.getBuildInfo(eBuild).getMissionType());
GET_PLAYER(getOwnerINLINE()).changeGold(-(GET_PLAYER(getOwnerINLINE()).getBuildCost(plot(), eBuild)));
/************************************************************************************************/
/* Afforess Start 02/15/10 */
/* */
/* */
/************************************************************************************************/
bFinished = plot()->changeBuildProgress(eBuild, workRate(false), getOwnerINLINE());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
finishMoves(); // needs to be at bottom because movesLeft() can affect workRate()...
if (bFinished)
{
//ls612: Workers now get XP on finishing a build
/* int iCost = GC.getBuildInfo(eBuild).getTime();
int iSpeedModifier = workRate(true);
if (iCost > 0 && !GC.getBuildInfo(eBuild).isKill())
{
changeExperience100(iCost / ((2 * iSpeedModifier) / 100), -1, false, false, false);
}
*/ //temporarily removed in AND2 by 45° as we don't use it for the moment (while C2C does)
if (GC.getBuildInfo(eBuild).isKill())
{
if ( plot()->getWorkingCity() != NULL )
{
OutputDebugString(CvString::format("Worker at (%d,%d) consumed by build for city %S\n", plot()->getX_INLINE(), plot()->getY_INLINE(), plot()->getWorkingCity()->getName().GetCString()).c_str());
//plot()->getWorkingCity()->AI_changeWorkersHave(-1);
}
kill(true, NO_PLAYER, true);
}
}
// Python Event
CvEventReporter::getInstance().unitBuildImprovement(this, eBuild, bFinished);
return bFinished;
}
bool CvUnit::canPromote(PromotionTypes ePromotion, int iLeaderUnitId) const
{
if (iLeaderUnitId >= 0)
{
if (iLeaderUnitId == getID())
{
return false;
}
// The command is always possible if it's coming from a Warlord unit that gives just experience points
CvUnit* pWarlord = GET_PLAYER(getOwnerINLINE()).getUnit(iLeaderUnitId);
if (pWarlord &&
NO_UNIT != pWarlord->getUnitType() &&
pWarlord->getUnitInfo().getLeaderExperience() > 0 &&
NO_PROMOTION == pWarlord->getUnitInfo().getLeaderPromotion() &&
canAcquirePromotionAny())
{
return true;
}
}
if (ePromotion == NO_PROMOTION)
{
return false;
}
if (!canAcquirePromotion(ePromotion))
{
return false;
}
if (GC.getPromotionInfo(ePromotion).isLeader())
{
if (iLeaderUnitId >= 0)
{
CvUnit* pWarlord = GET_PLAYER(getOwnerINLINE()).getUnit(iLeaderUnitId);
if (pWarlord && NO_UNIT != pWarlord->getUnitType())
{
return (pWarlord->getUnitInfo().getLeaderPromotion() == ePromotion);
}
}
return false;
}
else
{
if (!isPromotionReady())
{
return false;
}
}
return true;
}
void CvUnit::promote(PromotionTypes ePromotion, int iLeaderUnitId)
{
if (!canPromote(ePromotion, iLeaderUnitId))
{
return;
}
if (iLeaderUnitId >= 0)
{
CvUnit* pWarlord = GET_PLAYER(getOwnerINLINE()).getUnit(iLeaderUnitId);
if (pWarlord)
{
pWarlord->giveExperience();
if (!pWarlord->getNameNoDesc().empty())
{
setName(pWarlord->getNameKey());
}
//update graphics models
m_eLeaderUnitType = pWarlord->getUnitType();
reloadEntity();
}
}
if (!GC.getPromotionInfo(ePromotion).isLeader())
{
changeLevel(1);
changeDamage(-(getDamage() / 2));
}
setHasPromotion(ePromotion, true);
testPromotionReady();
if (IsSelected())
{
gDLL->getInterfaceIFace()->playGeneralSound(GC.getPromotionInfo(ePromotion).getSound());
gDLL->getInterfaceIFace()->setDirty(UnitInfo_DIRTY_BIT, true);
// BUG - Update Plot List - start
gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
// BUG - Update Plot List - end
}
else
{
setInfoBarDirty(true);
}
CvEventReporter::getInstance().unitPromoted(this, ePromotion);
}
bool CvUnit::lead(int iUnitId)
{
if (!canLead(plot(), iUnitId))
{
return false;
}
PromotionTypes eLeaderPromotion = (PromotionTypes)m_pUnitInfo->getLeaderPromotion();
if (-1 == iUnitId)
{
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_LEADUNIT, eLeaderPromotion, getID());
if (pInfo)
{
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE(), true);
}
return false;
}
else
{
CvUnit* pUnit = GET_PLAYER(getOwnerINLINE()).getUnit(iUnitId);
if (!pUnit || !pUnit->canPromote(eLeaderPromotion, getID()))
{
return false;
}
pUnit->joinGroup(NULL, true, true);
pUnit->promote(eLeaderPromotion, getID());
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_LEAD);
}
kill(true, NO_PLAYER, true);
return true;
}
}
int CvUnit::canLead(const CvPlot* pPlot, int iUnitId) const
{
PROFILE_FUNC();
if (isDelayedDeath())
{
return 0;
}
if (NO_UNIT == getUnitType())
{
return 0;
}
/************************************************************************************************/
/* Afforess Start 05/21/10 */
/* */
/* */
/************************************************************************************************/
if (isCommander())
{
return 0;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int iNumUnits = 0;
CvUnitInfo& kUnitInfo = getUnitInfo();
if (-1 == iUnitId)
{
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
while(pUnitNode != NULL)
{
CvUnit* pUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pUnit && pUnit != this && pUnit->getOwnerINLINE() == getOwnerINLINE() && pUnit->canPromote((PromotionTypes)kUnitInfo.getLeaderPromotion(), getID()))
{
++iNumUnits;
}
}
}
else
{
CvUnit* pUnit = GET_PLAYER(getOwnerINLINE()).getUnit(iUnitId);
if (pUnit && pUnit != this && pUnit->canPromote((PromotionTypes)kUnitInfo.getLeaderPromotion(), getID()))
{
iNumUnits = 1;
}
}
return iNumUnits;
}
int CvUnit::canGiveExperience(const CvPlot* pPlot) const
{
int iNumUnits = 0;
if (NO_UNIT != getUnitType() && m_pUnitInfo->getLeaderExperience() > 0)
{
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
while(pUnitNode != NULL)
{
CvUnit* pUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pUnit && pUnit != this && pUnit->getOwnerINLINE() == getOwnerINLINE() && pUnit->canAcquirePromotionAny())
{
/************************************************************************************************/
/* Afforess Start 03/30/10 */
/* */
/* Great Commanders: Do Not give commanders free XP */
/************************************************************************************************/
if (pUnit->getUnitInfo().isGreatGeneral())
continue;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
++iNumUnits;
}
}
}
return iNumUnits;
}
bool CvUnit::giveExperience()
{
CvPlot* pPlot = plot();
if (pPlot)
{
int iNumUnits = canGiveExperience(pPlot);
if (iNumUnits > 0)
{
int iTotalExperience = getStackExperienceToGive(iNumUnits);
int iMinExperiencePerUnit = iTotalExperience / iNumUnits;
int iRemainder = iTotalExperience % iNumUnits;
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
int i = 0;
while(pUnitNode != NULL)
{
CvUnit* pUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pUnit && pUnit != this && pUnit->getOwnerINLINE() == getOwnerINLINE() && pUnit->canAcquirePromotionAny())
{
/************************************************************************************************/
/* Afforess Start 03/30/10 */
/* */
/* Great Commanders: Do Not give commanders free XP */
/************************************************************************************************/
if (pUnit->isCommander())
continue;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
pUnit->changeExperience(i < iRemainder ? iMinExperiencePerUnit+1 : iMinExperiencePerUnit);
// Koshling - testing promotion readiness here is uneccessary since CvUnit::doTurn
// will do it. It is alo now dangerous to do it here (or indeed anywhere but controlled
// places) becaue it is not thread-safe and needs to run strictly on the main thread
//pUnit->testPromotionReady();
}
i++;
}
return true;
}
}
return false;
}
int CvUnit::getStackExperienceToGive(int iNumUnits) const
{
return (m_pUnitInfo->getLeaderExperience() * (100 + std::min(50, (iNumUnits - 1) * GC.getDefineINT("WARLORD_EXTRA_EXPERIENCE_PER_UNIT_PERCENT")))) / 100;
}
int CvUnit::upgradePrice(UnitTypes eUnit) const
{
int iPrice;
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_UPGRADE_UNIT_PRICE_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyArgsList argsList;
argsList.add(getOwnerINLINE());
argsList.add(getID());
argsList.add((int) eUnit);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "getUpgradePriceOverride", argsList.makeFunctionArgs(), &lResult);
if (lResult >= 0)
{
return lResult;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isBarbarian())
{
return 0;
}
iPrice = GC.getBASE_UNIT_UPGRADE_COST();
int iUpgradeCostModifier;
iUpgradeCostModifier = ((GET_PLAYER(getOwnerINLINE()).getProductionNeeded(eUnit) - GET_PLAYER(getOwnerINLINE()).getProductionNeeded(getUnitType())) * (GC.getDefineINT("UNIT_UPGRADE_COST_PER_PRODUCTION")));
iUpgradeCostModifier /= 100;
iPrice += (std::max(0, (iUpgradeCostModifier)));
if (!isHuman() && !isBarbarian())
{
iPrice *= GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIUnitUpgradePercent();
iPrice /= 100;
iPrice *= std::max(0, ((GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIPerEraModifier() * GET_PLAYER(getOwnerINLINE()).getCurrentEra()) + 100));
iPrice /= 100;
}
/************************************************************************************************/
/* REVDCM 02/16/10 phungus420 */
/* */
/* Leonardo's Workshop */
/************************************************************************************************/
iPrice = iPrice * (100 + GET_PLAYER(getOwnerINLINE()).getUnitUpgradePriceModifier()) / 100;
/************************************************************************************************/
/* REVDCM END */
/************************************************************************************************/
iPrice -= (iPrice * getUpgradeDiscount()) / 100;
//ls612: Upgrade price is now dependent on the level of a unit
if (iPrice != 0)
{
iPrice *= std::max(100, (100 + (5 * ((getLevel()) - 4))));
iPrice /= 100;
}
return iPrice;
}
bool CvUnit::upgradeAvailable(UnitTypes eFromUnit, UnitClassTypes eToUnitClass) const
{
return GET_PLAYER(getOwnerINLINE()).upgradeAvailable(eFromUnit, eToUnitClass);
}
bool CvUnit::canUpgrade(UnitTypes eUnit, bool bTestVisible) const
{
if (eUnit == NO_UNIT)
{
return false;
}
if(!isReadyForUpgrade())
{
return false;
}
if (!bTestVisible)
{
if (GET_PLAYER(getOwnerINLINE()).getGold() < upgradePrice(eUnit))
{
return false;
}
}
if (hasUpgrade(eUnit))
{
return true;
}
return false;
}
bool CvUnit::isReadyForUpgrade() const
{
/************************************************************************************************/
/* REVDCM 02/16/10 phungus420 */
/* */
/* RevTrait Effects */
/************************************************************************************************/
if ( !GET_PLAYER(getOwnerINLINE()).isUpgradeAnywhere())
{
if (!canMove())
{
return false;
}
}
if ( !GET_PLAYER(getOwnerINLINE()).isUpgradeAnywhere())
{
if (plot()->getTeam() != getTeam())
{
return false;
}
}
/************************************************************************************************/
/* REVDCM END */
/************************************************************************************************/
return true;
}
// has upgrade is used to determine if an upgrade is possible,
// it specifically does not check whether the unit can move, whether the current plot is owned, enough gold
// those are checked in canUpgrade()
// does not search all cities, only checks the closest one
bool CvUnit::hasUpgrade(bool bSearch) const
{
return (getUpgradeCity(bSearch) != NULL);
}
// has upgrade is used to determine if an upgrade is possible,
// it specifically does not check whether the unit can move, whether the current plot is owned, enough gold
// those are checked in canUpgrade()
// does not search all cities, only checks the closest one
bool CvUnit::hasUpgrade(UnitTypes eUnit, bool bSearch) const
{
return (getUpgradeCity(eUnit, bSearch) != NULL);
}
// finds the 'best' city which has a valid upgrade for the unit,
// it specifically does not check whether the unit can move, or if the player has enough gold to upgrade
// those are checked in canUpgrade()
// if bSearch is true, it will check every city, if not, it will only check the closest valid city
// NULL result means the upgrade is not possible
CvCity* CvUnit::getUpgradeCity(bool bSearch) const
{
PROFILE_FUNC();
CvPlayerAI& kPlayer = GET_PLAYER(getOwnerINLINE());
UnitAITypes eUnitAI = AI_getUnitAIType();
CvArea* pArea = area();
int iCurrentValue = kPlayer.AI_unitValue(getUnitType(), eUnitAI, pArea);
int iBestSearchValue = MAX_INT;
CvCity* pBestUpgradeCity = NULL;
for (int iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
int iNewValue = kPlayer.AI_unitValue(((UnitTypes)iI), eUnitAI, pArea);
if (iNewValue > iCurrentValue)
{
int iSearchValue;
CvCity* pUpgradeCity = getUpgradeCity((UnitTypes)iI, bSearch, &iSearchValue);
if (pUpgradeCity != NULL)
{
// if not searching or close enough, then this match will do
if (!bSearch || iSearchValue < 16)
{
return pUpgradeCity;
}
if (iSearchValue < iBestSearchValue)
{
iBestSearchValue = iSearchValue;
pBestUpgradeCity = pUpgradeCity;
}
}
}
}
return pBestUpgradeCity;
}
// finds the 'best' city which has a valid upgrade for the unit, to eUnit type
// it specifically does not check whether the unit can move, or if the player has enough gold to upgrade
// those are checked in canUpgrade()
// if bSearch is true, it will check every city, if not, it will only check the closest valid city
// if iSearchValue non NULL, then on return it will be the city's proximity value, lower is better
// NULL result means the upgrade is not possible
CvCity* CvUnit::getUpgradeCity(UnitTypes eUnit, bool bSearch, int* iSearchValue) const
{
PROFILE_FUNC();
if (eUnit == NO_UNIT)
{
return NULL;
}
CvPlayerAI& kPlayer = GET_PLAYER(getOwnerINLINE());
CvUnitInfo& kUnitInfo = GC.getUnitInfo(eUnit);
/************************************************************************************************/
/* Afforess Start 02/17/10 */
/* */
/* */
/************************************************************************************************/
if (!GC.getGameINLINE().isOption(GAMEOPTION_ASSIMILATION))
{
if (GC.getCivilizationInfo(kPlayer.getCivilizationType()).getCivilizationUnits(kUnitInfo.getUnitClassType()) != eUnit)
{
return NULL;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!upgradeAvailable(getUnitType(), ((UnitClassTypes)(kUnitInfo.getUnitClassType()))))
{
return NULL;
}
// Thomas SG - AC: Advanced Cargo START
{
if (kUnitInfo.getTotalCargoSpace() < getCargo())
{
return NULL;
}
}
// Thomas SG - AC: Advanced Cargo END
CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode();
while (pUnitNode != NULL)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = plot()->nextUnitNode(pUnitNode);
if (pLoopUnit->getTransportUnit() == this)
{
// Thomas SG - AC: Advanced Cargo START
{
bool set1 = true;
if (kUnitInfo.getNumSpecialCargos() > 0)
{
for (int iI = 0; iI < kUnitInfo.getNumSpecialCargos(); iI++)
{
if (set1 == true)
{
for (int iJ = 0; iJ < kUnitInfo.getNumSpecialUnitTypes(); iJ++)
{
if (set1 == true)
{
if (kUnitInfo.getSpecialCargo(iI) == pLoopUnit->getSpecialUnitType(iJ))
{
set1 = false;
}
}
}
}
}
}
bool set2 = true;
if (set1 == true)
{
if (kUnitInfo.getDomainCargo() != NO_DOMAIN)
{
if (kUnitInfo.getDomainCargo() != pLoopUnit->getDomainType())
{
set2 = false;
}
}
}
if (set1 && set2)
// Thomas SG - AC: Advanced Cargo END
{
return false;
}
}
if (kUnitInfo.getDomainCargo() != NO_DOMAIN)
{
if (kUnitInfo.getDomainCargo() != pLoopUnit->getDomainType())
{
return false;
}
}
}
}
// sea units must be built on the coast
bool bCoastalOnly = (getDomainType() == DOMAIN_SEA);
// results
int iBestValue = MAX_INT;
CvCity* pBestCity = NULL;
// if search is true, check every city for our team
if (bSearch)
{
// air units can travel any distance
bool bIgnoreDistance = (getDomainType() == DOMAIN_AIR);
TeamTypes eTeam = getTeam();
int iArea = getArea();
int iX = getX_INLINE(), iY = getY_INLINE();
// check every player on our team's cities
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
// is this player on our team?
CvPlayerAI& kLoopPlayer = GET_PLAYER((PlayerTypes)iI);
if (kLoopPlayer.isAlive() && kLoopPlayer.getTeam() == eTeam)
{
int iLoop;
for (CvCity* pLoopCity = kLoopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kLoopPlayer.nextCity(&iLoop))
{
// if coastal only, then make sure we are coast
CvArea* pWaterArea = NULL;
if (!bCoastalOnly || ((pWaterArea = pLoopCity->waterArea()) != NULL && !pWaterArea->isLake()))
{
// can this city tran this unit?
if (pLoopCity->canTrain(eUnit, false, false, true))
{
// if we do not care about distance, then the first match will do
if (bIgnoreDistance)
{
// if we do not care about distance, then return 1 for value
if (iSearchValue != NULL)
{
*iSearchValue = 1;
}
return pLoopCity;
}
int iValue = plotDistance(iX, iY, pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE());
// if not same area, not as good (lower numbers are better)
if (iArea != pLoopCity->getArea() && (!bCoastalOnly || iArea != pWaterArea->getID()))
{
iValue *= 16;
}
// if we cannot path there, not as good (lower numbers are better)
if (!generatePath(pLoopCity->plot(), 0, true))
{
iValue *= 16;
}
if (iValue < iBestValue)
{
iBestValue = iValue;
pBestCity = pLoopCity;
}
}
}
}
}
}
}
else
{
// find the closest city
CvCity* pClosestCity = GC.getMapINLINE().findCity(getX_INLINE(), getY_INLINE(), NO_PLAYER, getTeam(), true, bCoastalOnly);
if (pClosestCity != NULL)
{
// if we can train, then return this city (otherwise it will return NULL)
if (pClosestCity->canTrain(eUnit, false, false, true))
{
// did not search, always return 1 for search value
iBestValue = 1;
pBestCity = pClosestCity;
}
}
}
// return the best value, if non-NULL
if (iSearchValue != NULL)
{
*iSearchValue = iBestValue;
}
return pBestCity;
}
void CvUnit::upgrade(UnitTypes eUnit)
{
CvUnit* pUpgradeUnit;
if (!canUpgrade(eUnit))
{
return;
}
// BUG - Upgrade Unit Event - start
int iPrice = upgradePrice(eUnit);
GET_PLAYER(getOwnerINLINE()).changeGold(-iPrice);
// BUG - Upgrade Unit Event - end
// Preserve the AI type if that is possible
UnitAITypes eUnitAI = AI_getUnitAIType();
if ( !GC.getUnitInfo(eUnit).getUnitAIType(eUnitAI) )
{
eUnitAI = NO_UNITAI; // Will cause it to initialize with its default
}
pUpgradeUnit = GET_PLAYER(getOwnerINLINE()).initUnit(eUnit, getX_INLINE(), getY_INLINE(), eUnitAI, NO_DIRECTION, GC.getGameINLINE().getSorenRandNum(10000, "AI Unit Birthmark"));
FAssertMsg(pUpgradeUnit != NULL, "UpgradeUnit is not assigned a valid value");
pUpgradeUnit->joinGroup(getGroup());
pUpgradeUnit->convert(this);
pUpgradeUnit->finishMoves();
if (pUpgradeUnit->getLeaderUnitType() == NO_UNIT)
{
if (pUpgradeUnit->getExperience() > GC.getDefineINT("MAX_EXPERIENCE_AFTER_UPGRADE"))
{
pUpgradeUnit->setExperience(GC.getDefineINT("MAX_EXPERIENCE_AFTER_UPGRADE"));
}
}
// BUG - Upgrade Unit Event - start
CvEventReporter::getInstance().unitUpgraded(this, pUpgradeUnit, iPrice);
// BUG - Upgrade Unit Event - end
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/24/10 jdog5000 */
/* */
/* AI Logging */
/************************************************************************************************/
if( gUnitLogLevel > 2 )
{
CvWString szString;
getUnitAIString(szString, pUpgradeUnit->AI_getUnitAIType());
logBBAI(" %S spends %d to upgrade %S to %S, unit AI %S", GET_PLAYER(getOwnerINLINE()).getCivilizationDescription(0), upgradePrice(eUnit), getName(0).GetCString(), pUpgradeUnit->getName(0).GetCString(), szString.GetCString());
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
HandicapTypes CvUnit::getHandicapType() const
{
return GET_PLAYER(getOwnerINLINE()).getHandicapType();
}
CivilizationTypes CvUnit::getCivilizationType() const
{
return GET_PLAYER(getOwnerINLINE()).getCivilizationType();
}
const wchar* CvUnit::getVisualCivAdjective(TeamTypes eForTeam) const
{
if (getVisualOwner(eForTeam) == getOwnerINLINE())
{
return GC.getCivilizationInfo(getCivilizationType()).getAdjectiveKey();
}
return L"";
}
// Thomas SG - AC:Advanced Cargo START
//SpecialUnitTypes CvUnit::getSpecialUnitType() const
//{
// return ((SpecialUnitTypes)(m_pUnitInfo->getSpecialUnitType()));
//}
// Thomas SG - AC:Advanced Cargo END
UnitTypes CvUnit::getCaptureUnitType(CivilizationTypes eCivilization) const
{
FAssert(eCivilization != NO_CIVILIZATION);
return ((m_pUnitInfo->getUnitCaptureClassType() == NO_UNITCLASS) ? NO_UNIT : (UnitTypes)GC.getCivilizationInfo(eCivilization).getCivilizationUnits(m_pUnitInfo->getUnitCaptureClassType()));
}
UnitCombatTypes CvUnit::getUnitCombatType() const
{
return ((UnitCombatTypes)(m_pUnitInfo->getUnitCombatType()));
}
DomainTypes CvUnit::getDomainType() const
{
return ((DomainTypes)(m_pUnitInfo->getDomainType()));
}
InvisibleTypes CvUnit::getInvisibleType() const
{
return ((InvisibleTypes)(m_pUnitInfo->getInvisibleType()));
}
// Thomas SG - AC: Advanced Cargo START
SpecialUnitTypes CvUnit::getSpecialUnitType(int i) const
{
return (SpecialUnitTypes)(m_pUnitInfo->getSpecialUnitType(i));
}
int CvUnit::getNumSpecialUnitTypes() const
{
return m_pUnitInfo->getNumSpecialUnitTypes();
}
// Thomas SG - AC: Advanced Cargo END
int CvUnit::getNumSeeInvisibleTypes() const
{
return m_pUnitInfo->getNumSeeInvisibleTypes();
}
InvisibleTypes CvUnit::getSeeInvisibleType(int i) const
{
return (InvisibleTypes)(m_pUnitInfo->getSeeInvisibleType(i));
}
// Thomas SG - AC: Advanced Cargo START
int CvUnit::getNumSpecialCargos() const
{
return m_pUnitInfo->getNumSpecialCargos();
}
SpecialUnitTypes CvUnit::getSpecialCargo(int i) const
{
return (SpecialUnitTypes)(m_pUnitInfo->getSpecialCargo(i));
}
// Thomas SG - AC: Advanced Cargo END
int CvUnit::flavorValue(FlavorTypes eFlavor) const
{
return m_pUnitInfo->getFlavorValue(eFlavor);
}
bool CvUnit::isBarbarian() const
{
return GET_PLAYER(getOwnerINLINE()).isBarbarian();
}
bool CvUnit::isHuman() const
{
return GET_PLAYER(getOwnerINLINE()).isHuman();
}
int CvUnit::visibilityRange(const CvPlot* pPlot) const
{
// Super Forts begin *vision*
int iImprovementVisibilityChange = 0;
/* if(plot()->getImprovementType() != NO_IMPROVEMENT)
{
iImprovementVisibilityChange = GC.getImprovementInfo(plot()->getImprovementType()).getVisibilityChange();
}*/
return (GC.getUNIT_VISIBILITY_RANGE() + getExtraVisibilityRange() + iImprovementVisibilityChange);
// Super Forts end
/* Original
return (GC.getDefineINT("UNIT_VISIBILITY_RANGE") + getExtraVisibilityRange()); */
}
int CvUnit::baseMoves() const
{/************************************************************************************************/
/* Afforess Start 07/16/10 */
/* */
/* */
/************************************************************************************************/
/*
return (m_pUnitInfo->getMoves() + getExtraMoves() + GET_TEAM(getTeam()).getExtraMoves(getDomainType()));
*/
return (m_pUnitInfo->getMoves() + getExtraMoves() + (getDomainType() != DOMAIN_AIR ? GET_TEAM(getTeam()).getExtraMoves(getDomainType()) : 0));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
int CvUnit::maxMoves() const
{
PROFILE_FUNC();
if ( m_iMaxMoveCacheTurn != GC.getGameINLINE().getGameTurn() )
{
m_maxMoveCache = (baseMoves() * GC.getMOVE_DENOMINATOR());
m_iMaxMoveCacheTurn = GC.getGameINLINE().getGameTurn();
}
return m_maxMoveCache;
}
int CvUnit::movesLeft() const
{
return std::max(0, (maxMoves() - getMoves()));
}
bool CvUnit::canMove() const
{
if (isDead())
{
return false;
}
if (getMoves() >= maxMoves())
{
return false;
}
if (getImmobileTimer() > 0)
{
return false;
}
return true;
}
bool CvUnit::hasMoved() const
{
return (getMoves() > 0);
}
int CvUnit::airRange() const
{
/************************************************************************************************/
/* Afforess Start 08/5/10 */
/* */
/* */
/************************************************************************************************/
/*
return (m_pUnitInfo->getAirRange() + getExtraAirRange());
*/
if (getDomainType() == DOMAIN_AIR && nukeRange() == -1)
{
return (m_pUnitInfo->getAirRange() + getExtraAirRange() + GET_TEAM(getTeam()).getExtraMoves(DOMAIN_AIR));
}
return (m_pUnitInfo->getAirRange() + getExtraAirRange());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
int CvUnit::nukeRange() const
{
return m_pUnitInfo->getNukeRange();
}
// XXX should this test for coal?
bool CvUnit::canBuildRoute() const
{
int iI;
for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
{
if (GC.getBuildInfo((BuildTypes)iI).getRoute() != NO_ROUTE)
{
if (m_pUnitInfo->getBuilds(iI))
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getBuildInfo((BuildTypes)iI).getTechPrereq())))
{
return true;
}
}
}
}
return false;
}
BuildTypes CvUnit::getBuildType() const
{
BuildTypes eBuild;
if (getGroup()->headMissionQueueNode() != NULL)
{
switch (getGroup()->headMissionQueueNode()->m_data.eMissionType)
{
case MISSION_MOVE_TO:
// BUG - Sentry Actions - start
#ifdef _MOD_SENTRY
case MISSION_MOVE_TO_SENTRY:
#endif
// BUG - Sentry Actions - end
break;
case MISSION_ROUTE_TO:
if (getGroup()->getBestBuildRoute(plot(), &eBuild) != NO_ROUTE)
{
return eBuild;
}
break;
case MISSION_MOVE_TO_UNIT:
case MISSION_SKIP:
case MISSION_SLEEP:
case MISSION_FORTIFY:
case MISSION_PLUNDER:
case MISSION_AIRPATROL:
case MISSION_SEAPATROL:
case MISSION_HEAL:
case MISSION_SENTRY:
// BUG - Sentry Actions - start
#ifdef _MOD_SENTRY
case MISSION_SENTRY_WHILE_HEAL:
case MISSION_SENTRY_NAVAL_UNITS:
case MISSION_SENTRY_LAND_UNITS:
#endif
// BUG - Sentry Actions - end
case MISSION_AIRLIFT:
case MISSION_NUKE:
// < M.A.D. Nukes Start >
case MISSION_PRETARGET_NUKE:
// < M.A.D. Nukes End >
case MISSION_RECON:
case MISSION_PARADROP:
case MISSION_AIRBOMB:
case MISSION_BOMBARD:
case MISSION_RANGE_ATTACK:
case MISSION_PILLAGE:
case MISSION_SABOTAGE:
case MISSION_DESTROY:
case MISSION_STEAL_PLANS:
/************************************************************************************************/
/* Great Diplomat MOD START Stolenrays */
/************************************************************************************************/
case MISSION_BRIBE_BARBARIAN:
case MISSION_GOODWILL:
/************************************************************************************************/
/* Great Diplomat MOD END */
/************************************************************************************************/
case MISSION_FOUND:
case MISSION_SPREAD:
case MISSION_SPREAD_CORPORATION:
case MISSION_JOIN:
case MISSION_CONSTRUCT:
case MISSION_DISCOVER:
case MISSION_HURRY:
case MISSION_TRADE:
case MISSION_GREAT_WORK:
case MISSION_INFILTRATE:
case MISSION_GOLDEN_AGE:
case MISSION_LEAD:
case MISSION_ESPIONAGE:
case MISSION_DIE_ANIMATION:
// Dale - AB: Bombing START
case MISSION_AIRBOMB1:
case MISSION_AIRBOMB2:
case MISSION_AIRBOMB3:
case MISSION_AIRBOMB4:
case MISSION_AIRBOMB5:
// Dale - AB: Bombing END
// Dale - RB: Field Bombard START
case MISSION_RBOMBARD:
// Dale - RB: Field Bombard END
// Dale - ARB: Archer Bombard START
case MISSION_ABOMBARD:
// Dale - ARB: Archer Bombard END
// Dale - FE: Fighters START
case MISSION_FENGAGE:
// Dale - FE: Fighters END
/************************************************************************************************/
/* Afforess Start 06/05/10 */
/* */
/* */
/************************************************************************************************/
case MISSION_HURRY_FOOD:
case MISSION_INQUISITION:
case MISSION_CLAIM_TERRITORY:
case MISSION_ESPIONAGE_SLEEP:
case MISSION_GREAT_COMMANDER:
case MISSION_SHADOW:
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
break;
case MISSION_BUILD:
return (BuildTypes)getGroup()->headMissionQueueNode()->m_data.iData1;
break;
default:
// AIAndy: Assumed to be an outcome mission
// FAssert(false);
break;
}
}
return NO_BUILD;
}
int CvUnit::workRate(bool bMax) const
{
int iRate;
if (!bMax)
{
if (!canMove())
{
return 0;
}
}
iRate = m_pUnitInfo->getWorkRate();
iRate *= std::max(0, (GET_PLAYER(getOwnerINLINE()).getWorkerSpeedModifier() + 100));
iRate /= 100;
if (!isHuman() && !isBarbarian())
{
iRate *= std::max(0, (GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIWorkRateModifier() + 100));
iRate /= 100;
}
return iRate;
}
bool CvUnit::isAnimal() const
{
return m_pUnitInfo->isAnimal();
}
bool CvUnit::isNoBadGoodies() const
{
return m_pUnitInfo->isNoBadGoodies();
}
bool CvUnit::isOnlyDefensive() const
{
return m_pUnitInfo->isOnlyDefensive();
}
bool CvUnit::isNoCapture() const
{
return m_pUnitInfo->isNoCapture();
}
bool CvUnit::isRivalTerritory() const
{
return m_pUnitInfo->isRivalTerritory();
}
bool CvUnit::isMilitaryHappiness() const
{
return m_pUnitInfo->isMilitaryHappiness();
}
bool CvUnit::isInvestigate() const
{
return m_pUnitInfo->isInvestigate();
}
bool CvUnit::isCounterSpy() const
{
return m_pUnitInfo->isCounterSpy();
}
bool CvUnit::isSpy() const
{
return m_pUnitInfo->isSpy();
}
bool CvUnit::isFound() const
{
return m_pUnitInfo->isFound();
}
/********************************************************************************/
/** REVOLUTION_MOD 1/1/08 DPII */
/** */
/** */
/********************************************************************************/
/*
bool CvUnit::isCanBeRebel() const
{
return GC.getUnitInfo(getUnitType()).isCanBeRebel();
}
bool CvUnit::isCanRebelCapture() const
{
return GC.getUnitInfo(getUnitType()).isCanRebelCapture();
}
bool CvUnit::isCannotDefect() const
{
return GC.getUnitInfo(getUnitType()).isCannotDefect();
}
bool CvUnit::isCanQuellRebellion() const
{
return GC.getUnitInfo(getUnitType()).isCanQuellRebellion();
}
*/
/********************************************************************************/
/** REVOLUTION_MOD END */
/********************************************************************************/
bool CvUnit::isGoldenAge() const
{
if (isDelayedDeath())
{
return false;
}
return m_pUnitInfo->isGoldenAge();
}
bool CvUnit::canCoexistWithEnemyUnit(TeamTypes eTeam) const
{
if (NO_TEAM == eTeam)
{
if(alwaysInvisible())
{
return true;
}
return false;
}
if(isInvisible(eTeam, false))
{
return true;
}
return false;
}
bool CvUnit::isFighting() const
{
return (getCombatUnit() != NULL);
}
bool CvUnit::isAttacking() const
{
return (getAttackPlot() != NULL && !isDelayedDeath());
}
bool CvUnit::isDefending() const
{
return (isFighting() && !isAttacking());
}
bool CvUnit::isCombat() const
{
return (isFighting() || isAttacking());
}
int CvUnit::maxHitPoints() const
{
return GC.getMAX_HIT_POINTS();
}
int CvUnit::currHitPoints() const
{
return (AI_getPredictedHitPoints() == -1 ? maxHitPoints() - getDamage() : AI_getPredictedHitPoints());
}
bool CvUnit::isHurt() const
{
return (getDamage() > 0);
}
bool CvUnit::isDead() const
{
return (getDamage() >= maxHitPoints());
}
void CvUnit::setBaseCombatStr(int iCombat)
{
m_iBaseCombat = iCombat;
}
int CvUnit::baseCombatStr() const
{
/************************************************************************************************/
/* Afforess Start 6/7/11 */
/* */
/* */
/************************************************************************************************/
//TB Combat Mod (referred to FFHII code to see a necessary fix here thanks to Kael)
int iStr = (m_iBaseCombat + GET_TEAM(getTeam()).getUnitClassStrengthChange((UnitClassTypes)getUnitClassType()) + getExtraStrength());
if (iStr < 0)
{
iStr = 0;
}
return iStr;
//TB Combat Mod end
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
typedef struct CombatStrCacheEntry
{
int iLRUIndex;
int iResult;
const CvPlot* pPlot;
const CvPlot* pAttackedPlot;
const CvUnit* pAttacker;
const CvUnit* pForUnit;
} CombatStrCacheEntry;
#define COMBATSTR_CACHE_SIZE 100
static CombatStrCacheEntry CombatStrCache[COMBATSTR_CACHE_SIZE];
static int CombatStrCacheInitializedTurn = -1;
static int iNextCombatCacheLRU = 1;
static void FlushCombatStrCache(CvUnit* pMovingUnit)
{
if ( pMovingUnit == NULL || pMovingUnit->isCommander() )
{
memset(CombatStrCache, 0, sizeof(CombatStrCache));
CombatStrCacheInitializedTurn = GC.getGameINLINE().getGameTurn();
}
else
{
for(int iI = 0; iI < COMBATSTR_CACHE_SIZE; iI++)
{
CombatStrCacheEntry* pEntry = &CombatStrCache[iI];
if ( pEntry->pAttacker == pMovingUnit ||
pEntry->pForUnit == pMovingUnit )
{
pEntry->pForUnit = NULL;
pEntry->iLRUIndex = 1;
}
}
}
}
// maxCombatStr can be called in four different configurations
// pPlot == NULL, pAttacker == NULL for combat when this is the attacker
// pPlot valid, pAttacker valid for combat when this is the defender
/*** Dexy - Surround and Destroy START ****/
// pPlot == NULL, pAttacker valid for combat when this is the defender, attacker is just surrounding us (then defender gets no plot defensive bonuses)
/*** Dexy - Surround and Destroy END ****/
// pPlot valid, pAttacker == NULL (new case), when this is the defender, attacker unknown
// pPlot valid, pAttacker == this (new case), when the defender is unknown, but we want to calc approx str
// note, in this last case, it is expected pCombatDetails == NULL, it does not have to be, but some
// values may be unexpectedly reversed in this case (iModifierTotal will be the negative sum)
/*** Dexy - Surround and Destroy START ****/
int CvUnit::maxCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails, bool bSurroundedModifier) const
// OLD CODE
// int CvUnit::maxCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
/*** Dexy - Surround and Destroy END ****/
{
PROFILE_FUNC();
int iCombat;
int iI;
bool bInvolvesHuman = false;
if (pAttacker != NULL)
{
bInvolvesHuman = (GET_PLAYER(getOwner()).isHuman() || GET_PLAYER(pAttacker->getOwner()).isHuman());
}
else
{
bInvolvesHuman = GET_PLAYER(getOwner()).isHuman();
}
FAssertMsg((pPlot == NULL) || (pPlot->getTerrainType() != NO_TERRAIN), "(pPlot == NULL) || (pPlot->getTerrainType() is not expected to be equal with NO_TERRAIN)");
// handle our new special case
const CvPlot* pAttackedPlot = NULL;
bool bAttackingUnknownDefender = false;
if (pAttacker == this)
{
bAttackingUnknownDefender = true;
pAttackedPlot = pPlot;
// reset these values, we will fiddle with them below
pPlot = NULL;
pAttacker = NULL;
}
// otherwise, attack plot is the plot of us (the defender)
else if (pAttacker != NULL)
{
pAttackedPlot = plot();
}
CombatStrCacheEntry* pCacheEntry = NULL;
const CvUnit* pOriginalAttacker = pAttacker;
if (pCombatDetails != NULL)
{
pCombatDetails->iExtraCombatPercent = 0;
pCombatDetails->iAnimalCombatModifierTA = 0;
pCombatDetails->iAIAnimalCombatModifierTA = 0;
pCombatDetails->iAnimalCombatModifierAA = 0;
pCombatDetails->iAIAnimalCombatModifierAA = 0;
pCombatDetails->iBarbarianCombatModifierTB = 0;
pCombatDetails->iAIBarbarianCombatModifierTB = 0;
pCombatDetails->iBarbarianCombatModifierAB = 0;
pCombatDetails->iAIBarbarianCombatModifierAB = 0;
pCombatDetails->iPlotDefenseModifier = 0;
pCombatDetails->iFortifyModifier = 0;
pCombatDetails->iCityDefenseModifier = 0;
pCombatDetails->iHillsAttackModifier = 0;
pCombatDetails->iHillsDefenseModifier = 0;
pCombatDetails->iFeatureAttackModifier = 0;
pCombatDetails->iFeatureDefenseModifier = 0;
pCombatDetails->iTerrainAttackModifier = 0;
pCombatDetails->iTerrainDefenseModifier = 0;
pCombatDetails->iCityAttackModifier = 0;
pCombatDetails->iDomainDefenseModifier = 0;
pCombatDetails->iCityBarbarianDefenseModifier = 0;
pCombatDetails->iClassDefenseModifier = 0;
pCombatDetails->iClassAttackModifier = 0;
pCombatDetails->iCombatModifierA = 0;
pCombatDetails->iCombatModifierT = 0;
pCombatDetails->iDomainModifierA = 0;
pCombatDetails->iDomainModifierT = 0;
pCombatDetails->iAnimalCombatModifierA = 0;
pCombatDetails->iAnimalCombatModifierT = 0;
pCombatDetails->iRiverAttackModifier = 0;
pCombatDetails->iAmphibAttackModifier = 0;
pCombatDetails->iKamikazeModifier = 0;
pCombatDetails->iModifierTotal = 0;
pCombatDetails->iBaseCombatStr = 0;
pCombatDetails->iCombat = 0;
pCombatDetails->iMaxCombatStr = 0;
pCombatDetails->iCurrHitPoints = 0;
pCombatDetails->iMaxHitPoints = 0;
pCombatDetails->iCurrCombatStr = 0;
pCombatDetails->eOwner = getOwnerINLINE();
pCombatDetails->eVisualOwner = getVisualOwner();
pCombatDetails->sUnitName = getName().GetCString();
}
else if (baseCombatStr() == 0)
{
return 0;
}
else if ( bSurroundedModifier && !bInvolvesHuman)
{
PROFILE("maxCombatStr.Cachable");
if ( CombatStrCacheInitializedTurn != GC.getGameINLINE().getGameTurn())
{
FlushCombatStrCache(NULL);
}
int iBestLRU = MAX_INT;
for(iI = 0; iI < COMBATSTR_CACHE_SIZE; iI++)
{
CombatStrCacheEntry* pEntry = &CombatStrCache[iI];
if ( pEntry->iLRUIndex == 0 )
{
pCacheEntry = pEntry;
break;
}
else if ( pEntry->pPlot == pPlot && pEntry->pAttackedPlot == pAttackedPlot && pEntry->pAttacker == pOriginalAttacker && pEntry->pForUnit == this )
{
//OutputDebugString("maxCombatStr.CachHit\n");
PROFILE("maxCombatStr.CachHit");
pEntry->iLRUIndex = iNextCombatCacheLRU++;
return pEntry->iResult;
}
else if ( pEntry->iLRUIndex < iBestLRU )
{
iBestLRU = pEntry->iLRUIndex;
pCacheEntry = pEntry;
}
}
//char buffer[300];
//sprintf(buffer,"maxCombatStr cache miss for unit %d, attacker %d @(%d,%d)\n", getID(), pOriginalAttacker == NULL ? -1 : pOriginalAttacker->getID(), pPlot == NULL ? -1 : pPlot->getX_INLINE(), pPlot == NULL ? -1 : pPlot->getY_INLINE());
//OutputDebugString(buffer);
}
int iModifier = 0;
int iExtraModifier;
iExtraModifier = getExtraCombatPercent();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iExtraCombatPercent = iExtraModifier;
}
// do modifiers for animals and barbarians (leaving these out for bAttackingUnknownDefender case)
if (pAttacker != NULL)
{
if (isAnimal())
{
if (pAttacker->isHuman())
{
iExtraModifier = GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAnimalCombatModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAnimalCombatModifierTA = iExtraModifier;
}
}
else
{
iExtraModifier = GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIAnimalCombatModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAIAnimalCombatModifierTA = iExtraModifier;
}
}
}
if (pAttacker->isAnimal())
{
if (isHuman())
{
iExtraModifier = -GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAnimalCombatModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAnimalCombatModifierAA = iExtraModifier;
}
}
else
{
iExtraModifier = -GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIAnimalCombatModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAIAnimalCombatModifierAA = iExtraModifier;
}
}
}
if (isBarbarian())
{
if (pAttacker->isHuman())
{
iExtraModifier = GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getBarbarianCombatModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iBarbarianCombatModifierTB = iExtraModifier;
}
}
else
{
iExtraModifier = GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIBarbarianCombatModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAIBarbarianCombatModifierTB = iExtraModifier;
}
}
}
if (pAttacker->isBarbarian())
{
if (isHuman())
{
iExtraModifier = -GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getBarbarianCombatModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iBarbarianCombatModifierAB = iExtraModifier;
}
}
else
{
iExtraModifier = -GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIBarbarianCombatModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAIBarbarianCombatModifierTB = iExtraModifier;
}
}
}
}
// add defensive bonuses (leaving these out for bAttackingUnknownDefender case)
if (pPlot != NULL)
{
if (!noDefensiveBonus())
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/30/10 jdog5000 */
/* */
/* General AI */
/************************************************************************************************/
// When pAttacker is NULL but pPlot is not, this is a computation for this units defensive value
// against an unknown attacker. Always ignoring building defense in this case is a conservative estimate,
// but causes AI to suicide against castle walls of low culture cities in early game. Using this units
// ignoreBuildingDefense does a little better ... in early game it corrects undervalue of castles. One
// downside is when medieval unit is defending a walled city against gunpowder. Here, the over value
// makes attacker a little more cautious, but with their tech lead it shouldn't matter too much. Also
// makes vulnerable units (ships, etc) feel safer in this case and potentially not leave, but ships
// leave when ratio is pretty low anyway.
//iExtraModifier = pPlot->defenseModifier(getTeam(), (pAttacker != NULL) ? pAttacker->ignoreBuildingDefense() : true);
iExtraModifier = pPlot->defenseModifier(getTeam(), (pAttacker != NULL) ? pAttacker->ignoreBuildingDefense() : ignoreBuildingDefense());
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iPlotDefenseModifier = iExtraModifier;
}
}
iExtraModifier = fortifyModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iFortifyModifier = iExtraModifier;
}
if (pPlot->isCity(true, getTeam()))
{
iExtraModifier = cityDefenseModifier();
/************************************************************************************************/
/* Afforess Start 05/22/10 */
/* */
/* */
/************************************************************************************************/
if (pPlot->isCity(false))
{
iExtraModifier += pPlot->getPlotCity()->getUnitCombatExtraStrength(getUnitCombatType());
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iCityDefenseModifier = iExtraModifier;
}
}
if (pPlot->isHills())
{
iExtraModifier = hillsDefenseModifier();
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iHillsDefenseModifier = iExtraModifier;
}
}
if (pPlot->getFeatureType() != NO_FEATURE)
{
iExtraModifier = featureDefenseModifier(pPlot->getFeatureType());
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iFeatureDefenseModifier = iExtraModifier;
}
}
iExtraModifier = terrainDefenseModifier(pPlot->getTerrainType());
iModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iTerrainDefenseModifier = iExtraModifier;
}
}
// if we are attacking to an plot with an unknown defender, the calc the modifier in reverse
if (bAttackingUnknownDefender)
{
pAttacker = this;
}
// calc attacker bonueses
/************************************************************************************************/
/* UNOFFICIAL_PATCH 09/20/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original code
if (pAttacker != NULL)
*/
if (pAttacker != NULL && pAttackedPlot != NULL)
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
int iTempModifier = 0;
if (pAttackedPlot->isCity(true, getTeam()))
{
iExtraModifier = -pAttacker->cityAttackModifier();
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iCityAttackModifier = iExtraModifier;
}
if (pAttacker->isBarbarian())
{
iExtraModifier = GC.getCITY_BARBARIAN_DEFENSE_MODIFIER();
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iCityBarbarianDefenseModifier = iExtraModifier;
}
}
}
if (pAttackedPlot->isHills())
{
iExtraModifier = -pAttacker->hillsAttackModifier();
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iHillsAttackModifier = iExtraModifier;
}
}
if (pAttackedPlot->getFeatureType() != NO_FEATURE)
{
iExtraModifier = -pAttacker->featureAttackModifier(pAttackedPlot->getFeatureType());
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iFeatureAttackModifier = iExtraModifier;
}
}
else
{
iExtraModifier = -pAttacker->terrainAttackModifier(pAttackedPlot->getTerrainType());
/*** Dexy - Others' bug fixes START ****/
iTempModifier += iExtraModifier;
// OLD CODE
// iModifier += iExtraModifier;
/*** Dexy - Others' bug fixes END ****/
if (pCombatDetails != NULL)
{
pCombatDetails->iTerrainAttackModifier = iExtraModifier;
}
}
// only compute comparisions if we are the defender with a known attacker
// TB Debug: this exclusion appears to be fairly unnecessary and causes trouble for Assassins and such. The intention of it seems to be subverted in C2C in general.
// Thus we'll test disabling this exclusion entirely.
// Koshling - cannot be removed - it totally screws upteh case of evaluating our attack against a plot wheer we don;t know teh specific
// defender (we wind up evaluating our uni class bonuses against ourselves!). Put the test back - what issues does it cause for assassins??
if (!bAttackingUnknownDefender)
{
FAssertMsg(pAttacker != this, "pAttacker is not expected to be equal with this");
iExtraModifier = unitClassDefenseModifier(pAttacker->getUnitClassType());
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iClassDefenseModifier = iExtraModifier;
}
iExtraModifier = -pAttacker->unitClassAttackModifier(getUnitClassType());
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iClassAttackModifier = iExtraModifier;
}
if (pAttacker->getUnitCombatType() != NO_UNITCOMBAT)
{
iExtraModifier = unitCombatModifier(pAttacker->getUnitCombatType());
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iCombatModifierA = iExtraModifier;
}
}
if (getUnitCombatType() != NO_UNITCOMBAT)
{
iExtraModifier = -pAttacker->unitCombatModifier(getUnitCombatType());
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iCombatModifierT = iExtraModifier;
}
}
iExtraModifier = domainModifier(pAttacker->getDomainType());
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iDomainModifierA = iExtraModifier;
}
iExtraModifier = -pAttacker->domainModifier(getDomainType());
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iDomainModifierT = iExtraModifier;
}
if (pAttacker->isAnimal())
{
iExtraModifier = animalCombatModifier();
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAnimalCombatModifierA = iExtraModifier;
}
}
if (isAnimal())
{
iExtraModifier = -pAttacker->animalCombatModifier();
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAnimalCombatModifierT = iExtraModifier;
}
}
}
if (!(pAttacker->isRiver()))
{
if (pAttacker->plot()->isRiverCrossing(directionXY(pAttacker->plot(), pAttackedPlot)))
{
iExtraModifier = -GC.getRIVER_ATTACK_MODIFIER();
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iRiverAttackModifier = iExtraModifier;
}
}
}
if (!(pAttacker->isAmphib()))
{
if (!(pAttackedPlot->isWater()) && pAttacker->plot()->isWater())
{
iExtraModifier = -GC.getAMPHIB_ATTACK_MODIFIER();
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iAmphibAttackModifier = iExtraModifier;
}
}
}
if (pAttacker->getKamikazePercent() != 0)
{
/************************************************************************************************/
/* Afforess Start 02/05/10 Dexy */
/* */
/* Unofficial Patch */
/************************************************************************************************/
iExtraModifier = -pAttacker->getKamikazePercent();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iTempModifier += iExtraModifier;
if (pCombatDetails != NULL)
{
pCombatDetails->iKamikazeModifier = iExtraModifier;
}
}
/************************************************************************************************/
/* Afforess Start 02/05/10 */
/* */
/* */
/************************************************************************************************/
if (bSurroundedModifier)
{
// the stronger the surroundings -> decrease the iModifier more
iExtraModifier = -pAttacker->surroundedDefenseModifier(pAttackedPlot, bAttackingUnknownDefender ? NULL : this);
iTempModifier += iExtraModifier;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// if we are attacking an unknown defender, then use the reverse of the modifier
if (bAttackingUnknownDefender)
{
iModifier -= iTempModifier;
}
else
{
iModifier += iTempModifier;
}
}
if (pCombatDetails != NULL)
{
pCombatDetails->iModifierTotal = iModifier;
pCombatDetails->iBaseCombatStr = baseCombatStr();
}
if (iModifier > 0)
{
iCombat = (baseCombatStr() * (iModifier + 100));
}
else
{
iCombat = ((baseCombatStr() * 10000) / (100 - iModifier));
}
if (pCombatDetails != NULL)
{
pCombatDetails->iCombat = iCombat;
pCombatDetails->iMaxCombatStr = std::max(1, iCombat);
pCombatDetails->iCurrHitPoints = currHitPoints();
pCombatDetails->iMaxHitPoints = maxHitPoints();
pCombatDetails->iCurrCombatStr = ((pCombatDetails->iMaxCombatStr * pCombatDetails->iCurrHitPoints) / pCombatDetails->iMaxHitPoints);
}
if ( pCacheEntry != NULL )
{
pCacheEntry->iLRUIndex = iNextCombatCacheLRU++;
pCacheEntry->iResult = std::max(1, iCombat);
pCacheEntry->pPlot = pPlot;
pCacheEntry->pAttackedPlot = pAttackedPlot;
pCacheEntry->pAttacker = pOriginalAttacker;
pCacheEntry->pForUnit = this;
//char buffer[300];
//sprintf(buffer,"maxCombatStr cache result (%d) for unit %d, attacker %d @(%d,%d)\n", pCacheEntry->iResult, getID(), pOriginalAttacker == NULL ? -1 : pOriginalAttacker->getID(), pPlot == NULL ? -1 : pPlot->getX_INLINE(), pPlot == NULL ? -1 : pPlot->getY_INLINE());
//OutputDebugString(buffer);
}
return std::max(1, iCombat);
}
/*** Dexy - Surround and Destroy START ****/
int CvUnit::currCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails, bool bSurroundedModifier) const
{
return ((maxCombatStr(pPlot, pAttacker, pCombatDetails, bSurroundedModifier) * currHitPoints()) / maxHitPoints());
}
// OLD CODE
// int CvUnit::currCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
// {
// return ((maxCombatStr(pPlot, pAttacker, pCombatDetails) * currHitPoints()) / maxHitPoints());
// }
/*** Dexy - Surround and Destroy END ****/
int CvUnit::currFirepower(const CvPlot* pPlot, const CvUnit* pAttacker) const
{
return ((maxCombatStr(pPlot, pAttacker) + currCombatStr(pPlot, pAttacker) + 1) / 2);
}
// this nomalizes str by firepower, useful for quick odds calcs
// the effect is that a damaged unit will have an effective str lowered by firepower/maxFirepower
// doing the algebra, this means we mulitply by 1/2(1 + currHP)/maxHP = (maxHP + currHP) / (2 * maxHP)
int CvUnit::currEffectiveStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
{
int currStr = currCombatStr(pPlot, pAttacker, pCombatDetails);
currStr *= (maxHitPoints() + currHitPoints());
currStr /= (2 * maxHitPoints());
return currStr;
}
float CvUnit::maxCombatStrFloat(const CvPlot* pPlot, const CvUnit* pAttacker) const
{
return (((float)(maxCombatStr(pPlot, pAttacker))) / 100.0f);
}
float CvUnit::currCombatStrFloat(const CvPlot* pPlot, const CvUnit* pAttacker) const
{
return (((float)(currCombatStr(pPlot, pAttacker))) / 100.0f);
}
bool CvUnit::canFight() const
{
// Don't bother calculating modifiers for this call
return (m_iBaseCombat > 0);
}
bool CvUnit::canAttack() const
{
if (!canFight())
{
return false;
}
if (isOnlyDefensive())
{
return false;
}
return true;
}
bool CvUnit::canAttack(const CvUnit& defender) const
{
if (!canAttack())
{
return false;
}
if (defender.getDamage() >= combatLimit())
{
return false;
}
// Artillery can't amphibious attack
if (plot()->isWater() && !defender.plot()->isWater())
{
if (combatLimit() < 100)
{
return false;
}
}
return true;
}
bool CvUnit::canDefend(const CvPlot* pPlot) const
{
if (pPlot == NULL)
{
pPlot = plot();
}
if (!canFight())
{
return false;
}
if (!pPlot->isValidDomainForAction(*this))
{
if (GC.getLAND_UNITS_CAN_ATTACK_WATER_CITIES() == 0)
{
return false;
}
}
return true;
}
bool CvUnit::canSiege(TeamTypes eTeam) const
{
if (!canDefend())
{
return false;
}
if (!isEnemy(eTeam))
{
return false;
}
if (!isNeverInvisible())
{
return false;
}
return true;
}
int CvUnit::airBaseCombatStr() const
{
return m_pUnitInfo->getAirCombat();
}
int CvUnit::airMaxCombatStr(const CvUnit* pOther) const
{
int iModifier;
int iCombat;
if (airBaseCombatStr() == 0)
{
return 0;
}
iModifier = getExtraCombatPercent();
if (getKamikazePercent() != 0)
{
iModifier += getKamikazePercent();
}
/********************************************************************************/
/* BETTER_BTS_AI_MOD 8/16/08 DanF5771 & jdog5000 */
/* */
/* Bugfix */
/********************************************************************************/
/* original BTS code
if (getExtraCombatPercent() != 0)
{
iModifier += getExtraCombatPercent();
}
*/
// ExtraCombatPercent already counted above
/********************************************************************************/
/* BETTER_BTS_AI_MOD END */
/********************************************************************************/
if (NULL != pOther)
{
if (pOther->getUnitCombatType() != NO_UNITCOMBAT)
{
iModifier += unitCombatModifier(pOther->getUnitCombatType());
}
iModifier += domainModifier(pOther->getDomainType());
if (pOther->isAnimal())
{
iModifier += animalCombatModifier();
}
}
if (iModifier > 0)
{
iCombat = (airBaseCombatStr() * (iModifier + 100));
}
else
{
iCombat = ((airBaseCombatStr() * 10000) / (100 - iModifier));
}
return std::max(1, iCombat);
}
int CvUnit::airCurrCombatStr(const CvUnit* pOther) const
{
return ((airMaxCombatStr(pOther) * currHitPoints()) / maxHitPoints());
}
float CvUnit::airMaxCombatStrFloat(const CvUnit* pOther) const
{
return (((float)(airMaxCombatStr(pOther))) / 100.0f);
}
float CvUnit::airCurrCombatStrFloat(const CvUnit* pOther) const
{
return (((float)(airCurrCombatStr(pOther))) / 100.0f);
}
int CvUnit::combatLimit() const
{
return m_pUnitInfo->getCombatLimit();
}
int CvUnit::airCombatLimit() const
{
return m_pUnitInfo->getAirCombatLimit();
}
bool CvUnit::canAirAttack() const
{
return (airBaseCombatStr() > 0);
}
bool CvUnit::canAirDefend(const CvPlot* pPlot) const
{
if (pPlot == NULL)
{
pPlot = plot();
}
//TSHEEP - prevent spies from being used as SAMs
if(isSpy())
{
return false;
}
//TSHEEP end
if (maxInterceptionProbability() == 0)
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/30/09 Mongoose & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if (!pPlot->isValidDomainForLocation(*this))
*/
// From Mongoose SDK
// Land units which are cargo cannot intercept
if (!pPlot->isValidDomainForLocation(*this) || isCargo())
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
return false;
}
}
return true;
}
int CvUnit::airCombatDamage(const CvUnit* pDefender) const
{
CvCity* pCity;
CvPlot* pPlot;
int iOurStrength;
int iTheirStrength;
int iStrengthFactor;
int iDamage;
pPlot = pDefender->plot();
iOurStrength = airCurrCombatStr(pDefender);
FAssertMsg(iOurStrength > 0, "Air combat strength is expected to be greater than zero");
iTheirStrength = pDefender->maxCombatStr(pPlot, this);
iStrengthFactor = ((iOurStrength + iTheirStrength + 1) / 2);
iDamage = std::max(1, ((GC.getDefineINT("AIR_COMBAT_DAMAGE") * (iOurStrength + iStrengthFactor)) / (iTheirStrength + iStrengthFactor)));
pCity = pPlot->getPlotCity();
if (pCity != NULL)
{
iDamage *= std::max(0, (pCity->getAirModifier() + 100));
iDamage /= 100;
}
return iDamage;
}
int CvUnit::rangeCombatDamage(const CvUnit* pDefender) const
{
CvPlot* pPlot;
int iOurStrength;
int iTheirStrength;
int iStrengthFactor;
int iDamage;
pPlot = pDefender->plot();
iOurStrength = airCurrCombatStr(pDefender);
FAssertMsg(iOurStrength > 0, "Combat strength is expected to be greater than zero");
iTheirStrength = pDefender->maxCombatStr(pPlot, this);
iStrengthFactor = ((iOurStrength + iTheirStrength + 1) / 2);
iDamage = std::max(1, ((GC.getDefineINT("RANGE_COMBAT_DAMAGE") * (iOurStrength + iStrengthFactor)) / (iTheirStrength + iStrengthFactor)));
return iDamage;
}
CvUnit* CvUnit::bestInterceptor(const CvPlot* pPlot) const
{
CvUnit* pLoopUnit;
CvUnit* pBestUnit;
int iValue;
int iBestValue;
int iLoop;
int iI;
iBestValue = 0;
pBestUnit = NULL;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (isEnemy(GET_PLAYER((PlayerTypes)iI).getTeam()) && !isInvisible(GET_PLAYER((PlayerTypes)iI).getTeam(), false, false))
{
for(pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->canAirDefend())
{
if (!pLoopUnit->isMadeInterception())
{
if ((pLoopUnit->getDomainType() != DOMAIN_AIR) || !(pLoopUnit->hasMoved()))
{
if ((pLoopUnit->getDomainType() != DOMAIN_AIR) || (pLoopUnit->getGroup()->getActivityType() == ACTIVITY_INTERCEPT))
{
if (plotDistance(pLoopUnit->getX_INLINE(), pLoopUnit->getY_INLINE(), pPlot->getX_INLINE(), pPlot->getY_INLINE()) <= pLoopUnit->airRange())
{
iValue = pLoopUnit->currInterceptionProbability();
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestUnit = pLoopUnit;
}
}
}
}
}
}
}
}
}
}
return pBestUnit;
}
CvUnit* CvUnit::bestSeaPillageInterceptor(CvUnit* pPillager, int iMinOdds) const
{
CvUnit* pBestUnit = NULL;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */
/* */
/* Lead From Behind */
/************************************************************************************************/
// From Lead From Behind by UncutDragon
int pBestUnitRank = -1;
for (int iDX = -1; iDX <= 1; ++iDX)
{
for (int iDY = -1; iDY <= 1; ++iDY)
{
CvPlot* pLoopPlot = plotXY(pPillager->getX_INLINE(), pPillager->getY_INLINE(), iDX, iDY);
if (NULL != pLoopPlot)
{
CLLNode<IDInfo>* pUnitNode = pLoopPlot->headUnitNode();
while (NULL != pUnitNode)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (NULL != pLoopUnit)
{
if (pLoopUnit->area() == pPillager->plot()->area())
{
if (!pLoopUnit->isInvisible(getTeam(), false))
{
if (isEnemy(pLoopUnit->getTeam()))
{
if (DOMAIN_SEA == pLoopUnit->getDomainType())
{
if (ACTIVITY_PATROL == pLoopUnit->getGroup()->getActivityType())
{
// UncutDragon
/* original code
if (NULL == pBestUnit || pLoopUnit->isBetterDefenderThan(pBestUnit, this))
*/ // modified (added extra parameter)
if (NULL == pBestUnit || pLoopUnit->isBetterDefenderThan(pBestUnit, this, &pBestUnitRank))
// /UncutDragon
{
if (getCombatOdds(pPillager, pLoopUnit) < iMinOdds)
{
pBestUnit = pLoopUnit;
}
}
}
}
}
}
}
}
}
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
return pBestUnit;
}
bool CvUnit::isAutomated() const
{
return getGroup()->isAutomated();
}
bool CvUnit::isWaiting() const
{
return getGroup()->isWaiting();
}
bool CvUnit::isFortifyable() const
{
if (!canFight() || noDefensiveBonus() || ((getDomainType() != DOMAIN_LAND) && (getDomainType() != DOMAIN_IMMOBILE)))
{
return false;
}
return true;
}
int CvUnit::fortifyModifier() const
{
if (!isFortifyable())
{
return 0;
}
return (getFortifyTurns() * GC.getFORTIFY_MODIFIER_PER_TURN());
}
int CvUnit::experienceNeeded() const
{
int iExperienceNeeded = calculateExperience(getLevel(), getOwnerINLINE());
/************************************************************************************************/
/* Afforess Start 04/2/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
//adjust level thresholds to a world size.
if (isCommander())
{
// iExperienceNeeded *= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getCommandersLevelThresholdsPercent() / 100;
iExperienceNeeded *= 3;
iExperienceNeeded /= 2;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iExperienceNeeded;
}
int CvUnit::attackXPValue() const
{
return m_pUnitInfo->getXPValueAttack();
}
int CvUnit::defenseXPValue() const
{
return m_pUnitInfo->getXPValueDefense();
}
int CvUnit::maxXPValue() const
{
int iMaxValue;
iMaxValue = MAX_INT;
if (isAnimal())
{
iMaxValue = std::min(iMaxValue, GC.getDefineINT("ANIMAL_MAX_XP_VALUE"));
}
if (isBarbarian())
{
iMaxValue = std::min(iMaxValue, GC.getDefineINT("BARBARIAN_MAX_XP_VALUE"));
}
return iMaxValue;
}
int CvUnit::firstStrikes() const
{
return std::max(0, (m_pUnitInfo->getFirstStrikes() + getExtraFirstStrikes()));
}
int CvUnit::chanceFirstStrikes() const
{
return std::max(0, (m_pUnitInfo->getChanceFirstStrikes() + getExtraChanceFirstStrikes()));
}
int CvUnit::maxFirstStrikes() const
{
return (firstStrikes() + chanceFirstStrikes());
}
bool CvUnit::isRanged() const
{
int i;
CvUnitInfo * pkUnitInfo = &getUnitInfo();
for ( i = 0; i < pkUnitInfo->getGroupDefinitions(); i++ )
{
if ( !getArtInfo(i, GET_PLAYER(getOwnerINLINE()).getCurrentEra())->getActAsRanged() )
{
return false;
}
}
return true;
}
bool CvUnit::alwaysInvisible() const
{
return m_pUnitInfo->isInvisible();
}
bool CvUnit::immuneToFirstStrikes() const
{
return (m_pUnitInfo->isFirstStrikeImmune() || (getImmuneToFirstStrikesCount() > 0));
}
bool CvUnit::noDefensiveBonus() const
{
return m_pUnitInfo->isNoDefensiveBonus();
}
bool CvUnit::ignoreBuildingDefense() const
{
return m_pUnitInfo->isIgnoreBuildingDefense();
}
bool CvUnit::canMoveImpassable() const
{
return m_pUnitInfo->isCanMoveImpassable();
}
bool CvUnit::canMoveAllTerrain() const
{
return m_pUnitInfo->isCanMoveAllTerrain();
}
bool CvUnit::flatMovementCost() const
{
return m_pUnitInfo->isFlatMovementCost();
}
bool CvUnit::ignoreTerrainCost() const
{
return m_pUnitInfo->isIgnoreTerrainCost();
}
bool CvUnit::isNeverInvisible() const
{
return (!alwaysInvisible() && (getInvisibleType() == NO_INVISIBLE));
}
bool CvUnit::isInvisible(TeamTypes eTeam, bool bDebug, bool bCheckCargo) const
{
if (bDebug && GC.getGameINLINE().isDebugMode())
{
return false;
}
if (getTeam() == eTeam)
{
return false;
}
if (alwaysInvisible())
{
return true;
}
if (bCheckCargo && isCargo())
{
return true;
}
if (getInvisibleType() == NO_INVISIBLE)
{
return false;
}
return !(plot()->isInvisibleVisible(eTeam, getInvisibleType()));
}
bool CvUnit::isNukeImmune() const
{
return m_pUnitInfo->isNukeImmune();
}
/************************************************************************************************/
/* REVDCM_OC 02/16/10 phungus420 */
/* */
/* Inquisitions */
/************************************************************************************************/
bool CvUnit::isInquisitor() const
{
return m_pUnitInfo->isInquisitor();
}
/************************************************************************************************/
/* REVDCM_OC END */
/************************************************************************************************/
int CvUnit::maxInterceptionProbability(bool bIgnoreCommanders) const
{
return std::min(GC.getDefineINT("MAX_INTERCEPTION_PROBABILITY"),std::max(0, m_pUnitInfo->getInterceptionProbability() + getExtraIntercept(bIgnoreCommanders)));
}
int CvUnit::currInterceptionProbability() const
{
if (getDomainType() != DOMAIN_AIR && !GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_BETTER_INTERCETION))
{
return maxInterceptionProbability();
}
else
{
return ((maxInterceptionProbability() * currHitPoints()) / maxHitPoints());
}
}
int CvUnit::evasionProbability(bool bIgnoreCommanders) const
{
return std::min(GC.getDefineINT("MAX_EVASION_PROBABILITY"),std::max(0, m_pUnitInfo->getEvasionProbability() + getExtraEvasion(bIgnoreCommanders)));
}
int CvUnit::withdrawalProbability() const
{
if (getDomainType() == DOMAIN_LAND && plot()->isWater())
{
return 0;
}
/************************************************************************************************/
/* Afforess Start 04/02/10 */
/* */
/* */
/************************************************************************************************/
if (shouldUseWithdrawalOddsCap())
{
return std::min(GC.getDefineINT("MAX_WITHDRAWAL_PROBABILITY"), std::max(0, (m_pUnitInfo->getWithdrawalProbability() + getExtraWithdrawal())));
}
else
{
return std::max(0, (m_pUnitInfo->getWithdrawalProbability() + (((100 - m_pUnitInfo->getWithdrawalProbability()) * getExtraWithdrawal()) / 100)));
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
bool CvUnit::hasCombatType(UnitCombatTypes eCombatType) const
{
if (((getUnitCombatType() == eCombatType) || hasExtraSubCombatType(eCombatType)) && !hasRemovesUnitCombatType(eCombatType))
{
return true;
}
// AIAndy: That loop could be removed if the unit type sub combat types get added to the extra sub combat type counts
for (int iI = 0; iI < m_pUnitInfo->getNumSubCombatTypes(); iI++)
{
if (m_pUnitInfo->getSubCombatType(iI) == ((int)eCombatType))
{
return true;
}
}
return false;
}
bool CvUnit::hasSubCombatType(UnitCombatTypes eCombatType) const
{
int iI;
bool bSubCombat = false;
for (iI = 0; iI < m_pUnitInfo->getNumSubCombatTypes(); iI++)
{
if (m_pUnitInfo->getSubCombatType(iI) == ((int)eCombatType))
{
bSubCombat = true;
}
}
if ((((bSubCombat) || hasExtraSubCombatType(eCombatType)) && (m_pUnitInfo->getUnitCombatType() != eCombatType)) && !hasRemovesUnitCombatType(eCombatType))
{
return true;
}
return false;
}
int CvUnit::collateralDamage() const
{
return std::max(0, (m_pUnitInfo->getCollateralDamage()));
}
int CvUnit::collateralDamageLimit() const
{
return std::max(0, m_pUnitInfo->getCollateralDamageLimit() * GC.getMAX_HIT_POINTS() / 100);
}
int CvUnit::collateralDamageMaxUnits() const
{
return std::max(0, m_pUnitInfo->getCollateralDamageMaxUnits());
}
int CvUnit::cityAttackModifier() const
{
return (m_pUnitInfo->getCityAttackModifier() + getExtraCityAttackPercent());
}
int CvUnit::cityDefenseModifier() const
{
return (m_pUnitInfo->getCityDefenseModifier() + getExtraCityDefensePercent());
}
int CvUnit::animalCombatModifier() const
{
return m_pUnitInfo->getAnimalCombatModifier();
}
int CvUnit::hillsAttackModifier() const
{
return (m_pUnitInfo->getHillsAttackModifier() + getExtraHillsAttackPercent());
}
int CvUnit::hillsDefenseModifier() const
{
return (m_pUnitInfo->getHillsDefenseModifier() + getExtraHillsDefensePercent());
}
int CvUnit::terrainAttackModifier(TerrainTypes eTerrain) const
{
FAssertMsg(eTerrain >= 0, "eTerrain is expected to be non-negative (invalid Index)");
FAssertMsg(eTerrain < GC.getNumTerrainInfos(), "eTerrain is expected to be within maximum bounds (invalid Index)");
return (m_pUnitInfo->getTerrainAttackModifier(eTerrain) + getExtraTerrainAttackPercent(eTerrain));
}
int CvUnit::terrainDefenseModifier(TerrainTypes eTerrain) const
{
FAssertMsg(eTerrain >= 0, "eTerrain is expected to be non-negative (invalid Index)");
FAssertMsg(eTerrain < GC.getNumTerrainInfos(), "eTerrain is expected to be within maximum bounds (invalid Index)");
return (m_pUnitInfo->getTerrainDefenseModifier(eTerrain) + getExtraTerrainDefensePercent(eTerrain));
}
int CvUnit::featureAttackModifier(FeatureTypes eFeature) const
{
FAssertMsg(eFeature >= 0, "eFeature is expected to be non-negative (invalid Index)");
FAssertMsg(eFeature < GC.getNumFeatureInfos(), "eFeature is expected to be within maximum bounds (invalid Index)");
return (m_pUnitInfo->getFeatureAttackModifier(eFeature) + getExtraFeatureAttackPercent(eFeature));
}
int CvUnit::featureDefenseModifier(FeatureTypes eFeature) const
{
FAssertMsg(eFeature >= 0, "eFeature is expected to be non-negative (invalid Index)");
FAssertMsg(eFeature < GC.getNumFeatureInfos(), "eFeature is expected to be within maximum bounds (invalid Index)");
return (m_pUnitInfo->getFeatureDefenseModifier(eFeature) + getExtraFeatureDefensePercent(eFeature));
}
int CvUnit::unitClassAttackModifier(UnitClassTypes eUnitClass) const
{
FAssertMsg(eUnitClass >= 0, "eUnitClass is expected to be non-negative (invalid Index)");
FAssertMsg(eUnitClass < GC.getNumUnitClassInfos(), "eUnitClass is expected to be within maximum bounds (invalid Index)");
return m_pUnitInfo->getUnitClassAttackModifier(eUnitClass);
}
int CvUnit::unitClassDefenseModifier(UnitClassTypes eUnitClass) const
{
FAssertMsg(eUnitClass >= 0, "eUnitClass is expected to be non-negative (invalid Index)");
FAssertMsg(eUnitClass < GC.getNumUnitClassInfos(), "eUnitClass is expected to be within maximum bounds (invalid Index)");
return m_pUnitInfo->getUnitClassDefenseModifier(eUnitClass);
}
int CvUnit::unitCombatModifier(UnitCombatTypes eUnitCombat) const
{
FAssertMsg(eUnitCombat >= 0, "eUnitCombat is expected to be non-negative (invalid Index)");
FAssertMsg(eUnitCombat < GC.getNumUnitCombatInfos(), "eUnitCombat is expected to be within maximum bounds (invalid Index)");
return (m_pUnitInfo->getUnitCombatModifier(eUnitCombat) + getExtraUnitCombatModifier(eUnitCombat));
}
int CvUnit::domainModifier(DomainTypes eDomain) const
{
FAssertMsg(eDomain >= 0, "eDomain is expected to be non-negative (invalid Index)");
FAssertMsg(eDomain < NUM_DOMAIN_TYPES, "eDomain is expected to be within maximum bounds (invalid Index)");
return (m_pUnitInfo->getDomainModifier(eDomain) + getExtraDomainModifier(eDomain));
}
int CvUnit::bombardRate() const
{
int iData = m_pUnitInfo->getBombardRate();//Unit base.
iData += getExtraBombardRate();
return std::max(0, iData);
}
int CvUnit::airBombBaseRate() const
{
return m_pUnitInfo->getBombRate();
}
int CvUnit::airBombCurrRate() const
{
return ((airBombBaseRate() * currHitPoints()) / maxHitPoints());
}
// Thomas SG - AC: Advanced Cargo START
//SpecialUnitTypes CvUnit::specialCargo() const
//{
// return ((SpecialUnitTypes)(m_pUnitInfo->getSpecialCargo()));
//}
int CvUnit::getSpecialCargoSpace(int iI) const
{
return (m_pUnitInfo->getSpecialCargoSpace(iI));
}
int CvUnit::getTotalSpecialCargoSpace() const
{
return (m_pUnitInfo->getTotalSpecialCargoSpace());
}
// Thomas SG - AC: Advanced Cargo END
DomainTypes CvUnit::domainCargo() const
{
return ((DomainTypes)(m_pUnitInfo->getDomainCargo()));
}
int CvUnit::cargoSpace() const
{
return m_iCargoCapacity;
}
void CvUnit::changeCargoSpace(int iChange)
{
if (iChange != 0)
{
m_iCargoCapacity += iChange;
FAssert(m_iCargoCapacity >= 0);
setInfoBarDirty(true);
}
}
// Thomas SG - AC: Advanced Cargo START
int CvUnit::totalCargoSpace() const
{
return m_iTotalCargoCapacity;
}
void CvUnit::changeTotalCargoSpace(int iChange)
{
if (iChange != 0)
{
m_iTotalCargoCapacity += iChange;
FAssert(m_iTotalCargoCapacity >= 0);
setInfoBarDirty(true);
}
}
// Thomas SG - AC: Advanced Cargo END
bool CvUnit::isFull() const
{
// Thomas SG - AC: Advanced Cargo START
return (getCargo() >= totalCargoSpace());
// Thomas SG - AC: Advanced Cargo END
}
// Thomas SG - AC: Advanced Cargo START
int CvUnit::cargoSpaceAvailable(SpecialUnitTypes eSpecialCargo, DomainTypes eDomainCargo) const
{
{
if (totalCargoSpace() <= 0)
{
return 0;
}
bool set = false;
if (eSpecialCargo != NO_SPECIALUNIT)
{
if (getNumSpecialCargos() > 0)
{
for (int iI = 0; iI < getNumSpecialCargos(); iI++)
{
if (getSpecialCargo(iI) == eSpecialCargo)
{
set = true;
}
}
}
}
if (eDomainCargo == domainCargo())
{
set = true;
}
if (!set)
{
return 0;
}
if (totalCargoSpace() > 0 && getTotalSpecialCargoSpace() == 0)
{
if (domainCargo() != eDomainCargo)
{
return 0;
}
else
{
return std::max(0, (totalCargoSpace() - getCargo()));
}
}
int FreeCargoSpace = 0;
if (!hasCargo())
{
for (int iI = 0; iI < getNumSpecialCargos(); iI++)
{
if (getSpecialCargo(iI) == eSpecialCargo)
{
FreeCargoSpace += getSpecialCargoSpace(iI);
}
}
if (domainCargo() == eDomainCargo)
{
FreeCargoSpace += totalCargoSpace();
for (int iI = 0; iI < getNumSpecialCargos(); iI++)
{
if (getSpecialCargo(iI) != eSpecialCargo)
{
FreeCargoSpace -= getSpecialCargoSpace(iI);
}
}
}
return FreeCargoSpace;
}
// aUnits - Vektor mit allen geladenen Einheiten
std::vector<CvUnit*> aUnits;
aUnits.clear();
if (hasCargo())
{
CvPlot* pPlot = plot();
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTransportUnit() == this)
{
aUnits.push_back(pLoopUnit);
}
}
}
// aCargoVolumes - Vektor mit allen Kapazitäten der Frachträume
std::vector<int> aCargoVolumes;
aCargoVolumes.clear();
if (getNumSpecialCargos() > 0)
{
for (int iI = 0; iI < getNumSpecialCargos(); iI++)
{
aCargoVolumes.push_back(getSpecialCargoSpace(iI));
}
}
int CargoDomain = totalCargoSpace();
if (getNumSpecialCargos() > 0)
{
for (int iI = 0; iI < getNumSpecialCargos(); iI++)
{
CargoDomain -= getSpecialCargoSpace(iI);
}
}
aCargoVolumes.push_back(CargoDomain);
// Schaffe nun temp. Vektor mit Kapazitäten ohne Einheiten die kein Spezial treffen
// Schaffe nun temp. Vektor mit den übrigen Einheiten
std::vector<CvUnit*> aUnitsTemp;
aUnitsTemp.clear();
std::vector<int> aCargoVolumesTemp;
aCargoVolumesTemp.clear();
if (aUnits.size() > 0)
{
int size = aUnits.size();
for (int iI = 0; iI < size; iI++)
{
bool bSpecialCargo = false;
for (int iJ = 0; iJ < getNumSpecialCargos(); iJ++)
{
if (bSpecialCargo == false)
{
for (int iL = 0; iL < aUnits[iI]->getNumSpecialUnitTypes();iL++)
{
if (aUnits[iI]->getSpecialUnitType(iL) == getSpecialCargo(iJ))
{
bSpecialCargo = true;
}
}
}
}
if (bSpecialCargo == false)
{
int size = aCargoVolumes.size()-1;
for (int iK = 0; iK < size; iK++)
{
aCargoVolumesTemp.push_back(aCargoVolumes[iK]);
}
aCargoVolumesTemp.push_back(aCargoVolumes[aCargoVolumes.size()-1]-1);
aCargoVolumes = aCargoVolumesTemp;
aCargoVolumesTemp.clear();
}
// neuer Kapazitätenvektor fertig
if (bSpecialCargo == true)
{
aUnitsTemp.push_back(aUnits[iI]);
}
}
aUnits = aUnitsTemp;
aUnitsTemp.clear();
}
// neuer Einheitenvektor fertig
if (aUnits.size() > 0)
{
int size = aUnits.size();
for (int iI = 0; iI < size; iI++)
{
if (aUnits[iI]->getDomainType() == domainCargo())
{
aUnitsTemp.push_back(aUnits[iI]);
}
else
{
int size2 = aCargoVolumes.size()-1;
for (int iJ = 0; iJ < size2; iJ++)
{
bool doit = true;
for (int iK = 0;iK < aUnits[iI]->getNumSpecialUnitTypes();iK++)
{
if (doit == true)
{
if (getSpecialCargo(iJ) == aUnits[iI]->getSpecialUnitType(iK))
{
aCargoVolumesTemp.push_back(aCargoVolumes[iJ]-1);
doit = false;
}
}
}
if (doit == true)
{
aCargoVolumesTemp.push_back(aCargoVolumes[iJ]);
}
}
aCargoVolumesTemp.push_back(aCargoVolumes[aCargoVolumes.size()-1]);
aCargoVolumes = aCargoVolumesTemp;
aCargoVolumesTemp.clear();
}
}
aUnits = aUnitsTemp;
aUnitsTemp.clear();
}
// Habe nun die Vektoren nach Entfernung von nur Domain getroffen oder nur Spezial getroffen
// Sortiere die verbliebenen nun zunächst in Spezial ein (falls noch möglich), sofern sie nicht dem Spezial der angefragten Einheit entsprechen
if (aUnits.size() > 0)
{
int size = aUnits.size();
for (int iI = 0; iI < size; iI++)
{
bool doit = true;
for (int iJ = 0;iJ < aUnits[iI]->getNumSpecialUnitTypes();iJ++)
{
if (doit == true)
{
if (aUnits[iI]->getSpecialUnitType(iJ) == eSpecialCargo)
{
doit = false;
aUnitsTemp.push_back(aUnits[iI]);
}
}
}
if (doit == true)
{
int ReduceGeneralCargoSpace = 0;
for (int iJ = 0; iJ < getNumSpecialCargos(); iJ++)
{
bool doit = true;
for (int iK = 0;iK < aUnits[iI]->getNumSpecialUnitTypes();iK++)
{
if (doit = true)
{
if (aUnits[iI]->getSpecialUnitType(iK) == getSpecialCargo(iJ))
{
doit = false;
}
}
}
if (doit == true)
{
aCargoVolumesTemp.push_back(aCargoVolumes[iJ]);
}
bool doit2 = true;
bool doit3 = true;
if (doit == false)
{
for (int iK = 0;iK < aUnits[iI]->getNumSpecialUnitTypes();iK++)
{
if (doit2 = true)
{
if (aUnits[iI]->getSpecialUnitType(iK) != getSpecialCargo(iJ))
{
doit2 = false;
}
}
}
if (doit2 == true)
{
if (aCargoVolumes[iJ] > 0)
{
aCargoVolumesTemp.push_back(aCargoVolumes[iJ]-1);
}
else
{
doit3 = false;
}
}
}
if (doit3 == false)
{
aCargoVolumesTemp.push_back(aCargoVolumes[iJ]);
ReduceGeneralCargoSpace++;
}
}
aCargoVolumesTemp.push_back(aCargoVolumes[getNumSpecialCargos()]-ReduceGeneralCargoSpace);
aCargoVolumes = aCargoVolumesTemp;
aCargoVolumesTemp.clear();
}
}
aUnits = aUnitsTemp;
aUnitsTemp.clear();
}
int UnitsLeft = aUnits.size();
int SpecialSpace = 0;
int DomainSpace = 0;
for (int iI = 0; iI < getNumSpecialCargos(); iI++)
{
if (getSpecialCargo(iI) == eSpecialCargo)
{
SpecialSpace = aCargoVolumes[iI];
}
}
DomainSpace = aCargoVolumes[getNumSpecialCargos()];
if (eDomainCargo != domainCargo())
{
if (UnitsLeft > DomainSpace)
{
return (SpecialSpace + DomainSpace - UnitsLeft);
}
else
{
return (SpecialSpace);
}
}
else
{
return (SpecialSpace + DomainSpace - UnitsLeft);
}
return 0;
}
}
// Thomas SG - AC: Advanced Cargo END
bool CvUnit::hasCargo() const
{
return (getCargo() > 0);
}
bool CvUnit::canCargoAllMove() const
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pPlot;
pPlot = plot();
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTransportUnit() == this)
{
if (pLoopUnit->getDomainType() == DOMAIN_LAND)
{
if (!(pLoopUnit->canMove()))
{
return false;
}
}
}
}
return true;
}
bool CvUnit::canCargoEnterArea(TeamTypes eTeam, const CvArea* pArea, bool bIgnoreRightOfPassage) const
{
CvPlot* pPlot = plot();
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTransportUnit() == this)
{
if (!pLoopUnit->canEnterArea(eTeam, pArea, bIgnoreRightOfPassage))
{
return false;
}
}
}
return true;
}
int CvUnit::getUnitAICargo(UnitAITypes eUnitAI) const
{
int iCount = 0;
std::vector<CvUnit*> aCargoUnits;
getCargoUnits(aCargoUnits);
for (uint i = 0; i < aCargoUnits.size(); ++i)
{
if (aCargoUnits[i]->AI_getUnitAIType() == eUnitAI)
{
++iCount;
}
}
return iCount;
}
int CvUnit::getID() const
{
return m_iID;
}
int CvUnit::getIndex() const
{
return (getID() & FLTA_INDEX_MASK);
}
IDInfo CvUnit::getIDInfo() const
{
IDInfo unit(getOwnerINLINE(), getID());
return unit;
}
void CvUnit::setID(int iID)
{
m_iID = iID;
}
int CvUnit::getGroupID() const
{
return m_iGroupID;
}
bool CvUnit::isInGroup() const
{
return(getGroupID() != FFreeList::INVALID_INDEX);
}
bool CvUnit::isGroupHead() const // XXX is this used???
{
return (getGroup()->getHeadUnit() == this);
}
CvSelectionGroup* CvUnit::getGroup() const
{
return GET_PLAYER(getOwnerINLINE()).getSelectionGroup(getGroupID());
}
bool CvUnit::canJoinGroup(const CvPlot* pPlot, CvSelectionGroup* pSelectionGroup) const
{
CvUnit* pHeadUnit;
// do not allow someone to join a group that is about to be split apart
// this prevents a case of a never-ending turn
if (pSelectionGroup->AI_isForceSeparate())
{
return false;
}
if (pSelectionGroup->getOwnerINLINE() == NO_PLAYER)
{
pHeadUnit = pSelectionGroup->getHeadUnit();
if (pHeadUnit != NULL)
{
if (pHeadUnit->getOwnerINLINE() != getOwnerINLINE())
{
return false;
}
}
}
else
{
if (pSelectionGroup->getOwnerINLINE() != getOwnerINLINE())
{
return false;
}
}
if (pSelectionGroup->getNumUnits() > 0)
{
if (!(pSelectionGroup->atPlot(pPlot)))
{
return false;
}
// Can't join a group that is loaded onto a transport as this
// would bypass the transport's record of what units it has on
// board
if (pSelectionGroup->getHeadUnit()->isCargo())
{
return false;
}
if (pSelectionGroup->getDomainType() != getDomainType())
{
return false;
}
}
return true;
}
void CvUnit::joinGroup(CvSelectionGroup* pSelectionGroup, bool bRemoveSelected, bool bRejoin)
{
PROFILE_FUNC();
CvSelectionGroup* pOldSelectionGroup;
CvSelectionGroup* pNewSelectionGroup;
CvPlot* pPlot;
pOldSelectionGroup = GET_PLAYER(getOwnerINLINE()).getSelectionGroup(getGroupID());
//Afforess retain automation type
AutomateTypes eOldAutotype = NO_AUTOMATE;
if (pOldSelectionGroup != NULL)
{
eOldAutotype = pOldSelectionGroup->getAutomateType();
}
if ((pSelectionGroup != pOldSelectionGroup) || (pOldSelectionGroup == NULL))
{
pPlot = plot();
if (pSelectionGroup != NULL)
{
pNewSelectionGroup = pSelectionGroup;
}
else
{
if (bRejoin)
{
pNewSelectionGroup = GET_PLAYER(getOwnerINLINE()).addSelectionGroup();
if (pNewSelectionGroup != NULL)
{
pNewSelectionGroup->init(pNewSelectionGroup->getID(), getOwnerINLINE());
}
else
{
FAssert(pNewSelectionGroup != NULL);
}
}
else
{
pNewSelectionGroup = NULL;
}
}
if ((pNewSelectionGroup == NULL) || canJoinGroup(pPlot, pNewSelectionGroup))
{
if (pOldSelectionGroup != NULL)
{
bool bWasHead = false;
if (!isHuman())
{
if (pOldSelectionGroup->getNumUnits() > 1)
{
if (pOldSelectionGroup->getHeadUnit() == this)
{
bWasHead = true;
}
}
}
pOldSelectionGroup->removeUnit(this);
// if we were the head, if the head unitAI changed, then force the group to separate (non-humans)
if (bWasHead)
{
FAssert(pOldSelectionGroup->getHeadUnit() != NULL);
if (pOldSelectionGroup->getHeadUnit()->AI_getUnitAIType() != AI_getUnitAIType())
{
pOldSelectionGroup->AI_makeForceSeparate();
}
}
}
if (pNewSelectionGroup != NULL)
{
// Normal rules apply when we join someone else's group unless
// the priority chnage was actually to DOWNgrade our priority
if ( AI_groupFirstVal() != LEADER_PRIORITY_MIN )
{
AI_setLeaderPriority(-1);
}
m_iGroupID = pNewSelectionGroup->getID();
if ( !pNewSelectionGroup->addUnit(this, false) )
{
m_iGroupID = FFreeList::INVALID_INDEX;
}
}
else
{
// Normal rules apply when we are alone again
AI_setLeaderPriority(-1);
m_iGroupID = FFreeList::INVALID_INDEX;
}
if (getGroup() != NULL)
{
if (getGroup()->getNumUnits() > 1 )
{
if ( getGroup()->canAllMove() )
{
getGroup()->setActivityType(ACTIVITY_AWAKE);
}
}
else
{
GET_PLAYER(getOwnerINLINE()).updateGroupCycle(this, false);
}
}
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
if (pPlot != NULL)
{
pPlot->setFlagDirty(true);
}
}
if (pPlot == gDLL->getInterfaceIFace()->getSelectionPlot())
{
gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
}
}
if (bRemoveSelected)
{
if (IsSelected())
{
gDLL->getInterfaceIFace()->removeFromSelectionList(this);
}
}
}
if (eOldAutotype != NO_AUTOMATE)
{
if (getGroup() != NULL)
{
getGroup()->setAutomateType(eOldAutotype);
}
}
}
int CvUnit::getHotKeyNumber()
{
return m_iHotKeyNumber;
}
void CvUnit::setHotKeyNumber(int iNewValue)
{
CvUnit* pLoopUnit;
int iLoop;
FAssert(getOwnerINLINE() != NO_PLAYER);
if (getHotKeyNumber() != iNewValue)
{
if (iNewValue != -1)
{
for(pLoopUnit = GET_PLAYER(getOwnerINLINE()).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER(getOwnerINLINE()).nextUnit(&iLoop))
{
if (pLoopUnit->getHotKeyNumber() == iNewValue)
{
pLoopUnit->setHotKeyNumber(-1);
}
}
}
m_iHotKeyNumber = iNewValue;
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
}
}
}
int CvUnit::getViewportX() const
{
CvViewport* pCurrentViewPort = GC.getCurrentViewport();
FAssert(pCurrentViewPort != NULL);
return pCurrentViewPort->getViewportXFromMapX(m_iX);
}
int CvUnit::getViewportY() const
{
CvViewport* pCurrentViewPort = GC.getCurrentViewport();
FAssert(pCurrentViewPort != NULL);
return pCurrentViewPort->getViewportYFromMapY(m_iY);
}
bool CvUnit::isInViewport(void) const
{
return GC.getCurrentViewport()->isInViewport(m_iX, m_iY);
}
bool CvUnit::isTempUnit(void) const
{
return GET_PLAYER(getOwnerINLINE()).isTempUnit(this);
}
void CvUnit::setXY(int iX, int iY, bool bGroup, bool bUpdate, bool bShow, bool bCheckPlotVisible)
{
PROFILE_FUNC();
CLLNode<IDInfo>* pUnitNode;
CvCity* pOldCity;
CvCity* pNewCity;
CvCity* pWorkingCity;
CvUnit* pTransportUnit;
CvUnit* pLoopUnit;
CvPlot* pOldPlot;
CvPlot* pNewPlot;
CvPlot* pLoopPlot;
CLinkList<IDInfo> oldUnits;
ActivityTypes eOldActivityType;
int iI;
// Temp units do not really exist, and are just used to provide a data anchor for
// virtual pathing calculations. As such they do not need to process their position into
// the wider game state (and indeed should not without additional concurrency protection)
if ( isTempUnit() )
{
m_iX = iX;
m_iY = iY;
if ( getGroup() == NULL )
{
joinGroup(NULL);
}
return;
}
// OOS!! Temporary for Out-of-Sync madness debugging...
if (GC.getLogging())
{
PROFILE("CvUnit::setXY.OOSLogging");
if (gDLL->getChtLvl() > 0)
{
char szOut[1024];
sprintf(szOut, "Player %d Unit %d (%S's %S) moving from %d:%d to %d:%d\n", getOwnerINLINE(), getID(), GET_PLAYER(getOwnerINLINE()).getNameKey(), getName().GetCString(), getX_INLINE(), getY_INLINE(), iX, iY);
gDLL->messageControlLog(szOut);
}
}
FAssert(!at(iX, iY));
FAssert(!isFighting());
FAssert((iX == INVALID_PLOT_COORD) || (GC.getMapINLINE().plotINLINE(iX, iY)->getX_INLINE() == iX));
FAssert((iY == INVALID_PLOT_COORD) || (GC.getMapINLINE().plotINLINE(iX, iY)->getY_INLINE() == iY));
if (getGroup() != NULL)
{
eOldActivityType = getGroup()->getActivityType();
}
else
{
eOldActivityType = NO_ACTIVITY;
}
setBlockading(false);
if (!bGroup || isCargo())
{
bShow = false;
}
// Koshling - Forcing the unit into a new group causes rapid cycling through the group id
// space, which is a scaling issue, so only do it when necessary
// Note - it used o do this unconditionally for cargo and changing that behaviour
// might be dangerous, but it solves some scaling problems and I cannot think of a reason why
// it should be problematics, nor is it causing any isues in test cases I have tried
if ( !bGroup && (getGroup() == NULL || getGroup()->getNumUnits() > 1) )
{
joinGroup(NULL, true);
}
pNewPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (pNewPlot != NULL)
{
PROFILE("CvUnit::setXY.NewPlot");
pTransportUnit = getTransportUnit();
if (pTransportUnit != NULL)
{
if (!(pTransportUnit->atPlot(pNewPlot)))
{
setTransportUnit(NULL);
}
}
if (canFight())
{
oldUnits.clear();
pUnitNode = pNewPlot->headUnitNode();
while (pUnitNode != NULL)
{
oldUnits.insertAtEnd(pUnitNode->m_data);
pUnitNode = pNewPlot->nextUnitNode(pUnitNode);
}
pUnitNode = oldUnits.head();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = oldUnits.next(pUnitNode);
if (pLoopUnit != NULL)
{
if (isEnemy(pLoopUnit->getTeam(), pNewPlot) || pLoopUnit->isEnemy(getTeam()))
{
if (!pLoopUnit->canCoexistWithEnemyUnit(getTeam()))
{
if (NO_UNITCLASS == pLoopUnit->getUnitInfo().getUnitCaptureClassType() && pLoopUnit->canDefend(pNewPlot))
{
pLoopUnit->jumpToNearestValidPlot(); // can kill unit
}
else
{
if (!m_pUnitInfo->isHiddenNationality() && !pLoopUnit->getUnitInfo().isHiddenNationality())
{
GET_TEAM(pLoopUnit->getTeam()).changeWarWeariness(getTeam(), *pNewPlot, GC.getDefineINT("WW_UNIT_CAPTURED"));
GET_TEAM(getTeam()).changeWarWeariness(pLoopUnit->getTeam(), *pNewPlot, GC.getDefineINT("WW_CAPTURED_UNIT"));
GET_TEAM(getTeam()).AI_changeWarSuccess(pLoopUnit->getTeam(), GC.getDefineINT("WAR_SUCCESS_UNIT_CAPTURING"));
}
if (!isNoCapture())
{
pLoopUnit->setCapturingPlayer(getOwnerINLINE());
}
pLoopUnit->kill(false, getOwnerINLINE(), true);
}
}
}
}
}
}
if (pNewPlot->isGoody(getTeam()))
{
GET_PLAYER(getOwnerINLINE()).doGoody(pNewPlot, this);
}
pNewPlot->area()->changeUnitsPerPlayer(getOwnerINLINE(), 1);
pNewPlot->area()->changePower(getOwnerINLINE(), m_pUnitInfo->getPowerValue());
if (AI_getUnitAIType() != NO_UNITAI)
{
pNewPlot->area()->changeNumAIUnits(getOwnerINLINE(), AI_getUnitAIType(), 1);
}
if (isAnimal())
{
pNewPlot->area()->changeAnimalsPerPlayer(getOwnerINLINE(), 1);
}
if (pNewPlot->getTeam() != getTeam() && (pNewPlot->getTeam() == NO_TEAM || !GET_TEAM(pNewPlot->getTeam()).isVassal(getTeam())))
{
GET_PLAYER(getOwnerINLINE()).changeNumOutsideUnits(1);
}
}
pOldPlot = plot();
if (pOldPlot != NULL)
{
PROFILE("CvUnit::setXY.OldPlot");
pOldPlot->removeUnit(this, bUpdate && !hasCargo());
pOldPlot->changeAdjacentSight(getTeam(), visibilityRange(pOldPlot), false, this, true);
pOldPlot->area()->changeUnitsPerPlayer(getOwnerINLINE(), -1);
pOldPlot->area()->changePower(getOwnerINLINE(), -(m_pUnitInfo->getPowerValue()));
if (AI_getUnitAIType() != NO_UNITAI)
{
pOldPlot->area()->changeNumAIUnits(getOwnerINLINE(), AI_getUnitAIType(), -1);
}
if (isAnimal())
{
pOldPlot->area()->changeAnimalsPerPlayer(getOwnerINLINE(), -1);
}
if (pOldPlot->getTeam() != getTeam() && (pOldPlot->getTeam() == NO_TEAM || !GET_TEAM(pOldPlot->getTeam()).isVassal(getTeam())))
{
GET_PLAYER(getOwnerINLINE()).changeNumOutsideUnits(-1);
}
setLastMoveTurn(GC.getGameINLINE().getTurnSlice());
pOldCity = pOldPlot->getPlotCity();
if (pOldCity != NULL)
{
if (isMilitaryHappiness())
{
pOldCity->changeMilitaryHappinessUnits(-1);
}
pOldCity->noteUnitMoved(this);
}
pWorkingCity = pOldPlot->getWorkingCity();
if (pWorkingCity != NULL)
{
if (canSiege(pWorkingCity->getTeam()))
{
pWorkingCity->AI_setAssignWorkDirty(true);
}
}
if (pOldPlot->isWater())
{
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(pOldPlot->getX_INLINE(), pOldPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
if (pLoopPlot->isWater())
{
pWorkingCity = pLoopPlot->getWorkingCity();
if (pWorkingCity != NULL)
{
if (canSiege(pWorkingCity->getTeam()))
{
pWorkingCity->AI_setAssignWorkDirty(true);
}
}
}
}
}
}
if (pOldPlot->isActiveVisible(true))
{
pOldPlot->updateMinimapColor();
}
if (pOldPlot == gDLL->getInterfaceIFace()->getSelectionPlot())
{
gDLL->getInterfaceIFace()->verifyPlotListColumn();
gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
}
}
if (pNewPlot != NULL)
{
m_iX = pNewPlot->getX_INLINE();
m_iY = pNewPlot->getY_INLINE();
}
else
{
m_iX = INVALID_PLOT_COORD;
m_iY = INVALID_PLOT_COORD;
}
FAssertMsg(plot() == pNewPlot, "plot is expected to equal pNewPlot");
if (pNewPlot != NULL)
{
PROFILE("CvUnit::setXY.NewPlot2");
pNewCity = pNewPlot->getPlotCity();
if (pNewCity != NULL)
{
PROFILE("CvUnit::setXY.NewPlot2.NewCity");
if (isEnemy(pNewCity->getTeam()) && !canCoexistWithEnemyUnit(pNewCity->getTeam()) && canFight())
{
GET_TEAM(getTeam()).changeWarWeariness(pNewCity->getTeam(), *pNewPlot, GC.getDefineINT("WW_CAPTURED_CITY"));
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 06/14/09 jdog5000 */
/* */
/* General AI */
/************************************************************************************************/
/* original bts code
GET_TEAM(getTeam()).AI_changeWarSuccess(pNewCity->getTeam(), GC.getDefineINT("WAR_SUCCESS_CITY_CAPTURING"));
*/
// Double war success if capturing capital city, always a significant blow to enemy
// pNewCity still points to old city here, hasn't been acquired yet
GET_TEAM(getTeam()).AI_changeWarSuccess(pNewCity->getTeam(), (pNewCity->isCapital() ? 2 : 1)*GC.getWAR_SUCCESS_CITY_CAPTURING());
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
PlayerTypes eNewOwner = GET_PLAYER(getOwnerINLINE()).pickConqueredCityOwner(*pNewCity);
if (NO_PLAYER != eNewOwner)
{
GET_PLAYER(eNewOwner).acquireCity(pNewCity, true, false, true); // will delete the pointer
pNewCity = NULL;
}
}
else
{
pNewCity->noteUnitMoved(this);
}
}
/************************************************************************************************/
/* Afforess Start 02/15/10 */
/* */
/* */
/************************************************************************************************/
if (pNewPlot->isActsAsCity() && !m_pUnitInfo->isHiddenNationality())
{
PROFILE("CvUnit::setXY.NewPlot2.ActAsCity");
bool bDoAcquireFort = false;
if (pNewPlot->getOwnerINLINE() == NO_PLAYER)
{
bDoAcquireFort = true;
}
else
{
CvPlayer& pNewPlotOwner = GET_PLAYER(pNewPlot->getOwnerINLINE());
if ((isEnemy(pNewPlotOwner.getTeam()) || !pNewPlotOwner.isAlive()) && !canCoexistWithEnemyUnit(pNewPlotOwner.getTeam()) && canFight())
{
bDoAcquireFort = true;
}
}
if (bDoAcquireFort)
{
GET_PLAYER(getOwnerINLINE()).acquireFort(pNewPlot);
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
//update facing direction
if(pOldPlot != NULL)
{
DirectionTypes newDirection = estimateDirection(pOldPlot, pNewPlot);
if(newDirection != NO_DIRECTION)
m_eFacingDirection = newDirection;
}
//update cargo mission animations
if (isCargo())
{
PROFILE("CvUnit::setXY.NewPlot2.Cargo");
if (eOldActivityType != ACTIVITY_MISSION)
{
getGroup()->setActivityType(eOldActivityType);
}
}
setFortifyTurns(0);
pNewPlot->changeAdjacentSight(getTeam(), visibilityRange(pNewPlot), true, this, true); // needs to be here so that the square is considered visible when we move into it...
pNewPlot->addUnit(this, bUpdate && !hasCargo());
if (shouldLoadOnMove(pNewPlot))
{
PROFILE("CvUnit::setXY.NewPlot2.Load");
load();
}
for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
PROFILE("CvUnit::setXY.NewPlot2.Meet");
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (!isInvisible(((TeamTypes)iI), false))
{
if (pNewPlot->isVisible((TeamTypes)iI, false))
{
GET_TEAM((TeamTypes)iI).meet(getTeam(), true);
}
}
}
}
pNewCity = pNewPlot->getPlotCity();
if (pNewCity != NULL)
{
if (isMilitaryHappiness())
{
pNewCity->changeMilitaryHappinessUnits(1);
}
}
pWorkingCity = pNewPlot->getWorkingCity();
if (pWorkingCity != NULL)
{
PROFILE("CvUnit::setXY.NewPlot2.WorkingCity");
if (canSiege(pWorkingCity->getTeam()))
{
pWorkingCity->verifyWorkingPlot(pWorkingCity->getCityPlotIndex(pNewPlot));
}
}
if (pNewPlot->isWater())
{
PROFILE("CvUnit::setXY.NewPlot2.Water");
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(pNewPlot->getX_INLINE(), pNewPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
if (pLoopPlot->isWater())
{
pWorkingCity = pLoopPlot->getWorkingCity();
if (pWorkingCity != NULL)
{
if (canSiege(pWorkingCity->getTeam()))
{
pWorkingCity->verifyWorkingPlot(pWorkingCity->getCityPlotIndex(pLoopPlot));
}
}
}
}
}
}
if (pNewPlot->isActiveVisible(true))
{
PROFILE("CvUnit::setXY.NewPlot2.Minimap");
pNewPlot->updateMinimapColor();
}
if (GC.IsGraphicsInitialized() && isInViewport())
{
PROFILE("CvUnit::setXY.NewPlot2.Visibility");
//override bShow if check plot visible
if(bCheckPlotVisible && pNewPlot->isVisibleToWatchingHuman())
bShow = true;
if (bShow)
{
QueueMove(pNewPlot);
}
else
{
SetPosition(pNewPlot);
}
}
if (pNewPlot == gDLL->getInterfaceIFace()->getSelectionPlot())
{
PROFILE("CvUnit::setXY.NewPlot2.Selection");
gDLL->getInterfaceIFace()->verifyPlotListColumn();
gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
}
}
if (pOldPlot != NULL)
{
PROFILE("CvUnit::setXY.OldPlot2");
if (hasCargo())
{
pUnitNode = pOldPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pOldPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTransportUnit() == this)
{
pLoopUnit->setXY(iX, iY, bGroup, false);
}
}
#ifdef _DEBUG
pUnitNode = pOldPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pOldPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTransportUnit() == this)
{
pLoopUnit->getGroup()->validateLocations();
}
}
#endif
}
}
if (bUpdate)// && hasCargo())
{
PROFILE("CvUnit::setXY.updateCenter");
if (pOldPlot != NULL)
{
pOldPlot->updateCenterUnit();
pOldPlot->setFlagDirty(true);
}
if (pNewPlot != NULL)
{
pNewPlot->updateCenterUnit();
pNewPlot->setFlagDirty(true);
}
}
bool bFarMove;
if ( pOldPlot == NULL || pNewPlot == NULL )
{
bFarMove = true;
}
else
{
bFarMove = (stepDistance(pOldPlot->getX_INLINE(),
pOldPlot->getY_INLINE(),
pNewPlot->getX_INLINE(),
pNewPlot->getY_INLINE()) > 3);
}
FAssert(pOldPlot != pNewPlot);
GET_PLAYER(getOwnerINLINE()).updateGroupCycle(this, bFarMove);
if ( pNewPlot != NULL &&
((g_bUseDummyEntities &&
(pOldPlot != NULL && pOldPlot->isActiveVisible(false)) != (pNewPlot != NULL && pNewPlot->isActiveVisible(false))) ||
((pOldPlot != NULL && pOldPlot->isInViewport()) != pNewPlot->isInViewport())) )
{
reloadEntity();
}
setInfoBarDirty(true);
if (IsSelected())
{
PROFILE("CvUnit::setXY.IsSelected");
if (isFound())
{
gDLL->getInterfaceIFace()->setDirty(GlobeLayer_DIRTY_BIT, true);
if ( !isUsingDummyEntities() && isInViewport() )
{
gDLL->getEngineIFace()->updateFoundingBorder();
}
}
gDLL->getInterfaceIFace()->setDirty(ColoredPlots_DIRTY_BIT, true);
}
//update glow
if (pNewPlot != NULL)
{
PROFILE("CvUnit::setXY.UpdateGlow");
if ( !isUsingDummyEntities() && isInViewport() )
{
gDLL->getEntityIFace()->updateEnemyGlow(getUnitEntity());
}
}
// report event to Python, along with some other key state
CvEventReporter::getInstance().unitSetXY(pNewPlot, this);
}
bool CvUnit::at(int iX, int iY) const
{
return((getX_INLINE() == iX) && (getY_INLINE() == iY));
}
bool CvUnit::atPlot(const CvPlot* pPlot) const
{
return (plot() == pPlot);
}
CvPlot* CvUnit::plot() const
{
return GC.getMapINLINE().plotSorenINLINE(getX_INLINE(), getY_INLINE());
}
CvPlot* CvUnit::plotExternal() const
{
FAssert ( isInViewport() );
return GC.getMapINLINE().plotSorenINLINE(getX_INLINE(), getY_INLINE());
}
int CvUnit::getArea() const
{
return plot()->getArea();
}
CvArea* CvUnit::area() const
{
return plot()->area();
}
bool CvUnit::onMap() const
{
return (plot() != NULL);
}
int CvUnit::getLastMoveTurn() const
{
return m_iLastMoveTurn;
}
void CvUnit::setLastMoveTurn(int iNewValue)
{
m_iLastMoveTurn = iNewValue;
FAssert(getLastMoveTurn() >= 0);
}
CvPlot* CvUnit::getReconPlot() const
{
return GC.getMapINLINE().plotSorenINLINE(m_iReconX, m_iReconY);
}
void CvUnit::setReconPlot(CvPlot* pNewValue)
{
CvPlot* pOldPlot;
pOldPlot = getReconPlot();
if (pOldPlot != pNewValue)
{
if (pOldPlot != NULL)
{
pOldPlot->changeAdjacentSight(getTeam(), GC.getRECON_VISIBILITY_RANGE(), false, this, true);
pOldPlot->changeReconCount(-1); // changeAdjacentSight() tests for getReconCount()
}
if (pNewValue == NULL)
{
m_iReconX = INVALID_PLOT_COORD;
m_iReconY = INVALID_PLOT_COORD;
}
else
{
m_iReconX = pNewValue->getX_INLINE();
m_iReconY = pNewValue->getY_INLINE();
pNewValue->changeReconCount(1); // changeAdjacentSight() tests for getReconCount()
pNewValue->changeAdjacentSight(getTeam(), GC.getRECON_VISIBILITY_RANGE(), true, this, true);
}
}
}
int CvUnit::getGameTurnCreated() const
{
return m_iGameTurnCreated;
}
void CvUnit::setGameTurnCreated(int iNewValue)
{
m_iGameTurnCreated = iNewValue;
FAssert(getGameTurnCreated() >= 0);
}
int CvUnit::getDamage() const
{
return m_iDamage;
}
void CvUnit::setupPreCombatDamage(void)
{
m_iPreCombatDamage = m_iDamage;
}
int CvUnit::getPreCombatDamage(void) const
{
return m_iPreCombatDamage;
}
void CvUnit::setDamage(int iNewValue, PlayerTypes ePlayer, bool bNotifyEntity)
{
int iOldValue;
iOldValue = getDamage();
m_iDamage = range(iNewValue, 0, maxHitPoints());
FAssertMsg(currHitPoints() >= 0, "currHitPoints() is expected to be non-negative (invalid Index)");
if (iOldValue != getDamage())
{
if (GC.getGameINLINE().isFinalInitialized() && bNotifyEntity)
{
NotifyEntity(MISSION_DAMAGE);
}
setInfoBarDirty(true);
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
}
if (plot() == gDLL->getInterfaceIFace()->getSelectionPlot())
{
gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
}
}
if (isDead())
{
kill(true, ePlayer);
}
}
void CvUnit::changeDamage(int iChange, PlayerTypes ePlayer)
{
setDamage((getDamage() + iChange), ePlayer);
}
int CvUnit::getMoves() const
{
/************************************************************************************************/
/* Afforess Start 12/13/09 */
/* */
/* */
/************************************************************************************************/
int iMoves = m_iMoves;
iMoves *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getUnitMovementPercent() + 100;
iMoves /= 100;
return iMoves;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
void CvUnit::setMoves(int iNewValue)
{
CvPlot* pPlot;
if (getMoves() != iNewValue)
{
pPlot = plot();
m_iMoves = iNewValue;
FAssert(getMoves() >= 0);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
if (pPlot != NULL)
{
pPlot->setFlagDirty(true);
}
}
if (IsSelected())
{
gDLL->getFAStarIFace()->ForceReset(&GC.getInterfacePathFinder());
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
}
if (pPlot == gDLL->getInterfaceIFace()->getSelectionPlot())
{
gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
}
}
}
void CvUnit::changeMoves(int iChange)
{
setMoves(getMoves() + iChange);
}
void CvUnit::finishMoves()
{
setMoves(maxMoves());
}
/************************************************************************************************/
/* Afforess Start 04/25/10 */
/* */
/* */
/************************************************************************************************/
int CvUnit::getExperience100() const
{
return m_iExperience;
}
void CvUnit::setExperience100(int iNewValue, int iMax)
{
if ((getExperience100() != iNewValue) && (getExperience100() < ((iMax == -1) ? MAX_INT : iMax)))
{
m_iExperience = std::min(((iMax == -1) ? MAX_INT : iMax), iNewValue);
FAssert(getExperience100() >= 0);
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
}
}
}
void CvUnit::changeExperience100(int iChange, int iMax, bool bFromCombat, bool bInBorders, bool bUpdateGlobal)
{
int iUnitExperience = iChange;
if (bFromCombat)
{
CvPlayer& kPlayer = GET_PLAYER(getOwnerINLINE());
int iCombatExperienceMod = 100 + kPlayer.getGreatGeneralRateModifier();
if (bInBorders)
{
iCombatExperienceMod += kPlayer.getDomesticGreatGeneralRateModifier() + kPlayer.getExpInBorderModifier();
iUnitExperience += (iChange * kPlayer.getExpInBorderModifier()) / 100;
}
if (bUpdateGlobal)
{
kPlayer.changeFractionalCombatExperience((iChange * iCombatExperienceMod) / 100);
}
if (getExperiencePercent() != 0)
{
iUnitExperience *= std::max(0, 100 + getExperiencePercent());
iUnitExperience /= 100;
}
/* Great Commanders */
CvUnit* pCommander = getUsedCommander();
if (pCommander != NULL && bFromCombat)
{
pCommander->setExperience100(pCommander->getExperience100() + 100); //1 xp every time
}
}
setExperience100((getExperience100() + iUnitExperience), iMax);
}
int CvUnit::getExperience() const
{
return getExperience100() / 100;
}
void CvUnit::setExperience(int iNewValue, int iMax)
{
setExperience100(iNewValue * 100, iMax > 0 && iMax != MAX_INT ? iMax * 100 : -1);
}
void CvUnit::changeExperience(int iChange, int iMax, bool bFromCombat, bool bInBorders, bool bUpdateGlobal)
{
changeExperience100(iChange * 100, iMax > 0 && iMax != MAX_INT ? iMax * 100 : -1, bFromCombat, bInBorders, bUpdateGlobal);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int CvUnit::getLevel() const
{
return m_iLevel;
}
void CvUnit::setLevel(int iNewValue)
{
if (getLevel() != iNewValue)
{
m_iLevel = iNewValue;
FAssert(getLevel() >= 0);
if (getLevel() > GET_PLAYER(getOwnerINLINE()).getHighestUnitLevel())
{
GET_PLAYER(getOwnerINLINE()).setHighestUnitLevel(getLevel());
}
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
}
}
}
void CvUnit::changeLevel(int iChange)
{
setLevel(getLevel() + iChange);
}
int CvUnit::getCargo() const
{
return m_iCargo;
}
void CvUnit::changeCargo(int iChange)
{
m_iCargo += iChange;
FAssert(getCargo() >= 0);
}
void CvUnit::getCargoUnits(std::vector<CvUnit*>& aUnits) const
{
aUnits.clear();
if (hasCargo())
{
CvPlot* pPlot = plot();
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTransportUnit() == this)
{
aUnits.push_back(pLoopUnit);
}
}
}
FAssert(getCargo() == aUnits.size());
}
CvPlot* CvUnit::getAttackPlot() const
{
return GC.getMapINLINE().plotSorenINLINE(m_iAttackPlotX, m_iAttackPlotY);
}
void CvUnit::setAttackPlot(const CvPlot* pNewValue, bool bAirCombat)
{
if (getAttackPlot() != pNewValue)
{
if (pNewValue != NULL)
{
m_iAttackPlotX = pNewValue->getX_INLINE();
m_iAttackPlotY = pNewValue->getY_INLINE();
}
else
{
m_iAttackPlotX = INVALID_PLOT_COORD;
m_iAttackPlotY = INVALID_PLOT_COORD;
}
}
m_bAirCombat = bAirCombat;
}
bool CvUnit::isAirCombat() const
{
return m_bAirCombat;
}
int CvUnit::getCombatTimer() const
{
return m_iCombatTimer;
}
void CvUnit::setCombatTimer(int iNewValue)
{
m_iCombatTimer = iNewValue;
FAssert(getCombatTimer() >= 0);
}
void CvUnit::changeCombatTimer(int iChange)
{
setCombatTimer(getCombatTimer() + iChange);
}
int CvUnit::getCombatFirstStrikes() const
{
return m_iCombatFirstStrikes;
}
void CvUnit::setCombatFirstStrikes(int iNewValue)
{
m_iCombatFirstStrikes = iNewValue;
FAssert(getCombatFirstStrikes() >= 0);
}
void CvUnit::changeCombatFirstStrikes(int iChange)
{
setCombatFirstStrikes(getCombatFirstStrikes() + iChange);
}
int CvUnit::getFortifyTurns() const
{
return m_iFortifyTurns;
}
void CvUnit::setFortifyTurns(int iNewValue)
{
iNewValue = range(iNewValue, 0, GC.getDefineINT("MAX_FORTIFY_TURNS"));
if (iNewValue != getFortifyTurns())
{
m_iFortifyTurns = iNewValue;
setInfoBarDirty(true);
}
}
void CvUnit::changeFortifyTurns(int iChange)
{
setFortifyTurns(getFortifyTurns() + iChange);
}
int CvUnit::getBlitzCount() const
{
return m_iBlitzCount;
}
bool CvUnit::isBlitz() const
{
return (getBlitzCount() > 0);
}
void CvUnit::changeBlitzCount(int iChange)
{
m_iBlitzCount += iChange;
FAssert(getBlitzCount() >= 0);
}
int CvUnit::getAmphibCount() const
{
return m_iAmphibCount;
}
bool CvUnit::isAmphib() const
{
return (getAmphibCount() > 0);
}
void CvUnit::changeAmphibCount(int iChange)
{
m_iAmphibCount += iChange;
FAssert(getAmphibCount() >= 0);
}
int CvUnit::getRiverCount() const
{
return m_iRiverCount;
}
bool CvUnit::isRiver() const
{
return (getRiverCount() > 0);
}
void CvUnit::changeRiverCount(int iChange)
{
m_iRiverCount += iChange;
FAssert(getRiverCount() >= 0);
}
int CvUnit::getEnemyRouteCount() const
{
return m_iEnemyRouteCount;
}
bool CvUnit::isEnemyRoute() const
{
return (getEnemyRouteCount() > 0);
}
void CvUnit::changeEnemyRouteCount(int iChange)
{
m_iEnemyRouteCount += iChange;
FAssert(getEnemyRouteCount() >= 0);
}
int CvUnit::getAlwaysHealCount() const
{
return m_iAlwaysHealCount;
}
bool CvUnit::isAlwaysHeal() const
{
return (getAlwaysHealCount() > 0);
}
void CvUnit::changeAlwaysHealCount(int iChange)
{
m_iAlwaysHealCount += iChange;
FAssert(getAlwaysHealCount() >= 0);
}
int CvUnit::getHillsDoubleMoveCount() const
{
return m_iHillsDoubleMoveCount;
}
bool CvUnit::isHillsDoubleMove() const
{
return (getHillsDoubleMoveCount() > 0);
}
void CvUnit::changeHillsDoubleMoveCount(int iChange)
{
m_iHillsDoubleMoveCount += iChange;
FAssert(getHillsDoubleMoveCount() >= 0);
}
int CvUnit::getImmuneToFirstStrikesCount() const
{
return m_iImmuneToFirstStrikesCount;
}
void CvUnit::changeImmuneToFirstStrikesCount(int iChange)
{
m_iImmuneToFirstStrikesCount += iChange;
FAssert(getImmuneToFirstStrikesCount() >= 0);
}
int CvUnit::getOneUpCount() const
{
return m_iOneUpCount;
}
bool CvUnit::isOneUp() const
{
return (getOneUpCount() > 0);
}
void CvUnit::changeOneUpCount(int iChange)
{
m_iOneUpCount += iChange;
FAssert(getOneUpCount() >= 0);
}
int CvUnit::getSurvivorChance() const
{
return m_iSurvivorChance;
}
void CvUnit::changeSurvivorChance(int iChange)
{
if (iChange != 0)
{
m_iSurvivorChance += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraVisibilityRange() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraVisibilityRange + pCommander->getExtraVisibilityRange();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraVisibilityRange;
}
void CvUnit::changeExtraVisibilityRange(int iChange)
{
if (iChange != 0)
{
plot()->changeAdjacentSight(getTeam(), visibilityRange(), false, this, true);
m_iExtraVisibilityRange += iChange;
FAssert(getExtraVisibilityRange() >= 0);
plot()->changeAdjacentSight(getTeam(), visibilityRange(), true, this, true);
}
}
int CvUnit::getExtraMoves() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraMoves + pCommander->getExtraMoves();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraMoves;
}
void CvUnit::changeExtraMoves(int iChange)
{
m_iExtraMoves += iChange;
FAssert(getExtraMoves() >= 0);
}
int CvUnit::getExtraMoveDiscount() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraMoveDiscount + pCommander->getExtraMoveDiscount();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraMoveDiscount;
}
void CvUnit::changeExtraMoveDiscount(int iChange)
{
m_iExtraMoveDiscount += iChange;
FAssert(getExtraMoveDiscount() >= 0);
}
int CvUnit::getExtraAirRange() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraAirRange + pCommander->getExtraAirRange();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraAirRange;
}
void CvUnit::changeExtraAirRange(int iChange)
{
m_iExtraAirRange += iChange;
}
int CvUnit::getExtraIntercept(bool bIgnoreCommanders) const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!bIgnoreCommanders && !isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraIntercept + pCommander->getExtraIntercept();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraIntercept;
}
void CvUnit::changeExtraIntercept(int iChange)
{
m_iExtraIntercept += iChange;
}
int CvUnit::getExtraEvasion(bool bIgnoreCommanders) const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!bIgnoreCommanders && !isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraEvasion + pCommander->getExtraEvasion();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraEvasion;
}
void CvUnit::changeExtraEvasion(int iChange)
{
m_iExtraEvasion += iChange;
}
int CvUnit::getExtraFirstStrikes() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraFirstStrikes + pCommander->getExtraFirstStrikes();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraFirstStrikes;
}
void CvUnit::changeExtraFirstStrikes(int iChange)
{
m_iExtraFirstStrikes += iChange;
FAssert(getExtraFirstStrikes() >= 0);
}
int CvUnit::getExtraChanceFirstStrikes() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraChanceFirstStrikes + pCommander->getExtraChanceFirstStrikes();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraChanceFirstStrikes;
}
void CvUnit::changeExtraChanceFirstStrikes(int iChange)
{
m_iExtraChanceFirstStrikes += iChange;
FAssert(getExtraChanceFirstStrikes() >= 0);
}
int CvUnit::getExtraWithdrawal(bool bIgnoreCommanders) const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!bIgnoreCommanders && !isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
{
return m_iExtraWithdrawal + pCommander->getExtraWithdrawal();
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraWithdrawal;
}
void CvUnit::changeExtraWithdrawal(int iChange)
{
m_iExtraWithdrawal += iChange;
FAssert(getExtraWithdrawal() >= 0);
}
int CvUnit::getExtraCollateralDamage() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraCollateralDamage + pCommander->getExtraCollateralDamage();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraCollateralDamage;
}
void CvUnit::changeExtraCollateralDamage(int iChange)
{
m_iExtraCollateralDamage += iChange;
FAssert(getExtraCollateralDamage() >= 0);
}
int CvUnit::getExtraBombardRate() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraBombardRate + pCommander->getExtraBombardRate();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraBombardRate;
}
void CvUnit::changeExtraBombardRate(int iChange)
{
m_iExtraBombardRate += iChange;
FAssert(getExtraBombardRate() >= 0);
}
int CvUnit::getExtraEnemyHeal() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraEnemyHeal + pCommander->getExtraEnemyHeal();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraEnemyHeal;
}
void CvUnit::changeExtraEnemyHeal(int iChange)
{
m_iExtraEnemyHeal += iChange;
FAssert(getExtraEnemyHeal() >= 0);
}
int CvUnit::getExtraNeutralHeal() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraNeutralHeal + pCommander->getExtraNeutralHeal();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraNeutralHeal;
}
void CvUnit::changeExtraNeutralHeal(int iChange)
{
m_iExtraNeutralHeal += iChange;
FAssert(getExtraNeutralHeal() >= 0);
}
int CvUnit::getExtraFriendlyHeal() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraFriendlyHeal + pCommander->getExtraFriendlyHeal();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraFriendlyHeal;
}
void CvUnit::changeExtraFriendlyHeal(int iChange)
{
m_iExtraFriendlyHeal += iChange;
FAssert(getExtraFriendlyHeal() >= 0);
}
int CvUnit::getSameTileHeal() const
{
return m_iSameTileHeal;
}
void CvUnit::changeSameTileHeal(int iChange)
{
m_iSameTileHeal += iChange;
FAssert(getSameTileHeal() >= 0);
}
int CvUnit::getAdjacentTileHeal() const
{
return m_iAdjacentTileHeal;
}
void CvUnit::changeAdjacentTileHeal(int iChange)
{
m_iAdjacentTileHeal += iChange;
FAssert(getAdjacentTileHeal() >= 0);
}
int CvUnit::getExtraCombatPercent() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraCombatPercent + pCommander->getExtraCombatPercent();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraCombatPercent;
}
void CvUnit::changeExtraCombatPercent(int iChange)
{
if (iChange != 0)
{
m_iExtraCombatPercent += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraCityAttackPercent() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraCityAttackPercent + pCommander->getExtraCityAttackPercent();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraCityAttackPercent;
}
void CvUnit::changeExtraCityAttackPercent(int iChange)
{
if (iChange != 0)
{
m_iExtraCityAttackPercent += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraCityDefensePercent() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraCityDefensePercent + pCommander->getExtraCityDefensePercent();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraCityDefensePercent;
}
void CvUnit::changeExtraCityDefensePercent(int iChange)
{
if (iChange != 0)
{
m_iExtraCityDefensePercent += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraHillsAttackPercent() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraHillsAttackPercent + pCommander->getExtraHillsAttackPercent();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraHillsAttackPercent;
}
void CvUnit::changeExtraHillsAttackPercent(int iChange)
{
if (iChange != 0)
{
m_iExtraHillsAttackPercent += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraHillsDefensePercent() const
{
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExtraHillsDefensePercent + pCommander->getExtraHillsDefensePercent();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExtraHillsDefensePercent;
}
void CvUnit::changeExtraHillsDefensePercent(int iChange)
{
if (iChange != 0)
{
m_iExtraHillsDefensePercent += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getRevoltProtection() const
{
return m_iRevoltProtection;
}
void CvUnit::changeRevoltProtection(int iChange)
{
if (iChange != 0)
{
m_iRevoltProtection += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getCollateralDamageProtection() const
{
return m_iCollateralDamageProtection;
}
void CvUnit::changeCollateralDamageProtection(int iChange)
{
if (iChange != 0)
{
m_iCollateralDamageProtection += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getPillageChange() const
{
return m_iPillageChange;
}
void CvUnit::changePillageChange(int iChange)
{
if (iChange != 0)
{
m_iPillageChange += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getUpgradeDiscount() const
{
return m_iUpgradeDiscount;
}
void CvUnit::changeUpgradeDiscount(int iChange)
{
if (iChange != 0)
{
m_iUpgradeDiscount += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExperiencePercent() const
{
/************************************************************************************************/
/* Afforess Start 05/4/10 */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander())
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_iExperiencePercent + pCommander->getExperiencePercent();
}
else
{
return 0; //Great Commanders can not gain XP faster
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_iExperiencePercent;
}
void CvUnit::changeExperiencePercent(int iChange)
{
if (iChange != 0)
{
m_iExperiencePercent += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getKamikazePercent() const
{
return m_iKamikazePercent;
}
void CvUnit::changeKamikazePercent(int iChange)
{
if (iChange != 0)
{
m_iKamikazePercent += iChange;
setInfoBarDirty(true);
}
}
DirectionTypes CvUnit::getFacingDirection(bool checkLineOfSightProperty) const
{
if (checkLineOfSightProperty)
{
if (m_pUnitInfo->isLineOfSight())
{
return m_eFacingDirection; //only look in facing direction
}
else
{
return NO_DIRECTION; //look in all directions
}
}
else
{
return m_eFacingDirection;
}
}
void CvUnit::setFacingDirection(DirectionTypes eFacingDirection)
{
if (eFacingDirection != m_eFacingDirection)
{
if (m_pUnitInfo->isLineOfSight())
{
//remove old fog
plot()->changeAdjacentSight(getTeam(), visibilityRange(), false, this, true);
//change direction
m_eFacingDirection = eFacingDirection;
//clear new fog
plot()->changeAdjacentSight(getTeam(), visibilityRange(), true, this, true);
gDLL->getInterfaceIFace()->setDirty(ColoredPlots_DIRTY_BIT, true);
}
else
{
m_eFacingDirection = eFacingDirection;
}
//update formation
NotifyEntity(NO_MISSION);
}
}
void CvUnit::rotateFacingDirectionClockwise()
{
//change direction
DirectionTypes eNewDirection = (DirectionTypes) ((m_eFacingDirection + 1) % NUM_DIRECTION_TYPES);
setFacingDirection(eNewDirection);
}
void CvUnit::rotateFacingDirectionCounterClockwise()
{
//change direction
DirectionTypes eNewDirection = (DirectionTypes) ((m_eFacingDirection + NUM_DIRECTION_TYPES - 1) % NUM_DIRECTION_TYPES);
setFacingDirection(eNewDirection);
}
int CvUnit::getImmobileTimer() const
{
return m_iImmobileTimer;
}
void CvUnit::setImmobileTimer(int iNewValue)
{
if (iNewValue != getImmobileTimer())
{
m_iImmobileTimer = iNewValue;
setInfoBarDirty(true);
}
}
void CvUnit::changeImmobileTimer(int iChange)
{
if (iChange != 0)
{
setImmobileTimer(std::max(0, getImmobileTimer() + iChange));
}
}
//Team Project (2)
/*****************************************************************************************************/
/** Author: TheLadiesOgre **/
/** Date: 21.09.2009 **/
/** ModComp: TLOTags **/
/** Reason Added: New Bool Flags **/
/** Notes: **/
/*****************************************************************************************************/
bool CvUnit::isCanRespawn() const
{
return m_bCanRespawn;
}
void CvUnit::setCanRespawn(bool bNewValue)
{
m_bCanRespawn = bNewValue;
}
bool CvUnit::isSurvivor() const
{
return m_bSurvivor;
}
void CvUnit::setSurvivor(bool bNewValue)
{
m_bSurvivor = bNewValue;
}
/*****************************************************************************************************/
/** TheLadiesOgre; 21.09.2009; TLOTags **/
/*****************************************************************************************************/
bool CvUnit::isMadeAttack() const
{
return m_bMadeAttack;
}
void CvUnit::setMadeAttack(bool bNewValue)
{
m_bMadeAttack = bNewValue;
}
bool CvUnit::isMadeInterception() const
{
return m_bMadeInterception;
}
void CvUnit::setMadeInterception(bool bNewValue)
{
m_bMadeInterception = bNewValue;
}
bool CvUnit::isPromotionReady() const
{
return m_bPromotionReady;
}
void CvUnit::setPromotionReady(bool bNewValue)
{
if (isPromotionReady() != bNewValue)
{
m_bPromotionReady = bNewValue;
/************************************************************************************************/
/* Afforess Start 09/16/10 */
/* */
/* Advanced Automations */
/************************************************************************************************/
if ( !isUsingDummyEntities() && isInViewport())
{
gDLL->getEntityIFace()->showPromotionGlow(getUnitEntity(), bNewValue);
}
if (m_bPromotionReady)
{
if (isAutoPromoting())
{
AI_promote();
setPromotionReady(false);
testPromotionReady();
}
else
{
MissionAITypes eMissionAI = getGroup()->AI_getMissionAIType();
// Don't interrupt units on their way to delivery or rally plots
if ( (MISSIONAI_CONTRACT != eMissionAI && MISSIONAI_CONTRACT_UNIT != eMissionAI) ||
getGroup()->AI_getMissionAIPlot() == plot() )
{
getGroup()->setAutomateType(NO_AUTOMATE);
getGroup()->clearMissionQueue();
getGroup()->setActivityType(ACTIVITY_AWAKE);
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
}
}
}
void CvUnit::testPromotionReady()
{
//TB Combat Mod
bool bPromotionReady = false;
if (getExperience() >= experienceNeeded() && canAcquirePromotionAny())
{
logBBAI("Exp for promotion present (%d vs %d)\n", getExperience(), experienceNeeded());
bPromotionReady = true;
}
if (getRetrainsAvailable() > 0 && canAcquirePromotionAny())
{
logBBAI("Retrain available (%d left)\n", getRetrainsAvailable());
bPromotionReady = true;
}
setPromotionReady(bPromotionReady);
//TB Combat Mod end
}
bool CvUnit::isDelayedDeath() const
{
return m_bDeathDelay;
}
void CvUnit::startDelayedDeath()
{
m_bDeathDelay = true;
}
// Returns true if killed...
bool CvUnit::doDelayedDeath()
{
// Koshling - added 'isDead' check to clean up units with 100% damage that have somehow
// been left behind
if ((m_bDeathDelay || isDead()) && !isFighting())
{
killUnconditional(false, NO_PLAYER, true);
return true;
}
return false;
}
bool CvUnit::isCombatFocus() const
{
return m_bCombatFocus;
}
bool CvUnit::isInfoBarDirty() const
{
return m_bInfoBarDirty;
}
void CvUnit::setInfoBarDirty(bool bNewValue)
{
m_bInfoBarDirty = bNewValue;
}
bool CvUnit::isBlockading() const
{
return m_bBlockading;
}
void CvUnit::setBlockading(bool bNewValue)
{
if (bNewValue != isBlockading())
{
m_bBlockading = bNewValue;
updatePlunder(isBlockading() ? 1 : -1, true);
}
}
void CvUnit::collectBlockadeGold()
{
if (plot()->getTeam() == getTeam())
{
return;
}
int iBlockadeRange = GC.getDefineINT("SHIP_BLOCKADE_RANGE");
for (int i = -iBlockadeRange; i <= iBlockadeRange; ++i)
{
for (int j = -iBlockadeRange; j <= iBlockadeRange; ++j)
{
CvPlot* pLoopPlot = ::plotXY(getX_INLINE(), getY_INLINE(), i, j);
if (NULL != pLoopPlot && pLoopPlot->isRevealed(getTeam(), false))
{
CvCity* pCity = pLoopPlot->getPlotCity();
if (NULL != pCity && !pCity->isPlundered() && isEnemy(pCity->getTeam()) && !atWar(pCity->getTeam(), getTeam()))
{
if (pCity->area() == area() || pCity->plot()->isAdjacentToArea(area()))
{
int iGold = 0;
if (GC.getMAX_TRADE_ROUTES() > 0)
{
iGold += pCity->calculateTradeProfit(pCity) * pCity->getTradeRoutes();
}
iGold += pCity->getForeignConnectednessCommerce() * 33 + pCity->getDomesticConnectednessCommerce() * 20;
iGold /= 100;
/************************************************************************************************/
/* Afforess Start 06/20/10 */
/* */
/* */
/************************************************************************************************/
iGold *= (100 + GET_PLAYER(getOwnerINLINE()).calculateInflationRate());
iGold /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iGold > 0)
{
pCity->setPlundered(true);
GET_PLAYER(getOwnerINLINE()).changeGold(iGold);
GET_PLAYER(pCity->getOwnerINLINE()).changeGold(-iGold);
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_TRADE_ROUTE_PLUNDERED", getNameKey(), pCity->getNameKey(), iGold);
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BUILD_BANK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_TRADE_ROUTE_PLUNDER", getNameKey(), pCity->getNameKey(), iGold);
AddDLLMessage(pCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BUILD_BANK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE());
}
}
}
}
}
}
}
PlayerTypes CvUnit::getOwner() const
{
return getOwnerINLINE();
}
PlayerTypes CvUnit::getVisualOwner(TeamTypes eForTeam) const
{
if (NO_TEAM == eForTeam)
{
eForTeam = GC.getGameINLINE().getActiveTeam();
}
if (getTeam() != eForTeam && eForTeam != BARBARIAN_TEAM)
{
if (m_pUnitInfo->isHiddenNationality())
{
if (!plot()->isCity(true, getTeam()))
{
return BARBARIAN_PLAYER;
}
}
}
return getOwnerINLINE();
}
PlayerTypes CvUnit::getCombatOwner(TeamTypes eForTeam, const CvPlot* pPlot) const
{
if (eForTeam != NO_TEAM && getTeam() != eForTeam && eForTeam != BARBARIAN_TEAM)
{
if (isAlwaysHostile(pPlot))
{
return BARBARIAN_PLAYER;
}
}
return getOwnerINLINE();
}
TeamTypes CvUnit::getTeam() const
{
return GET_PLAYER(getOwnerINLINE()).getTeam();
}
PlayerTypes CvUnit::getCapturingPlayer() const
{
return m_eCapturingPlayer;
}
void CvUnit::setCapturingPlayer(PlayerTypes eNewValue)
{
m_eCapturingPlayer = eNewValue;
}
const UnitTypes CvUnit::getUnitType() const
{
return m_eUnitType;
}
CvUnitInfo &CvUnit::getUnitInfo() const
{
return *m_pUnitInfo;
}
UnitClassTypes CvUnit::getUnitClassType() const
{
return (UnitClassTypes)m_pUnitInfo->getUnitClassType();
}
const UnitTypes CvUnit::getLeaderUnitType() const
{
return m_eLeaderUnitType;
}
void CvUnit::setLeaderUnitType(UnitTypes leaderUnitType)
{
if(m_eLeaderUnitType != leaderUnitType)
{
m_eLeaderUnitType = leaderUnitType;
reloadEntity();
}
}
CvUnit* CvUnit::getCombatUnit() const
{
return getUnit(m_combatUnit);
}
void CvUnit::setCombatUnit(CvUnit* pCombatUnit, bool bAttacking)
{
if (isCombatFocus())
{
gDLL->getInterfaceIFace()->setCombatFocus(false);
}
if (pCombatUnit != NULL)
{
if (bAttacking)
{
if (GC.getLogging())
{
if (gDLL->getChtLvl() > 0)
{
// Log info about this combat...
char szOut[1024];
sprintf( szOut, "*** KOMBAT!\n ATTACKER: Player %d Unit %d (%S's %S), CombatStrength=%d\n DEFENDER: Player %d Unit %d (%S's %S), CombatStrength=%d\n",
getOwnerINLINE(), getID(), GET_PLAYER(getOwnerINLINE()).getName(), getName().GetCString(), currCombatStr(NULL, NULL),
pCombatUnit->getOwnerINLINE(), pCombatUnit->getID(), GET_PLAYER(pCombatUnit->getOwnerINLINE()).getName(), pCombatUnit->getName().GetCString(), pCombatUnit->currCombatStr(pCombatUnit->plot(), this));
gDLL->messageControlLog(szOut);
}
}
/* original bts code
if (getDomainType() == DOMAIN_LAND
&& !m_pUnitInfo->isIgnoreBuildingDefense()
&& pCombatUnit->plot()->getPlotCity()
&& pCombatUnit->plot()->getPlotCity()->getBuildingDefense() > 0
&& cityAttackModifier() >= GC.getDefineINT("MIN_CITY_ATTACK_MODIFIER_FOR_SIEGE_TOWER")) */
if (showSeigeTower(pCombatUnit)) // K-Mod
{
if ( !isUsingDummyEntities() && isInViewport())
{
CvDLLEntity::SetSiegeTower(true);
}
}
}
FAssertMsg(getCombatUnit() == NULL, "Combat Unit is not expected to be assigned");
FAssertMsg(!(plot()->isFighting()), "(plot()->isFighting()) did not return false as expected");
m_bCombatFocus = (bAttacking && !(gDLL->getInterfaceIFace()->isFocusedWidget()) && ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) || ((pCombatUnit->getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && !(GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS)))));
m_combatUnit = pCombatUnit->getIDInfo();
setCombatFirstStrikes((pCombatUnit->immuneToFirstStrikes()) ? 0 : (firstStrikes() + GC.getGameINLINE().getSorenRandNum(chanceFirstStrikes() + 1, "First Strike")));
}
else
{
if(getCombatUnit() != NULL)
{
FAssertMsg(getCombatUnit() != NULL, "getCombatUnit() is not expected to be equal with NULL");
FAssertMsg(plot()->isFighting(), "plot()->isFighting is expected to be true");
m_bCombatFocus = false;
m_combatUnit.reset();
setCombatFirstStrikes(0);
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
}
if (plot() == gDLL->getInterfaceIFace()->getSelectionPlot())
{
gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
}
if ( !isUsingDummyEntities() && isInViewport())
{
CvDLLEntity::SetSiegeTower(false);
}
}
}
setCombatTimer(0);
setInfoBarDirty(true);
if (isCombatFocus())
{
gDLL->getInterfaceIFace()->setCombatFocus(true);
}
}
// K-Mod. Return true if the combat animation should include a seige tower
// (code copied from setCombatUnit, above)
bool CvUnit::showSeigeTower(CvUnit* pDefender) const
{
return getDomainType() == DOMAIN_LAND
&& !m_pUnitInfo->isIgnoreBuildingDefense()
&& pDefender->plot()->getPlotCity()
&& pDefender->plot()->getPlotCity()->getBuildingDefense() > 0
&& cityAttackModifier() >= GC.getDefineINT("MIN_CITY_ATTACK_MODIFIER_FOR_SIEGE_TOWER");
}
// K-Mod end
CvUnit* CvUnit::getTransportUnit() const
{
return getUnit(m_transportUnit);
}
bool CvUnit::isCargo() const
{
return (getTransportUnit() != NULL);
}
void CvUnit::setTransportUnit(CvUnit* pTransportUnit)
{
CvUnit* pOldTransportUnit;
pOldTransportUnit = getTransportUnit();
if (pOldTransportUnit != pTransportUnit)
{
if (pOldTransportUnit != NULL)
{
pOldTransportUnit->changeCargo(-1);
}
if (pTransportUnit != NULL)
{
//FAssertMsg(pTransportUnit->cargoSpaceAvailable(getSpecialUnitType(), getDomainType()) > 0, "Cargo space is expected to be available");
joinGroup(NULL, true); // Because what if a group of 3 tries to get in a transport which can hold 2...
m_transportUnit = pTransportUnit->getIDInfo();
if (getDomainType() != DOMAIN_AIR)
{
getGroup()->setActivityType(ACTIVITY_SLEEP);
}
if (GC.getGameINLINE().isFinalInitialized())
{
finishMoves();
}
pTransportUnit->changeCargo(1);
pTransportUnit->getGroup()->setActivityType(ACTIVITY_AWAKE);
}
else
{
m_transportUnit.reset();
getGroup()->setActivityType(ACTIVITY_AWAKE);
/************************************************************************************************/
/* Afforess Start 09/01/10 */
/* */
/* After a Barb Transport is done, set it to attack AI */
/************************************************************************************************/
if (pOldTransportUnit != NULL && pOldTransportUnit->getCargo() == 0)
{
if (pOldTransportUnit->getOwnerINLINE() == BARBARIAN_PLAYER)
{
pOldTransportUnit->AI_setUnitAIType(UNITAI_ATTACK_SEA);
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// Koshling - have the AI prioritize regrouping with other units when unloaded
getGroup()->AI_setMissionAI(MISSIONAI_REGROUP, NULL, NULL);
}
#ifdef _DEBUG
std::vector<CvUnit*> aCargoUnits;
if (pOldTransportUnit != NULL)
{
pOldTransportUnit->getCargoUnits(aCargoUnits);
}
if (pTransportUnit != NULL)
{
pTransportUnit->getCargoUnits(aCargoUnits);
}
getGroup()->validateLocations();
#endif
}
}
int CvUnit::getExtraDomainModifier(DomainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < NUM_DOMAIN_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_aiExtraDomainModifier[eIndex] + pCommander->getExtraDomainModifier(eIndex);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_aiExtraDomainModifier[eIndex];
}
void CvUnit::changeExtraDomainModifier(DomainTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < NUM_DOMAIN_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");
m_aiExtraDomainModifier[eIndex] = (m_aiExtraDomainModifier[eIndex] + iChange);
}
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
const CvWString CvUnit::getDescription(uint uiForm) const
{
if (m_pUnitInfo->getCivilizationName(getCivilizationType()) != NULL)
{
if (!CvWString(m_pUnitInfo->getCivilizationName(getCivilizationType())).empty())
{
return gDLL->getText(m_pUnitInfo->getCivilizationName(getCivilizationType()));
}
}
return m_pUnitInfo->getDescription(uiForm);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
const CvWString CvUnit::getName(uint uiForm) const
{
CvWString szBuffer;
if (m_szName.empty())
{
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
/*
return m_pUnitInfo->getDescription(uiForm);
*/
return getDescription(uiForm);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
// BUG - Unit Name - start
else if (isDescInName())
{
return m_szName;
}
// BUG - Unit Name - end
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
/*
szBuffer.Format(L"%s (%s)", m_szName.GetCString(), m_pUnitInfo->getDescription(uiForm));
*/
szBuffer.Format(L"%s (%s)", m_szName.GetCString(), getDescription(uiForm).GetCString());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return szBuffer;
}
// BUG - Unit Name - start
bool CvUnit::isDescInName() const
{
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
/*
return (m_szName.find(m_pUnitInfo->getDescription()) != -1);
*/
return (m_szName.find(getDescription()) != -1);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
// BUG - Unit Name - end
const wchar* CvUnit::getNameKey() const
{
if (m_szName.empty())
{
return m_pUnitInfo->getTextKeyWide();
}
else
{
return m_szName.GetCString();
}
}
const CvWString& CvUnit::getNameNoDesc() const
{
return m_szName;
}
void CvUnit::setName(CvWString szNewValue)
{
gDLL->stripSpecialCharacters(szNewValue);
m_szName = szNewValue;
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
}
}
std::string CvUnit::getScriptData() const
{
return m_szScriptData;
}
void CvUnit::setScriptData(std::string szNewValue)
{
m_szScriptData = szNewValue;
}
int CvUnit::getTerrainDoubleMoveCount(TerrainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return m_paiTerrainDoubleMoveCount[eIndex];
}
bool CvUnit::isTerrainDoubleMove(TerrainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return (getTerrainDoubleMoveCount(eIndex) > 0);
}
void CvUnit::changeTerrainDoubleMoveCount(TerrainTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
m_paiTerrainDoubleMoveCount[eIndex] = (m_paiTerrainDoubleMoveCount[eIndex] + iChange);
FAssert(getTerrainDoubleMoveCount(eIndex) >= 0);
}
int CvUnit::getFeatureDoubleMoveCount(FeatureTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumFeatureInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return m_paiFeatureDoubleMoveCount[eIndex];
}
bool CvUnit::isFeatureDoubleMove(FeatureTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumFeatureInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return (getFeatureDoubleMoveCount(eIndex) > 0);
}
void CvUnit::changeFeatureDoubleMoveCount(FeatureTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumFeatureInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
m_paiFeatureDoubleMoveCount[eIndex] = (m_paiFeatureDoubleMoveCount[eIndex] + iChange);
FAssert(getFeatureDoubleMoveCount(eIndex) >= 0);
}
int CvUnit::getExtraTerrainAttackPercent(TerrainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_paiExtraTerrainAttackPercent[eIndex] + pCommander->getExtraTerrainAttackPercent(eIndex);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_paiExtraTerrainAttackPercent[eIndex];
}
void CvUnit::changeExtraTerrainAttackPercent(TerrainTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
if (iChange != 0)
{
m_paiExtraTerrainAttackPercent[eIndex] += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraTerrainDefensePercent(TerrainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_paiExtraTerrainDefensePercent[eIndex] + pCommander->getExtraTerrainDefensePercent(eIndex);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_paiExtraTerrainDefensePercent[eIndex];
}
void CvUnit::changeExtraTerrainDefensePercent(TerrainTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
if (iChange != 0)
{
m_paiExtraTerrainDefensePercent[eIndex] += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraFeatureAttackPercent(FeatureTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumFeatureInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_paiExtraFeatureAttackPercent[eIndex] + pCommander->getExtraFeatureAttackPercent(eIndex);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_paiExtraFeatureAttackPercent[eIndex];
}
void CvUnit::changeExtraFeatureAttackPercent(FeatureTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumFeatureInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
if (iChange != 0)
{
m_paiExtraFeatureAttackPercent[eIndex] += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraFeatureDefensePercent(FeatureTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumFeatureInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_paiExtraFeatureDefensePercent[eIndex] + pCommander->getExtraFeatureDefensePercent(eIndex);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_paiExtraFeatureDefensePercent[eIndex];
}
void CvUnit::changeExtraFeatureDefensePercent(FeatureTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumFeatureInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
if (iChange != 0)
{
m_paiExtraFeatureDefensePercent[eIndex] += iChange;
setInfoBarDirty(true);
}
}
int CvUnit::getExtraUnitCombatModifier(UnitCombatTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumUnitCombatInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
if (!isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
return m_paiExtraUnitCombatModifier[eIndex] + pCommander->getExtraUnitCombatModifier(eIndex);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return m_paiExtraUnitCombatModifier[eIndex];
}
void CvUnit::changeExtraUnitCombatModifier(UnitCombatTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumUnitCombatInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
m_paiExtraUnitCombatModifier[eIndex] = (m_paiExtraUnitCombatModifier[eIndex] + iChange);
}
bool CvUnit::canAcquirePromotion(PromotionTypes ePromotion, bool bIgnoreHas) const
{
FAssertMsg(ePromotion >= 0 || ePromotion == NO_PROMOTION, "ePromotion is expected to be non-negative (invalid Index)");
FAssertMsg(ePromotion < GC.getNumPromotionInfos(), "ePromotion is expected to be within maximum bounds (invalid Index)");
if ( ePromotion == NO_PROMOTION )
{
return false;
}
if (!bIgnoreHas && isHasPromotion(ePromotion))
{
return false;
}
if (GC.getPromotionInfo(ePromotion).getPrereqPromotion() != NO_PROMOTION)
{
if (!isHasPromotion((PromotionTypes)(GC.getPromotionInfo(ePromotion).getPrereqPromotion())))
{
return false;
}
}
if (GC.getPromotionInfo(ePromotion).getPrereqOrPromotion1() != NO_PROMOTION)
{
if (!isHasPromotion((PromotionTypes)(GC.getPromotionInfo(ePromotion).getPrereqOrPromotion1())))
{
if ((GC.getPromotionInfo(ePromotion).getPrereqOrPromotion2() == NO_PROMOTION) || !isHasPromotion((PromotionTypes)(GC.getPromotionInfo(ePromotion).getPrereqOrPromotion2())))
{
return false;
}
}
}
if (GC.getPromotionInfo(ePromotion).getTechPrereq() != NO_TECH)
{
if (!(GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getPromotionInfo(ePromotion).getTechPrereq()))))
{
return false;
}
}
/************************************************************************************************/
/* Afforess Start 01/01/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getPromotionInfo(ePromotion).getObsoleteTech() != NO_TECH)
{
if ((GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getPromotionInfo(ePromotion).getTechPrereq()))))
{
return false;
}
}
if (getUnitCombatType() == NO_UNITCOMBAT)
return false;
if (!GC.getPromotionInfo(ePromotion).getUnitCombat(getUnitCombatType()))
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (GC.getPromotionInfo(ePromotion).getStateReligionPrereq() != NO_RELIGION)
{
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != GC.getPromotionInfo(ePromotion).getStateReligionPrereq())
{
return false;
}
}
if (!isPromotionValid(ePromotion))
{
return false;
}
return true;
}
bool CvUnit::isPromotionValid(PromotionTypes ePromotion) const
{
if (!::isPromotionValid(ePromotion, getUnitType(), true))
{
return false;
}
/************************************************************************************************/
/* SUPER_SPIES 05/24/08 TSheep */
/* */
/* */
/************************************************************************************************/
if (isSpy())
{
return true;
}
/************************************************************************************************/
/* SUPER_SPIES END TSheep */
/************************************************************************************************/
CvPromotionInfo& promotionInfo = GC.getPromotionInfo(ePromotion);
if (promotionInfo.getWithdrawalChange() + m_pUnitInfo->getWithdrawalProbability() + getExtraWithdrawal(true) > GC.getDefineINT("MAX_WITHDRAWAL_PROBABILITY"))
{
return false;
}
if (promotionInfo.getInterceptChange() + maxInterceptionProbability(true) > GC.getDefineINT("MAX_INTERCEPTION_PROBABILITY"))
{
return false;
}
if (promotionInfo.getEvasionChange() + evasionProbability(true) > GC.getDefineINT("MAX_EVASION_PROBABILITY"))
{
return false;
}
return true;
}
bool CvUnit::canAcquirePromotionAny() const
{
int iI;
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if (canAcquirePromotion((PromotionTypes)iI))
{
return true;
}
}
return false;
}
PromotionIterator CvUnit::getPromotionBegin()
{
return m_promotionKeyedInfo.begin();
}
PromotionIterator CvUnit::getPromotionEnd()
{
return m_promotionKeyedInfo.end();
}
UnitCombatKeyedInfo* CvUnit::findOrCreateUnitCombatKeyedInfo(UnitCombatTypes eUnitCombat)
{
std::map<UnitCombatTypes, UnitCombatKeyedInfo>::iterator itr = m_unitCombatKeyedInfo.find(eUnitCombat);
if ( itr == m_unitCombatKeyedInfo.end() )
{
UnitCombatKeyedInfo newInfo;
return &(m_unitCombatKeyedInfo.insert(std::make_pair(eUnitCombat, newInfo)).first->second);
}
else
{
return &(itr->second);
}
}
const UnitCombatKeyedInfo* CvUnit::findUnitCombatKeyedInfo(UnitCombatTypes eUnitCombat) const
{
std::map<UnitCombatTypes, UnitCombatKeyedInfo>::const_iterator itr = m_unitCombatKeyedInfo.find(eUnitCombat);
if ( itr == m_unitCombatKeyedInfo.end() )
{
return NULL;
}
else
{
return &(itr->second);
}
}
bool CvUnit::isHasPromotion(PromotionTypes eIndex) const
{
FAssertMsg(eIndex >= 0 || eIndex == NO_PROMOTION, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumPromotionInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return eIndex == NO_PROMOTION ? false : m_pabHasPromotion[eIndex];
}
int CvUnit::getSubCombatTypeCount(UnitCombatTypes eCombatType) const
{
FAssertMsg(eCombatType >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eCombatType < GC.getNumUnitCombatInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
const UnitCombatKeyedInfo* info = findUnitCombatKeyedInfo(eCombatType);
return info == NULL ? 0 : info->m_iSubCombatTypeCount;
}
bool CvUnit::hasExtraSubCombatType(UnitCombatTypes eCombatType) const
{
FAssertMsg(eCombatType >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eCombatType < GC.getNumUnitCombatInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return (getSubCombatTypeCount(eCombatType) > 0);
}
void CvUnit::changeSubCombatTypeCount(UnitCombatTypes eCombatType, int iChange)
{
FAssertMsg(eCombatType >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eCombatType < GC.getNumUnitCombatInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
UnitCombatKeyedInfo* info = findOrCreateUnitCombatKeyedInfo(eCombatType);
info->m_iSubCombatTypeCount += iChange;
FAssert(info->m_iSubCombatTypeCount >= 0);
}
int CvUnit::getRemovesUnitCombatTypeCount(UnitCombatTypes eCombatType) const
{
FAssertMsg(eCombatType >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eCombatType < GC.getNumUnitCombatInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
const UnitCombatKeyedInfo* info = findUnitCombatKeyedInfo(eCombatType);
return info == NULL ? 0 : info->m_iRemovesUnitCombatTypeCount;
}
bool CvUnit::hasRemovesUnitCombatType(UnitCombatTypes eCombatType) const
{
FAssertMsg(eCombatType >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eCombatType < GC.getNumUnitCombatInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return (getRemovesUnitCombatTypeCount(eCombatType) > 0);
}
void CvUnit::changeRemovesUnitCombatTypeCount(UnitCombatTypes eCombatType, int iChange)
{
FAssertMsg(eCombatType >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eCombatType < GC.getNumUnitCombatInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
UnitCombatKeyedInfo* info = findOrCreateUnitCombatKeyedInfo(eCombatType);
info->m_iRemovesUnitCombatTypeCount += iChange;
FAssert(info->m_iRemovesUnitCombatTypeCount >= 0);
}
//TB SubCombat Mod End
void CvUnit::setHasPromotion(PromotionTypes eIndex, bool bNewValue)
{
int iChange;
int iI;
/************************************************************************************************/
/* SUPER_SPIES 05/24/08 TSheep */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM - super spies
// Disable spy promotions mechanism
bool canPromote = true;
if (isSpy() && !GC.isSS_ENABLED() && !GC.getPromotionInfo(eIndex).isEnemyRoute())//exempt commando promotion
{
canPromote = false;
}
if (isHasPromotion(eIndex) != bNewValue && canPromote)
// RevolutionDCM - end
/************************************************************************************************/
/* SUPER_SPIES END TSheep */
/************************************************************************************************/
{
m_pabHasPromotion[eIndex] = bNewValue;
if ( GC.getPromotionInfo(eIndex).changesMoveThroughPlots() )
{
m_movementCharacteristicsHash ^= GC.getPromotionInfo(eIndex).getZobristValue();
m_iMaxMoveCacheTurn = -1;
}
iChange = ((isHasPromotion(eIndex)) ? 1 : -1);
changeBlitzCount((GC.getPromotionInfo(eIndex).isBlitz()) ? iChange : 0);
changeAmphibCount((GC.getPromotionInfo(eIndex).isAmphib()) ? iChange : 0);
changeRiverCount((GC.getPromotionInfo(eIndex).isRiver()) ? iChange : 0);
changeEnemyRouteCount((GC.getPromotionInfo(eIndex).isEnemyRoute()) ? iChange : 0);
changeAlwaysHealCount((GC.getPromotionInfo(eIndex).isAlwaysHeal()) ? iChange : 0);
changeHillsDoubleMoveCount((GC.getPromotionInfo(eIndex).isHillsDoubleMove()) ? iChange : 0);
/************************************************************************************************/
/* Afforess Mountaineering Promotion 10/13/09 */
/* */
/* */
/************************************************************************************************/
changeCanMovePeaksCount((GC.getPromotionInfo(eIndex).isCanMovePeaks()) ? iChange : 0);
// Koshling - enhanced mountaineering mode to differentiate between ability to move through
// mountains, and ability to lead a stack through mountains
changeCanLeadThroughPeaksCount((GC.getPromotionInfo(eIndex).isCanLeadThroughPeaks()) ? iChange : 0);
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
changeExtraControlPoints(GC.getPromotionInfo(eIndex).getControlPoints() * iChange);
changeExtraCommandRange(GC.getPromotionInfo(eIndex).getCommandRange() * iChange);
changeOnslaughtCount((GC.getPromotionInfo(eIndex).isOnslaught()) ? iChange : 0);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
changeImmuneToFirstStrikesCount((GC.getPromotionInfo(eIndex).isImmuneToFirstStrikes()) ? iChange : 0);
changeExtraVisibilityRange(GC.getPromotionInfo(eIndex).getVisibilityChange() * iChange);
changeExtraMoves(GC.getPromotionInfo(eIndex).getMovesChange() * iChange);
changeExtraMoveDiscount(GC.getPromotionInfo(eIndex).getMoveDiscountChange() * iChange);
changeExtraAirRange(GC.getPromotionInfo(eIndex).getAirRangeChange() * iChange);
changeExtraIntercept(GC.getPromotionInfo(eIndex).getInterceptChange() * iChange);
changeExtraEvasion(GC.getPromotionInfo(eIndex).getEvasionChange() * iChange);
changeExtraFirstStrikes(GC.getPromotionInfo(eIndex).getFirstStrikesChange() * iChange);
changeExtraChanceFirstStrikes(GC.getPromotionInfo(eIndex).getChanceFirstStrikesChange() * iChange);
changeExtraWithdrawal(GC.getPromotionInfo(eIndex).getWithdrawalChange() * iChange);
changeExtraCollateralDamage(GC.getPromotionInfo(eIndex).getCollateralDamageChange() * iChange);
changeExtraBombardRate(GC.getPromotionInfo(eIndex).getBombardRateChange() * iChange);
changeExtraEnemyHeal(GC.getPromotionInfo(eIndex).getEnemyHealChange() * iChange);
changeExtraNeutralHeal(GC.getPromotionInfo(eIndex).getNeutralHealChange() * iChange);
changeExtraFriendlyHeal(GC.getPromotionInfo(eIndex).getFriendlyHealChange() * iChange);
changeSameTileHeal(GC.getPromotionInfo(eIndex).getSameTileHealChange() * iChange);
changeAdjacentTileHeal(GC.getPromotionInfo(eIndex).getAdjacentTileHealChange() * iChange);
changeExtraCombatPercent(GC.getPromotionInfo(eIndex).getCombatPercent() * iChange);
changeExtraCityAttackPercent(GC.getPromotionInfo(eIndex).getCityAttackPercent() * iChange);
changeExtraCityDefensePercent(GC.getPromotionInfo(eIndex).getCityDefensePercent() * iChange);
changeExtraHillsAttackPercent(GC.getPromotionInfo(eIndex).getHillsAttackPercent() * iChange);
changeExtraHillsDefensePercent(GC.getPromotionInfo(eIndex).getHillsDefensePercent() * iChange);
changeRevoltProtection(GC.getPromotionInfo(eIndex).getRevoltProtection() * iChange);
changeCollateralDamageProtection(GC.getPromotionInfo(eIndex).getCollateralDamageProtection() * iChange);
changePillageChange(GC.getPromotionInfo(eIndex).getPillageChange() * iChange);
changeUpgradeDiscount(GC.getPromotionInfo(eIndex).getUpgradeDiscount() * iChange);
changeExperiencePercent(GC.getPromotionInfo(eIndex).getExperiencePercent() * iChange);
changeKamikazePercent((GC.getPromotionInfo(eIndex).getKamikazePercent()) * iChange);
changeCargoSpace(GC.getPromotionInfo(eIndex).getCargoChange() * iChange);
// Thomas SG - AC: Advanced Cargo START
changeTotalCargoSpace(GC.getPromotionInfo(eIndex).getCargoChange() * iChange);
// Thomas SG - AC: Advanced Cargo END
/************************************************************************************************/
/* Afforess Promotion Changes 12/5/09 */
/* */
/* */
/************************************************************************************************/
CvPromotionInfo &kPromotion = GC.getPromotionInfo(eIndex);
if (bNewValue)
{
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if (kPromotion.getNumPromotionOverwrites() > 0)
{
for (iI = 0; iI < kPromotion.getNumPromotionOverwrites(); iI++)
{
if (isHasPromotion(kPromotion.getPromotionOverwrites(iI)))
{
setHasPromotion(kPromotion.getPromotionOverwrites(iI), false);
}
}
}
}
}
int iAsset = m_pUnitInfo->getAssetValue();
int iMultiplier = 100 + kPromotion.getAssetMultiplier();
iAsset *= iMultiplier;
iAsset /= 100;
iAsset -= m_pUnitInfo->getAssetValue();
GET_PLAYER(getOwnerINLINE()).changeAssets(iAsset * iChange);
int iPower = m_pUnitInfo->getPowerValue();
iMultiplier = 100 + kPromotion.getPowerMultiplier();
iPower *= iMultiplier;
iPower /= 100;
iPower -= m_pUnitInfo->getPowerValue();
GET_PLAYER(getOwnerINLINE()).changeUnitPower(iPower * iChange);
if (kPromotion.isZoneOfControl())
{
changeZoneOfControlCount(iChange > 0 ? 1 : -1);
}
if (kPromotion.getIgnoreTerrainDamage() != NO_TERRAIN)
{
changeTerrainProtected((TerrainTypes)kPromotion.getIgnoreTerrainDamage(), iChange);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumTerrainInfos(); iI++)
{
changeExtraTerrainAttackPercent(((TerrainTypes)iI), (GC.getPromotionInfo(eIndex).getTerrainAttackPercent(iI) * iChange));
changeExtraTerrainDefensePercent(((TerrainTypes)iI), (GC.getPromotionInfo(eIndex).getTerrainDefensePercent(iI) * iChange));
changeTerrainDoubleMoveCount(((TerrainTypes)iI), ((GC.getPromotionInfo(eIndex).getTerrainDoubleMove(iI)) ? iChange : 0));
}
for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
{
changeExtraFeatureAttackPercent(((FeatureTypes)iI), (GC.getPromotionInfo(eIndex).getFeatureAttackPercent(iI) * iChange));
changeExtraFeatureDefensePercent(((FeatureTypes)iI), (GC.getPromotionInfo(eIndex).getFeatureDefensePercent(iI) * iChange));
changeFeatureDoubleMoveCount(((FeatureTypes)iI), ((GC.getPromotionInfo(eIndex).getFeatureDoubleMove(iI)) ? iChange : 0));
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
changeExtraUnitCombatModifier(((UnitCombatTypes)iI), (GC.getPromotionInfo(eIndex).getUnitCombatModifierPercent(iI) * iChange));
}
for (iI = 0; iI < NUM_DOMAIN_TYPES; iI++)
{
changeExtraDomainModifier(((DomainTypes)iI), (GC.getPromotionInfo(eIndex).getDomainModifierPercent(iI) * iChange));
}
if (IsSelected())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
}
//update graphics
if ( !isUsingDummyEntities() && isInViewport())
{
gDLL->getEntityIFace()->updatePromotionLayers(getUnitEntity());
}
}
}
int CvUnit::getSubUnitCount() const
{
return m_pUnitInfo->getGroupSize();
}
int CvUnit::getSubUnitsAlive() const
{
return getSubUnitsAlive( getDamage());
}
int CvUnit::getSubUnitsAlive(int iDamage) const
{
if (iDamage >= maxHitPoints())
{
return 0;
}
else
{
return std::max(1, (((m_pUnitInfo->getGroupSize() * (maxHitPoints() - iDamage)) + (maxHitPoints() / ((m_pUnitInfo->getGroupSize() * 2) + 1))) / maxHitPoints()));
}
}
// returns true if unit can initiate a war action with plot (possibly by declaring war)
bool CvUnit::potentialWarAction(const CvPlot* pPlot) const
{
TeamTypes ePlotTeam = pPlot->getTeam();
TeamTypes eUnitTeam = getTeam();
if (ePlotTeam == NO_TEAM)
{
return false;
}
if (isEnemy(ePlotTeam, pPlot))
{
return true;
}
if (getGroup()->AI_isDeclareWar(pPlot) && GET_TEAM(eUnitTeam).AI_getWarPlan(ePlotTeam) != NO_WARPLAN)
{
return true;
}
return false;
}
void CvUnit::read(FDataStreamBase* pStream)
{
// Init data before load
reset();
CvTaggedSaveFormatWrapper& wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();
wrapper.AttachToStream(pStream);
WRAPPER_READ_OBJECT_START(wrapper);
uint uiFlag=0;
WRAPPER_READ(wrapper, "CvUnit", &uiFlag); // flags for expansion
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - RB: Field Bombard START
WRAPPER_READ(wrapper, "CvUnit", &m_iDCMBombRange);
WRAPPER_READ(wrapper, "CvUnit", &m_iDCMBombAccuracy);
// Dale - RB: Field Bombard END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
// < M.A.D. Nukes Start >
WRAPPER_READ(wrapper, "CvUnit", &m_bMADEnabled);
WRAPPER_READ(wrapper, "CvUnit", &m_iMADTargetPlotX);
WRAPPER_READ(wrapper, "CvUnit", &m_iMADTargetPlotY);
WRAPPER_READ(wrapper, "CvUnit", (int*)&m_pMADTargetPlotOwner);
// < M.A.D. Nukes End >
WRAPPER_READ(wrapper, "CvUnit", &m_iID);
WRAPPER_READ(wrapper, "CvUnit", &m_iGroupID);
WRAPPER_READ(wrapper, "CvUnit", &m_iHotKeyNumber);
WRAPPER_READ(wrapper, "CvUnit", &m_iX);
WRAPPER_READ(wrapper, "CvUnit", &m_iY);
WRAPPER_READ(wrapper, "CvUnit", &m_iLastMoveTurn);
WRAPPER_READ(wrapper, "CvUnit", &m_iReconX);
WRAPPER_READ(wrapper, "CvUnit", &m_iReconY);
WRAPPER_READ(wrapper, "CvUnit", &m_iGameTurnCreated);
WRAPPER_READ(wrapper, "CvUnit", &m_iDamage);
WRAPPER_READ(wrapper, "CvUnit", &m_iMoves);
WRAPPER_READ(wrapper, "CvUnit", &m_iExperience);
WRAPPER_READ(wrapper, "CvUnit", &m_iLevel);
WRAPPER_READ(wrapper, "CvUnit", &m_iCargo);
WRAPPER_READ(wrapper, "CvUnit", &m_iCargoCapacity);
// Thomas SG - AC: Advanced Cargo START
WRAPPER_READ(wrapper, "CvUnit", &m_iTotalCargoCapacity);
// Thomas SG - AC: Advanced Cargo END
WRAPPER_READ(wrapper, "CvUnit", &m_iAttackPlotX);
WRAPPER_READ(wrapper, "CvUnit", &m_iAttackPlotY);
WRAPPER_READ(wrapper, "CvUnit", &m_iCombatTimer);
WRAPPER_READ(wrapper, "CvUnit", &m_iCombatFirstStrikes);
if (uiFlag < 2)
{
int iCombatDamage;
WRAPPER_READ(wrapper, "CvUnit", &iCombatDamage);
}
WRAPPER_READ(wrapper, "CvUnit", &m_iFortifyTurns);
WRAPPER_READ(wrapper, "CvUnit", &m_iBlitzCount);
WRAPPER_READ(wrapper, "CvUnit", &m_iAmphibCount);
WRAPPER_READ(wrapper, "CvUnit", &m_iRiverCount);
WRAPPER_READ(wrapper, "CvUnit", &m_iEnemyRouteCount);
WRAPPER_READ(wrapper, "CvUnit", &m_iAlwaysHealCount);
WRAPPER_READ(wrapper, "CvUnit", &m_iHillsDoubleMoveCount);
/************************************************************************************************/
/* Afforess Mountaineering Promotion 10/13/09 */
/* */
/* */
/************************************************************************************************/
WRAPPER_READ(wrapper, "CvUnit", &m_iCanMovePeaksCount);
// Koshling - enhanced mountaineering mode to differentiate between ability to move through
// mountains, and ability to lead a stack through mountains
WRAPPER_READ(wrapper, "CvUnit", &m_iCanLeadThroughPeaksCount);
WRAPPER_READ(wrapper, "CvUnit", &m_iSleepTimer);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraControlPoints);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraCommandRange);
WRAPPER_READ(wrapper, "CvUnit", &m_iControlPointsLeft);
WRAPPER_READ(wrapper, "CvUnit", &m_iCommanderID); //id will be used later on player initialization to get m_pUsedCommander pointer
WRAPPER_READ(wrapper, "CvUnit", (int*)&m_eOriginalOwner);
WRAPPER_READ(wrapper, "CvUnit", &m_bCommander);
if (uiFlag > 2)
{
WRAPPER_READ(wrapper, "CvUnit", &m_bAutoPromoting);
WRAPPER_READ(wrapper, "CvUnit", &m_bAutoUpgrading);
}
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TERRAINS, GC.getNumTerrainInfos(), m_paiTerrainProtected);
WRAPPER_READ(wrapper, "CvUnit", (int*)&m_shadowUnit.eOwner);
WRAPPER_READ(wrapper, "CvUnit", &m_shadowUnit.iID);
WRAPPER_READ_CLASS_ENUM(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TECHS, (int*)&m_eDesiredDiscoveryTech);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
WRAPPER_READ(wrapper, "CvUnit", &m_iImmuneToFirstStrikesCount);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraVisibilityRange);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraMoves);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraMoveDiscount);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraAirRange);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraIntercept);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraEvasion);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraFirstStrikes);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraChanceFirstStrikes);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraWithdrawal);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraCollateralDamage);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraBombardRate);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraEnemyHeal);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraNeutralHeal);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraFriendlyHeal);
WRAPPER_READ(wrapper, "CvUnit", &m_iSameTileHeal);
WRAPPER_READ(wrapper, "CvUnit", &m_iAdjacentTileHeal);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraCombatPercent);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraCityAttackPercent);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraCityDefensePercent);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraHillsAttackPercent);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraHillsDefensePercent);
WRAPPER_READ(wrapper, "CvUnit", &m_iRevoltProtection);
WRAPPER_READ(wrapper, "CvUnit", &m_iCollateralDamageProtection);
WRAPPER_READ(wrapper, "CvUnit", &m_iPillageChange);
WRAPPER_READ(wrapper, "CvUnit", &m_iUpgradeDiscount);
WRAPPER_READ(wrapper, "CvUnit", &m_iExperiencePercent);
WRAPPER_READ(wrapper, "CvUnit", &m_iKamikazePercent);
WRAPPER_READ(wrapper, "CvUnit", &m_iBaseCombat);
WRAPPER_READ(wrapper, "CvUnit", (int*)&m_eFacingDirection);
WRAPPER_READ(wrapper, "CvUnit", &m_iImmobileTimer);
WRAPPER_READ(wrapper, "CvUnit", &m_bMadeAttack);
WRAPPER_READ(wrapper, "CvUnit", &m_bMadeInterception);
WRAPPER_READ(wrapper, "CvUnit", &m_bPromotionReady);
WRAPPER_READ(wrapper, "CvUnit", &m_bDeathDelay);
WRAPPER_READ(wrapper, "CvUnit", &m_bCombatFocus);
// m_bInfoBarDirty not saved...
WRAPPER_READ(wrapper, "CvUnit", &m_bBlockading);
if (uiFlag > 0)
{
WRAPPER_READ(wrapper, "CvUnit", &m_bAirCombat);
}
WRAPPER_READ(wrapper, "CvUnit", (int*)&m_eOwner);
WRAPPER_READ(wrapper, "CvUnit", (int*)&m_eCapturingPlayer);
WRAPPER_READ_CLASS_ENUM(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_UNITS, (int*)&m_eUnitType);
if( NO_UNIT == m_eUnitType)
{
// Assets must have removed this type (which will have been flagged in a queued error messae). Just
// give it a valid type temporarily and mark it to be killed
int tempUnitType = 0;
// Pick an arbitrary military unit
do
{
tempUnitType++;
} while(!GC.getUnitInfo((UnitTypes)tempUnitType).isMilitaryHappiness());
m_eUnitType = (UnitTypes)tempUnitType;
GET_TEAM(getTeam()).changeUnitClassCount((UnitClassTypes)GC.getUnitInfo(m_eUnitType).getUnitClassType(), 1);
GET_PLAYER(getOwnerINLINE()).changeUnitClassCount((UnitClassTypes)GC.getUnitInfo(m_eUnitType).getUnitClassType(), 1);
m_bDeathDelay = true;
}
m_pUnitInfo = &GC.getUnitInfo(m_eUnitType);
m_movementCharacteristicsHash = m_pUnitInfo->getZobristValue();
WRAPPER_READ_CLASS_ENUM(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_UNITS, (int*)&m_eLeaderUnitType);
WRAPPER_READ(wrapper, "CvUnit", (int*)&m_combatUnit.eOwner);
WRAPPER_READ(wrapper, "CvUnit", &m_combatUnit.iID);
WRAPPER_READ(wrapper, "CvUnit", (int*)&m_transportUnit.eOwner);
WRAPPER_READ(wrapper, "CvUnit", &m_transportUnit.iID);
WRAPPER_READ_ARRAY(wrapper, "CvUnit", NUM_DOMAIN_TYPES, m_aiExtraDomainModifier);
WRAPPER_READ_STRING(wrapper, "CvUnit", m_szName);
WRAPPER_READ_STRING(wrapper, "CvUnit", m_szScriptData);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_PROMOTIONS, GC.getNumPromotionInfos(), m_pabHasPromotion);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TERRAINS, GC.getNumTerrainInfos(), m_paiTerrainDoubleMoveCount);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_FEATURES, GC.getNumFeatureInfos(), m_paiFeatureDoubleMoveCount);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TERRAINS, GC.getNumTerrainInfos(), m_paiExtraTerrainAttackPercent);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TERRAINS, GC.getNumTerrainInfos(), m_paiExtraTerrainDefensePercent);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_FEATURES, GC.getNumFeatureInfos(), m_paiExtraFeatureAttackPercent);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_FEATURES, GC.getNumFeatureInfos(), m_paiExtraFeatureDefensePercent);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_COMBATINFOS, GC.getNumUnitCombatInfos(), m_paiExtraUnitCombatModifier);
m_Properties.readWrapper(pStream);
WRAPPER_READ(wrapper, "CvUnit", &m_iExtraStrength);
WRAPPER_READ(wrapper, "CvUnit", &m_bCanRespawn);
WRAPPER_READ(wrapper, "CvUnit", &m_bSurvivor);
WRAPPER_READ_OBJECT_END(wrapper);
// Koshling - we don't serialise mountian leader presence counts (why bother?) but
// recalculate them on load
if ( isCanLeadThroughPeaks() )
{
plot()->changeMountainLeaderCount(getTeam(),1);
}
// Zobrist characteristic hashes are not serialized so recalculate
// Right now it's just characteristics that affect what a unit might
// be able to move through that matter, so its unit class + certain promotions
for(int iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if ( m_pabHasPromotion[iI] && GC.getPromotionInfo((PromotionTypes)iI).changesMoveThroughPlots() )
{
m_movementCharacteristicsHash ^= GC.getPromotionInfo((PromotionTypes)iI).getZobristValue();
}
}
}
void CvUnit::write(FDataStreamBase* pStream)
{
CvTaggedSaveFormatWrapper& wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();
wrapper.AttachToStream(pStream);
WRAPPER_WRITE_OBJECT_START(wrapper);
uint uiFlag=3;
WRAPPER_WRITE(wrapper, "CvUnit", uiFlag); // flag for expansion
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - RB: Field Bombard START
WRAPPER_WRITE(wrapper, "CvUnit", m_iDCMBombRange);
WRAPPER_WRITE(wrapper, "CvUnit", m_iDCMBombAccuracy);
// Dale - RB: Field Bombard END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
// < M.A.D. Nukes Start >
WRAPPER_WRITE(wrapper, "CvUnit", m_bMADEnabled);
WRAPPER_WRITE(wrapper, "CvUnit", m_iMADTargetPlotX);
WRAPPER_WRITE(wrapper, "CvUnit", m_iMADTargetPlotY);
WRAPPER_WRITE(wrapper, "CvUnit", m_pMADTargetPlotOwner);
// < M.A.D. Nukes End >
WRAPPER_WRITE(wrapper, "CvUnit", m_iID);
WRAPPER_WRITE(wrapper, "CvUnit", m_iGroupID);
WRAPPER_WRITE(wrapper, "CvUnit", m_iHotKeyNumber);
WRAPPER_WRITE(wrapper, "CvUnit", m_iX);
WRAPPER_WRITE(wrapper, "CvUnit", m_iY);
WRAPPER_WRITE(wrapper, "CvUnit", m_iLastMoveTurn);
WRAPPER_WRITE(wrapper, "CvUnit", m_iReconX);
WRAPPER_WRITE(wrapper, "CvUnit", m_iReconY);
WRAPPER_WRITE(wrapper, "CvUnit", m_iGameTurnCreated);
WRAPPER_WRITE(wrapper, "CvUnit", m_iDamage);
WRAPPER_WRITE(wrapper, "CvUnit", m_iMoves);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExperience);
WRAPPER_WRITE(wrapper, "CvUnit", m_iLevel);
WRAPPER_WRITE(wrapper, "CvUnit", m_iCargo);
WRAPPER_WRITE(wrapper, "CvUnit", m_iCargoCapacity);
// Thomas SG - AC: Advanced Cargo START
WRAPPER_WRITE(wrapper, "CvUnit", m_iTotalCargoCapacity);
// Thomas SG - AC: Advanced Cargo END
WRAPPER_WRITE(wrapper, "CvUnit", m_iAttackPlotX);
WRAPPER_WRITE(wrapper, "CvUnit", m_iAttackPlotY);
WRAPPER_WRITE(wrapper, "CvUnit", m_iCombatTimer);
WRAPPER_WRITE(wrapper, "CvUnit", m_iCombatFirstStrikes);
WRAPPER_WRITE(wrapper, "CvUnit", m_iFortifyTurns);
WRAPPER_WRITE(wrapper, "CvUnit", m_iBlitzCount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iAmphibCount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iRiverCount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iEnemyRouteCount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iAlwaysHealCount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iHillsDoubleMoveCount);
/************************************************************************************************/
/* Afforess Mountaineering Promotion 10/13/09 */
/* */
/* */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvUnit", m_iCanMovePeaksCount);
// Koshling - enhanced mountaineering mode to differentiate between ability to move through
// mountains, and ability to lead a stack through mountains
WRAPPER_WRITE(wrapper, "CvUnit", m_iCanLeadThroughPeaksCount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iSleepTimer);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraControlPoints);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraCommandRange);
WRAPPER_WRITE(wrapper, "CvUnit", m_iControlPointsLeft);
WRAPPER_WRITE(wrapper, "CvUnit", m_iCommanderID); //-1 means there is no used commander
WRAPPER_WRITE(wrapper, "CvUnit", m_eOriginalOwner);
WRAPPER_WRITE(wrapper, "CvUnit", m_bCommander);
WRAPPER_WRITE(wrapper, "CvUnit", m_bAutoPromoting);
WRAPPER_WRITE(wrapper, "CvUnit", m_bAutoUpgrading);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TERRAINS, GC.getNumTerrainInfos(), m_paiTerrainProtected);
WRAPPER_WRITE(wrapper, "CvUnit", m_shadowUnit.eOwner);
WRAPPER_WRITE(wrapper, "CvUnit", m_shadowUnit.iID);
WRAPPER_WRITE_CLASS_ENUM(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TECHS, m_eDesiredDiscoveryTech);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvUnit", m_iImmuneToFirstStrikesCount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraVisibilityRange);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraMoves);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraMoveDiscount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraAirRange);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraIntercept);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraEvasion);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraFirstStrikes);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraChanceFirstStrikes);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraWithdrawal);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraCollateralDamage);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraBombardRate);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraEnemyHeal);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraNeutralHeal);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraFriendlyHeal);
WRAPPER_WRITE(wrapper, "CvUnit", m_iSameTileHeal);
WRAPPER_WRITE(wrapper, "CvUnit", m_iAdjacentTileHeal);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraCombatPercent);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraCityAttackPercent);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraCityDefensePercent);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraHillsAttackPercent);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraHillsDefensePercent);
WRAPPER_WRITE(wrapper, "CvUnit", m_iRevoltProtection);
WRAPPER_WRITE(wrapper, "CvUnit", m_iCollateralDamageProtection);
WRAPPER_WRITE(wrapper, "CvUnit", m_iPillageChange);
WRAPPER_WRITE(wrapper, "CvUnit", m_iUpgradeDiscount);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExperiencePercent);
WRAPPER_WRITE(wrapper, "CvUnit", m_iKamikazePercent);
WRAPPER_WRITE(wrapper, "CvUnit", m_iBaseCombat);
WRAPPER_WRITE(wrapper, "CvUnit", m_eFacingDirection);
WRAPPER_WRITE(wrapper, "CvUnit", m_iImmobileTimer);
WRAPPER_WRITE(wrapper, "CvUnit", m_bMadeAttack);
WRAPPER_WRITE(wrapper, "CvUnit", m_bMadeInterception);
WRAPPER_WRITE(wrapper, "CvUnit", m_bPromotionReady);
WRAPPER_WRITE(wrapper, "CvUnit", m_bDeathDelay);
WRAPPER_WRITE(wrapper, "CvUnit", m_bCombatFocus);
// m_bInfoBarDirty not saved...
WRAPPER_WRITE(wrapper, "CvUnit", m_bBlockading);
WRAPPER_WRITE(wrapper, "CvUnit", m_bAirCombat);
WRAPPER_WRITE(wrapper, "CvUnit", m_eOwner);
WRAPPER_WRITE(wrapper, "CvUnit", m_eCapturingPlayer);
WRAPPER_WRITE_CLASS_ENUM(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_UNITS, m_eUnitType);
WRAPPER_WRITE_CLASS_ENUM(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_UNITS, m_eLeaderUnitType);
WRAPPER_WRITE(wrapper, "CvUnit", m_combatUnit.eOwner);
WRAPPER_WRITE(wrapper, "CvUnit", m_combatUnit.iID);
WRAPPER_WRITE(wrapper, "CvUnit", m_transportUnit.eOwner);
WRAPPER_WRITE(wrapper, "CvUnit", m_transportUnit.iID);
WRAPPER_WRITE_ARRAY(wrapper, "CvUnit", NUM_DOMAIN_TYPES, m_aiExtraDomainModifier);
WRAPPER_WRITE_STRING(wrapper, "CvUnit", m_szName);
WRAPPER_WRITE_STRING(wrapper, "CvUnit", m_szScriptData);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_PROMOTIONS, GC.getNumPromotionInfos(), m_pabHasPromotion);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TERRAINS, GC.getNumTerrainInfos(), m_paiTerrainDoubleMoveCount);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_FEATURES, GC.getNumFeatureInfos(), m_paiFeatureDoubleMoveCount);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TERRAINS, GC.getNumTerrainInfos(), m_paiExtraTerrainAttackPercent);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_TERRAINS, GC.getNumTerrainInfos(), m_paiExtraTerrainDefensePercent);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_FEATURES, GC.getNumFeatureInfos(), m_paiExtraFeatureAttackPercent);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_FEATURES, GC.getNumFeatureInfos(), m_paiExtraFeatureDefensePercent);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvUnit", REMAPPED_CLASS_TYPE_COMBATINFOS, GC.getNumUnitCombatInfos(), m_paiExtraUnitCombatModifier);
m_Properties.writeWrapper(pStream);
WRAPPER_WRITE(wrapper, "CvUnit", m_iExtraStrength);
WRAPPER_WRITE(wrapper, "CvUnit", m_bCanRespawn);
WRAPPER_WRITE(wrapper, "CvUnit", m_bSurvivor);
WRAPPER_WRITE_OBJECT_END(wrapper);
}
// Protected Functions...
bool CvUnit::canAdvance(const CvPlot* pPlot, int iThreshold) const
{
FAssert(canFight());
FAssert(!(isAnimal() && pPlot->isCity()));
FAssert(getDomainType() != DOMAIN_AIR);
FAssert(getDomainType() != DOMAIN_IMMOBILE);
if (pPlot->getNumVisibleEnemyDefenders(this) > iThreshold)
{
return false;
}
if (isNoCapture())
{
if (pPlot->isEnemyCity(*this))
{
return false;
}
}
return true;
}
void CvUnit::collateralCombat(const CvPlot* pPlot, CvUnit* pSkipUnit)
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvUnit* pBestUnit;
CvWString szBuffer;
int iTheirStrength;
int iStrengthFactor;
int iCollateralDamage;
int iUnitDamage;
int iDamageCount;
int iPossibleTargets;
int iCount;
int iValue;
int iBestValue;
std::map<CvUnit*, int> mapUnitDamage;
std::map<CvUnit*, int>::iterator it;
int iCollateralStrength = (getDomainType() == DOMAIN_AIR ? airBaseCombatStr() : baseCombatStr()) * collateralDamage() / 100;
// UNOFFICIAL_PATCH Start
// * Barrage promotions made working again on Tanks and other units with no base collateral ability
if (iCollateralStrength == 0 && getExtraCollateralDamage() == 0)
// UNOFFICIAL_PATCH End
{
return;
}
iPossibleTargets = std::min((pPlot->getNumVisibleEnemyDefenders(this) - 1), collateralDamageMaxUnits());
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit != pSkipUnit)
{
if (isEnemy(pLoopUnit->getTeam(), pPlot))
{
if (!(pLoopUnit->isInvisible(getTeam(), false)))
{
if (pLoopUnit->canDefend())
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "Collateral Damage"));
iValue *= pLoopUnit->currHitPoints();
mapUnitDamage[pLoopUnit] = iValue;
}
}
}
}
}
CvCity* pCity = NULL;
if (getDomainType() == DOMAIN_AIR)
{
pCity = pPlot->getPlotCity();
}
iDamageCount = 0;
iCount = 0;
while (iCount < iPossibleTargets)
{
iBestValue = 0;
pBestUnit = NULL;
for (it = mapUnitDamage.begin(); it != mapUnitDamage.end(); ++it)
{
if (it->second > iBestValue)
{
iBestValue = it->second;
pBestUnit = it->first;
}
}
if (pBestUnit != NULL)
{
mapUnitDamage.erase(pBestUnit);
if (NO_UNITCOMBAT == getUnitCombatType() || !pBestUnit->getUnitInfo().getUnitCombatCollateralImmune(getUnitCombatType()))
{
iTheirStrength = pBestUnit->baseCombatStr();
iStrengthFactor = ((iCollateralStrength + iTheirStrength + 1) / 2);
iCollateralDamage = (GC.getDefineINT("COLLATERAL_COMBAT_DAMAGE") * (iCollateralStrength + iStrengthFactor)) / (iTheirStrength + iStrengthFactor);
iCollateralDamage *= 100 + getExtraCollateralDamage();
iCollateralDamage *= std::max(0, 100 - pBestUnit->getCollateralDamageProtection());
iCollateralDamage /= 100;
if (pCity != NULL)
{
iCollateralDamage *= 100 + pCity->getAirModifier();
iCollateralDamage /= 100;
}
iCollateralDamage /= 100;
iCollateralDamage = std::max(0, iCollateralDamage);
int iMaxDamage = std::min(collateralDamageLimit(), (collateralDamageLimit() * (iCollateralStrength + iStrengthFactor)) / (iTheirStrength + iStrengthFactor));
iUnitDamage = std::max(pBestUnit->getDamage(), std::min(pBestUnit->getDamage() + iCollateralDamage, iMaxDamage));
if (pBestUnit->getDamage() != iUnitDamage)
{
// BUG - Combat Events - start
int iDamageDone = iUnitDamage - pBestUnit->getDamage();
pBestUnit->setDamage(iUnitDamage, getOwnerINLINE());
CvEventReporter::getInstance().combatLogCollateral(this, pBestUnit, iDamageDone);
// BUG - Combat Events - end
iDamageCount++;
}
}
iCount++;
}
else
{
break;
}
}
if (iDamageCount > 0)
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_SUFFER_COL_DMG", iDamageCount);
AddMessage(pSkipUnit->getOwnerINLINE(), (pSkipUnit->getDomainType() != DOMAIN_AIR), GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_COLLATERAL", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pSkipUnit->getX_INLINE(), pSkipUnit->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_INFLICT_COL_DMG", getNameKey(), iDamageCount);
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_COLLATERAL", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pSkipUnit->getX_INLINE(), pSkipUnit->getY_INLINE());
}
}
void CvUnit::flankingStrikeCombat(const CvPlot* pPlot, int iAttackerStrength, int iAttackerFirepower, int iDefenderOdds, int iDefenderDamage, CvUnit* pSkipUnit)
{
if (pSkipUnit)
{
if (pPlot->isCity(true, pSkipUnit->getTeam()))
{
return;
}
}
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
std::vector< std::pair<CvUnit*, int> > listFlankedUnits;
while (NULL != pUnitNode)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit != pSkipUnit)
{
if (!pLoopUnit->isDead() && isEnemy(pLoopUnit->getTeam(), pPlot))
{
if (!(pLoopUnit->isInvisible(getTeam(), false)))
{
if (pLoopUnit->canDefend())
{
int iFlankingStrength = m_pUnitInfo->getFlankingStrikeUnitClass(pLoopUnit->getUnitClassType());
if (iFlankingStrength > 0)
{
int iFlankedDefenderStrength;
int iFlankedDefenderOdds;
int iAttackerDamage;
int iFlankedDefenderDamage;
getDefenderCombatValues(*pLoopUnit, pPlot, iAttackerStrength, iAttackerFirepower, iFlankedDefenderOdds, iFlankedDefenderStrength, iAttackerDamage, iFlankedDefenderDamage);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Lead From Behind by UncutDragon
/* original code
if (GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("COMBAT_DIE_SIDES"), "Flanking Combat") >= iDefenderOdds)
*/ // modified
if (GC.getGameINLINE().getSorenRandNum(GC.getCOMBAT_DIE_SIDES(), "Flanking Combat") >= iDefenderOdds)
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
int iCollateralDamage = (iFlankingStrength * iDefenderDamage) / 100;
int iUnitDamage = std::max(pLoopUnit->getDamage(), std::min(pLoopUnit->getDamage() + iCollateralDamage, collateralDamageLimit()));
if (pLoopUnit->getDamage() != iUnitDamage)
{
listFlankedUnits.push_back(std::make_pair(pLoopUnit, iUnitDamage));
}
}
}
}
}
}
}
}
int iNumUnitsHit = std::min((int)listFlankedUnits.size(), collateralDamageMaxUnits());
for (int i = 0; i < iNumUnitsHit; ++i)
{
int iIndexHit = GC.getGameINLINE().getSorenRandNum(listFlankedUnits.size(), "Pick Flanked Unit");
CvUnit* pUnit = listFlankedUnits[iIndexHit].first;
int iDamage = listFlankedUnits[iIndexHit].second;
// BUG - Combat Events - start
int iDamageDone = iDamage - pUnit->getDamage();
// BUG - Combat Events - end
pUnit->setDamage(iDamage, getOwnerINLINE());
if (pUnit->isDead())
{
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_KILLED_UNIT_BY_FLANKING", getNameKey(), pUnit->getNameKey(), pUnit->getVisualCivAdjective(getTeam()));
AddMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_UNIT_DIED_BY_FLANKING", pUnit->getNameKey(), getNameKey(), getVisualCivAdjective(pUnit->getTeam()));
AddMessage(pUnit->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
pUnit->kill(false, NO_PLAYER, true);
}
// BUG - Combat Events - start
CvEventReporter::getInstance().combatLogFlanking(this, pUnit, iDamageDone);
// BUG - Combat Events - end
listFlankedUnits.erase(std::remove(listFlankedUnits.begin(), listFlankedUnits.end(), listFlankedUnits[iIndexHit]));
}
if (iNumUnitsHit > 0)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_DAMAGED_UNITS_BY_FLANKING", getNameKey(), iNumUnitsHit);
AddMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
if (NULL != pSkipUnit)
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_UNITS_DAMAGED_BY_FLANKING", getNameKey(), iNumUnitsHit);
AddMessage(pSkipUnit->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
}
}
// Returns true if we were intercepted...
bool CvUnit::interceptTest(const CvPlot* pPlot)
{
if (GC.getGameINLINE().getSorenRandNum(100, "Evasion Rand") >= evasionProbability())
{
CvUnit* pInterceptor = bestInterceptor(pPlot);
if (pInterceptor != NULL)
{
/************************************************************************************************/
/* Afforess Start 03/6/10 */
/* */
/* Better Air Interception */
/************************************************************************************************/
int iInterceptionOdds;
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_BETTER_INTERCETION))
{
iInterceptionOdds = interceptionChance(pPlot);
}
else
{
iInterceptionOdds = pInterceptor->currInterceptionProbability();
}
if (GC.getGameINLINE().getSorenRandNum(100, "Intercept Rand (Air)") < iInterceptionOdds)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
fightInterceptor(pPlot, false);
return true;
}
}
}
return false;
}
CvUnit* CvUnit::airStrikeTarget(const CvPlot* pPlot) const
{
CvUnit* pDefender;
pDefender = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
if (pDefender != NULL)
{
if (!pDefender->isDead())
{
if (pDefender->canDefend())
{
return pDefender;
}
}
}
return NULL;
}
bool CvUnit::canAirStrike(const CvPlot* pPlot) const
{
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (!canAirAttack())
{
return false;
}
if (pPlot == plot())
{
return false;
}
if (!pPlot->isVisible(getTeam(), false))
{
return false;
}
if (plotDistance(getX_INLINE(), getY_INLINE(), pPlot->getX_INLINE(), pPlot->getY_INLINE()) > airRange())
{
return false;
}
if (airStrikeTarget(pPlot) == NULL)
{
return false;
}
return true;
}
bool CvUnit::airStrike(CvPlot* pPlot)
{
if (!canAirStrike(pPlot))
{
return false;
}
if (interceptTest(pPlot))
{
return false;
}
CvUnit* pDefender = airStrikeTarget(pPlot);
/************************************************************************************************/
/* DCM Start 05/31/10 Johnny Smith */
/* Afforess */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot, pDefender);
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
FAssert(pDefender != NULL);
FAssert(pDefender->canDefend());
setReconPlot(pPlot);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
int iDamage = airCombatDamage(pDefender);
int iUnitDamage = std::max(pDefender->getDamage(), std::min((pDefender->getDamage() + iDamage), airCombatLimit()));
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ARE_ATTACKED_BY_AIR", pDefender->getNameKey(), getNameKey(), -(((iUnitDamage - pDefender->getDamage()) * 100) / pDefender->maxHitPoints()));
AddMessage(pDefender->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_AIR_ATTACK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ATTACK_BY_AIR", getNameKey(), pDefender->getNameKey(), -(((iUnitDamage - pDefender->getDamage()) * 100) / pDefender->maxHitPoints()));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_AIR_ATTACKED", MESSAGE_TYPE_INFO, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
collateralCombat(pPlot, pDefender);
pDefender->setDamage(iUnitDamage, getOwnerINLINE());
/************************************************************************************************/
/* Afforess Start 08/03/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP))
{
setExperience100(getExperience100() + 25);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return true;
}
bool CvUnit::canRangeStrike() const
{
if (getDomainType() == DOMAIN_AIR)
{
return false;
}
if (airRange() <= 0)
{
return false;
}
if (airBaseCombatStr() <= 0)
{
return false;
}
if (!canFight())
{
return false;
}
if (isMadeAttack() && !isBlitz())
{
return false;
}
if (!canMove() && getMoves() > 0)
{
return false;
}
return true;
}
bool CvUnit::canRangeStrikeAt(const CvPlot* pPlot, int iX, int iY) const
{
if (!canRangeStrike())
{
return false;
}
CvPlot* pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (NULL == pTargetPlot)
{
return false;
}
if (!pPlot->isVisible(getTeam(), false))
{
return false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 05/10/10 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// Need to check target plot too
//Fuyu: AI-controlled units can strike even when tile is invisible
if (isHuman() && !isAutomated() && !pTargetPlot->isVisible(getTeam(), false))
{
return false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > airRange())
{
return false;
}
CvUnit* pDefender = airStrikeTarget(pTargetPlot);
if (NULL == pDefender)
{
return false;
}
if (!pPlot->canSeePlot(pTargetPlot, getTeam(), airRange(), getFacingDirection(true)))
{
return false;
}
return true;
}
bool CvUnit::rangeStrike(int iX, int iY)
{
CvUnit* pDefender;
CvWString szBuffer;
int iUnitDamage;
int iDamage;
CvPlot* pPlot = GC.getMapINLINE().plot(iX, iY);
if (NULL == pPlot)
{
return false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 05/10/10 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if (!canRangeStrikeAt(pPlot, iX, iY))
{
return false;
}
*/
if (!canRangeStrikeAt(plot(), iX, iY))
{
return false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
pDefender = airStrikeTarget(pPlot);
FAssert(pDefender != NULL);
FAssert(pDefender->canDefend());
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pDefender->plot(), pDefender);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
if (GC.getDefineINT("RANGED_ATTACKS_USE_MOVES") == 0)
{
setMadeAttack(true);
}
changeMoves(GC.getMOVE_DENOMINATOR());
iDamage = rangeCombatDamage(pDefender);
iUnitDamage = std::max(pDefender->getDamage(), std::min((pDefender->getDamage() + iDamage), airCombatLimit()));
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ARE_ATTACKED_BY_AIR", pDefender->getNameKey(), getNameKey(), -(((iUnitDamage - pDefender->getDamage()) * 100) / pDefender->maxHitPoints()));
//red icon over attacking unit
AddMessage(pDefender->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_COMBAT", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), this->getX_INLINE(), this->getY_INLINE(), true, true);
//white icon over defending unit
AddMessage(pDefender->getOwnerINLINE(), false, 0, L"", "AS2D_COMBAT", MESSAGE_TYPE_DISPLAY_ONLY, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), pDefender->getX_INLINE(), pDefender->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ATTACK_BY_AIR", getNameKey(), pDefender->getNameKey(), -(((iUnitDamage - pDefender->getDamage()) * 100) / pDefender->maxHitPoints()));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_COMBAT", MESSAGE_TYPE_INFO, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
collateralCombat(pPlot, pDefender);
//set damage but don't update entity damage visibility
pDefender->setDamage(iUnitDamage, getOwnerINLINE(), false);
if (pPlot->isActiveVisible(false) && !pDefender->isUsingDummyEntities() )
{
// Range strike entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_RANGE_ATTACK).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_RANGE_ATTACK);
kDefiniton.setPlot(pDefender->plot());
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
addMission(&kDefiniton);
//delay death
/************************************************************************************************/
/* UNOFFICIAL_PATCH 05/10/10 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
pDefender->getGroup()->setMissionTimer(GC.getMissionInfo(MISSION_RANGE_ATTACK).getTime());
*/
// mission timer is not used like this in any other part of code, so it might cause OOS
// issues ... at worst I think unit dies before animation is complete, so no real
// harm in commenting it out.
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
return true;
}
//------------------------------------------------------------------------------------------------
// FUNCTION: CvUnit::planBattle
//! \brief Determines in general how a battle will progress.
//!
//! Note that the outcome of the battle is not determined here. This function plans
//! how many sub-units die and in which 'rounds' of battle.
//! \param kBattleDefinition The battle definition, which receives the battle plan.
//! \retval The number of game turns that the battle should be given.
//------------------------------------------------------------------------------------------------
int CvUnit::planBattle( CvBattleDefinition & kBattleDefinition ) const
{
#define BATTLE_TURNS_SETUP 4
/************************************************************************************************/
/* Afforess Start 6/20/11 */
/* */
/* Boost ending rounds to allow all unit animations to end */
/************************************************************************************************/
/*
#define BATTLE_TURNS_ENDING 4
*/
#define BATTLE_TURNS_ENDING 6
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
#define BATTLE_TURNS_MELEE 6
#define BATTLE_TURNS_RANGED 6
#define BATTLE_TURN_RECHECK 4
int aiUnitsBegin[BATTLE_UNIT_COUNT];
int aiUnitsEnd[BATTLE_UNIT_COUNT];
int aiToKillMelee[BATTLE_UNIT_COUNT];
int aiToKillRanged[BATTLE_UNIT_COUNT];
CvBattleRoundVector::iterator iIterator;
int i, j;
bool bIsLoser;
int iRoundIndex;
int iTotalRounds = 0;
int iRoundCheck = BATTLE_TURN_RECHECK;
// Initial conditions
kBattleDefinition.setNumRangedRounds(0);
kBattleDefinition.setNumMeleeRounds(0);
int iFirstStrikesDelta = kBattleDefinition.getFirstStrikes(BATTLE_UNIT_ATTACKER) - kBattleDefinition.getFirstStrikes(BATTLE_UNIT_DEFENDER);
if (iFirstStrikesDelta > 0) // Attacker first strikes
{
int iKills = computeUnitsToDie( kBattleDefinition, true, BATTLE_UNIT_DEFENDER );
kBattleDefinition.setNumRangedRounds(std::max(iFirstStrikesDelta, iKills / iFirstStrikesDelta));
}
else if (iFirstStrikesDelta < 0) // Defender first strikes
{
int iKills = computeUnitsToDie( kBattleDefinition, true, BATTLE_UNIT_ATTACKER );
iFirstStrikesDelta = -iFirstStrikesDelta;
kBattleDefinition.setNumRangedRounds(std::max(iFirstStrikesDelta, iKills / iFirstStrikesDelta));
}
increaseBattleRounds( kBattleDefinition);
// Keep randomizing until we get something valid
do
{
iRoundCheck++;
if ( iRoundCheck >= BATTLE_TURN_RECHECK )
{
increaseBattleRounds( kBattleDefinition);
iTotalRounds = kBattleDefinition.getNumRangedRounds() + kBattleDefinition.getNumMeleeRounds();
iRoundCheck = 0;
}
// Make sure to clear the battle plan, we may have to do this again if we can't find a plan that works.
kBattleDefinition.clearBattleRounds();
// Create the round list
CvBattleRound kRound;
kBattleDefinition.setBattleRound(iTotalRounds, kRound);
// For the attacker and defender
for ( i = 0; i < BATTLE_UNIT_COUNT; i++ )
{
// Gather some initial information
BattleUnitTypes unitType = (BattleUnitTypes) i;
aiUnitsBegin[unitType] = kBattleDefinition.getUnit(unitType)->getSubUnitsAlive(kBattleDefinition.getDamage(unitType, BATTLE_TIME_BEGIN));
aiToKillRanged[unitType] = computeUnitsToDie( kBattleDefinition, true, unitType);
aiToKillMelee[unitType] = computeUnitsToDie( kBattleDefinition, false, unitType);
aiUnitsEnd[unitType] = aiUnitsBegin[unitType] - aiToKillMelee[unitType] - aiToKillRanged[unitType];
// Make sure that if they aren't dead at the end, they have at least one unit left
if ( aiUnitsEnd[unitType] == 0 && !kBattleDefinition.getUnit(unitType)->isDead() )
{
aiUnitsEnd[unitType]++;
if ( aiToKillMelee[unitType] > 0 )
{
aiToKillMelee[unitType]--;
}
else
{
aiToKillRanged[unitType]--;
}
}
// If one unit is the loser, make sure that at least one of their units dies in the last round
if ( aiUnitsEnd[unitType] == 0 )
{
kBattleDefinition.getBattleRound(iTotalRounds - 1).addNumKilled(unitType, 1);
if ( aiToKillMelee[unitType] > 0)
{
aiToKillMelee[unitType]--;
}
else
{
aiToKillRanged[unitType]--;
}
}
// Randomize in which round each death occurs
bIsLoser = aiUnitsEnd[unitType] == 0;
// Randomize the ranged deaths
for ( j = 0; j < aiToKillRanged[unitType]; j++ )
{
iRoundIndex = GC.getGameINLINE().getSorenRandNum( range( kBattleDefinition.getNumRangedRounds(), 0, kBattleDefinition.getNumRangedRounds()), "Ranged combat death");
kBattleDefinition.getBattleRound(iRoundIndex).addNumKilled(unitType, 1);
}
// Randomize the melee deaths
for ( j = 0; j < aiToKillMelee[unitType]; j++ )
{
iRoundIndex = GC.getGameINLINE().getSorenRandNum( range( kBattleDefinition.getNumMeleeRounds() - (bIsLoser ? 1 : 2 ), 0, kBattleDefinition.getNumMeleeRounds()), "Melee combat death");
kBattleDefinition.getBattleRound(kBattleDefinition.getNumRangedRounds() + iRoundIndex).addNumKilled(unitType, 1);
}
// Compute alive sums
int iNumberKilled = 0;
for(int j=0;j<kBattleDefinition.getNumBattleRounds();j++)
{
CvBattleRound &round = kBattleDefinition.getBattleRound(j);
round.setRangedRound(j < kBattleDefinition.getNumRangedRounds());
iNumberKilled += round.getNumKilled(unitType);
round.setNumAlive(unitType, aiUnitsBegin[unitType] - iNumberKilled);
}
}
// Now compute wave sizes
for(int i=0;i<kBattleDefinition.getNumBattleRounds();i++)
{
CvBattleRound &round = kBattleDefinition.getBattleRound(i);
round.setWaveSize(computeWaveSize(round.isRangedRound(), round.getNumAlive(BATTLE_UNIT_ATTACKER) + round.getNumKilled(BATTLE_UNIT_ATTACKER), round.getNumAlive(BATTLE_UNIT_DEFENDER) + round.getNumKilled(BATTLE_UNIT_DEFENDER)));
}
if ( iTotalRounds > 400 )
{
kBattleDefinition.setNumMeleeRounds(1);
kBattleDefinition.setNumRangedRounds(0);
break;
}
}
while ( !verifyRoundsValid( kBattleDefinition ));
//add a little extra time for leader to surrender
bool attackerLeader = false;
bool defenderLeader = false;
bool attackerDie = false;
bool defenderDie = false;
int lastRound = kBattleDefinition.getNumBattleRounds() - 1;
if(kBattleDefinition.getUnit(BATTLE_UNIT_ATTACKER)->getLeaderUnitType() != NO_UNIT)
attackerLeader = true;
if(kBattleDefinition.getUnit(BATTLE_UNIT_DEFENDER)->getLeaderUnitType() != NO_UNIT)
defenderLeader = true;
if(kBattleDefinition.getBattleRound(lastRound).getNumAlive(BATTLE_UNIT_ATTACKER) == 0)
attackerDie = true;
if(kBattleDefinition.getBattleRound(lastRound).getNumAlive(BATTLE_UNIT_DEFENDER) == 0)
defenderDie = true;
int extraTime = 0;
if((attackerLeader && attackerDie) || (defenderLeader && defenderDie))
extraTime = BATTLE_TURNS_MELEE;
if ( (!kBattleDefinition.getUnit(BATTLE_UNIT_ATTACKER)->isUsingDummyEntities() && kBattleDefinition.getUnit(BATTLE_UNIT_ATTACKER)->isInViewport() && showSeigeTower(kBattleDefinition.getUnit(BATTLE_UNIT_ATTACKER))) || //K-mod
(!kBattleDefinition.getUnit(BATTLE_UNIT_DEFENDER)->isUsingDummyEntities() && kBattleDefinition.getUnit(BATTLE_UNIT_DEFENDER)->isInViewport() && showSeigeTower(kBattleDefinition.getUnit(BATTLE_UNIT_DEFENDER))) ) //K-mod
{
extraTime = BATTLE_TURNS_MELEE;
}
return BATTLE_TURNS_SETUP + BATTLE_TURNS_ENDING + kBattleDefinition.getNumMeleeRounds() * BATTLE_TURNS_MELEE + kBattleDefinition.getNumRangedRounds() * BATTLE_TURNS_MELEE + extraTime;
}
//------------------------------------------------------------------------------------------------
// FUNCTION: CvBattleManager::computeDeadUnits
//! \brief Computes the number of units dead, for either the ranged or melee portion of combat.
//! \param kDefinition The battle definition.
//! \param bRanged true if computing the number of units that die during the ranged portion of combat,
//! false if computing the number of units that die during the melee portion of combat.
//! \param iUnit The index of the unit to compute (BATTLE_UNIT_ATTACKER or BATTLE_UNIT_DEFENDER).
//! \retval The number of units that should die for the given unit in the given portion of combat
//------------------------------------------------------------------------------------------------
int CvUnit::computeUnitsToDie( const CvBattleDefinition & kDefinition, bool bRanged, BattleUnitTypes iUnit ) const
{
FAssertMsg( iUnit == BATTLE_UNIT_ATTACKER || iUnit == BATTLE_UNIT_DEFENDER, "Invalid unit index");
BattleTimeTypes iBeginIndex = bRanged ? BATTLE_TIME_BEGIN : BATTLE_TIME_RANGED;
BattleTimeTypes iEndIndex = bRanged ? BATTLE_TIME_RANGED : BATTLE_TIME_END;
return kDefinition.getUnit(iUnit)->getSubUnitsAlive(kDefinition.getDamage(iUnit, iBeginIndex)) -
kDefinition.getUnit(iUnit)->getSubUnitsAlive( kDefinition.getDamage(iUnit, iEndIndex));
}
//------------------------------------------------------------------------------------------------
// FUNCTION: CvUnit::verifyRoundsValid
//! \brief Verifies that all rounds in the battle plan are valid
//! \param vctBattlePlan The battle plan
//! \retval true if the battle plan (seems) valid, false otherwise
//------------------------------------------------------------------------------------------------
bool CvUnit::verifyRoundsValid( const CvBattleDefinition & battleDefinition ) const
{
for(int i=0;i<battleDefinition.getNumBattleRounds();i++)
{
if(!battleDefinition.getBattleRound(i).isValid())
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------
// FUNCTION: CvUnit::increaseBattleRounds
//! \brief Increases the number of rounds in the battle.
//! \param kBattleDefinition The definition of the battle
//------------------------------------------------------------------------------------------------
void CvUnit::increaseBattleRounds( CvBattleDefinition & kBattleDefinition ) const
{
if ( kBattleDefinition.getUnit(BATTLE_UNIT_ATTACKER)->isRanged() && kBattleDefinition.getUnit(BATTLE_UNIT_DEFENDER)->isRanged())
{
kBattleDefinition.addNumRangedRounds(1);
}
else
{
kBattleDefinition.addNumMeleeRounds(1);
}
}
//------------------------------------------------------------------------------------------------
// FUNCTION: CvUnit::computeWaveSize
//! \brief Computes the wave size for the round.
//! \param bRangedRound true if the round is a ranged round
//! \param iAttackerMax The maximum number of attackers that can participate in a wave (alive)
//! \param iDefenderMax The maximum number of Defenders that can participate in a wave (alive)
//! \retval The desired wave size for the given parameters
//------------------------------------------------------------------------------------------------
int CvUnit::computeWaveSize( bool bRangedRound, int iAttackerMax, int iDefenderMax ) const
{
FAssertMsg( getCombatUnit() != NULL, "You must be fighting somebody!" );
int aiDesiredSize[BATTLE_UNIT_COUNT];
if ( bRangedRound )
{
aiDesiredSize[BATTLE_UNIT_ATTACKER] = getUnitInfo().getRangedWaveSize();
aiDesiredSize[BATTLE_UNIT_DEFENDER] = getCombatUnit()->getUnitInfo().getRangedWaveSize();
}
else
{
aiDesiredSize[BATTLE_UNIT_ATTACKER] = getUnitInfo().getMeleeWaveSize();
aiDesiredSize[BATTLE_UNIT_DEFENDER] = getCombatUnit()->getUnitInfo().getMeleeWaveSize();
}
aiDesiredSize[BATTLE_UNIT_DEFENDER] = aiDesiredSize[BATTLE_UNIT_DEFENDER] <= 0 ? iDefenderMax : aiDesiredSize[BATTLE_UNIT_DEFENDER];
aiDesiredSize[BATTLE_UNIT_ATTACKER] = aiDesiredSize[BATTLE_UNIT_ATTACKER] <= 0 ? iDefenderMax : aiDesiredSize[BATTLE_UNIT_ATTACKER];
return std::min( std::min( aiDesiredSize[BATTLE_UNIT_ATTACKER], iAttackerMax ), std::min( aiDesiredSize[BATTLE_UNIT_DEFENDER],
iDefenderMax) );
}
bool CvUnit::isTargetOf(const CvUnit& attacker) const
{
CvUnitInfo& attackerInfo = attacker.getUnitInfo();
CvUnitInfo& ourInfo = getUnitInfo();
if (!plot()->isCity(true, getTeam()))
{
if (NO_UNITCLASS != getUnitClassType() && attackerInfo.getTargetUnitClass(getUnitClassType()))
{
return true;
}
if (NO_UNITCOMBAT != getUnitCombatType() && attackerInfo.getTargetUnitCombat(getUnitCombatType()))
{
return true;
}
}
if (NO_UNITCLASS != attackerInfo.getUnitClassType() && ourInfo.getDefenderUnitClass(attackerInfo.getUnitClassType()))
{
return true;
}
if (NO_UNITCOMBAT != attackerInfo.getUnitCombatType() && ourInfo.getDefenderUnitCombat(attackerInfo.getUnitCombatType()))
{
return true;
}
return false;
}
bool CvUnit::isEnemy(TeamTypes eTeam, const CvPlot* pPlot) const
{
if (NULL == pPlot)
{
pPlot = plot();
}
return (atWar(GET_PLAYER(getCombatOwner(eTeam, pPlot)).getTeam(), eTeam));
}
bool CvUnit::isPotentialEnemy(TeamTypes eTeam, const CvPlot* pPlot) const
{
if (NULL == pPlot)
{
pPlot = plot();
}
return (::isPotentialEnemy(GET_PLAYER(getCombatOwner(eTeam, pPlot)).getTeam(), eTeam));
}
bool CvUnit::isSuicide() const
{
return (m_pUnitInfo->isSuicide() || getKamikazePercent() != 0);
}
int CvUnit::getDropRange() const
{
return (m_pUnitInfo->getDropRange());
}
void CvUnit::getDefenderCombatValues(CvUnit& kDefender, const CvPlot* pPlot, int iOurStrength, int iOurFirepower, int& iTheirOdds, int& iTheirStrength, int& iOurDamage, int& iTheirDamage, CombatDetails* pTheirDetails, CvUnit* pDefender) const
{
iTheirStrength = kDefender.currCombatStr(pPlot, this, pTheirDetails);
int iTheirFirepower = kDefender.currFirepower(pPlot, this);
FAssert((iOurStrength + iTheirStrength) > 0);
FAssert((iOurFirepower + iTheirFirepower) > 0);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Lead From Behind by UncutDragon
/* original code
iTheirOdds = ((GC.getDefineINT("COMBAT_DIE_SIDES") * iTheirStrength) / (iOurStrength + iTheirStrength));
*/ // modified
iTheirOdds = ((GC.getCOMBAT_DIE_SIDES() * iTheirStrength) / (iOurStrength + iTheirStrength));
if (kDefender.isBarbarian())
{
if (GET_PLAYER(getOwnerINLINE()).getWinsVsBarbs() < GC.getHandicapInfo(GET_PLAYER(getOwnerINLINE()).getHandicapType()).getFreeWinsVsBarbs())
{
// UncutDragon
/* original code
iTheirOdds = std::min((10 * GC.getDefineINT("COMBAT_DIE_SIDES")) / 100, iTheirOdds);
*/ // modified
iTheirOdds = std::min((10 * GC.getCOMBAT_DIE_SIDES()) / 100, iTheirOdds);
// /UncutDragon
}
}
if (isBarbarian())
{
if (GET_PLAYER(kDefender.getOwnerINLINE()).getWinsVsBarbs() < GC.getHandicapInfo(GET_PLAYER(kDefender.getOwnerINLINE()).getHandicapType()).getFreeWinsVsBarbs())
{
// UncutDragon
/* original code
iTheirOdds = std::max((90 * GC.getDefineINT("COMBAT_DIE_SIDES")) / 100, iTheirOdds);
*/ // modified
iTheirOdds = std::max((90 * GC.getCOMBAT_DIE_SIDES()) / 100, iTheirOdds);
// /UncutDragon
}
}
int iStrengthFactor = ((iOurFirepower + iTheirFirepower + 1) / 2);
/* original code
iOurDamage = std::max(1, ((GC.getDefineINT("COMBAT_DAMAGE") * (iTheirFirepower + iStrengthFactor)) / (iOurFirepower + iStrengthFactor)));
iTheirDamage = std::max(1, ((GC.getDefineINT("COMBAT_DAMAGE") * (iOurFirepower + iStrengthFactor)) / (iTheirFirepower + iStrengthFactor)));
*/ // modified by both UncutDragon and TB
iOurDamage = std::max(1, ((((GC.getCOMBAT_DAMAGE() * (iTheirFirepower + iStrengthFactor)) / (iOurFirepower + iStrengthFactor)))));
iTheirDamage = std::max(1, ((((GC.getCOMBAT_DAMAGE() * (iOurFirepower + iStrengthFactor)) / (iTheirFirepower + iStrengthFactor)))));
// /UncutDragon
//TB Combat Mods End
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
int CvUnit::getTriggerValue(EventTriggerTypes eTrigger, const CvPlot* pPlot, bool bCheckPlot) const
{
CvEventTriggerInfo& kTrigger = GC.getEventTriggerInfo(eTrigger);
if (kTrigger.getNumUnits() <= 0)
{
return MIN_INT;
}
if (isDead())
{
return MIN_INT;
}
if (kTrigger.getNumUnitsRequired() > 0)
{
bool bFoundValid = false;
for (int i = 0; i < kTrigger.getNumUnitsRequired(); ++i)
{
if (getUnitClassType() == kTrigger.getUnitRequired(i))
{
bFoundValid = true;
break;
}
}
if (!bFoundValid)
{
return MIN_INT;
}
}
if (bCheckPlot)
{
if (kTrigger.isUnitsOnPlot())
{
if (!plot()->canTrigger(eTrigger, getOwnerINLINE()))
{
return MIN_INT;
}
}
}
int iValue = 0;
if (0 == getDamage() && kTrigger.getUnitDamagedWeight() > 0)
{
return MIN_INT;
}
// Call out to Python last as its the most expensive part of the calcuation
// and we'll often have decided the trigger is inapplicable before this
if (!CvString(kTrigger.getPythonCanDoUnit()).empty())
{
PYTHON_ACCESS_LOCK_SCOPE
long lResult;
CyArgsList argsList;
argsList.add(eTrigger);
argsList.add(getOwnerINLINE());
argsList.add(getID());
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYRandomEventModule, kTrigger.getPythonCanDoUnit(), argsList.makeFunctionArgs(), &lResult);
if (0 == lResult)
{
return MIN_INT;
}
}
iValue += getDamage() * kTrigger.getUnitDamagedWeight();
iValue += getExperience() * kTrigger.getUnitExperienceWeight();
if (NULL != pPlot)
{
iValue += plotDistance(getX_INLINE(), getY_INLINE(), pPlot->getX_INLINE(), pPlot->getY_INLINE()) * kTrigger.getUnitDistanceWeight();
}
return iValue;
}
bool CvUnit::canApplyEvent(EventTypes eEvent) const
{
CvEventInfo& kEvent = GC.getEventInfo(eEvent);
if (0 != kEvent.getUnitExperience())
{
if (!canAcquirePromotionAny())
{
return false;
}
}
if (NO_PROMOTION != kEvent.getUnitPromotion())
{
if (!canAcquirePromotion((PromotionTypes)kEvent.getUnitPromotion()))
{
return false;
}
}
if (kEvent.getUnitImmobileTurns() > 0)
{
if (!canAttack())
{
return false;
}
}
return true;
}
void CvUnit::applyEvent(EventTypes eEvent)
{
if (!canApplyEvent(eEvent))
{
return;
}
CvEventInfo& kEvent = GC.getEventInfo(eEvent);
if (0 != kEvent.getUnitExperience())
{
setDamage(0);
changeExperience(kEvent.getUnitExperience());
}
if (NO_PROMOTION != kEvent.getUnitPromotion())
{
setHasPromotion((PromotionTypes)kEvent.getUnitPromotion(), true);
}
if (kEvent.getUnitImmobileTurns() > 0)
{
changeImmobileTimer(kEvent.getUnitImmobileTurns());
MEMORY_TRACK_EXEMPT();
CvWString szText = gDLL->getText("TXT_KEY_EVENT_UNIT_IMMOBILE", getNameKey(), kEvent.getUnitImmobileTurns());
AddMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szText, "AS2D_UNITGIFTED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_UNIT_TEXT"), getX_INLINE(), getY_INLINE(), true, true);
}
CvWString szNameKey(kEvent.getUnitNameKey());
if (!szNameKey.empty())
{
setName(gDLL->getText(kEvent.getUnitNameKey()));
}
if (kEvent.isDisbandUnit())
{
kill(false, NO_PLAYER, true);
}
}
const CvArtInfoUnit* CvUnit::getArtInfo(int i, EraTypes eEra) const
{
return m_pUnitInfo->getArtInfo(i, eEra, (UnitArtStyleTypes) GC.getCivilizationInfo(getCivilizationType()).getUnitArtStyleType());
}
const TCHAR* CvUnit::getButton() const
{
const CvArtInfoUnit* pArtInfo = getArtInfo(0, GET_PLAYER(getOwnerINLINE()).getCurrentEra());
if (NULL != pArtInfo)
{
return pArtInfo->getButton();
}
return m_pUnitInfo->getButton();
}
int CvUnit::getGroupSize() const
{
return m_pUnitInfo->getGroupSize();
}
int CvUnit::getGroupDefinitions() const
{
return m_pUnitInfo->getGroupDefinitions();
}
int CvUnit::getUnitGroupRequired(int i) const
{
return m_pUnitInfo->getUnitGroupRequired(i);
}
bool CvUnit::isRenderAlways() const
{
return m_pUnitInfo->isRenderAlways();
}
float CvUnit::getAnimationMaxSpeed() const
{
return m_pUnitInfo->getUnitMaxSpeed();
}
float CvUnit::getAnimationPadTime() const
{
return m_pUnitInfo->getUnitPadTime();
}
const char* CvUnit::getFormationType() const
{
return m_pUnitInfo->getFormationType();
}
bool CvUnit::isMechUnit() const
{
return m_pUnitInfo->isMechUnit();
}
bool CvUnit::isRenderBelowWater() const
{
return m_pUnitInfo->isRenderBelowWater();
}
int CvUnit::getRenderPriority(UnitSubEntityTypes eUnitSubEntity, int iMeshGroupType, int UNIT_MAX_SUB_TYPES) const
{
if (eUnitSubEntity == UNIT_SUB_ENTITY_SIEGE_TOWER)
{
return (getOwner() * (GC.getNumUnitInfos() + 2) * UNIT_MAX_SUB_TYPES) + iMeshGroupType;
}
else
{
return (getOwner() * (GC.getNumUnitInfos() + 2) * UNIT_MAX_SUB_TYPES) + m_eUnitType * UNIT_MAX_SUB_TYPES + iMeshGroupType;
}
}
bool CvUnit::isAlwaysHostile(const CvPlot* pPlot) const
{
if (!m_pUnitInfo->isAlwaysHostile())
{
return false;
}
if (NULL != pPlot && pPlot->isCity(true, getTeam()))
{
return false;
}
return true;
}
bool CvUnit::verifyStackValid()
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pPlot;
pPlot = plot();
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if ( pLoopUnit != this )
{
if (isEnemy(pLoopUnit->getTeam()) && !pLoopUnit->isInvisible(getTeam(),false) && !canCoexistWithEnemyUnit(pLoopUnit->getTeam()))
{
return jumpToNearestValidPlot();
}
}
}
return true;
}
// Private Functions...
//check if quick combat
bool CvUnit::isCombatVisible(const CvUnit* pDefender) const
{
bool bVisible = false;
if (!m_pUnitInfo->isQuickCombat())
{
if (NULL == pDefender || !pDefender->getUnitInfo().isQuickCombat())
{
if (isHuman())
{
if (!GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_QUICK_ATTACK))
{
bVisible = true;
}
}
else if (NULL != pDefender && pDefender->isHuman())
{
if (!GET_PLAYER(pDefender->getOwnerINLINE()).isOption(PLAYEROPTION_QUICK_DEFENSE))
{
bVisible = true;
}
}
}
}
return bVisible;
}
// used by the executable for the red glow and plot indicators
bool CvUnit::shouldShowEnemyGlow(TeamTypes eForTeam) const
{
if (isDelayedDeath())
{
return false;
}
if (getDomainType() == DOMAIN_AIR)
{
return false;
}
if (!canFight())
{
return false;
}
CvPlot* pPlot = plot();
if (pPlot == NULL)
{
return false;
}
TeamTypes ePlotTeam = pPlot->getTeam();
if (ePlotTeam != eForTeam)
{
return false;
}
if (!isEnemy(ePlotTeam))
{
return false;
}
return true;
}
bool CvUnit::shouldShowFoundBorders() const
{
return isFound();
}
void CvUnit::cheat(bool bCtrl, bool bAlt, bool bShift)
{
if (gDLL->getChtLvl() > 0)
{
if (bCtrl)
{
setPromotionReady(true);
}
}
}
float CvUnit::getHealthBarModifier() const
{
return (GC.getDefineFLOAT("HEALTH_BAR_WIDTH") / (GC.getGameINLINE().getBestLandUnitCombat() * 2));
}
void CvUnit::getLayerAnimationPaths(std::vector<AnimationPathTypes>& aAnimationPaths) const
{
for (int i=0; i < GC.getNumPromotionInfos(); ++i)
{
PromotionTypes ePromotion = (PromotionTypes) i;
if (isHasPromotion(ePromotion))
{
AnimationPathTypes eAnimationPath = (AnimationPathTypes) GC.getPromotionInfo(ePromotion).getLayerAnimationPath();
if(eAnimationPath != ANIMATIONPATH_NONE)
{
aAnimationPaths.push_back(eAnimationPath);
}
}
}
}
int CvUnit::getSelectionSoundScript() const
{
int iScriptId = getArtInfo(0, GET_PLAYER(getOwnerINLINE()).getCurrentEra())->getSelectionSoundScriptId();
if (iScriptId == -1)
{
iScriptId = GC.getCivilizationInfo(getCivilizationType()).getSelectionSoundScriptId();
}
return iScriptId;
}
/************************************************************************************************/
/* DCM 04/19/09 Johny Smith */
/************************************************************************************************/
// Dale - AB: Bombing START
bool CvUnit::canAirBomb1(const CvPlot* pPlot) const
{
if (!GC.isDCM_AIR_BOMBING())
{
return false;
}
if (!GC.getUnitInfo(getUnitType()).getDCMAirBomb1())
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (airBombBaseRate() == 0)
{
return false;
}
if (isMadeAttack())
{
return false;
}
return true;
}
bool CvUnit::canAirBomb1At(const CvPlot* pPlot, int iX, int iY) const
{
CvCity* pCity;
CvPlot* pTargetPlot;
if (!canAirBomb1(pPlot))
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > airRange())
{
return false;
}
if (pTargetPlot->isOwned())
{
if (!atWar(pTargetPlot->getTeam(), getTeam()))
{
return false;
}
}
pCity = pTargetPlot->getPlotCity();
if (pCity != NULL)
{
if (!(pCity->isBombardable(this)))
{
return false;
}
}
else
{
if (pTargetPlot->getImprovementType() == NO_IMPROVEMENT)
{
return false;
}
if (GC.getImprovementInfo(pTargetPlot->getImprovementType()).isPermanent())
{
return false;
}
if (GC.getImprovementInfo(pTargetPlot->getImprovementType()).getAirBombDefense() == -1)
{
return false;
}
}
return true;
}
bool CvUnit::airBomb1(int iX, int iY)
{
CvCity* pCity;
CvPlot* pPlot;
CvWString szBuffer;
bool bBitter = true;
if (!canAirBomb1At(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (interceptTest(pPlot))
{
return true;
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
pCity = pPlot->getPlotCity();
if (bBitter)
{
if (pCity != NULL)
{
pCity->changeDefenseDamage(airBombCurrRate());
bool bBarb = pCity->isBarbarian();
changeExperience(GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), bBarb ? GC.getDefineINT("BARBARIAN_MAX_XP_VALUE") : -1, !bBarb, pCity->getOwnerINLINE() == getOwnerINLINE());
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_DEFENSES_REDUCED_TO", pCity->getNameKey(), pCity->getDefenseModifier(false), getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_DEFENSES_REDUCED_TO", getNameKey(), pCity->getNameKey(), pCity->getDefenseModifier(false));
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE());
}
else
{
if (pPlot->getImprovementType() != NO_IMPROVEMENT)
{
if (GC.getGameINLINE().getSorenRandNum(airBombCurrRate(), "Air Bomb - Offense") >=
GC.getGameINLINE().getSorenRandNum(GC.getImprovementInfo(pPlot->getImprovementType()).getAirBombDefense(), "Air Bomb - Defense"))
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_DESTROYED_IMP", getNameKey(), GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_PILLAGE", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
if (pPlot->isOwned())
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_IMP_WAS_DESTROYED", GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide(), getNameKey(), GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey());
AddMessage(pPlot->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_PILLAGED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
bool bBarb = GET_PLAYER(pPlot->getOwnerINLINE()).isBarbarian();
changeExperience(GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), bBarb ? GC.getDefineINT("BARBARIAN_MAX_XP_VALUE") : -1, !bBarb, pPlot->getOwnerINLINE() == getOwnerINLINE());
}
pPlot->setImprovementType((ImprovementTypes)(GC.getImprovementInfo(pPlot->getImprovementType()).getImprovementPillage()));
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_FAIL_DESTROY_IMP", getNameKey(), GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMB_FAILS", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
}
}
}
setReconPlot(pPlot);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
if (pPlot->isActiveVisible(false))
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRBOMB);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo((MissionTypes)MISSION_AIRBOMB).getTime() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
if (isSuicide())
{
kill(true);
}
return true;
}
bool CvUnit::canAirBomb2(const CvPlot* pPlot) const
{
if (!GC.isDCM_AIR_BOMBING())
{
return false;
}
if (!GC.getUnitInfo(getUnitType()).getDCMAirBomb2())
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (airBombBaseRate() == 0)
{
return false;
}
if (isMadeAttack())
{
return false;
}
return true;
}
bool CvUnit::canAirBomb2At(const CvPlot* pPlot, int iX, int iY) const
{
CvCity* pCity;
CvPlot* pTargetPlot;
if (!canAirBomb2(pPlot))
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > airRange())
{
return false;
}
if (pTargetPlot->isOwned())
{
if (!atWar(pTargetPlot->getTeam(), getTeam()))
{
return false;
}
}
pCity = pTargetPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
return true;
}
bool CvUnit::airBomb2(int iX, int iY)
{
CvCity* pCity;
CvPlot* pPlot;
CvWString szBuffer;
int build, iI, iAttempts, iMaxAttempts;
bool bBitter = true;
bool bNoTarget = true;
bool abTech1 = false;
bool abTech2 = false;
CLinkList<int> buildingList;
if (!canAirBomb2At(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (interceptTest(pPlot))
{
return true;
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
pCity = pPlot->getPlotCity();
for (iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (GC.getTechInfo((TechTypes)iI).getDCMAirBombTech1())
{
if (GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isHasTech((TechTypes)iI))
{
abTech1 = true;
}
}
if (GC.getTechInfo((TechTypes)iI).getDCMAirBombTech2())
{
if (GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isHasTech((TechTypes)iI))
{
abTech2 = true;
}
}
}
if (bBitter)
{
if (pCity != NULL)
{
buildingList.clear();
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getDCMAirbombMission() == 2)
{
buildingList.insertAtEnd(iI);
}
}
if (buildingList.getLength() > 0)
{
iI = GC.getGameINLINE().getSorenRandNum(buildingList.getLength(), "Airbomb building");
build = buildingList.nodeNum(iI)->m_data;
if (pCity->getNumRealBuilding((BuildingTypes)build) > 0)
{
bNoTarget = false;
}
if (abTech1)
{
iAttempts = 0;
if (abTech2)
{
iMaxAttempts = 8;
}
else
{
iMaxAttempts = 4;
}
while (bNoTarget)
{
iAttempts++;
iI = GC.getGameINLINE().getSorenRandNum(buildingList.getLength(), "Airbomb building");
build = buildingList.nodeNum(iI)->m_data;
if (pCity->getNumRealBuilding((BuildingTypes)build) > 0 || iAttempts > iMaxAttempts)
{
bNoTarget = false;
}
}
}
if (pCity->getNumRealBuilding((BuildingTypes)build) > 0)
{
bNoTarget = false;
bool bBarb = pCity->isBarbarian();
changeExperience(GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), bBarb ? GC.getDefineINT("BARBARIAN_MAX_XP_VALUE") : -1, !bBarb, pCity->getOwnerINLINE() == getOwnerINLINE());
pCity->setNumRealBuilding((BuildingTypes)build, 0);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB2SUCCESS", GC.getBuildingInfo((BuildingTypes)build).getTextKeyWide(), pCity->getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB2SUCCESS", GC.getBuildingInfo((BuildingTypes)build).getTextKeyWide(), pCity->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB2FAIL", pCity->getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB2FAIL", pCity->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
}
if(bNoTarget)
{
if(pCity->getPopulation() > 1)
{
if(GC.getGameINLINE().getSorenRandNum(5, "Airbomb population") < 2)
{
pCity->changePopulation(-1);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB_POP");
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB_POP");
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
}
}
}
}
}
setReconPlot(pPlot);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
if (pPlot->isActiveVisible(false))
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRBOMB);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo((MissionTypes)MISSION_AIRBOMB).getTime() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
if (isSuicide())
{
kill(true);
}
return true;
}
bool CvUnit::canAirBomb3(const CvPlot* pPlot) const
{
if (!GC.isDCM_AIR_BOMBING())
{
return false;
}
if (!GC.getUnitInfo(getUnitType()).getDCMAirBomb3())
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (airBombBaseRate() == 0)
{
return false;
}
if (isMadeAttack())
{
return false;
}
return true;
}
bool CvUnit::canAirBomb3At(const CvPlot* pPlot, int iX, int iY) const
{
CvCity* pCity;
CvPlot* pTargetPlot;
if (!canAirBomb3(pPlot))
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > airRange())
{
return false;
}
if (pTargetPlot->isOwned())
{
if (!atWar(pTargetPlot->getTeam(), getTeam()))
{
return false;
}
}
pCity = pTargetPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
return true;
}
bool CvUnit::airBomb3(int iX, int iY)
{
CvCity* pCity;
CvPlot* pPlot;
CvWString szBuffer;
int build, iI, iAttempts, iMaxAttempts;
bool bNoTarget = true;
bool bBitter = true;
bool abTech1 = false;
bool abTech2 = false;
CLinkList<int> buildingList;
if (!canAirBomb3At(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (interceptTest(pPlot))
{
return true;
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
pCity = pPlot->getPlotCity();
for (iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (GC.getTechInfo((TechTypes)iI).getDCMAirBombTech1())
{
if (GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isHasTech((TechTypes)iI))
{
abTech1 = true;
}
}
if (GC.getTechInfo((TechTypes)iI).getDCMAirBombTech2())
{
if (GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isHasTech((TechTypes)iI))
{
abTech2 = true;
}
}
}
if (bBitter)
{
if (pCity != NULL)
{
buildingList.clear();
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getDCMAirbombMission() == 3)
{
buildingList.insertAtEnd(iI);
}
}
if (buildingList.getLength() > 0)
{
iI = GC.getGameINLINE().getSorenRandNum(buildingList.getLength(), "Airbomb building");
build = buildingList.nodeNum(iI)->m_data;
if (pCity->getNumRealBuilding((BuildingTypes)build) > 0)
{
bNoTarget = false;
}
if (abTech1)
{
iAttempts = 0;
if (abTech2)
{
iMaxAttempts = 8;
}
else
{
iMaxAttempts = 4;
}
while (bNoTarget)
{
iAttempts++;
iI = GC.getGameINLINE().getSorenRandNum(buildingList.getLength(), "Airbomb building");
build = buildingList.nodeNum(iI)->m_data;
if (pCity->getNumRealBuilding((BuildingTypes)build) > 0 || iAttempts > iMaxAttempts)
{
bNoTarget = false;
}
}
}
if (pCity->getNumRealBuilding((BuildingTypes)build) > 0)
{
pCity->setNumRealBuilding((BuildingTypes)build, 0);
bool bBarb = pCity->isBarbarian();
changeExperience(GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), bBarb ? GC.getDefineINT("BARBARIAN_MAX_XP_VALUE") : -1, !bBarb, pCity->getOwnerINLINE() == getOwnerINLINE());
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB3SUCCESS", GC.getBuildingInfo((BuildingTypes)build).getTextKeyWide(), pCity->getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB3SUCCESS", GC.getBuildingInfo((BuildingTypes)build).getTextKeyWide(), pCity->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB3FAIL", pCity->getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB3FAIL", pCity->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
}
if(bNoTarget)
{
if(pCity->getPopulation() > 1)
{
if(GC.getGameINLINE().getSorenRandNum(5, "Airbomb population") < 1)
{
pCity->changePopulation(-1);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB_POP");
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB_POP");
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
}
}
}
}
}
setReconPlot(pPlot);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
if (pPlot->isActiveVisible(false))
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRBOMB);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo((MissionTypes)MISSION_AIRBOMB).getTime() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
if (isSuicide())
{
kill(true);
}
return true;
}
bool CvUnit::canAirBomb4(const CvPlot* pPlot) const
{
if (!GC.isDCM_AIR_BOMBING())
{
return false;
}
if (!GC.getUnitInfo(getUnitType()).getDCMAirBomb4())
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (airBombBaseRate() == 0)
{
return false;
}
if (isMadeAttack())
{
return false;
}
return true;
}
bool CvUnit::canAirBomb4At(const CvPlot* pPlot, int iX, int iY) const
{
CvCity* pCity;
CvPlot* pTargetPlot;
CvUnit* pLoopUnit;
int iI, iLoop;
if (!canAirBomb4(pPlot))
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > airRange())
{
return false;
}
if (pTargetPlot->isOwned())
{
if (!atWar(pTargetPlot->getTeam(), getTeam()))
{
return false;
}
}
pCity = pTargetPlot->getPlotCity();
if (pCity != NULL)
{
for (iI = 0; iI < MAX_PLAYERS; ++iI)
{
if (atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), getTeam()))
{
for(pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->plot() == pTargetPlot)
{
if (pLoopUnit->getDomainType() == DOMAIN_SEA)
{
return true;
}
}
}
}
}
}
if (pTargetPlot->getImprovementType() != NO_IMPROVEMENT)
{
if (GC.getImprovementInfo(pTargetPlot->getImprovementType()).isActsAsCity() && pCity == NULL)
{
for (iI = 0; iI < MAX_PLAYERS; ++iI)
{
if (atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), getTeam()))
{
for(pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->plot() == pTargetPlot)
{
if (pLoopUnit->getDomainType() == DOMAIN_SEA)
{
return true;
}
}
}
}
}
}
}
return false;
}
bool CvUnit::airBomb4(int iX, int iY)
{
CvCity* pCity;
CvUnit* pUnit;
CvPlot* pPlot;
CvWString szBuffer;
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
int iDamage, iCount;
int iUnitDamage;
bool bBitter = true;
bool bNoTarget = true;
if (!canAirBomb4At(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (interceptTest(pPlot))
{
return true;
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
pCity = pPlot->getPlotCity();
iCount = 0;
pUnit = NULL;
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getDomainType() == DOMAIN_SEA)
{
iCount++;
}
}
iCount = (GC.getGameINLINE().getSorenRandNum(iCount, "Choose ship") + 1);
pUnitNode = pPlot->headUnitNode();
while (iCount > 0)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getDomainType() == DOMAIN_SEA)
{
iCount--;
pUnit = pLoopUnit;
}
}
if (bBitter)
{
// if (pCity != NULL)
{
if (pUnit != NULL)
{
bNoTarget = false;
if (pCity != NULL)
{
bool bBarb = pCity->isBarbarian();
changeExperience(GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), bBarb ? GC.getDefineINT("BARBARIAN_MAX_XP_VALUE") : -1, !bBarb, pCity->getOwnerINLINE() == getOwnerINLINE());
}
iDamage = (airCombatDamage(pUnit) * 2);
iUnitDamage = std::max(pUnit->getDamage(), std::min((pUnit->getDamage() + iDamage), airCombatLimit()));
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ARE_ATTACKED_BY_AIR", pUnit->getNameKey(), getNameKey(), -(((iUnitDamage - pUnit->getDamage()) * 100) / pUnit->maxHitPoints()));
AddMessage(pUnit->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_AIR_ATTACK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ATTACK_BY_AIR", getNameKey(), pUnit->getNameKey(), -(((iUnitDamage - pUnit->getDamage()) * 100) / pUnit->maxHitPoints()));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_AIR_ATTACKED", MESSAGE_TYPE_INFO, pUnit->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
pUnit->setDamage(iUnitDamage, getOwnerINLINE());
if (GC.getGameINLINE().getSorenRandNum(100, "Spin the dice") < 50)
{
pUnit->setDamage(GC.getMAX_HIT_POINTS());
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMYSINK_AIRBOMB4SUCCESS", pUnit->getNameKey());
AddMessage(pUnit->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUSINK_AIRBOMB4SUCCESS", pUnit->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
}
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB4FAIL", pCity->getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB4FAIL", pCity->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
if(bNoTarget)
{
if (pCity != NULL)
{
if(pCity->getPopulation() > 1)
{
if(GC.getGameINLINE().getSorenRandNum(5, "Airbomb population") < 1)
{
pCity->changePopulation(-1);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB_POP");
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB_POP");
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
}
}
}
}
}
}
setReconPlot(pPlot);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
if (pPlot->isActiveVisible(false))
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRBOMB);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo((MissionTypes)MISSION_AIRBOMB).getTime() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
if (isSuicide())
{
kill(true);
}
return true;
}
bool CvUnit::canAirBomb5(const CvPlot* pPlot) const
{
if (!GC.isDCM_AIR_BOMBING())
{
return false;
}
if (!GC.getUnitInfo(getUnitType()).getDCMAirBomb5())
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (airBombBaseRate() == 0)
{
return false;
}
if (isMadeAttack())
{
return false;
}
return true;
}
bool CvUnit::canAirBomb5At(const CvPlot* pPlot, int iX, int iY) const
{
CvCity* pCity;
CvPlot* pTargetPlot;
if (!canAirBomb5(pPlot))
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > airRange())
{
return false;
}
if (pTargetPlot->isOwned())
{
if (!atWar(pTargetPlot->getTeam(), getTeam()))
{
return false;
}
}
pCity = pTargetPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
return true;
}
bool CvUnit::airBomb5(int iX, int iY)
{
CvCity* pCity;
CvPlot* pPlot;
CvWString szBuffer;
bool bNoTarget = true;
bool bBitter = true;
if (!canAirBomb5At(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (interceptTest(pPlot))
{
return true;
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
pCity = pPlot->getPlotCity();
if (bBitter)
{
if (pCity != NULL)
{
if (GC.getGameINLINE().getSorenRandNum(100, "Airbomb") < 50)
{
bNoTarget = false;
pCity->setProduction(pCity->getProduction() / 2);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB5SUCCESS", pCity->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB5SUCCESS", pCity->getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
bool bBarb = pCity->isBarbarian();
changeExperience(GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), bBarb ? GC.getDefineINT("BARBARIAN_MAX_XP_VALUE") : -1, !bBarb, pCity->getOwnerINLINE() == getOwnerINLINE());
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB5FAIL", pCity->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB5FAIL", pCity->getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
if(bNoTarget)
{
if(pCity->getPopulation() > 1)
{
if(GC.getGameINLINE().getSorenRandNum(5, "Airbomb population") < 1)
{
pCity->changePopulation(-1);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIRBOMB_POP");
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_AIRBOMB_POP");
AddMessage(pCity->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
}
}
}
}
}
}
setReconPlot(pPlot);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
if (pPlot->isActiveVisible(false))
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRBOMB);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, NULL);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo((MissionTypes)MISSION_AIRBOMB).getTime() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
if (isSuicide())
{
kill(true);
}
return true;
}
// Dale - AB: Bombing END
// Dale - RB: Field Bombard START
bool CvUnit::canRBombard(const CvPlot* pPlot) const
{
if (!GC.isDCM_RANGE_BOMBARD())
{
return false;
}
if(getDCMBombRange() < 1)
{
return false;
}
if (getDomainType() == DOMAIN_AIR)
{
return false;
}
// RevolutionDCM - end
if (bombardRate() <= 0)
{
return false;
}
if (isMadeAttack())
{
return false;
}
if (isCargo())
{
return false;
}
return true;
}
bool CvUnit::canBombardAtRanged(const CvPlot* pPlot, int iX, int iY) const
{
CvCity* pCity;
CvPlot* pTargetPlot;
if (!canRBombard(pPlot))
{
return false;
}
if(iX < 0 || iY < 0)
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > getDCMBombRange())
{
return false;
}
if (pTargetPlot->isOwned())
{
if(pTargetPlot->getTeam() != getTeam())
{
if (!atWar(pTargetPlot->getTeam(), getTeam()))
{
return false;
}
}
}
pCity = pTargetPlot->getPlotCity();
if (pCity != NULL)
{
if (!(pCity->isBombardable(this)))
{
if(pTargetPlot->getNumVisibleEnemyDefenders(this) == 0)
{
return false;
}
}
} else {
if(pTargetPlot->getNumVisibleEnemyDefenders(this) == 0)
{
if (pTargetPlot->getImprovementType() == NO_IMPROVEMENT)
{
return false;
}
if (GC.getImprovementInfo(pTargetPlot->getImprovementType()).isPermanent())
{
return false;
}
if (GC.getImprovementInfo(pTargetPlot->getImprovementType()).getAirBombDefense() == -1)
{
return false;
}
}
}
return true;
}
// RevolutionDCM - significant chances to this function
bool CvUnit::bombardRanged(int iX, int iY, bool sAttack)
{
CvCity* pCity;
CvPlot* pPlot;
CvWString szBuffer;
CvUnit* pLoopUnit = NULL;
if (!canBombardAtRanged(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (pPlot == NULL)
{
return false;
}
int modified_accuracy = GC.getDCM_RB_CITY_INACCURACY();
if (modified_accuracy <= 0)
{
modified_accuracy = 350; // default
}
int modified_miss = GC.getDCM_RB_CITYBOMBARD_CHANCE();
if (modified_miss <= 0)
{
modified_miss = 5; // default
}
pCity = pPlot->getPlotCity();
if (pCity != NULL)
{
int bombardCity = GC.getGameINLINE().getSorenRandNum(modified_miss, "Range Bombard City");
// Introduce a slight chance that ranged bombardment hits city defenders rather than defenses
if(pCity->isBombardable(this) && bombardCity > 0)
{
// RevolutionDCM - city bombard no different from vanilla in essense.
int iBombardModifier = 0;
if (!ignoreBuildingDefense())
{
iBombardModifier -= pCity->getBuildingBombardDefense();
}
// RevolutionDCM - only difference to standard city bombardment is to scale back
// bombard damage according to distance from the city.
int distance = plotDistance(plot()->getX_INLINE(), plot()->getY_INLINE(), pPlot->getX_INLINE(), pPlot->getY_INLINE());
if (distance > 1)
{
iBombardModifier = -50;
}
pCity->changeDefenseModifier(-(bombardRate() * std::max(0, 100 + iBombardModifier)) / 100);
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_DEFENSES_IN_CITY_REDUCED_TO", pCity->getNameKey(), pCity->getDefenseModifier(false), GET_PLAYER(getOwnerINLINE()).getNameKey());
AddMessage(pCity->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCity->getX_INLINE(), pCity->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_REDUCE_CITY_DEFENSES", getNameKey(), pCity->getNameKey(), pCity->getDefenseModifier(false));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pCity->getX_INLINE(), pCity->getY_INLINE());
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
} else
{
// Give a reduced chance of range bombarding city defenders by default
int odds = modified_accuracy;
// Occasionally give city bombard a better chance at hitting city defenders if the city defenses
// are still bombardable. This produces differentiation from standard bombard that seige also have
// available to them as an option, and compensates range bombard for not lowering city defenses.
if (pCity->isBombardable(this) && bombardCity == 0)
{
odds = 100;
}
// standard odds made worse if greater than one tile out
int shotDistance = plotDistance(plot()->getX_INLINE(), plot()->getY_INLINE(), pPlot->getX_INLINE(), pPlot->getY_INLINE());
odds += (shotDistance - 1) * 50;
pLoopUnit = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
if (pLoopUnit != NULL)
{
// RevolutionDCM - change proposal to ranged bombardment. Only collateral damage can be issued.
if (GC.getGameINLINE().getSorenRandNum(odds, "Bombard Accuracy") <= getDCMBombAccuracy())
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_HAS_RANGED_BOMBARD_ATTACKED", getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_HAS_BEEN_RANGED_BOMBARD_ATTACKED", getNameKey());
AddMessage(pLoopUnit->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
}
collateralCombat(pPlot, pLoopUnit);
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_BOMB_MISSED", getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_BOMB_MISSED", getNameKey());
AddMessage(pLoopUnit->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
}
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
}
} else
{
// Field bombard case. If bombarding from a city, odds of success are reduced
// For the sake of game balance by default.
int odds = 100;
if (plot()->getPlotCity() != NULL)
{
odds = modified_accuracy;
}
// standard odds made worse if greater than one tile out
int shotDistance = plotDistance(plot()->getX_INLINE(), plot()->getY_INLINE(), pPlot->getX_INLINE(), pPlot->getY_INLINE());
odds += (shotDistance - 1) * 50;
pLoopUnit = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
if (pLoopUnit != NULL)
{
//RevolutionDCM - change proposal to ranged bombardment. Only collateral damage can be issued.
if (GC.getGameINLINE().getSorenRandNum(odds, "Bombard Accuracy") <= getDCMBombAccuracy())
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_HAS_RANGED_BOMBARD_ATTACKED", getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_HAS_BEEN_RANGED_BOMBARD_ATTACKED", getNameKey());
AddMessage(pLoopUnit->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
}
collateralCombat(pPlot, pLoopUnit);
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_BOMB_MISSED", getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_BOMB_MISSED", getNameKey());
AddMessage(pLoopUnit->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
} else
{
// Plot bombardment
if (pPlot->getImprovementType() != NO_IMPROVEMENT)
{
if (GC.getGameINLINE().getSorenRandNum(bombardRate(), "Bomb - Offense") >=
GC.getGameINLINE().getSorenRandNum(GC.getImprovementInfo(pPlot->getImprovementType()).getAirBombDefense(), "Bomb - Defense"))
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_DESTROYED_IMP", getNameKey(), GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_PILLAGE", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
if (pPlot->isOwned())
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_IMP_WAS_DESTROYED", GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide(), getNameKey(), GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey());
AddMessage(pPlot->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_PILLAGE", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
}
pPlot->setImprovementType((ImprovementTypes)(GC.getImprovementInfo(pPlot->getImprovementType()).getImprovementPillage()));
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_FAIL_DESTROY_IMP", getNameKey(), GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide());
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMB_FAILS", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
}
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
}
}
if (!sAttack)
{
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
}
if (pPlot->isActiveVisible(false))
{
// Bombard entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_RBOMBARD).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_BOMBARD);
kDefiniton.setPlot(pPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, pLoopUnit);
addMission(&kDefiniton);
}
return true;
}
// RevolutionDCM - ranged bombard
// Estimate if a unit stack is worth range bombarding
bool CvUnit::isRbombardable(int iMinStack)
{
int collateralCount = 0;
int averageDamage = 0;
int averageProtection = 0;
int seigeCount = 0;
CvUnit* nextUnit = NULL;
int unitCount = plot()->getNumUnits();
if (unitCount >= iMinStack)
{
for (int i = 0; i < unitCount; i++)
{
nextUnit = plot()->getUnitByIndex(i);
if (nextUnit != NULL)
{
if (nextUnit->canRBombard(plot()))
{
seigeCount++;
}
averageDamage += nextUnit->getDamage();
averageProtection += nextUnit->getCollateralDamageProtection();
}
}
if (unitCount > 0)
{
collateralCount = unitCount - seigeCount;
averageDamage /= unitCount;
averageProtection /= unitCount;
if (collateralCount > 1 && collateralCount < 8 && averageDamage < 40 && averageProtection < 10)
{
return true;
}
}
}
return false;
}
int CvUnit::getRbombardSeigeCount(CvPlot* pPlot)
{
CvUnit* nextUnit = NULL;
int seigeCount = 0;
if (pPlot == NULL)
{
return 0;
}
int unitCount = pPlot->getNumUnits();
for (int i = 0; i < unitCount; i++)
{
nextUnit = pPlot->getUnitByIndex(i);
if (nextUnit != NULL)
{
if (nextUnit->canRBombard(pPlot))
{
seigeCount++;
}
}
}
return seigeCount;
}
// RevolutionDCM - end
int CvUnit::getDCMBombRange() const
{
return GC.getUnitInfo(getUnitType()).getDCMBombRange();
}
int CvUnit::getDCMBombAccuracy() const
{
return GC.getUnitInfo(getUnitType()).getDCMBombAccuracy();
}
// Dale - RB: Field Bombard END
// Dale - SA: Stack Attack START
void CvUnit::updateStackCombat(bool bQuick)
{
CvWString szBuffer;
bool bFinish = false;
bool bVisible = false;
if (getCombatTimer() > 0)
{
changeCombatTimer(-1);
if (getCombatTimer() > 0)
{
return;
}
else
{
bFinish = true;
}
}
CvPlot* pPlot = getAttackPlot();
if (pPlot == NULL)
{
return;
}
if (getDomainType() == DOMAIN_AIR)
{
updateAirStrike(pPlot, bQuick, bFinish);
return;
}
CvUnit* pDefender = NULL;
if (bFinish)
{
pDefender = getCombatUnit();
}
else
{
pDefender = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
}
if (pDefender == NULL)
{
setAttackPlot(NULL, false);
setCombatUnit(NULL);
getGroup()->groupMove(pPlot, true, ((canAdvance(pPlot, 0)) ? this : NULL));
getGroup()->clearMissionQueue();
return;
}
//check if quick combat
if (!bQuick)
{
bVisible = isCombatVisible(pDefender);
}
//FAssertMsg((pPlot == pDefender->plot()), "There is not expected to be a defender or the defender's plot is expected to be pPlot (the attack plot)");
//if not finished and not fighting yet, set up combat damage and mission
if (!bFinish)
{
if (!isFighting())
{
// if (plot()->isFighting() || pPlot->isFighting())
// {
// return;
// }
setMadeAttack(true);
//rotate to face plot
DirectionTypes newDirection = estimateDirection(this->plot(), pDefender->plot());
if (newDirection != NO_DIRECTION)
{
setFacingDirection(newDirection);
}
//rotate enemy to face us
newDirection = estimateDirection(pDefender->plot(), this->plot());
if (newDirection != NO_DIRECTION)
{
pDefender->setFacingDirection(newDirection);
}
setCombatUnit(pDefender, true);
pDefender->setCombatUnit(this, false);
pDefender->getGroup()->clearMissionQueue();
bool bFocused = (bVisible && isCombatFocus() && gDLL->getInterfaceIFace()->isCombatFocus() && plot()->isInViewport() && pDefender->isInViewport());
if (bFocused)
{
DirectionTypes directionType = directionXY(plot(), pPlot);
// N NE E SE S SW W NW
NiPoint2 directions[8] = {NiPoint2(0, 1), NiPoint2(1, 1), NiPoint2(1, 0), NiPoint2(1, -1), NiPoint2(0, -1), NiPoint2(-1, -1), NiPoint2(-1, 0), NiPoint2(-1, 1)};
NiPoint3 attackDirection = NiPoint3(directions[directionType].x, directions[directionType].y, 0);
float plotSize = GC.getPLOT_SIZE();
NiPoint3 lookAtPoint(plot()->getPoint().x + plotSize / 2 * attackDirection.x, plot()->getPoint().y + plotSize / 2 * attackDirection.y, (plot()->getPoint().z + pPlot->getPoint().z) / 2);
attackDirection.Unitize();
gDLL->getInterfaceIFace()->lookAt(lookAtPoint, (((getOwnerINLINE() != GC.getGameINLINE().getActivePlayer()) || gDLL->getGraphicOption(GRAPHICOPTION_NO_COMBAT_ZOOM)) ? CAMERALOOKAT_BATTLE : CAMERALOOKAT_BATTLE_ZOOM_IN), attackDirection);
}
else
{
MEMORY_TRACK_EXEMPT();
PlayerTypes eAttacker = getVisualOwner(pDefender->getTeam());
CvWString szMessage;
if (BARBARIAN_PLAYER != eAttacker)
{
szMessage = gDLL->getText("TXT_KEY_MISC_YOU_UNITS_UNDER_ATTACK", GET_PLAYER(getOwnerINLINE()).getNameKey());
}
else
{
szMessage = gDLL->getText("TXT_KEY_MISC_YOU_UNITS_UNDER_ATTACK_UNKNOWN");
}
//OutputDebugString("UI interaction - unit attacked 3\n");
GET_PLAYER(pDefender->getOwnerINLINE()).setTurnHadUIInteraction(true);
AddMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szMessage, "AS2D_COMBAT", MESSAGE_TYPE_DISPLAY_ONLY, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true);
}
}
FAssertMsg(pDefender != NULL, "Defender is not assigned a valid value");
FAssertMsg(plot()->isFighting(), "Current unit instance plot is not fighting as expected");
FAssertMsg(pPlot->isFighting(), "pPlot is not fighting as expected");
if (!pDefender->canDefend())
{
if (!bVisible)
{
bFinish = true;
}
else
{
CvMissionDefinition kMission;
kMission.setMissionTime(getCombatTimer() * gDLL->getSecsPerTurn());
kMission.setMissionType(MISSION_SURRENDER);
kMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kMission.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
kMission.setPlot(pPlot);
addMission(&kMission);
// Surrender mission
setCombatTimer(GC.getMissionInfo(MISSION_SURRENDER).getTime());
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
}
// Kill them!
pDefender->setDamage(GC.getMAX_HIT_POINTS());
}
else
{
CvBattleDefinition kBattle;
kBattle.setUnit(BATTLE_UNIT_ATTACKER, this);
kBattle.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
kBattle.setDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_BEGIN, getDamage());
kBattle.setDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_BEGIN, pDefender->getDamage());
// Koshling - save pre-combat helath so we can use health loss as
// a basis for more granular war weariness
setupPreCombatDamage();
pDefender->setupPreCombatDamage();
resolveCombat(pDefender, pPlot, kBattle);
if (!bVisible)
{
bFinish = true;
}
else
{
kBattle.setDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_END, getDamage());
kBattle.setDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_END, pDefender->getDamage());
kBattle.setAdvanceSquare(canAdvance(pPlot, pDefender->isDead() ? 0 : 1));
if (isRanged() && pDefender->isRanged())
{
kBattle.setDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_END));
kBattle.setDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_END));
}
else
{
kBattle.addDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_ATTACKER, BATTLE_TIME_BEGIN));
kBattle.addDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_RANGED, kBattle.getDamage(BATTLE_UNIT_DEFENDER, BATTLE_TIME_BEGIN));
}
int iTurns = planBattle( kBattle);
kBattle.setMissionTime(iTurns * gDLL->getSecsPerTurn());
setCombatTimer(iTurns);
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
if (pPlot->isActiveVisible(false) && !pDefender->isUsingDummyEntities())
{
ExecuteMove(0.5f, true);
// RevolutionDCM - stack attack - this next line is one source of stack attack CTD's
addMission(&kBattle);
}
}
}
}
if (bFinish)
{
if (bVisible)
{
if (isCombatFocus() && gDLL->getInterfaceIFace()->isCombatFocus())
{
if (getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())
{
gDLL->getInterfaceIFace()->releaseLockedCamera();
}
}
}
//end the combat mission if this code executes first
if ( !isUsingDummyEntities() && isInViewport() )
{
gDLL->getEntityIFace()->RemoveUnitFromBattle(this);
}
if ( !pDefender->isUsingDummyEntities() && pDefender->isInViewport() )
{
gDLL->getEntityIFace()->RemoveUnitFromBattle(pDefender);
}
setAttackPlot(NULL, false);
setCombatUnit(NULL);
pDefender->setCombatUnit(NULL);
NotifyEntity(MISSION_DAMAGE);
pDefender->NotifyEntity(MISSION_DAMAGE);
if (isDead())
{
if (isBarbarian())
{
GET_PLAYER(pDefender->getOwnerINLINE()).changeWinsVsBarbs(1);
}
if (!m_pUnitInfo->isHiddenNationality() && !pDefender->getUnitInfo().isHiddenNationality())
{
int attackerPreCombatDamage = getPreCombatDamage();
int defenderPreCombatDamage = pDefender->getPreCombatDamage();
int attackerWarWearinessChangeTimes100 = std::max(1, (GC.getDefineINT("WW_UNIT_KILLED_ATTACKING")*(maxHitPoints() - attackerPreCombatDamage))/maxHitPoints());
GET_TEAM(getTeam()).changeWarWearinessTimes100(pDefender->getTeam(), *pPlot, attackerWarWearinessChangeTimes100);
int defenderWarWearinessChangeTimes100 = (GC.getDefineINT("WW_KILLED_UNIT_DEFENDING")*(pDefender->getDamage() - pDefender->getPreCombatDamage()))/pDefender->maxHitPoints();
GET_TEAM(pDefender->getTeam()).changeWarWearinessTimes100(getTeam(), *pPlot, defenderWarWearinessChangeTimes100);
GET_TEAM(pDefender->getTeam()).AI_changeWarSuccess(getTeam(), GC.getDefineINT("WAR_SUCCESS_DEFENDING"));
}
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_DIED_ATTACKING", getNameKey(), pDefender->getNameKey());
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
float fInfluenceRatio = 0.0;
if (GC.isIDW_ENABLED())
{
fInfluenceRatio = pDefender->doVictoryInfluence(this, false, false);
/*** Dexy - Fixed Borders START ****/
if (fInfluenceRatio > 0.0f)
/*** Dexy - Fixed Borders END ****/
{
CvWString szTempBuffer;
szTempBuffer.Format(L" Influence: -%.1f%%", fInfluenceRatio);
szBuffer += szTempBuffer;
}
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_KILLED_ENEMY_UNIT", pDefender->getNameKey(), getNameKey(), getVisualCivAdjective(pDefender->getTeam()));
}
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
if (GC.isIDW_ENABLED())
{
/*** Dexy - Fixed Borders START ****/
if (fInfluenceRatio > 0.0f)
/*** Dexy - Fixed Borders END ****/
{
CvWString szTempBuffer;
szTempBuffer.Format(L" Influence: +%.1f%%", fInfluenceRatio);
szBuffer += szTempBuffer;
}
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
AddMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
// report event to Python, along with some other key state
CvEventReporter::getInstance().combatResult(pDefender, this);
getUnitInfo().getKillOutcomeList()->execute(*pDefender, getOwnerINLINE(), getUnitType());
}
else if (pDefender->isDead())
{
if (pDefender->isBarbarian())
{
GET_PLAYER(getOwnerINLINE()).changeWinsVsBarbs(1);
}
if (!m_pUnitInfo->isHiddenNationality() && !pDefender->getUnitInfo().isHiddenNationality())
{
int attackerPreCombatDamage = getPreCombatDamage();
int defenderPreCombatDamage = pDefender->getPreCombatDamage();
int defenderWarWearinessChangeTimes100 = std::max(1, (GC.getDefineINT("WW_UNIT_KILLED_DEFENDING")*(pDefender->maxHitPoints() - defenderPreCombatDamage))/pDefender->maxHitPoints());
GET_TEAM(pDefender->getTeam()).changeWarWearinessTimes100(getTeam(), *pPlot, defenderWarWearinessChangeTimes100);
int attackerWarWearinessChangeTimes100 = (GC.getDefineINT("WW_KILLED_UNIT_ATTACKING")*(getDamage() - getPreCombatDamage()))/maxHitPoints();
GET_TEAM(getTeam()).changeWarWearinessTimes100(pDefender->getTeam(), *pPlot, attackerWarWearinessChangeTimes100);
GET_TEAM(getTeam()).AI_changeWarSuccess(pDefender->getTeam(), GC.getDefineINT("WAR_SUCCESS_ATTACKING"));
}
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_DESTROYED_ENEMY", getNameKey(), pDefender->getNameKey());
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
float fInfluenceRatio = 0.0;
if (GC.isIDW_ENABLED())
{
fInfluenceRatio = doVictoryInfluence(pDefender, true, false);
/*** Dexy - Fixed Borders START ****/
if (fInfluenceRatio > 0.0f)
/*** Dexy - Fixed Borders END ****/
{
CvWString szTempBuffer;
szTempBuffer.Format(L" Influence: +%.1f%%", fInfluenceRatio);
szBuffer += szTempBuffer;
}
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
if (getVisualOwner(pDefender->getTeam()) != getOwnerINLINE())
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WAS_DESTROYED_UNKNOWN", pDefender->getNameKey(), getNameKey());
}
else
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WAS_DESTROYED", pDefender->getNameKey(), getNameKey(), getVisualCivAdjective(pDefender->getTeam()));
}
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
if (GC.isIDW_ENABLED())
{
/*** Dexy - Fixed Borders START ****/
if (fInfluenceRatio > 0.0f)
/*** Dexy - Fixed Borders END ****/
{
CvWString szTempBuffer;
szTempBuffer.Format(L" Influence: -%.1f%%", fInfluenceRatio);
szBuffer += szTempBuffer;
}
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
AddMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer,GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
// report event to Python, along with some other key state
CvEventReporter::getInstance().combatResult(this, pDefender);
CvUnitInfo* pDefenderUnitInfo = &(pDefender->getUnitInfo());
PlayerTypes eDefenderUnitPlayer = pDefender->getOwnerINLINE();
UnitTypes eDefenderUnitType = pDefender->getUnitType();
bool bAdvance = false;
if (isSuicide())
{
kill(true);
pDefender->kill(false, NO_PLAYER, true);
pDefender = NULL;
}
else
{
bAdvance = canAdvance(pPlot, ((pDefender->canDefend() && !pDefender->isDead()) ? 1 : 0));
if (bAdvance)
{
if (!isNoCapture())
{
pDefender->setCapturingPlayer(getOwnerINLINE());
}
}
pDefender->kill(false, NO_PLAYER, true);
pDefender = NULL;
if (!bAdvance)
{
changeMoves(std::max(GC.getMOVE_DENOMINATOR(), pPlot->movementCost(this, plot())));
checkRemoveSelectionAfterAttack();
if (!canMove() || !isBlitz())
{
if (IsSelected())
{
if (gDLL->getInterfaceIFace()->getLengthSelectionList() > 1)
{
gDLL->getInterfaceIFace()->removeFromSelectionList(this);
}
}
}
}
}
if (pPlot->getNumVisibleEnemyDefenders(this) == 0)
{
getGroup()->groupMove(pPlot, true, ((bAdvance) ? this : NULL));
}
// This is is put before the plot advancement, the unit will always try to walk back
// to the square that they came from, before advancing.
getGroup()->clearMissionQueue();
pDefenderUnitInfo->getKillOutcomeList()->execute(*this, eDefenderUnitPlayer, eDefenderUnitType);
}
else
{
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WITHDRAW", getNameKey(), pDefender->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_OUR_WITHDRAWL", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_UNIT_WITHDRAW", getNameKey(), pDefender->getNameKey());
AddMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_THEIR_WITHDRAWL", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
changeMoves(std::max(GC.getMOVE_DENOMINATOR(), pPlot->movementCost(this, plot())));
checkRemoveSelectionAfterAttack();
getGroup()->clearMissionQueue();
}
}
}
// Dale - SA: Stack Attack END
// Dale - SA: Opp Fire START
void CvUnit::doOpportunityFire()
{
//TB Notes regarding Opportunity Fire:
//While the mechanism has been updated to only target the strongest adjacent unit with this opportunity fire, there is still some work here to do.
//There is absolutely zero resistability to this damage and no potential for failure to strike, making it far more powerful than any player determined
//action. Once I get to focusing in on the Bombard function and adding some more dynamics there to address the above noted issues,
//I'll have to enforce those mechanisms onto this Opportunity Fire process as well.
int iI;
int iUnitDamage = 0;
int iVolumeDefenders = 0;
int iBestUnitStr = 0;
int ipDefenderStr = 0;
CvPlot* pLoopPlot;
CvPlot* pAttackPlot = NULL;
CvUnit* pDefender = NULL;
CvWString szBuffer;
CvUnit* pBestUnit;
if (!GC.isDCM_OPP_FIRE())
{
return;
}
if (bombardRate() <= 0 || getDCMBombRange() <= 0)
{
return;
}
if (getFortifyTurns() > 0)
{
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(plot()->getX_INLINE(), plot()->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
iVolumeDefenders = pLoopPlot->getNumUnits();
if (iVolumeDefenders > 0)
{
pBestUnit = NULL;
pBestUnit = pLoopPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
if (pBestUnit != NULL)
{
iBestUnitStr = pBestUnit->currCombatStr(pLoopPlot, this, NULL, true);
if (pDefender != NULL)
{
if (iBestUnitStr > ipDefenderStr)
{
pDefender = pBestUnit;
pAttackPlot = pLoopPlot;
ipDefenderStr = pDefender->currCombatStr(pLoopPlot, this, NULL, true);
}
}
else
{
pDefender = pBestUnit;
pAttackPlot = pLoopPlot;
ipDefenderStr = pDefender->currCombatStr(pLoopPlot, this, NULL, true);
}
}
}
}
}
if (pDefender != NULL)
{
setBattlePlot(pAttackPlot, pDefender);
iUnitDamage = (GC.getGameINLINE().getSorenRandNum(bombardRate(), "Bombard damage") * 5);
pDefender->changeDamage(iUnitDamage, getOwner());
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_OPP_FIRE", getNameKey(), pDefender->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_OUR_WITHDRAWL", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pAttackPlot->getX_INLINE(), pAttackPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_OPP_FIRE", getNameKey(), pDefender->getNameKey());
AddMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_THEIR_WITHDRAWL", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pAttackPlot->getX_INLINE(), pAttackPlot->getY_INLINE(), true, true);
}
if (pAttackPlot->isActiveVisible(false) && !pDefender->isUsingDummyEntities())
{
// Bombard entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_BOMBARD).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_BOMBARD);
kDefiniton.setPlot(pAttackPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
addMission(&kDefiniton);
}
}
}
}
// Dale - SA: Opp Fire END
// Dale - SA: Active Defense START
void CvUnit::doActiveDefense()
{
int iDamage, iUnitDamage, iSearchRange, iDX, iDY;
CvPlot* pLoopPlot;
CvPlot* pAttackPlot = NULL;
CvUnit* pDefender = NULL;
CvWString szBuffer;
if (!GC.isDCM_ACTIVE_DEFENSE())
{
return;
}
if (getGroup()->getActivityType() != ACTIVITY_INTERCEPT)
{
return;
}
iSearchRange = 2;
for (iDX = -(iSearchRange); iDX <= iSearchRange; iDX++)
{
for (iDY = -(iSearchRange); iDY <= iSearchRange; iDY++)
{
pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getNumUnits() > 0)
{
pDefender = airStrikeTarget(pLoopPlot);
if (pDefender != NULL)
{
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pLoopPlot, pDefender);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
iDamage = airCombatDamage(pDefender);
iUnitDamage = std::max(pDefender->getDamage(), std::min((pDefender->getDamage() + iDamage), airCombatLimit()));
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ARE_ATTACKED_BY_AIR", pDefender->getNameKey(), getNameKey(), -(((iUnitDamage - pDefender->getDamage()) * 100) / pDefender->maxHitPoints()));
AddMessage(pDefender->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_AIR_ATTACK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ATTACK_BY_AIR", getNameKey(), pDefender->getNameKey(), -(((iUnitDamage - pDefender->getDamage()) * 100) / pDefender->maxHitPoints()));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_AIR_ATTACKED", MESSAGE_TYPE_INFO, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
}
collateralCombat(pLoopPlot, pDefender);
pDefender->setDamage(iUnitDamage, getOwnerINLINE());
if (pLoopPlot->isActiveVisible(false) && !pDefender->isUsingDummyEntities())
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRSTRIKE);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
kAirMission.setDamage(BATTLE_UNIT_DEFENDER, 0);
kAirMission.setDamage(BATTLE_UNIT_ATTACKER, 0);
kAirMission.setPlot(pLoopPlot);
setCombatTimer(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime());
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
kAirMission.setMissionTime(getCombatTimer() * gDLL->getSecsPerTurn());
addMission(&kAirMission);
}
}
}
}
}
}
}
// Dale - SA: Active Defense END
// Dale - ARB: Archer Bombard START
bool CvUnit::canArcherBombard(const CvPlot* pPlot) const
{
if(!GC.isDCM_ARCHER_BOMBARD())
{
return false;
}
if (!(GC.getUnitInfo(getUnitType()).getUnitCombatType() == (UnitCombatTypes)1))
{
return false;
}
if (isMadeAttack())
{
return false;
}
if (isCargo())
{
return false;
}
return true;
}
bool CvUnit::canArcherBombardAt(const CvPlot* pPlot, int iX, int iY) const
{
CvPlot* pTargetPlot;
if (!canArcherBombard(pPlot))
{
return false;
}
if(iX < 0 || iY < 0)
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > 1)
{
return false;
}
if(pTargetPlot->getNumVisibleEnemyDefenders(this) == 0)
{
return false;
}
if (pTargetPlot->isOwned())
{
if(pTargetPlot->getTeam() != getTeam())
{
if (!atWar(pTargetPlot->getTeam(), getTeam()))
{
return false;
}
}
}
return true;
}
bool CvUnit::archerBombard(int iX, int iY, bool supportAttack)
{
CvPlot* pPlot;
CvWString szBuffer;
CvUnit* pLoopUnit = NULL;
int iDefenderNum, iUnitDamage;
bool bTarget = false;
if (!canArcherBombardAt(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
iDefenderNum = pPlot->getNumVisibleEnemyDefenders(this);
if(iDefenderNum == 0)
{
return false;
}
// RevolutionDCM - getBestDefender() now used in all cases
pLoopUnit = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
if (GC.getGameINLINE().getSorenRandNum(100, "Bombard Accuracy") <= getDCMBombAccuracy())
{
bTarget = true;
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_BOMB_MISSED", getNameKey());
AddMessage(getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARDED", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_ENEMY_BOMB_MISSED", getNameKey());
// RevolutionDCM - Archer Bombard fix - text display
AddMessage(pLoopUnit->getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_BOMBARD", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE());
}
if (bTarget)
{
//RevolutionDCM - Archer Bombard fix - report killed units
iUnitDamage = std::max(1, ((GC.getDefineINT("COMBAT_DAMAGE") * (currFirepower(NULL, NULL) + ((currFirepower(NULL, NULL) + pLoopUnit->currFirepower(NULL, NULL) + 1) / 2))) / (pLoopUnit->currFirepower(pPlot, this) + ((currFirepower(NULL, NULL) + pLoopUnit->currFirepower(NULL, NULL) + 1) / 2))));
pLoopUnit->setupPreCombatDamage();
pLoopUnit->changeDamage(iUnitDamage, getOwner());
if (pLoopUnit->isDead())
{
if (pLoopUnit->isBarbarian())
{
GET_PLAYER(getOwnerINLINE()).changeWinsVsBarbs(1);
}
if (!m_pUnitInfo->isHiddenNationality() && !pLoopUnit->getUnitInfo().isHiddenNationality())
{
int attackerPreCombatDamage = getPreCombatDamage();
int defenderPreCombatDamage = pLoopUnit->getPreCombatDamage();
int defenderWarWearinessChangeTimes100 = std::max(1, (GC.getDefineINT("WW_UNIT_KILLED_DEFENDING")*(pLoopUnit->maxHitPoints() - defenderPreCombatDamage))/pLoopUnit->maxHitPoints());
GET_TEAM(pLoopUnit->getTeam()).changeWarWearinessTimes100(getTeam(), *pPlot, defenderWarWearinessChangeTimes100);
GET_TEAM(getTeam()).AI_changeWarSuccess(pLoopUnit->getTeam(), GC.getDefineINT("WAR_SUCCESS_ATTACKING"));
}
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_DESTROYED_ENEMY", getNameKey(), pLoopUnit->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitVictoryScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
if (getVisualOwner(pLoopUnit->getTeam()) != getOwnerINLINE())
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WAS_DESTROYED_UNKNOWN", pLoopUnit->getNameKey(), getNameKey());
}
else
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WAS_DESTROYED", pLoopUnit->getNameKey(), getNameKey(), getVisualCivAdjective(pLoopUnit->getTeam()));
}
AddMessage(pLoopUnit->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer,GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
// report event to Python, along with some other key state
CvEventReporter::getInstance().combatResult(this, pLoopUnit);
pLoopUnit->getUnitInfo().getKillOutcomeList()->execute(*this, pLoopUnit->getOwnerINLINE(), pLoopUnit->getUnitType());
}
else
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ATTACK_BY_AIR", getNameKey(), pLoopUnit->getNameKey(), -(((iUnitDamage - pLoopUnit->getDamage()) * 100) / pLoopUnit->maxHitPoints()));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_AIR_ATTACKED", MESSAGE_TYPE_INFO, pLoopUnit->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ARE_ATTACKED_BY_AIR", pLoopUnit->getNameKey(), getNameKey(), -(((iUnitDamage - pLoopUnit->getDamage()) * 100) / pLoopUnit->maxHitPoints()));
AddMessage(pLoopUnit->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_AIR_ATTACK", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
// RevolutionDCM - end
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
setBattlePlot(pPlot, pLoopUnit);
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
}
if (!supportAttack)
{
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
}
if (pPlot->isActiveVisible(false) && !pLoopUnit->isUsingDummyEntities())
{
// Bombard entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_ABOMBARD).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_BOMBARD);
kDefiniton.setPlot(pPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, this);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, pLoopUnit);
addMission(&kDefiniton);
}
return true;
}
// Dale - ARB: Archer Bombard END
// Dale - FE: Fighters START
bool CvUnit::canFEngage(const CvPlot* pPlot) const
{
if(!GC.isDCM_FIGHTER_ENGAGE())
{
return false;
}
if (!GC.getUnitInfo(getUnitType()).getDCMFighterEngage())
{
return false;
}
if (getDomainType() != DOMAIN_AIR)
{
return false;
}
if (isMadeAttack())
{
return false;
}
// if (isCargo())
// {
// return false;
// }
return true;
}
bool CvUnit::canFEngageAt(const CvPlot* pPlot, int iX, int iY) const
{
CvPlot* pTargetPlot;
int iI, iLoop;
CvUnit* pLoopUnit;
if (!canFEngage(pPlot))
{
return false;
}
if(iX < 0 || iY < 0)
{
return false;
}
pTargetPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pTargetPlot->getX_INLINE(), pTargetPlot->getY_INLINE()) > airRange())
{
return false;
}
if (pTargetPlot->isOwned())
{
if(pTargetPlot->getTeam() != getTeam())
{
if (!atWar(pTargetPlot->getTeam(), getTeam()))
{
return false;
}
}
}
for (iI = 0; iI < MAX_PLAYERS; ++iI)
{
if (atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), getTeam()))
{
for(pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->plot() == pTargetPlot)
{
if (pLoopUnit->getDomainType() == DOMAIN_AIR)
{
return true;
}
}
}
}
}
return false;
}
bool CvUnit::fighterEngage(int iX, int iY)
{
CvPlot* pPlot;
CvWString szBuffer;
CvUnit* pDefender = NULL;
CvUnit* pLoopUnit;
CLLNode<IDInfo>* pUnitNode;
int iCount;
bool bTarget = false;
if (!canFEngageAt(plot(), iX, iY))
{
return false;
}
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (interceptTest(pPlot))
{
return true;
}
iCount = 0;
pDefender = NULL;
pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getDomainType() == DOMAIN_AIR)
{
iCount++;
}
}
iCount = (GC.getGameINLINE().getSorenRandNum(iCount, "Choose plane") + 1);
pUnitNode = pPlot->headUnitNode();
while (iCount > 0)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getDomainType() == DOMAIN_AIR)
{
iCount--;
pDefender = pLoopUnit;
}
}
if (pDefender != NULL)
{
CvAirMissionDefinition kAirMission;
kAirMission.setMissionType(MISSION_AIRSTRIKE);
kAirMission.setUnit(BATTLE_UNIT_ATTACKER, this);
kAirMission.setUnit(BATTLE_UNIT_DEFENDER, pDefender);
resolveAirCombat(pDefender, pPlot, kAirMission);
if (pPlot->isActiveVisible(false))
{
kAirMission.setPlot(pPlot);
kAirMission.setMissionTime(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime() * gDLL->getSecsPerTurn());
setCombatTimer(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime());
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
addMission(&kAirMission);
}
if (isDead())
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_SHOT_DOWN_ENEMY", pDefender->getNameKey(), getNameKey(), getVisualCivAdjective(pDefender->getTeam()));
AddMessage(pDefender->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_SHOT_DOWN", getNameKey(), pDefender->getNameKey());
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
else if (kAirMission.getDamage(BATTLE_UNIT_ATTACKER) > 0)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_HURT_ENEMY_AIR", pDefender->getNameKey(), getNameKey(), -(kAirMission.getDamage(BATTLE_UNIT_ATTACKER)), getVisualCivAdjective(pDefender->getTeam()));
AddMessage(pDefender->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_AIR_UNIT_HURT", getNameKey(), pDefender->getNameKey(), -(kAirMission.getDamage(BATTLE_UNIT_ATTACKER)));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
if (pDefender->isDead())
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_SHOT_DOWN_ENEMY", getNameKey(), pDefender->getNameKey(), pDefender->getVisualCivAdjective(getTeam()));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_SHOT_DOWN", pDefender->getNameKey(), getNameKey());
AddMessage(pDefender->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
else if (kAirMission.getDamage(BATTLE_UNIT_DEFENDER) > 0)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_DAMAGED_ENEMY_AIR", getNameKey(), pDefender->getNameKey(), -(kAirMission.getDamage(BATTLE_UNIT_DEFENDER)), pDefender->getVisualCivAdjective(getTeam()));
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_AIR_UNIT_DAMAGED", pDefender->getNameKey(), getNameKey(), -(kAirMission.getDamage(BATTLE_UNIT_DEFENDER)));
AddMessage(pDefender->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
if (0 == kAirMission.getDamage(BATTLE_UNIT_ATTACKER) + kAirMission.getDamage(BATTLE_UNIT_DEFENDER))
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_ABORTED_ENEMY_AIR", pDefender->getNameKey(), getNameKey(), getVisualCivAdjective(getTeam()));
AddMessage(pDefender->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPT", MESSAGE_TYPE_INFO, pDefender->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_AIR_UNIT_ABORTED", getNameKey(), pDefender->getNameKey());
AddMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_INTERCEPTED", MESSAGE_TYPE_INFO, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
}
setMadeAttack(true);
changeMoves(GC.getMOVE_DENOMINATOR());
return true;
}
// Dale - FE: Fighters END
/************************************************************************************************/
/* DCM END */
/************************************************************************************************/
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
// unit influences combat area after victory
// returns influence % in defended plot
float CvUnit::doVictoryInfluence(CvUnit* pLoserUnit, bool bAttacking, bool bWithdrawal)
{
PROFILE_FUNC();
/*** Dexy - Fixed Borders START ****/
if (GET_PLAYER(pLoserUnit->getOwnerINLINE()).hasFixedBorders())
{
return 0.0f;
}
/*** Dexy - Fixed Borders END ****/
/************************************************************************************************/
/* Afforess Start 06/14/10 */
/* */
/* */
/************************************************************************************************/
if (plot()->getWorkingCity() != NULL)
{
if (plot()->getWorkingCity()->isProtectedCulture())
{
return 0.0f;
}
}
if (pLoserUnit->plot()->getWorkingCity() != NULL)
{
if (pLoserUnit->plot()->getWorkingCity()->isProtectedCulture())
{
return 0.0f;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if( isAnimal() || pLoserUnit->isAnimal() )
{
return 0.0f;
}
if( isAlwaysHostile(plot()) || pLoserUnit->isAlwaysHostile(pLoserUnit->plot()) )
{
return 0.0f;
}
if (GC.isIDW_NO_BARBARIAN_INFLUENCE())
{
if (isBarbarian() || pLoserUnit->isBarbarian())
{
return 0.0f;
}
}
if (GC.isIDW_NO_NAVAL_INFLUENCE())
{
if (DOMAIN_SEA == getDomainType())
{
return 0.0f;
}
}
CvPlot* pWinnerPlot = plot();
CvPlot* pLoserPlot = pLoserUnit->plot();
CvPlot* pDefenderPlot = NULL;
if (!bAttacking)
{
pDefenderPlot = pWinnerPlot;
}
else
{
pDefenderPlot = pLoserPlot;
}
int iWinnerCultureBefore = pDefenderPlot->getCulture(getOwnerINLINE()); //used later for influence %
float fWinnerPlotMultiplier = GC.getIDW_WINNER_PLOT_MULTIPLIER();
float fLoserPlotMultiplier = GC.getIDW_LOSER_PLOT_MULTIPLIER();
float bWithdrawalMultiplier = 0.5f;
if (bWithdrawal)
{
fWinnerPlotMultiplier *= bWithdrawalMultiplier;
fLoserPlotMultiplier *= bWithdrawalMultiplier;
}
if (pLoserPlot->isEnemyCity(*this)) // city combat
{
if (pLoserPlot->getNumVisibleEnemyDefenders(this) > 1)
{
// if there are still some city defenders ->
// we use same influence rules as for field combat
influencePlots(pLoserPlot, pLoserUnit->getOwnerINLINE(), fLoserPlotMultiplier);
influencePlots(pWinnerPlot, pLoserUnit->getOwnerINLINE(), fWinnerPlotMultiplier);
}
else // last defender is dead
{
float fNoCityDefenderMultiplier = GC.getIDW_NO_CITY_DEFENDER_MULTIPLIER();
// last city defender is dead -> influence is increased
influencePlots(pLoserPlot, pLoserUnit->getOwnerINLINE(), fLoserPlotMultiplier * fNoCityDefenderMultiplier);
influencePlots(pWinnerPlot, pLoserUnit->getOwnerINLINE(), fWinnerPlotMultiplier * fNoCityDefenderMultiplier);
if (GC.isIDW_EMERGENCY_DRAFT_ENABLED())
{
int iDefenderCulture = pLoserPlot->getCulture(pLoserUnit->getOwnerINLINE());
int iAttackerCulture = pLoserPlot->getCulture(getOwnerINLINE());
if (iDefenderCulture >= iAttackerCulture)
{
// if defender culture in city's central tile is still higher then atacker culture
// -> city is not captured yet but emergency militia is drafted
pLoserPlot->getPlotCity()->emergencyConscript();
// calculate city resistence % (to be displayed in game log)
float fResistence = ((iDefenderCulture-iAttackerCulture)*100.0f)/(2*pDefenderPlot->countTotalCulture());
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer;
szBuffer.Format(L"City militia has emerged! Resistance: %.1f%%", fResistence);
AddMessage(pLoserUnit->getOwnerINLINE(), false, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_UNIT_BUILD_UNIT", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pLoserPlot->getX_INLINE(), pLoserPlot->getY_INLINE(), true, true);
AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_UNIT_BUILD_UNIT", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pLoserPlot->getX_INLINE(), pLoserPlot->getY_INLINE());
}
}
}
}
}
else // field combat
{
if (!pLoserUnit->canDefend())
{
// no influence from worker capture
return 0.0f;
}
if (pLoserPlot->getImprovementType() != NO_IMPROVEMENT
&& GC.getImprovementInfo(pLoserPlot->getImprovementType()).getDefenseModifier() > 0
&& pLoserPlot->getNumVisibleEnemyDefenders(this) > 1)
{
// fort captured
float fFortCaptureMultiplier = GC.getIDW_FORT_CAPTURE_MULTIPLIER();
// influence is increased
influencePlots(pLoserPlot, pLoserUnit->getOwnerINLINE(), fLoserPlotMultiplier * fFortCaptureMultiplier);
influencePlots(pWinnerPlot, pLoserUnit->getOwnerINLINE(), fWinnerPlotMultiplier * fFortCaptureMultiplier);
}
else
{
influencePlots(pLoserPlot, pLoserUnit->getOwnerINLINE(), fLoserPlotMultiplier);
influencePlots(pWinnerPlot, pLoserUnit->getOwnerINLINE(), fWinnerPlotMultiplier);
}
}
// calculate influence % in defended plot (to be displayed in game log)
int iWinnerCultureAfter = pDefenderPlot->getCulture(getOwnerINLINE());
int iTotalCulture = pDefenderPlot->countTotalCulture();
float fInfluenceRatio = 0.0f;
if (iTotalCulture > 0)
{
fInfluenceRatio = ((iWinnerCultureAfter-iWinnerCultureBefore)*100.0f)/iTotalCulture;
}
return fInfluenceRatio;
}
// unit influences given plot and surounding area i.e. transfers culture from target civ to unit's owner
void CvUnit::influencePlots(CvPlot* pCentralPlot, PlayerTypes eTargetPlayer, float fLocationMultiplier)
{
float fBaseCombatInfluence = GC.getIDW_BASE_COMBAT_INFLUENCE();
// calculate base multiplier used for all plots
float fGameSpeedMultiplier = (float) GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent();
fGameSpeedMultiplier /= 100;
fGameSpeedMultiplier *= GC.getEraInfo(GC.getGameINLINE().getStartEra()).getConstructPercent();
fGameSpeedMultiplier /= 100;
fGameSpeedMultiplier = sqrt(fGameSpeedMultiplier);
float fExperienceFactor = GC.getIDW_EXPERIENCE_FACTOR();
float fExperienceMultiplier = 1.0f + (getExperience() * 0.01f);
float fWarlordMultiplier = 1.0;
if (NO_UNIT != getLeaderUnitType()) // warlord is here
{
fWarlordMultiplier = GC.getIDW_WARLORD_MULTIPLIER();
}
float fCityPlotMultiplier = (!GC.isIDW_EMERGENCY_DRAFT_ENABLED()) ? GC.getIDW_CITY_TILE_MULTIPLIER() : 1.0f;
float fBaseMultiplier = fBaseCombatInfluence * fGameSpeedMultiplier * fLocationMultiplier * fExperienceMultiplier * fWarlordMultiplier;
if (fBaseMultiplier <= 0.0f)
return;
// get influence radius
int iInfluenceRadius = GC.getIDW_INFLUENCE_RADIUS();
if (iInfluenceRadius < 0)
return;
float fPlotDistanceFactor = GC.getIDW_PLOT_DISTANCE_FACTOR();
// CvWString szBuffer;
// szBuffer.Format(L"Factors: %.1f, %.1f, %.1f, %.1f, %.1f, %.1f, %.3f, %d", fBaseCombatInfluence, fLocationMultiplier, fGameSpeedMultiplier, fPlotDistanceFactor, fExperienceMultiplier, fWarlordMultiplier, fBaseMultiplier, iInfluenceRadius);
// AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_UNIT_BUILD_UNIT", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pCentralPlot->getX_INLINE(), pCentralPlot->getY_INLINE());
for (int iDX = -iInfluenceRadius; iDX <= iInfluenceRadius; iDX++)
{
for (int iDY = -iInfluenceRadius; iDY <= iInfluenceRadius; iDY++)
{
int iDistance = plotDistance(0, 0, iDX, iDY);
if (iDistance <= iInfluenceRadius)
{
CvPlot* pLoopPlot = plotXY(pCentralPlot->getX_INLINE(), pCentralPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
// calculate distance multiplier for current plot
float fDistanceMultiplier = 0.5f+0.5f*fPlotDistanceFactor-fPlotDistanceFactor*iDistance;
if (fDistanceMultiplier <= 0.0f)
continue;
int iTargetCulture = pLoopPlot->getCulture(eTargetPlayer);
if (iTargetCulture <= 0)
continue;
if ( pLoopPlot->isCity() )
{
fBaseMultiplier *= fCityPlotMultiplier;
}
if (fBaseMultiplier <= 0.0f)
continue;
int iCultureTransfer = int (fBaseMultiplier * fDistanceMultiplier * sqrt((float) iTargetCulture));
if (iTargetCulture < iCultureTransfer)
{
// cannot transfer more culture than remaining target culure
iCultureTransfer = iTargetCulture;
}
if (iCultureTransfer == 0 && iTargetCulture > 0)
{
// always at least 1 point of culture must be transfered
// othervise we may have the problems with capturing of very low culture cities.
iCultureTransfer = 1;
}
/************************************************************************************************/
/* Afforess Start 02/15/10 */
/* */
/* */
/************************************************************************************************/
/* plot that is not a city can't lose the last point of culture if its owner has fixed borders, otherwise it'd flip */
if ((pLoopPlot->getPlotCity() == NULL) && GET_PLAYER(eTargetPlayer).hasFixedBorders())
{
if (iCultureTransfer == iTargetCulture)
{
iCultureTransfer = iTargetCulture - 1;
}
}
/* plots with forts can't lose the last point of culture, otherwise they could become unowned with some units remaining inside */
if (pLoopPlot->isActsAsCity())
{
if (iCultureTransfer == iTargetCulture)
{
iCultureTransfer = iTargetCulture - 1;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iCultureTransfer > 0)
{
// target player's culture in plot is lowered
pLoopPlot->changeCulture(eTargetPlayer, -iCultureTransfer, false);
if( iTargetCulture > 0 && pLoopPlot->getCulture(eTargetPlayer) <= 0 )
{
// Don't allow complete loss of all culture
pLoopPlot->setCulture(eTargetPlayer,1,false,false);
}
// owners's culture in plot is raised
pLoopPlot->changeCulture(getOwnerINLINE(), iCultureTransfer, true);
}
}
}
}
}
}
// unit influences current tile via pillaging
// returns influence % in current plot
float CvUnit::doPillageInfluence()
{
if (isBarbarian() && GC.isIDW_NO_BARBARIAN_INFLUENCE())
{
return 0.0f;
}
if ((DOMAIN_SEA == getDomainType()) && GC.isIDW_NO_NAVAL_INFLUENCE())
{
return 0.0f;
}
if (m_pUnitInfo->isHiddenNationality())
{
return 0.0f;
}
CvPlot* pPlot = plot();
if (pPlot == NULL)
{
//should not happen
return 0.0f;
}
/************************************************************************************************/
/* Afforess Start 06/14/10 */
/* */
/* */
/************************************************************************************************/
if (pPlot->getWorkingCity() != NULL)
{
if (pPlot->getWorkingCity()->isProtectedCulture())
{
return 0.0f;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int iOurCultureBefore = pPlot->getCulture(getOwnerINLINE()); //used later for influence %
float fBasePillageInfluence = GC.getIDW_BASE_PILLAGE_INFLUENCE();
float fGameSpeedMultiplier = (float) GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent();
fGameSpeedMultiplier /= 100;
fGameSpeedMultiplier *= GC.getEraInfo(GC.getGameINLINE().getStartEra()).getConstructPercent();
fGameSpeedMultiplier /= 100;
fGameSpeedMultiplier = sqrt(fGameSpeedMultiplier);
PlayerTypes eTargetPlayer = pPlot->getOwner();
int iTargetCulture = pPlot->getCulture(eTargetPlayer);
if (iTargetCulture <= 0)
{
//should not happen
return 0.0f;
}
int iCultureTransfer = int (fBasePillageInfluence * fGameSpeedMultiplier * sqrt((float) iTargetCulture));
if (iTargetCulture < iCultureTransfer)
{
// cannot transfer more culture than remaining target culure
iCultureTransfer = iTargetCulture;
}
// target player's culture in plot is lowered
pPlot->changeCulture(eTargetPlayer, -iCultureTransfer, false);
// owners's culture in plot is raised
pPlot->changeCulture(getOwnerINLINE(), iCultureTransfer, true);
// calculate influence % in pillaged plot (to be displayed in game log)
int iOurCultureAfter = pPlot->getCulture(getOwnerINLINE());
float fInfluenceRatio = ((iOurCultureAfter-iOurCultureBefore)*100.0f)/pPlot->countTotalCulture();
// CvWString szBuffer;
// szBuffer.Format(L"Factors: %.1f, %.1f, %d, Result: %.3f, ", fGameSpeedMultiplier, fBasePillageInfluence, iTargetCulture, fInfluenceRatio);
// AddMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_UNIT_BUILD_UNIT", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), plot()->getX_INLINE(), plot()->getY_INLINE());
return fInfluenceRatio;
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 02/14/10 */
/* */
/* */
/************************************************************************************************/
bool CvUnit::canPerformInquisition(const CvPlot* pPlot) const
{
CvCity* pCity;
pCity = pPlot->getPlotCity();
if (!m_pUnitInfo->isInquisitor())
{
return false;
}
if (pCity == NULL)
{
return false;
}
if( GET_PLAYER(getOwnerINLINE()).getStateReligion() == NO_RELIGION )
{
return false;
}
if (!pCity->isHasReligion(GET_PLAYER(getOwnerINLINE()).getStateReligion()))
{
return false;
}
//Allow inquisitions in vassals
if( (pCity->getTeam() != getTeam()) && !(GET_TEAM(pCity->getTeam()).isVassal(getTeam())) )
{
return false;
}
if (!pCity->isInquisitionConditions() || !GET_PLAYER(getOwnerINLINE()).isInquisitionConditions())
{
return false;
}
if(GET_PLAYER(getOwnerINLINE()).getStateReligion() != GET_PLAYER(pCity->getOwnerINLINE()).getStateReligion())
{
return false;
}
return true;
}
bool CvUnit::performInquisition()
{
CvCity* pCity;
int iI, iJ;
int iReligionCount = 0;
int iHolyCityVal = 0;
int iCompensationGold = 0;
int iCount;
int iBestCount = 0;
PlayerTypes eBestPlayer = NO_PLAYER;
bool bRelocateHolyCity;
int iLoop;
CvCity* pLoopCity;
CvWString szBuffer;
if (!canPerformInquisition(plot()))
{
return false;
}
pCity = plot()->getPlotCity();
if (pCity != NULL)
{
for(iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iI);
if (kLoopPlayer.isAlive())
{
if( plot()->isVisible(kLoopPlayer.getTeam(), true)
|| plot()->isRevealed(kLoopPlayer.getTeam(), true) )
{
gDLL->getInterfaceIFace()->playGeneralSound("AS3D_UN_CHRIST_MISSIONARY_ACTIVATE", plot()->getPoint());
}
}
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if ((ReligionTypes)iI != GET_PLAYER(getOwnerINLINE()).getStateReligion())
{
if (pCity->isHasReligion((ReligionTypes)iI))
{
iReligionCount++;
if (pCity->isHolyCity((ReligionTypes)iI))
{
iHolyCityVal = 50;
}
}
}
}
if (GC.getGameINLINE().getSorenRandNum(100, "Inquisition Persection Chance") < std::max(25, (95 - iHolyCityVal - (5 * iReligionCount))))
{
//Change memory if we are removing a religion that is another player's state religion
for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iI);
if (kLoopPlayer.isAlive())
{
if( plot()->isVisible(kLoopPlayer.getTeam(), false)
|| plot()->isRevealed(kLoopPlayer.getTeam(), false) )
{
if (GET_TEAM(kLoopPlayer.getTeam()).isHasMet(GET_PLAYER(getOwnerINLINE()).getTeam()))
{
for (iJ = 0; iJ < GC.getNumReligionInfos(); iJ++)
{
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != (ReligionTypes)iJ)
{
//if the player has the holy city, or has the religion as a state religion.
if (kLoopPlayer.hasHolyCity((ReligionTypes)iJ) || ((pCity->isHasReligion((ReligionTypes)iJ)) && (kLoopPlayer.getStateReligion() == (ReligionTypes)iJ)))
{
kLoopPlayer.AI_changeMemoryCount(getOwnerINLINE(), MEMORY_INQUISITION, 1);
break;
}
}
}
}
}
}
}
//Remove temples, monasteries, etc...
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (pCity->getNumRealBuilding((BuildingTypes)iI) > 0)
{
CvBuildingInfo& kLoopBuilding = GC.getBuildingInfo((BuildingTypes)iI);
for (iJ = 0; iJ < GC.getNumReligionInfos(); iJ++)
{
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != (ReligionTypes)iJ)
{
if (kLoopBuilding.getPrereqReligion() == (ReligionTypes)iJ)
{
pCity->setNumRealBuilding((BuildingTypes)iI, 0);
iCompensationGold += kLoopBuilding.getProductionCost() * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent() / std::max(1, GC.getDefineINT("INQUISITION_BUILDING_GOLD_DIVISOR"));
}
}
}
}
}
//Remove the Religion & Holy Cities
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != (ReligionTypes)iI)
{
if (pCity->isHolyCity((ReligionTypes)iI))
{
//TODO: This value needs to be set from python
if (GC.getDefineINT("OC_RESPAWN_HOLY_CITIES"))
{
GC.getGameINLINE().setHolyCity((ReligionTypes)iI, NULL , false);
iCompensationGold += GC.getDefineINT("HOLYCITY_REMOVAL_GOLD");
pCity->setHasReligion((ReligionTypes)iI, false, false, false);
//Find the best place to replace the holy city
for (iJ = 0; iJ < MAX_CIV_PLAYERS; iJ++)
{
CvPlayerAI& kLoopPlayer = GET_PLAYER((PlayerTypes)iJ);
if (kLoopPlayer.isAlive())
{
iCount = kLoopPlayer.getHasReligionCount((ReligionTypes)iI);
if (iCount > iBestCount)
{
iBestCount = iCount;
eBestPlayer = (PlayerTypes)iJ;
}
}
}
//Relocate the holy city
bRelocateHolyCity = false;
if (eBestPlayer != NO_PLAYER)
{
CvPlayerAI& kPlayer = GET_PLAYER(eBestPlayer);
for (pLoopCity = kPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kPlayer.nextCity(&iLoop))
{
if (pCity->isHasReligion((ReligionTypes)iI))
{
bRelocateHolyCity = true;
break;
}
}
if (bRelocateHolyCity)
{
GC.getGameINLINE().setHolyCity((ReligionTypes)iI, pLoopCity, true);
//TODO: Create a text entry: "A Holy City Religion has been Respawned"
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MESSAGE_HOLY_CITY_RESPAWNED");
AddMessage(GC.getGameINLINE().getActivePlayer(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_DISCOVERBONUS", MESSAGE_TYPE_MAJOR_EVENT, "Art/Interface/Buttons/TerrainFeatures/Forest.dds", ColorTypes(8), getX_INLINE(), getY_INLINE(), false, false);
}
}
}
}
}
else if (pCity->isHasReligion((ReligionTypes)iI))
{
pCity->setHasReligion((ReligionTypes)iI, false, false, false);
iCompensationGold += GC.getDefineINT("RELIGION_REMOVAL_GOLD");
}
}
}
for(iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iI);
if (kLoopPlayer.isAlive())
{
if( plot()->isVisible(kLoopPlayer.getTeam(), true)
|| plot()->isRevealed(kLoopPlayer.getTeam(), true) )
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MESSAGE_INQUISITION", pCity->getNameKey());
AddMessage(((PlayerTypes)iI), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PLAGUE", MESSAGE_TYPE_MAJOR_EVENT, m_pUnitInfo->getButton() , ColorTypes(8), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
//Increase Temp Anger
pCity->changeHurryAngerTimer(pCity->flatHurryAngerLength());
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
{
//Avoid setting the Rev Index below 0...
pCity->changeLocalRevIndex(-std::min(pCity->getRevolutionIndex(), iCompensationGold));
}
else
{
GET_PLAYER(getOwnerINLINE()).changeGold(iCompensationGold);
}
}
//Inquisition Fails...
else
{
for(iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iI);
if (kLoopPlayer.isAlive())
{
if( plot()->isVisible(kLoopPlayer.getTeam(), true)
|| plot()->isRevealed(kLoopPlayer.getTeam(), true) )
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MESSAGE_INQUISITION_FAIL", pCity->getNameKey());
AddMessage(((PlayerTypes)iI), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_SABOTAGE", MESSAGE_TYPE_MAJOR_EVENT, m_pUnitInfo->getButton() , ColorTypes(8), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
pCity->changeHurryAngerTimer(pCity->flatHurryAngerLength());
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
{
pCity->changeLocalRevIndex(iCompensationGold / 2);
}
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_INQUISITION);
}
}
kill(true, NO_PLAYER, true);
return true;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/************************************************************************************************/
/* RevolutionDCM Start 05/31/10 Afforess */
/* */
/* Battle Effects */
/************************************************************************************************/
void CvUnit::setBattlePlot(CvPlot* pPlot, const CvUnit* pDefender)
{
if (pPlot->canHaveBattleEffect(this, pDefender))
{
if (pPlot->getBattleCountdown() < GC.getMAX_BATTLE_TURNS())
{
pPlot->changeBattleCountdown(GC.getBATTLE_EFFECTS_MINIMUM_TURN_INCREMENTS());
}
}
}
/************************************************************************************************/
/* RevolutionDCM Battle Effects END */
/************************************************************************************************/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */
/* */
/* Lead From Behind */
/************************************************************************************************/
// From Lead From Behind by UncutDragon
// Original isBetterDefenderThan call (without the extra parameter) - now just a pass-through
bool CvUnit::isBetterDefenderThan(CvUnit* pDefender, CvUnit* pAttacker) const
{
return isBetterDefenderThan(pDefender, pAttacker, NULL);
}
// Modified version of best defender code (minus the initial boolean tests,
// which we still check in the original method)
bool CvUnit::LFBisBetterDefenderThan(const CvUnit* pDefender, const CvUnit* pAttacker, int* pBestDefenderRank) const
{
// We adjust ranking based on ratio of our adjusted strength compared to twice that of attacker
// Effect is if we're over twice as strong as attacker, we increase our ranking
// (more likely to be picked as defender) - otherwise, we reduce our ranking (less likely)
// Get our adjusted rankings based on combat odds
int iOurRanking = LFBgetDefenderRank(pAttacker);
int iTheirRanking = -1;
if (pBestDefenderRank)
iTheirRanking = (*pBestDefenderRank);
if (iTheirRanking == -1)
iTheirRanking = pDefender->LFBgetDefenderRank(pAttacker);
// In case of equal value, fall back on unit cycle order
if (iOurRanking == iTheirRanking)
{
if (isBeforeUnitCycle(this, pDefender))
iTheirRanking--;
else
iTheirRanking++;
}
// Retain the basic rank (before value adjustment) for the best defender
if (pBestDefenderRank)
if (iOurRanking > iTheirRanking)
(*pBestDefenderRank) = iOurRanking;
return (iOurRanking > iTheirRanking);
}
// Get the (adjusted) odds of attacker winning to use in deciding best attacker
int CvUnit::LFBgetAttackerRank(const CvUnit* pDefender, int& iUnadjustedRank) const
{
if (pDefender)
{
int iDefOdds = pDefender->LFBgetDefenderOdds(this);
iUnadjustedRank = 1000 - iDefOdds;
// If attacker has a chance to withdraw, factor that in as well
if (withdrawalProbability() > 0)
iUnadjustedRank += ((iDefOdds * withdrawalProbability()) / 100);
} else {
// No defender ... just use strength, but try to make it a number out of 1000
iUnadjustedRank = currCombatStr(NULL, NULL) / 5;
}
int iRank = LFBgetValueAdjustedOdds(iUnadjustedRank);
return iRank;
}
// Get the (adjusted) odds of defender winning to use in deciding best defender
int CvUnit::LFBgetDefenderRank(const CvUnit* pAttacker) const
{
int iRank = LFBgetDefenderOdds(pAttacker);
// Don't adjust odds for value if attacker is limited in their damage (i.e: no risk of death)
if ((pAttacker != NULL) && (maxHitPoints() == pAttacker->combatLimit()))
iRank = LFBgetValueAdjustedOdds(iRank);
return iRank;
}
// Get the unadjusted odds of defender winning (used for both best defender and best attacker)
int CvUnit::LFBgetDefenderOdds(const CvUnit* pAttacker) const
{
// Check if we have a valid attacker
bool bUseAttacker = false;
int iAttStrength = 0;
if (pAttacker)
iAttStrength = pAttacker->currCombatStr(NULL, NULL);
if (iAttStrength > 0)
bUseAttacker = true;
int iDefense = 0;
if (bUseAttacker && GC.getLFBUseCombatOdds())
{
// We start with straight combat odds
iDefense = LFBgetDefenderCombatOdds(pAttacker);
} else {
// Lacking a real opponent (or if combat odds turned off) fall back on just using strength
iDefense = currCombatStr(plot(), pAttacker);
if (bUseAttacker)
{
// Similiar to the standard method, except I reduced the affect (cut it in half) handle attacker
// and defender together (instead of applying one on top of the other) and substract the
// attacker first strikes (instead of adding attacker first strikes when defender is immune)
int iFirstStrikes = 0;
if (!pAttacker->immuneToFirstStrikes())
iFirstStrikes += (firstStrikes() * 2) + chanceFirstStrikes();
if (!immuneToFirstStrikes())
iFirstStrikes -= ((pAttacker->firstStrikes() * 2) + pAttacker->chanceFirstStrikes());
if (iFirstStrikes != 0)
{
// With COMBAT_DAMAGE=20, this makes each first strike worth 8% (and each chance worth 4%)
iDefense *= ((iFirstStrikes * GC.getCOMBAT_DAMAGE() / 5) + 100);
iDefense /= 100;
}
// Make it a number out of 1000, taking attacker into consideration
iDefense = (iDefense * 1000) / (iDefense + iAttStrength);
}
}
if (hasCargo())
{
// This part is taken directly from the standard method
// Reduces value if a unit is carrying other units
int iAssetValue = std::max(1, getUnitInfo().getAssetValue());
int iCargoAssetValue = 0;
std::vector<CvUnit*> aCargoUnits;
getCargoUnits(aCargoUnits);
for (uint i = 0; i < aCargoUnits.size(); ++i)
{
iCargoAssetValue += aCargoUnits[i]->getUnitInfo().getAssetValue();
}
iDefense = iDefense * iAssetValue / std::max(1, iAssetValue + iCargoAssetValue);
}
return iDefense;
}
// Take the unadjusted odds and adjust them based on unit value
int CvUnit::LFBgetValueAdjustedOdds(int iOdds) const
{
// Adjust odds based on value
int iValue = LFBgetRelativeValueRating();
long iAdjustment = -250;
if (GC.getLFBUseSlidingScale())
iAdjustment = (iOdds - 990);
// Value Adjustment = (odds-990)*(value*num/denom)^2
long iValueAdj = (long)(iValue * GC.getLFBAdjustNumerator());
iValueAdj *= iValueAdj;
iValueAdj *= iAdjustment;
iValueAdj /= (long)(GC.getLFBAdjustDenominator() * GC.getLFBAdjustDenominator());
int iRank = iOdds + iValueAdj + 10000;
// Note that the +10000 is just to try keeping it > 0 - doesn't really matter, other than that -1
// would be interpreted later as not computed yet, which would cause us to compute it again each time
return iRank;
}
// Method to evaluate the value of a unit relative to another
int CvUnit::LFBgetRelativeValueRating() const
{
int iValueRating = 0;
// Check if led by a Great General
if (GC.getLFBBasedOnGeneral() > 0)
if (NO_UNIT != getLeaderUnitType())
iValueRating += GC.getLFBBasedOnGeneral();
// Assign experience value in tiers
if (GC.getLFBBasedOnExperience() > 0)
{
int iTier = 10;
while (getExperience() >= iTier)
{
iValueRating += GC.getLFBBasedOnExperience();
iTier *= 2;
}
}
// Check if unit is limited in how many can exist
if (GC.getLFBBasedOnLimited() > 0)
if (isLimitedUnitClass(getUnitClassType()))
iValueRating += GC.getLFBBasedOnLimited();
// Check if unit has ability to heal
if (GC.getLFBBasedOnHealer() > 0)
if (getSameTileHeal() > 0)
iValueRating += GC.getLFBBasedOnHealer();
return iValueRating;
}
int CvUnit::LFBgetDefenderCombatOdds(const CvUnit* pAttacker) const
{
int iAttackerStrength;
int iAttackerFirepower;
int iDefenderStrength;
int iDefenderFirepower;
int iDefenderOdds;
int iStrengthFactor;
int iDamageToAttacker;
int iDamageToDefender;
int iNeededRoundsAttacker;
int iNeededRoundsDefender;
int iAttackerLowFS;
int iAttackerHighFS;
int iDefenderLowFS;
int iDefenderHighFS;
int iDefenderHitLimit;
iAttackerStrength = pAttacker->currCombatStr(NULL, NULL);
iAttackerFirepower = pAttacker->currFirepower(NULL, NULL);
iDefenderStrength = currCombatStr(plot(), pAttacker);
iDefenderFirepower = currFirepower(plot(), pAttacker);
FAssert((iAttackerStrength + iDefenderStrength) > 0);
FAssert((iAttackerFirepower + iDefenderFirepower) > 0);
iDefenderOdds = ((GC.getCOMBAT_DIE_SIDES() * iDefenderStrength) / (iAttackerStrength + iDefenderStrength));
iStrengthFactor = ((iAttackerFirepower + iDefenderFirepower + 1) / 2);
// calculate damage done in one round
//////
iDamageToAttacker = std::max(1,((GC.getCOMBAT_DAMAGE() * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)));
iDamageToDefender = std::max(1,((GC.getCOMBAT_DAMAGE() * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)));
// calculate needed rounds.
// Needed rounds = round_up(health/damage)
//////
iDefenderHitLimit = maxHitPoints() - pAttacker->combatLimit();
iNeededRoundsAttacker = (std::max(0, currHitPoints() - iDefenderHitLimit) + iDamageToDefender - 1 ) / iDamageToDefender;
iNeededRoundsDefender = (pAttacker->currHitPoints() + iDamageToAttacker - 1 ) / iDamageToAttacker;
// calculate possible first strikes distribution.
// We can't use the getCombatFirstStrikes() function (only one result,
// no distribution), so we need to mimic it.
//////
iAttackerLowFS = (immuneToFirstStrikes()) ? 0 : pAttacker->firstStrikes();
iAttackerHighFS = (immuneToFirstStrikes()) ? 0 : (pAttacker->firstStrikes() + pAttacker->chanceFirstStrikes());
iDefenderLowFS = (pAttacker->immuneToFirstStrikes()) ? 0 : firstStrikes();
iDefenderHighFS = (pAttacker->immuneToFirstStrikes()) ? 0 : (firstStrikes() + chanceFirstStrikes());
return LFBgetCombatOdds(iDefenderLowFS, iDefenderHighFS, iAttackerLowFS, iAttackerHighFS, iNeededRoundsDefender, iNeededRoundsAttacker, iDefenderOdds);
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 06/15/10 */
/* */
/* */
/************************************************************************************************/
bool CvUnit::canTradeUnit(PlayerTypes eReceivingPlayer)
{
CvArea* pWaterArea = NULL;
CvCity* pCapitalCity;
int iLoop;
bool bShip = false;
bool bCoast = false;
pCapitalCity = GET_PLAYER(eReceivingPlayer).getCapitalCity();
if (eReceivingPlayer == NO_PLAYER || eReceivingPlayer > MAX_PLAYERS)
{
return false;
}
if (getCargo() > 0)
{
return false;
}
if (getDomainType() == DOMAIN_SEA)
{
bShip = true;
for (CvCity* pLoopCity = GET_PLAYER(eReceivingPlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eReceivingPlayer).nextCity(&iLoop))
{
if (((pWaterArea = pLoopCity->waterArea()) != NULL && !pWaterArea->isLake()))
{
bCoast = true;
}
}
}
if (bShip && !bCoast)
{
return false;
}
return true;
}
void CvUnit::tradeUnit(PlayerTypes eReceivingPlayer)
{
CvUnit* pTradeUnit;
CvWString szBuffer;
CvCity* pBestCity;
PlayerTypes eOwner;
int iLoop;
eOwner = getOwnerINLINE();
if (eReceivingPlayer != NO_PLAYER)
{
pBestCity = GET_PLAYER(eReceivingPlayer).getCapitalCity();
int iBestDistance = MAX_INT;
if (getDomainType() == DOMAIN_SEA)
//Find the closest coastal city, and put the ship there
{
for (CvCity* pLoopCity = GET_PLAYER(eReceivingPlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eReceivingPlayer).nextCity(&iLoop))
{
if (pLoopCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN()))
{
int iDistance = plotDistance(getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE());
if (iDistance < iBestDistance)
{
pBestCity = pLoopCity;
iBestDistance = iDistance;
}
}
}
}
//Dump any cargo we are carrying
if (hasCargo())
{
CvPlot* pPlot = plot();
CLLNode<IDInfo>* pUnitNode = pPlot->headUnitNode();
while (pUnitNode != NULL)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTransportUnit() == this)
{
pLoopUnit->setTransportUnit(NULL);
pLoopUnit->jumpToNearestValidPlot();
}
}
}
pTradeUnit = GET_PLAYER(eReceivingPlayer).initUnit(getUnitType(), pBestCity->getX_INLINE(), pBestCity->getY_INLINE(), AI_getUnitAIType(), NO_DIRECTION, GC.getGameINLINE().getSorenRandNum(10000, "AI Unit Birthmark"));
pTradeUnit->convert(this);
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_MISC_TRADED_UNIT_TO_YOU", GET_PLAYER(eOwner).getNameKey(), pTradeUnit->getNameKey());
AddMessage(pTradeUnit->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_UNITGIFTED", MESSAGE_TYPE_INFO, pTradeUnit->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), pTradeUnit->getX_INLINE(), pTradeUnit->getY_INLINE(), true, true);
}
}
}
bool CvUnit::spyNukeAffected(const CvPlot* pPlot, TeamTypes eTeam, int iRange) const
{
CvPlot* pLoopPlot;
int iDX, iDY;
if (!(GET_TEAM(eTeam).isAlive()))
{
return false;
}
if (eTeam == getTeam())
{
return false;
}
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getTeam() == eTeam)
{
return true;
}
if (pLoopPlot->plotCheck(PUF_isCombatTeam, eTeam, getTeam()) != NULL)
{
return true;
}
}
}
}
return false;
}
bool CvUnit::spyNuke(int iX, int iY, bool bCaught)
{
CvPlot* pPlot;
CvWString szBuffer;
bool abTeamsAffected[MAX_TEAMS];
int iI, iJ, iK;
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
for (iI = 0; iI < MAX_TEAMS; iI++)
{
abTeamsAffected[iI] = spyNukeAffected(pPlot, (TeamTypes)iI, 1);
}
if (bCaught)
{
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (abTeamsAffected[iI])
{
if (!isEnemy((TeamTypes)iI))
{
if (!((TeamTypes)iI == GET_TEAM(getTeam()).getID()))
{
GET_TEAM(getTeam()).declareWar(((TeamTypes)iI), false, WARPLAN_TOTAL);
}
}
}
}
}
if (bCaught)
{
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (abTeamsAffected[iI])
{
GET_TEAM((TeamTypes)iI).changeWarWeariness(getTeam(), 100 * GC.getDefineINT("WW_HIT_BY_NUKE"));
GET_TEAM(getTeam()).changeWarWeariness(((TeamTypes)iI), 100 * GC.getDefineINT("WW_ATTACKED_WITH_NUKE"));
GET_TEAM(getTeam()).AI_changeWarSuccess(((TeamTypes)iI), GC.getDefineINT("WAR_SUCCESS_NUKE"));
}
}
}
if (bCaught)
{
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (iI != getTeam())
{
if (abTeamsAffected[iI])
{
for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
if (GET_PLAYER((PlayerTypes)iJ).isAlive())
{
if (GET_PLAYER((PlayerTypes)iJ).getTeam() == ((TeamTypes)iI))
{
GET_PLAYER((PlayerTypes)iJ).AI_changeMemoryCount(getOwnerINLINE(), MEMORY_NUKED_US, 1);
}
}
}
}
else
{
for (iJ = 0; iJ < MAX_TEAMS; iJ++)
{
if (GET_TEAM((TeamTypes)iJ).isAlive())
{
if (abTeamsAffected[iJ])
{
if (GET_TEAM((TeamTypes)iI).isHasMet((TeamTypes)iJ))
{
if (GET_TEAM((TeamTypes)iI).AI_getAttitude((TeamTypes)iJ) >= ATTITUDE_CAUTIOUS)
{
for (iK = 0; iK < MAX_PLAYERS; iK++)
{
if (GET_PLAYER((PlayerTypes)iK).isAlive())
{
if (GET_PLAYER((PlayerTypes)iK).getTeam() == ((TeamTypes)iI))
{
GET_PLAYER((PlayerTypes)iK).AI_changeMemoryCount(getOwnerINLINE(), MEMORY_NUKED_FRIEND, 1);
}
}
}
break;
}
}
}
}
}
}
}
}
}
}
szBuffer = gDLL->getText("TXT_KEY_MISC_NUKE_UNKNOWN", GET_PLAYER(pPlot->getOwnerINLINE()).getNameKey());
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
MEMORY_TRACK_EXEMPT();
if (bCaught)
szBuffer = gDLL->getText("TXT_KEY_MISC_NUKE_ENEMY_SPY", GET_PLAYER(getOwnerINLINE()).getNameKey(), GET_PLAYER(pPlot->getOwnerINLINE()).getNameKey());
AddMessage(((PlayerTypes)iI), (((PlayerTypes)iI) == getOwnerINLINE()), GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NUKE_EXPLODES", MESSAGE_TYPE_MAJOR_EVENT, getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
}
}
//This is just so the espionage mission makes the cool explosion effect.
if (GC.getInfoTypeForString("EFFECT_ICBM_NUCLEAR_EXPLOSION") != -1)
{
gDLL->getEngineIFace()->TriggerEffect((EffectTypes)GC.getInfoTypeForString("EFFECT_ICBM_NUCLEAR_EXPLOSION"), pPlot->getPoint(), 0);
gDLL->getInterfaceIFace()->playGeneralSound("AS2D_NUKE_EXPLODES", pPlot->getPoint());
}
pPlot->nukeExplosion(1);
return true;
}
bool CvUnit::canClaimTerritory(const CvPlot* pPlot) const
{
if (!GET_PLAYER(getOwnerINLINE()).hasFixedBorders())
{
return false;
}
if (getOwnerINLINE() == BARBARIAN_PLAYER)
{
return false;
}
if (m_pUnitInfo->isAlwaysHostile())
{
return false;
}
if (!(m_pUnitInfo->isPillage()))
{
return false;
}
if (m_pUnitInfo->isHiddenNationality())
{
return false;
}
if (pPlot != NULL)
{
if (getOwnerINLINE() == pPlot->getOwnerINLINE())
{
return false;
}
/* if we or someone else (a friend) already claimed this plot in this turn */
if (pPlot->getClaimingOwner() != NO_PLAYER)
{
return false;
}
if (!pPlot->isPotentialCityWork())
{
return false;
}
if (pPlot->isCity())
{
return false;
}
else if (pPlot->isCity(true))
{
if (!GET_TEAM(getTeam()).isAtWar(pPlot->getTeam()))
{
return false;
}
}
if (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_CANNOT_CLAIM_OCEAN) && pPlot->isWater())
{
return false;
}
/* plots adjacent to cities always belong to those cities' owners */
if (pPlot->getAdjacentCity() != NULL)
{
return false;
}
if (!pPlot->isOwned() || potentialWarAction(pPlot))
{
//Require that have a tile with culture adjacent to this one
for (int iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
{
CvPlot* pAdjacentPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pAdjacentPlot != NULL)
{
if (pAdjacentPlot->getCulture(getOwnerINLINE()) > 0)
{
return true;
}
}
}
return false;
}
return false;
}
return true;
}
bool CvUnit::claimTerritory()
{
//logMsg("%S claims territory from %S at (%d, %d)", GET_PLAYER(getOwner()).getCivilizationShortDescription(), GET_PLAYER(plot()->getOwner()).getCivilizationShortDescription(), plot()->getX(), plot()->getY());
CvPlot* pPlot = plot();
bool bWasOwned = false;
PlayerTypes pPlayerThatLostTerritory;
if (!canClaimTerritory(pPlot))
{
return false;
}
if (pPlot->isOwned())
{
pPlayerThatLostTerritory = pPlot->getOwnerINLINE();
TeamTypes tTeamThatLostTerritory = GET_PLAYER(pPlayerThatLostTerritory).getTeam();
GET_TEAM(tTeamThatLostTerritory).changeWarWeariness(getTeam(), *pPlot, GC.getDefineINT("WW_PLOT_CAPTURED"));
GET_TEAM(getTeam()).changeWarWeariness(tTeamThatLostTerritory, *pPlot, GC.getDefineINT("WW_CAPTURED_PLOT"));
GET_TEAM(getTeam()).AI_changeWarSuccess(tTeamThatLostTerritory, GC.getDefineINT("WAR_SUCCESS_PLOT_CAPTURING"));
bWasOwned = true;
}
pPlot->setClaimingOwner(getOwnerINLINE());
if (bWasOwned)
{
CvWString szBuffer;
CvCity *pNearestCity = GC.getMapINLINE().findCity(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pPlayerThatLostTerritory, NO_TEAM, false);
MEMORY_TRACK_EXEMPT();
if (pNearestCity != NULL)
{
szBuffer = gDLL->getText("TXT_KEY_MISSION_CLAIM_TERRITORY_CIV_SUCCESS_NEAR", GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey(), pNearestCity->getName().GetCString());
}
else
{
szBuffer = gDLL->getText("TXT_KEY_MISSION_CLAIM_TERRITORY_CIV_SUCCESS", GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey());
}
AddMessage(pPlayerThatLostTerritory, true, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getEraInfo(GC.getGameINLINE().getCurrentEra()).getAudioUnitDefeatScript(), MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
finishMoves();
return true;
}
int CvUnit::surroundedDefenseModifier(const CvPlot *pPlot, const CvUnit *pDefender) const
{
if (true)
{
return 0;
}
CvPlot *pLoopPlot;
DirectionTypes dtDirectionAttacker = directionXY(pPlot, plot());
int iExtraModifier = 0, iI;
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if ((pLoopPlot != NULL) && (pLoopPlot->isWater() == pPlot->isWater()) && (iI != dtDirectionAttacker))
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvUnit* pBestUnit;
int iBestCurrCombatStr, iLowestCurrCombatStr;
pBestUnit = NULL;
iBestCurrCombatStr = 0;
iLowestCurrCombatStr = INT_MAX;
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTeam() == getTeam())
{
// if pDefender == null - use the last config of maxCombatStr() where pAttacker == this, else - use the pDefender to find the best attacker
if (pDefender != NULL)
{
// pPlot == NULL -> defender gets no plot defense bonuses (hills, forest, fortify, etc.) against surrounding units (will have them for the attacker)
int iTmpCurrCombatStr = pDefender->currCombatStr(NULL, pLoopUnit, NULL, false);
if (iTmpCurrCombatStr < iLowestCurrCombatStr)
{
iLowestCurrCombatStr = iTmpCurrCombatStr;
pBestUnit = pLoopUnit;
}
}
else
{
int iTmpCurrCombatStr = pLoopUnit->currCombatStr(pPlot, pLoopUnit, NULL, false);
if (iTmpCurrCombatStr > iBestCurrCombatStr)
{
iBestCurrCombatStr = iTmpCurrCombatStr;
pBestUnit = pLoopUnit;
}
}
}
}
double fAttDeffFactor = 0.0;
if (pDefender != NULL)
{
if (pBestUnit != NULL)
{
fAttDeffFactor = ((double) pBestUnit->currCombatStr(pPlot, pBestUnit, NULL, false)) / iLowestCurrCombatStr;
}
}
else
{
fAttDeffFactor = (double) iBestCurrCombatStr;
}
/* surrounding distance = 1, 2, 3 or 4; the bigger the better */
int iSurroundingDistance = abs(std::min(abs(iI - dtDirectionAttacker), abs(abs(iI - dtDirectionAttacker) - 8)));
double fSurroundingDistanceFactor;
switch (iSurroundingDistance)
{
case 1:
fSurroundingDistanceFactor = GC.getDefineFLOAT("SAD_FACTOR_1");
break;
case 2:
fSurroundingDistanceFactor = GC.getDefineFLOAT("SAD_FACTOR_2");
break;
case 3:
fSurroundingDistanceFactor = GC.getDefineFLOAT("SAD_FACTOR_3");
break;
case 4:
fSurroundingDistanceFactor = GC.getDefineFLOAT("SAD_FACTOR_4");
break;
default:
fSurroundingDistanceFactor = GC.getDefineFLOAT("SAD_FACTOR_1");
break;
}
if (fAttDeffFactor == 1)
{
iExtraModifier += int(fSurroundingDistanceFactor);
}
else if (fAttDeffFactor != 0)
{
iExtraModifier += int(fSurroundingDistanceFactor * ((fAttDeffFactor - 1) * pow(abs(fAttDeffFactor - 1), -0.75) + 1));
}
}
}
return std::min(GC.getDefineINT("SAD_MAX_MODIFIER"), iExtraModifier);
}
int CvUnit::getCanMovePeaksCount() const
{
return m_iCanMovePeaksCount;
}
bool CvUnit::isCanMovePeaks() const
{
return (getCanMovePeaksCount() > 0);
}
void CvUnit::changeCanMovePeaksCount(int iChange)
{
m_iCanMovePeaksCount += iChange;
FAssert(getCanMovePeaksCount() >= 0);
}
// Koshling - enhanced mountaineering mode to differentiate between ability to move through
// mountains, and ability to lead a stack through mountains
int CvUnit::getCanLeadThroughPeaksCount() const
{
return m_iCanLeadThroughPeaksCount;
}
bool CvUnit::isCanLeadThroughPeaks() const
{
return (getCanLeadThroughPeaksCount() > 0);
}
void CvUnit::changeCanLeadThroughPeaksCount(int iChange)
{
m_iCanLeadThroughPeaksCount += iChange;
FAssert(getCanLeadThroughPeaksCount() >= 0);
}
void CvUnit::combatWon(CvUnit* pLoser, bool bAttacking)
{
/* //PromotionTypes ePromotion;
//bool bConvert = false;
int iUnit = NO_UNIT;
int POW = NO_UNIT;
//CvUnit* pLoopUnit;
//CLLNode<IDInfo>* pUnitNode;
//CvUnit* pLoopUnit;
//CvPlot* pPlot;
CvUnit* pUnit;
if ((//m_pUnitInfo->getEnslavementChance() +
GET_PLAYER(getOwnerINLINE()).getEnslavementChance()) > 0)
{
if (//getDuration() == 0 && pLoser->isAlive() && !pLoser->isAnimal() &&
iUnit == NO_UNIT)
{
if (GC.getGameINLINE().getSorenRandNum(100, "Enslavement") <= (//m_pUnitInfo->getEnslavementChance() +
GET_PLAYER(getOwnerINLINE()).getEnslavementChance()))
{
iUnit = ((UnitTypes)(GC.getDefineINT("SLAVE_UNIT")));
//GC.getInfoTypeForString("UNIT_SLAVE");
}
}
}
if (iUnit != NO_UNIT)
{
if ((//!pLoser->isImmuneToCapture() &&
!isNoCapture()// && !pLoser->isImmortal()
)
//|| GC.getUnitInfo((UnitTypes)pLoser->getUnitType()).getEquipmentPromotion() != NO_PROMOTION
)
{
pUnit = GET_PLAYER(getOwnerINLINE()).initUnit((UnitTypes)iUnit, plot()->getX_INLINE(), plot()->getY_INLINE());
}
}*/
}
int CvUnit::getMaxHurryFood(CvCity* pCity) const
{
int iFood;
iFood = (m_pUnitInfo->getBaseFoodChange());
iFood *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getUnitHurryPercent();
iFood /= 100;
return std::max(0, iFood);
}
int CvUnit::getHurryFood(const CvPlot* pPlot) const
{
CvCity* pCity;
int iFood;
int iFoodLeft;
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return 0;
}
iFood = getMaxHurryFood(pCity);
iFoodLeft = (pCity->growthThreshold() - pCity->getFood());
iFood = std::min(iFoodLeft, iFood);
return std::max(0, iFood);
}
bool CvUnit::canHurryFood(const CvPlot* pPlot) const
{
if (isDelayedDeath())
{
return false;
}
CvCity* pCity;
if (getHurryFood(pPlot) == 0)
{
return false;
}
pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
return false;
}
if (pCity->getOwnerINLINE() != getOwnerINLINE())
{
return false;
}
if (pCity->getFoodTurnsLeft() == 1)
{
return false;
}
return true;
}
bool CvUnit::hurryFood()
{
CvCity* pCity;
if (!canHurryFood(plot()))
{
return false;
}
pCity = plot()->getPlotCity();
if (pCity != NULL)
{
pCity->changeFood(getHurryFood(plot()));
}
if (plot()->isActiveVisible(false))
{
NotifyEntity(MISSION_HURRY_FOOD);
}
kill(true, NO_PLAYER, true);
return true;
}
bool CvUnit::sleepForEspionage()
{
if (!canSleep(plot()) || !canEspionage(plot(), true))
{
return false;
}
if (getFortifyTurns() == GC.getDefineINT("MAX_FORTIFY_TURNS"))
{
return false;
}
getGroup()->setActivityType(ACTIVITY_SLEEP);
m_iSleepTimer = 1;
return true;
}
CvUnit* CvUnit::getCommander() const
{
PROFILE_FUNC();
// This routine gets called a HUGE number of times per turn (100s of millions in large games!)
// so short-circuit the most common case of the unit having no commander when we can
if ( m_iCachedCommander == NO_COMMANDER_ID )
{
return NULL;
}
CvUnit* pBestCommander = getUsedCommander();
if (pBestCommander != NULL) //return already used one if it is not dead.
{
int iDistance = plotDistance(pBestCommander->plot()->getX_INLINE(), pBestCommander->plot()->getY_INLINE(), plot()->getX_INLINE(), plot()->getY_INLINE());
if (pBestCommander->controlPointsLeft() > 0 && iDistance <= pBestCommander->commandRange())
{
return pBestCommander;
}
else
{
pBestCommander = NULL;
m_iCommanderCacheTurn = -1; // the one we used would have been the cached one so will be to search again
}
}
if ( m_iCommanderCacheTurn == GC.getGameINLINE().getGameTurn() )
{
pBestCommander = GET_PLAYER(getOwnerINLINE()).getUnit(m_iCachedCommander);
if ( pBestCommander != NULL )
{
// Guard against this being called durign the detah of said GC!
if ( pBestCommander->plot() != NULL )
{
int iDistance = plotDistance(pBestCommander->plot()->getX_INLINE(), pBestCommander->plot()->getY_INLINE(), plot()->getX_INLINE(), plot()->getY_INLINE());
if (pBestCommander->controlPointsLeft() > 0 && iDistance <= pBestCommander->commandRange())
{
return pBestCommander;
}
else
{
pBestCommander = NULL;
}
}
else
{
pBestCommander = NULL;
}
}
}
int iBestCommanderDistance = 9999999;
if (getOwnerINLINE() != NO_PLAYER)
{
CvPlayer& kPlayer = GET_PLAYER(getOwnerINLINE());
for (int i=0; i < (int)kPlayer.Commanders.size(); i++) //loop through player's commanders
{
CvUnit* pCommander = kPlayer.Commanders[i];
CvPlot* pCommPlot = pCommander->plot();
FAssertMsg(pCommPlot != NULL, "Commander Should Exist!");
if (pCommPlot == NULL)
{
continue;
}
int iDistance = plotDistance(pCommPlot->getX_INLINE(), pCommPlot->getY_INLINE(), plot()->getX_INLINE(), plot()->getY_INLINE());
if (pCommander->controlPointsLeft() <= 0 || iDistance > pCommander->commandRange())
{
continue;
}
if (pBestCommander == NULL ||
//best commander is at shorter distance, or at same distance but has more XP:
(iBestCommanderDistance < iDistance || (iBestCommanderDistance == iDistance && pCommander->getExperience() > pBestCommander->getExperience())))
{
pBestCommander = pCommander;
iBestCommanderDistance = iDistance;
}
}
}
// Don't cache the human player because they may make abortive odds calculations
// that lock a unit to a commander via the caching, and then not execute the attack that
// gave rise to the odds calculation
//Perhaps do not cache commanders because it causes an OOS error to do so? AIs make abortive odds calcs as well do they not?
if ( !isHuman() && !GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS))
{
m_iCommanderCacheTurn = GC.getGameINLINE().getGameTurn();
m_iCachedCommander = (pBestCommander == NULL ? NO_COMMANDER_ID : pBestCommander->getID());
}
return pBestCommander;
}
void CvUnit::tryUseCommander()
{
CvUnit* pCommander = getUsedCommander();
if ( pCommander == NULL || pCommander->controlPointsLeft() <= 0 )
{
pCommander = getCommander();
}
if (pCommander != NULL) //commander is used when any unit under his command fights in combat
{
pCommander->m_iControlPointsLeft -= 1;
m_iCommanderID = pCommander->getID();
}
}
bool CvUnit::isCommander() const
{
return m_bCommander;
}
void CvUnit::setCommander(bool bNewVal)
{
m_bCommander = bNewVal;
if (isCommander())
{
GET_PLAYER(getOwnerINLINE()).Commanders.push_back(this);
m_iControlPointsLeft = controlPoints();
}
}
void CvUnit::nullUsedCommander()
{
m_iCommanderID = -1;
}
CvUnit* CvUnit::getUsedCommander() const
{
return (m_iCommanderID == -1 ? NULL : GET_PLAYER(getOwnerINLINE()).getUnit(m_iCommanderID));
}
void CvUnit::clearCommanderCache(void)
{
if (!GC.getGameINLINE().isOption(GAMEOPTION_GREAT_COMMANDERS))
{
m_iCachedCommander = NO_COMMANDER_ID;
}
else
{
m_iCachedCommander = -1;
}
}
int CvUnit::getExtraControlPoints() const //control points
{
return m_iExtraControlPoints;
}
void CvUnit::changeExtraControlPoints(int iChange)
{
m_iExtraControlPoints += iChange;
m_iControlPointsLeft += iChange;
}
int CvUnit::controlPoints() const
{
return m_pUnitInfo->getControlPoints() + getExtraControlPoints();
}
int CvUnit::controlPointsLeft() const
{
return m_iControlPointsLeft;
}
int CvUnit::getExtraCommandRange() const //command range
{
return m_iExtraCommandRange;
}
void CvUnit::changeExtraCommandRange(int iChange)
{
m_iExtraCommandRange += iChange;
}
int CvUnit::commandRange() const
{
return m_pUnitInfo->getCommandRange() + getExtraCommandRange();
}
int CvUnit::getExtraStrength (bool bIgnoreCommanders) const
{
if (!bIgnoreCommanders && !isCommander()) //this is not a commander
{
CvUnit* pCommander = getCommander();
if (pCommander != NULL)
{
return m_iExtraStrength + pCommander->getExtraStrength();
}
}
return m_iExtraStrength;
}
int CvUnit::getOnslaughtCount() const //onslaught
{
return m_iOnslaughtCount;
}
bool CvUnit::isOnslaught() const
{
return (getOnslaughtCount() > 0);
}
void CvUnit::changeOnslaughtCount(int iChange)
{
m_iOnslaughtCount += iChange;
FAssert(getOnslaughtCount() >= 0);
}
int CvUnit::interceptionChance(const CvPlot* pPlot) const
{
int iValue;
int iLoop;
int iI;
int iNoInterceptionChanceTimes100 = 10000;
CvUnit* pLoopUnit;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (isEnemy(GET_PLAYER((PlayerTypes)iI).getTeam()) && !isInvisible(GET_PLAYER((PlayerTypes)iI).getTeam(), false, false))
{
for(pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->canAirDefend())
{
if (!pLoopUnit->isMadeInterception())
{
if ((pLoopUnit->getDomainType() != DOMAIN_AIR) || !(pLoopUnit->hasMoved()))
{
if ((pLoopUnit->getDomainType() != DOMAIN_AIR) || (pLoopUnit->getGroup()->getActivityType() == ACTIVITY_INTERCEPT))
{
if (plotDistance(pLoopUnit->getX_INLINE(), pLoopUnit->getY_INLINE(), pPlot->getX_INLINE(), pPlot->getY_INLINE()) <= pLoopUnit->airRange())
{
iValue = pLoopUnit->currInterceptionProbability();
if (iValue > 0 && iValue < 100)
{
iNoInterceptionChanceTimes100 *= (100 - iValue);
iNoInterceptionChanceTimes100 /= 100;
}
else if (iValue > 100)
return 100;
}
}
}
}
}
}
}
}
}
return 100 - (iNoInterceptionChanceTimes100 / 100);
}
PlayerTypes CvUnit::getOriginalOwner() const
{
return m_eOriginalOwner;
}
void CvUnit::doBattleFieldPromotions(CvUnit* pDefender, CombatDetails cdDefenderDetails, const CvPlot* pPlot, bool bAttackerHasLostNoHP, bool bAttackerWithdrawn, int iAttackerInitialDamage, int iWinningOdds, int iInitialAttXP, int iInitialAttGGXP, int iDefenderInitialDamage, int iInitialDefXP, int iInitialDefGGXP, bool &bAttackerPromoted, bool &bDefenderPromoted)
{
if (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_BATTLEFIELD_PROMOTIONS))
{
if (getUnitCombatType() != NO_UNITCOMBAT && pDefender->getUnitCombatType() != NO_UNITCOMBAT)
{
std::vector<PromotionTypes> aAttackerAvailablePromotions;
std::vector<PromotionTypes> aDefenderAvailablePromotions;
int iI;
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++) //loop through promotions
{
CvPromotionInfo &kPromotion = GC.getPromotionInfo((PromotionTypes)iI);
/* Block These Promotions */
if (kPromotion.getKamikazePercent() > 0)
continue;
if (kPromotion.getStateReligionPrereq() != NO_RELIGION)
{
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != kPromotion.getStateReligionPrereq())
continue;
}
if (kPromotion.isLeader())
continue;
/* Block These Promotions */
if (pDefender->isDead() || m_combatResult.bDefenderWithdrawn)
{
//* defender withdrawn, give him withdrawal promo
if (m_combatResult.bDefenderWithdrawn && kPromotion.getWithdrawalChange() > 0 &&
pDefender->canAcquirePromotion((PromotionTypes)iI))
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
if (!canAcquirePromotion((PromotionTypes)iI)) //attacker can not acquire this promotion
{
continue;
}
//* attacker was crossing river
if (kPromotion.isRiver() && cdDefenderDetails.iRiverAttackModifier != 0) //this bonus is being applied to defender
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* attack from water
else if (kPromotion.isAmphib() && cdDefenderDetails.iAmphibAttackModifier != 0)
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* attack terrain
else if (kPromotion.getTerrainAttackPercent((int)pPlot->getTerrainType()) > 0)
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* attack feature
else if (pPlot->getFeatureType() != NO_FEATURE &&
kPromotion.getFeatureAttackPercent((int)pPlot->getFeatureType()) > 0)
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* attack hills
else if (kPromotion.getHillsAttackPercent() > 0 && pPlot->isHills())
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* attack city
else if (kPromotion.getCityAttackPercent() > 0 && pPlot->isCity(true)) //count forts too
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* first strikes/chanses promotions
else if ((kPromotion.getFirstStrikesChange() > 0 ||
kPromotion.getChanceFirstStrikesChange() > 0) && (firstStrikes() > 0 || chanceFirstStrikes() > 0) )
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* unit combat mod
else if (kPromotion.getUnitCombatModifierPercent((int)pDefender->getUnitCombatType()) > 0)
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* combat strength promotions
else if (kPromotion.getCombatPercent() > 0 && !kPromotion.isAmphib())
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* domain mod
else if (kPromotion.getDomainModifierPercent((int)pDefender->getDomainType()))
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
//* blitz
else if (kPromotion.isBlitz() && bAttackerHasLostNoHP)
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
} //if defender is dead or withdrawn
else //attacker is dead or withdrawn
{
//* attacker withdrawn
if (bAttackerWithdrawn && kPromotion.getWithdrawalChange() > 0 && canAcquirePromotion((PromotionTypes)iI))
{
aAttackerAvailablePromotions.push_back((PromotionTypes)iI);
}
if (!pDefender->canAcquirePromotion((PromotionTypes)iI))
{
continue;
}
//* defend terrain
if (kPromotion.getTerrainDefensePercent((int)pPlot->getTerrainType()) > 0)
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
//* defend feature
else if (pPlot->getFeatureType() != NO_FEATURE &&
kPromotion.getFeatureDefensePercent((int)pPlot->getFeatureType()) > 0)
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
//* defend hills
else if (kPromotion.getHillsDefensePercent() > 0 && pPlot->isHills())
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
//* defend city
else if (kPromotion.getCityDefensePercent() > 0 && pPlot->isCity(true)) //count forts too
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
//* first strikes/chanses promotions
else if ((kPromotion.getFirstStrikesChange() > 0 ||
kPromotion.getChanceFirstStrikesChange() > 0) &&
(pDefender->firstStrikes() > 0 || pDefender->chanceFirstStrikes() > 0))
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
//* unit combat mod vs attacker unit type
else if (kPromotion.getUnitCombatModifierPercent((int)getUnitCombatType()) > 0)
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
//* combat strength promotions
else if (kPromotion.getCombatPercent() > 0)
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
//* domain mod
else if (kPromotion.getDomainModifierPercent((int)getDomainType()))
{
aDefenderAvailablePromotions.push_back((PromotionTypes)iI);
}
} //if attacker withdrawn
} //end promotion types cycle
//promote attacker:
if (!isDead() && aAttackerAvailablePromotions.size() > 0)
{
FAssertMsg(maxHitPoints() - iAttackerInitialDamage > 0, "Attacker is Dead!");
int iHealthPercent = (maxHitPoints() - getDamage()) * 100 / std::max(1,(maxHitPoints() - iAttackerInitialDamage));
int iPromotionChanceModifier = iHealthPercent * iHealthPercent / maxHitPoints();
int iPromotionChance = (GC.getCOMBAT_DIE_SIDES() - iWinningOdds) * (100 + iPromotionChanceModifier) / 100;
if (GC.getGameINLINE().getSorenRandNum(GC.getCOMBAT_DIE_SIDES(), "Occasional Promotion") < iPromotionChance)
{
//select random promotion from available
PromotionTypes ptPromotion = aAttackerAvailablePromotions[
GC.getGameINLINE().getSorenRandNum(aAttackerAvailablePromotions.size(), "Select Promotion Type")];
//promote
setHasPromotion(ptPromotion, true);
bAttackerPromoted = true;
//Reset XP
setExperience100(iInitialAttXP);
GET_PLAYER(getOwnerINLINE()).setCombatExperience(iInitialAttGGXP);
// Great Commander XP
if (getUsedCommander() != NULL)
{
getUsedCommander()->setExperience100(getUsedCommander()->getExperience100() + 100);
}
//show message
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_UNIT_PROMOTED_IN_BATTLE", getNameKey(), GC.getPromotionInfo(ptPromotion).getText());
AddMessage(
getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer,
GC.getPromotionInfo((PromotionTypes)0).getSound(), MESSAGE_TYPE_INFO, NULL,
(ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), this->plot()->getX_INLINE(), this->plot()->getY_INLINE());
}
}
}
//promote defender:
if (!pDefender->isDead() && aDefenderAvailablePromotions.size() > 0)
{
FAssertMsg(pDefender->maxHitPoints() - iDefenderInitialDamage > 0, "Defender is Dead!");
int iHealthPercent = (pDefender->maxHitPoints() - pDefender->getDamage()) * 100 / std::max(1,(pDefender->maxHitPoints() - iDefenderInitialDamage));
int iPromotionChanceModifier = iHealthPercent * iHealthPercent / pDefender->maxHitPoints();
int iPromotionChance = iWinningOdds * (100 + iPromotionChanceModifier) / 100;
if (GC.getGameINLINE().getSorenRandNum(GC.getCOMBAT_DIE_SIDES(), "Occasional Promotion") < iPromotionChance)
{
//select random promotion from available
PromotionTypes ptPromotion = aDefenderAvailablePromotions[
GC.getGameINLINE().getSorenRandNum(aDefenderAvailablePromotions.size(), "Select Promotion Type")];
//promote
pDefender->setHasPromotion(ptPromotion, true);
//Reset XP
pDefender->setExperience100(iInitialDefXP);
GET_PLAYER(pDefender->getOwnerINLINE()).setCombatExperience(iInitialDefGGXP);
bDefenderPromoted = true;
// Great Commander XP
if (pDefender->getUsedCommander() != NULL)
{
pDefender->getUsedCommander()->setExperience100(pDefender->getUsedCommander()->getExperience100() + 100);
}
//show message
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = gDLL->getText("TXT_KEY_MISC_YOUR_UNIT_PROMOTED_IN_BATTLE", pDefender->getNameKey(),
GC.getPromotionInfo(ptPromotion).getText());
AddMessage(
pDefender->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer,
GC.getPromotionInfo((PromotionTypes)0).getSound(), MESSAGE_TYPE_INFO, NULL,
(ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
}
}
}
}
}
}
void CvUnit::doDynamicXP(CvUnit* pDefender, const CvPlot* pPlot, int iAttackerInitialDamage, int iWinningOdds, int iDefenderInitialDamage, int iInitialAttXP, int iInitialDefXP, int iInitialAttGGXP, int iInitialDefGGXP, bool bPromotion, bool bDefPromotion)
{
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP))
{
if (!isDead() && !bPromotion && getUnitCombatType() != NO_UNITCOMBAT)
{
//reset XP
setExperience100(iInitialAttXP);
FAssertMsg(maxHitPoints() - iAttackerInitialDamage > 0, "Attacker is Dead!");
int iHealthPercent = (maxHitPoints() - getDamage()) * 100 / std::max(1,(maxHitPoints() - iAttackerInitialDamage));
int iExperienceModifier = (100 - iHealthPercent) * (100 - iHealthPercent) / maxHitPoints();
//Chance of losing
int iOdds = GC.getCOMBAT_DIE_SIDES() - iWinningOdds;
int iExperience = iOdds * (100 + iExperienceModifier) / 100;
if ( attackXPValue() > 0 )
{
iExperience = range(iExperience, getRandomMinExperienceTimes100(), attackXPValue() * 100);
changeExperience100(iExperience, maxXPValue() == MAX_INT ? MAX_INT : maxXPValue() * 100, true, pPlot->getOwnerINLINE() == getOwnerINLINE(), (!pDefender->isBarbarian() || GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_GENERALS)));
}
}
if (!pDefender->isDead() && !bDefPromotion && pDefender->getUnitCombatType() != NO_UNITCOMBAT)
{
//reset XP
pDefender->setExperience100(iInitialDefXP);
FAssertMsg(pDefender->maxHitPoints() - iDefenderInitialDamage > 0, "Defender is Dead!");
int iHealthPercent = (pDefender->maxHitPoints() - pDefender->getDamage()) * 100 / std::max(1,(pDefender->maxHitPoints() - iDefenderInitialDamage));
int iExperienceModifier = (100 - iHealthPercent) * (100 - iHealthPercent) / pDefender->maxHitPoints();
//Chance of Losing
int iOdds = iWinningOdds;
//iOdds = std::max(iOdds, -iOdds);
int iExperience = iOdds * (100 + iExperienceModifier) / 100;
if ( pDefender->defenseXPValue() > 0 )
{
iExperience = range(iExperience, getRandomMinExperienceTimes100(), pDefender->defenseXPValue() * 100);
pDefender->changeExperience100(iExperience, pDefender->maxXPValue() == MAX_INT ? MAX_INT : pDefender->maxXPValue() * 100, true, pPlot->getOwnerINLINE() == pDefender->getOwnerINLINE(), (!isBarbarian() || GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_GENERALS)));
}
}
}
}
int CvUnit::getRandomMinExperienceTimes100() const
{
int rand = GC.getGame().getSorenRandNum(26, "Random Min XP") + (GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT") * 25);
return rand;
}
bool CvUnit::isTerrainProtected(TerrainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return m_paiTerrainProtected[eIndex] > 0;
}
int CvUnit::getTerrainProtectedCount(TerrainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
return m_paiTerrainProtected[eIndex];
}
void CvUnit::changeTerrainProtected(TerrainTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < GC.getNumTerrainInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
m_paiTerrainProtected[eIndex] += iNewValue;
}
void CvUnit::doCommerceAttacks(const CvUnit* pDefender, const CvPlot* pPlot)
{
if (pDefender->isDead())
{
if (pPlot->getPlotCity() != NULL)
{
for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
if (pPlot->getPlotCity()->getMaxCommerceAttacks((CommerceTypes)iI) > 0)
{
pPlot->getPlotCity()->changeCommerceAttacks((CommerceTypes)iI, 1);
}
}
}
}
}
int CvUnit::getZoneOfControlCount() const
{
return m_iZoneOfControlCount;
}
bool CvUnit::isZoneOfControl() const
{
return (getZoneOfControlCount() > 0);
}
void CvUnit::changeZoneOfControlCount(int iChange)
{
m_iZoneOfControlCount += iChange;
if (isZoneOfControl())
{
GC.getGameINLINE().toggleAnyoneHasUnitZoneOfControl();
}
FAssert(getOnslaughtCount() >= 0);
}
bool CvUnit::isAutoPromoting() const
{
return m_bAutoPromoting;
}
void CvUnit::setAutoPromoting(bool bNewValue)
{
m_bAutoPromoting = bNewValue;
if (bNewValue)
{
//Force recalculation
setPromotionReady(false);
testPromotionReady();
}
}
bool CvUnit::isAutoUpgrading() const
{
return m_bAutoUpgrading;
}
void CvUnit::setAutoUpgrading(bool bNewValue)
{
m_bAutoUpgrading = bNewValue;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// < M.A.D. Nukes Start >
bool CvUnit::isMADEnabled() const
{
return m_bMADEnabled;
}
void CvUnit::setMADEnabled(bool bValue)
{
if (m_bMADEnabled != bValue)
{
m_bMADEnabled = bValue;
if (!isMADEnabled())
{
GET_PLAYER(getOwnerINLINE()).changeMADOutgoing(-1);
if(getMADTargetPlotOwner() != NO_PLAYER)
{
GET_PLAYER(getMADTargetPlotOwner()).changeMADIncoming(-1);
getMADTargetPlot()->getPlotCity()->changeMADIncoming(-1);
}
setMADTargetPlot(NULL);
setMADTargetPlotOwner(NO_PLAYER);
getGroup()->setActivityType(ACTIVITY_AWAKE);
}
}
}
// Dale - MAD: get MAD plot
CvPlot* CvUnit::getMADTargetPlot() const
{
return GC.getMapINLINE().plotINLINE(m_iMADTargetPlotX, m_iMADTargetPlotY);
}
int CvUnit::getMADTargetPlotX() const
{
return m_iMADTargetPlotX;
}
int CvUnit::getMADTargetPlotY() const
{
return m_iMADTargetPlotY;
}
// Dale - MAD: set MAD plot
void CvUnit::setMADTargetPlot(CvPlot* pPlot)
{
if(pPlot)
{
m_iMADTargetPlotX = pPlot->getX();
m_iMADTargetPlotY = pPlot->getY();
setMADTargetPlotOwner(pPlot->getPlotCity()->getOwnerINLINE());
}
else
{
m_iMADTargetPlotX = INVALID_PLOT_COORD;
m_iMADTargetPlotY = INVALID_PLOT_COORD;
m_pMADTargetPlotOwner = NO_PLAYER;
}
}
PlayerTypes CvUnit::getMADTargetPlotOwner()
{
return m_pMADTargetPlotOwner;
}
void CvUnit::setMADTargetPlotOwner(PlayerTypes pPlayer)
{
m_pMADTargetPlotOwner = pPlayer;
}
void CvUnit::doMADNukes(bool bForceRetarget)
{
CvCity* pCity;
CvWString szBuffer;
CvCity* pBestCity; // Dale - MAD: city required for AI stuff
int iLoop, iValue, iBestValue; // Dale - MAD: ints required for AI stuff
// Dale - MAD: check validity of Human nuke targets
if(isMADEnabled())
{
FAssertMsg(GET_PLAYER(getOwnerINLINE()).isEnabledMAD(), "Nukes Should Not Be Targeted!");
pCity = getMADTargetPlot()->getPlotCity();
if(pCity == NULL || pCity->getOwnerINLINE() != getMADTargetPlotOwner())
{
setMADEnabled(false);
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_NUKE_TARGET_LOST");
AddMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NUKE_EXPLODES", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), true, true);
}
}
if (!isHuman())
{
if ((!isMADEnabled() || bForceRetarget) && getUnitInfo().getUnitAIType(UNITAI_ICBM))
{
// Dale - MAD: code to make AI select target from potential enemys
// 1. AI finds a random player each turn (till a target is set)
// 2. AI checks that not at war and a potential enemy
// 3. AI picks a random city of player to target
//Clear existing targets
CvCity* pOldTarget = NULL;
if (bForceRetarget && isMADEnabled())
{
pOldTarget = getMADTargetPlot()->getPlotCity();
setMADEnabled(false);
}
pBestCity = NULL;
iBestValue = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive() && iI != GET_PLAYER(getOwnerINLINE()).getID())
{
if (GET_TEAM(getTeam()).AI_getWarPlan(GET_PLAYER((PlayerTypes)iI).getTeam()) != NO_WARPLAN || GET_PLAYER(getOwnerINLINE()).AI_getAttitudeVal((PlayerTypes)iI) < 0)
{
for (CvCity* pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
if (pLoopCity->isRevealed(getTeam(), false))
{
if (canNukeAt(plot(), pLoopCity->plot()->getX(), pLoopCity->plot()->getY(), false))
{
iValue = GET_PLAYER(getOwnerINLINE()).AI_targetCityValue(pLoopCity, true, false) / std::max(1, pLoopCity->getMADIncoming() / 3);
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestCity = pLoopCity;
}
}
}
}
}
}
}
if(pBestCity != NULL)
{
setMADTargetPlot(pBestCity->plot());
setMADEnabled(true);
setMADTargetPlotOwner(pBestCity->getOwnerINLINE());
GET_PLAYER(getOwnerINLINE()).changeMADOutgoing(1);
GET_PLAYER(pBestCity->getOwnerINLINE()).changeMADIncoming(1);
getMADTargetPlot()->getPlotCity()->changeMADIncoming(1);
if (pOldTarget != pBestCity)
{
MEMORY_TRACK_EXEMPT();
szBuffer = gDLL->getText("TXT_KEY_NUKE_TARGET_FRIENDLY_CITY", GET_PLAYER(getOwnerINLINE()).getCivilizationAdjective(), pBestCity->getNameKey());
AddMessage(pBestCity->getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_NUKE_EXPLODES", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pBestCity->getX_INLINE(), pBestCity->getY_INLINE(), true, true);
}
}
}
}
// Dale - MAD: if unit has a target, and trigger active for that player, then NUKE!
if(isMADEnabled() && !bForceRetarget)
{
if(canNuke(getMADTargetPlot()))
{
pCity = getMADTargetPlot()->getPlotCity();
if(pCity != NULL && (GET_PLAYER(getOwnerINLINE()).getMADTrigger(pCity->getOwnerINLINE()) || atWar(getTeam(), pCity->getTeam())))
{
nuke(getMADTargetPlot()->getX_INLINE(), getMADTargetPlot()->getY_INLINE());
}
}
}
}
// < M.A.D. Nukes End >
bool CvUnit::canShadow() const
{
if (!canAttack())
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_AUTO_PROTECT))
{
return false;
}
return true;
}
bool CvUnit::canShadowAt(CvPlot* pShadowPlot, CvUnit* pShadowUnit) const
{
if (!canShadow())
{
return false;
}
if (pShadowPlot == NULL)
{
return false;
}
if (pShadowUnit == NULL)
{
pShadowUnit = pShadowPlot->getCenterUnit();
}
if (pShadowUnit == NULL)
{
return false;
}
if (pShadowUnit->getTeam() != getTeam())
{
return false;
}
if (pShadowUnit->baseMoves() > baseMoves())
{
return false;
}
if (pShadowUnit == this)
{
return false;
}
int iPathTurns;
if (!generatePath(pShadowPlot, 0, true, &iPathTurns))
{
return false;
}
return true;
}
CvUnit* CvUnit::getShadowUnit() const
{
return getUnit(m_shadowUnit);
}
void CvUnit::setShadowUnit(CvUnit* pUnit)
{
if (pUnit != NULL)
{
m_shadowUnit = pUnit->getIDInfo();
}
else
{
m_shadowUnit.reset();
}
}
void CvUnit::setDesiredDiscoveryTech(TechTypes eTech)
{
m_eDesiredDiscoveryTech = eTech;
getGroup()->setActivityType(ACTIVITY_SLEEP);
}
TechTypes CvUnit::getDesiredDiscoveryTech() const
{
return m_eDesiredDiscoveryTech;
}
void CvUnit::waitForTech(int iFlag, int eTech)
{
if (iFlag != GC.getNumTechInfos())
{
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_SELECT_DISCOVERY_TECH, getID(), 0, 0);
if (pInfo)
{
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE(), true);
}
}
else
{
setDesiredDiscoveryTech((TechTypes)eTech);
}
}
CvProperties* CvUnit::getProperties()
{
return &m_Properties;
}
const CvProperties* CvUnit::getPropertiesConst() const
{
return &m_Properties;
}
void CvUnit::addMission(CvMissionDefinition* pMission)
{
if ( !isUsingDummyEntities() && isInViewport() )
{
if ( (pMission->getPlot() != NULL && !pMission->getPlot()->isInViewport()) ||
(pMission->getUnit(BATTLE_UNIT_ATTACKER) != NULL && !pMission->getUnit(BATTLE_UNIT_ATTACKER)->isInViewport()) ||
(pMission->getUnit(BATTLE_UNIT_DEFENDER) != NULL && !pMission->getUnit(BATTLE_UNIT_DEFENDER)->isInViewport()) )
{
return;
}
gDLL->getEntityIFace()->AddMission(pMission);
}
}
bool CvUnit::meetsUnitSelectionCriteria(CvUnitSelectionCriteria* criteria) const
{
if ( criteria != NULL )
{
if ( criteria->m_eUnitAI != NO_UNITAI && AI_getUnitAIType() != criteria->m_eUnitAI )
{
return false;
}
if ( criteria->m_eProperty != NO_PROPERTY )
{
int iPropertyDelta = AI_beneficialPropertyValueToCity(NULL);
if ( iPropertyDelta == 0 )
{
return false;
}
else if ( iPropertyDelta > 0 )
{
if ( !criteria->m_bPropertyBeneficial )
{
return false;
}
}
else
{
if ( criteria->m_bPropertyBeneficial )
{
return false;
}
}
}
if ( criteria->m_bIsHealer )
{
if ( getSameTileHeal() == 0 && getAdjacentTileHeal() == 0 )
{
return false;
}
}
if ( criteria->m_bIsCommander )
{
if ( !isCommander() )
{
return false;
}
}
}
return true;
}
bool CvUnit::shouldUseWithdrawalOddsCap() const
{
static bool bCached = false;
static bool bCachedResult;
if ( bCached )
{
return bCachedResult;
}
bCachedResult = isPursuitinUse();
bCached = true;
return bCachedResult;
}
bool CvUnit::isPursuitinUse() const
{
bool bInUse = false;
int iI;
for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
if (GC.getUnitInfo((UnitTypes)iI).getPursuit() > 0)
{
bInUse = true;
}
}
if (!bInUse)
{
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if (GC.getPromotionInfo((PromotionTypes)iI).getPursuitChange() > 0)
{
bInUse = true;
}
}
}
return bInUse;
}
int CvUnit::getRetrainsAvailable() const
{
return m_iRetrainsAvailable;
}