Из SDK, расчёт силы юнита:
Код:
/// Update military Power
void CvUnitEntry::DoUpdatePower()
{
int iPower;
// ***************
// Main Factors - Strength & Moves
// ***************
// We want a Unit that has twice the strength to be roughly worth 3x as much with regards to Power
iPower = int(pow((double) GetCombat(), 1.5));
// Ranged Strength
int iRangedStrength = int(pow((double) GetRangedCombat(), 1.45));
// Naval ranged attacks are less useful
if(GetDomainType() == DOMAIN_SEA)
{
iRangedStrength /= 2;
}
if(iRangedStrength > 0)
{
iPower = iRangedStrength;
}
// We want Movement rate to be important, but not a dominating factor; a Unit with double the moves of a similarly-strengthed Unit should be ~1.5x as Powerful
iPower = int((float) iPower * pow((double) GetMoves(), 0.3));
// ***************
// Other modifiers
// ***************
// Suicide Units are obviously less useful
if(IsSuicide())
{
iPower /= 2;
}
// Nukes are cool
if(GetNukeDamageLevel() > 0)
{
iPower += 4000;
}
// ***************
// Promotion modifiers
// ***************
int iTemp;
int iLoop;
for(int iPromotionLoop = 0; iPromotionLoop < GC.getNumPromotionInfos(); iPromotionLoop++)
{
CvPromotionEntry* kPromotion = GC.getPromotionInfo((PromotionTypes)iPromotionLoop);
if(kPromotion == NULL)
continue;
if(GetFreePromotions(iPromotionLoop))
{
// City Attack - add half of the bonus
if(kPromotion->GetCityAttackPercent() > 0)
{
iTemp = (iPower * kPromotion->GetCityAttackPercent() / 2);
iTemp /= 100;
iPower += iTemp;
}
// Attack - add half of the bonus
if(kPromotion->GetAttackMod() > 0)
{
iTemp = (iPower * kPromotion->GetAttackMod() / 2);
iTemp /= 100;
iPower += iTemp;
}
// Defense - add half of the bonus
if(kPromotion->GetDefenseMod() > 0)
{
iTemp = (iPower * kPromotion->GetDefenseMod() / 2);
iTemp /= 100;
iPower += iTemp;
}
// Paradrop - add 25%
if(kPromotion->GetDropRange() > 0)
{
iTemp = iPower;
iTemp /= 4;
iPower += iTemp;
}
// Blitz - add 20%
if(kPromotion->IsBlitz())
{
iTemp = iPower;
iTemp /= 5;
iPower += iTemp;
}
// Set Up For Ranged Attack - reduce by 20%
if(kPromotion->IsMustSetUpToRangedAttack())
{
iTemp = iPower;
iTemp /= 5;
iPower -= iTemp;
}
// Only Defensive - reduce by 25%, but only if the Unit has no ranged capability
if(kPromotion->IsOnlyDefensive() && GetRangedCombat() == 0)
{
iTemp = iPower;
iTemp /= 4;
iPower -= iTemp;
}
for(iLoop = 0; iLoop < GC.getNumTerrainInfos(); iLoop++)
{
// Terrain Attack - add one quarter of the bonus
if(kPromotion->GetTerrainAttackPercent(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetTerrainAttackPercent(iLoop) / 4);
iTemp /= 100;
iPower += iTemp;
}
// Terrain Defense - add one quarter of the bonus
if(kPromotion->GetTerrainDefensePercent(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetTerrainDefensePercent(iLoop) / 4);
iTemp /= 100;
iPower += iTemp;
}
}
for(iLoop = 0; iLoop < GC.getNumFeatureInfos(); iLoop++)
{
// Feature Attack - add one quarter of the bonus
if(kPromotion->GetFeatureAttackPercent(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetFeatureAttackPercent(iLoop) / 4);
iTemp /= 100;
iPower += iTemp;
}
// Feature Defense - add one quarter of the bonus
if(kPromotion->GetFeatureDefensePercent(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetFeatureDefensePercent(iLoop) / 4);
iTemp /= 100;
iPower += iTemp;
}
}
for(iLoop = 0; iLoop < GC.getNumUnitCombatClassInfos(); iLoop++)
{
// Unit Combat Class (e.g. Pikemen) - add one quarter of the bonus
if(kPromotion->GetUnitCombatModifierPercent(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetUnitCombatModifierPercent(iLoop) / 4);
iTemp /= 100;
iPower += iTemp;
}
}
for(iLoop = 0; iLoop < GC.getNumUnitClassInfos(); iLoop++)
{
// Unit Class (e.g. bonus ONLY against Galleys) - add one eighth of the bonus
// We're assuming here that the bonus against the other Unit is at least going to be somewhat useful - trust the XML! :o
if(kPromotion->GetUnitClassModifierPercent(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetUnitClassModifierPercent(iLoop) / 8);
iTemp /= 100;
iPower += iTemp;
}
// Unit Class Attack - one tenth of the bonus
if(kPromotion->GetUnitClassAttackModifier(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetUnitClassAttackModifier(iLoop) / 10);
iTemp /= 100;
iPower += iTemp;
}
// Unit Class Defense - one tenth of the bonus
if(kPromotion->GetUnitClassDefenseModifier(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetUnitClassDefenseModifier(iLoop) / 10);
iTemp /= 100;
iPower += iTemp;
}
}
for(iLoop = 0; iLoop < NUM_DOMAIN_TYPES; iLoop++)
{
// Domain - add one quarter of the bonus
if(kPromotion->GetDomainModifierPercent(iLoop) > 0)
{
iTemp = (iPower * kPromotion->GetDomainModifierPercent(iLoop) / 4);
iTemp /= 100;
iPower += iTemp;
}
}
}
}
// Debug output
//char temp[256];
//sprintf(temp, "%s: %i\n", GetDescription(), iPower);
//OutputDebugString(temp);
m_iCachedPower = iPower;
}
//...
/// Current power of unit (raw unit type power adjusted for health)
int CvUnit::GetPower() const
{
VALIDATE_OBJECT
int iPower = getUnitInfo().GetPower();
//Take promotions into account: unit with 4 promotions worth ~50% more
iPower = int((float) iPower * pow((double) getLevel(), 0.3));
iPower *= GetCurrHitPoints();
iPower /= GetMaxHitPoints();
return iPower;
}
Вся функция расчёта:
Код:
// Calculates a basic score for whether the major can bully this minor based on many factors.
// Negative score if bully attempt is a failure, zero or positive if success.
// May be modified after return, if the task is easier or harder (ex. bully a worker vs. bully gold)
int CvMinorCivAI::CalculateBullyMetric(PlayerTypes eBullyPlayer)
{
//antonjs: todo: once the algorithm is more decided on, put values into constant/XML
const int iDefaultScore = -110;
const int iFailScore = -999;
CvAssertMsg(GetPlayer()->GetID() != eBullyPlayer, "Minor civ and bully civ not expected to have the same ID!");
if(GetPlayer()->GetID() == eBullyPlayer) return iFailScore;
CvAssertMsg(eBullyPlayer >= 0, "eBullyPlayer is expected to be non-negative (invalid Index)");
CvAssertMsg(eBullyPlayer < MAX_MAJOR_CIVS, "eBullyPlayer is expected to be within maximum bounds (invalid Index)");
if(eBullyPlayer < 0 || eBullyPlayer >= MAX_MAJOR_CIVS) return iFailScore;
// Can't bully the dead
if(!GetPlayer()->isAlive())
return iFailScore;
// The score beings negative, and has to be bumped up to positive by the below factors in order for the bullying to be successful
int iScore = iDefaultScore;
// **************************
// Global military power ranking of major
//
// +0 ~ +100
// **************************
CvWeightedVector<PlayerTypes, MAX_MAJOR_CIVS, true> veMilitaryRankings;
PlayerTypes eMajorLoop;
for(int iMajorLoop = 0; iMajorLoop < MAX_MAJOR_CIVS; iMajorLoop++)
{
eMajorLoop = (PlayerTypes) iMajorLoop;
if(GET_PLAYER(eMajorLoop).isAlive() && !GET_PLAYER(eMajorLoop).isMinorCiv())
{
veMilitaryRankings.push_back(eMajorLoop, GET_PLAYER(eMajorLoop).GetMilitaryMight()); // Don't recalculate within a turn, can cause inconsistency
}
}
CvAssertMsg(veMilitaryRankings.size() > 0, "WeightedVector of military might rankings not expected to be size 0");
veMilitaryRankings.SortItems();
for(int iRanking = 0; iRanking < veMilitaryRankings.size(); iRanking++)
{
if(veMilitaryRankings.GetElement(iRanking) == eBullyPlayer)
{
float fRankRatio = (float)(veMilitaryRankings.size() - iRanking) / (float)(veMilitaryRankings.size());
int iScoreMod = (int)(fRankRatio * 100); // A score between 100*(1 / num majors alive) and 100, with the highest rank major getting 100
iScore += iScoreMod;
break;
}
}
// **************************
// General military power comparison
// **************************
// **************************
// Local military power comparison
//
// +0 ~ +100
// **************************
int iComparisonRadius = std::max(GC.getMap().getGridWidth() / 10, 5);
CvCity* pMinorCapital = GetPlayer()->getCapitalCity();
if(pMinorCapital == NULL)
return iFailScore;
CvPlot* pMinorCapitalPlot = pMinorCapital->plot();
if(pMinorCapitalPlot == NULL)
return iFailScore;
int iMinorCapitalX = pMinorCapitalPlot->getX();
int iMinorCapitalY = pMinorCapitalPlot->getY();
int iMinorLocalPower = 0;
int iBullyLocalPower = 0;
CvPlot* pLoopPlot;
IDInfo* pUnitNode;
CvUnit* pLoopUnit;
// Include the minor's city power
iMinorLocalPower += pMinorCapital->GetPower();
for(int iDX = -iComparisonRadius; iDX <= iComparisonRadius; iDX++)
{
for(int iDY = -iComparisonRadius; iDY <= iComparisonRadius; iDY++)
{
pLoopPlot = ::plotXYWithRangeCheck(iMinorCapitalX, iMinorCapitalY, iDX, iDY, iComparisonRadius);
if(pLoopPlot != NULL)
{
// If there are Units here, loop through them
if(pLoopPlot->getNumUnits() > 0)
{
pUnitNode = pLoopPlot->headUnitNode();
while(pUnitNode != NULL)
{
pLoopUnit = ::getUnit(*pUnitNode);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
// Is a combat unit
if(pLoopUnit && pLoopUnit->IsCombatUnit())
{
if(pLoopUnit->getOwner() == eBullyPlayer)
{
iBullyLocalPower += pLoopUnit->GetPower();
}
else if(pLoopUnit->getOwner() == GetPlayer()->GetID())
{
iMinorLocalPower += pLoopUnit->GetPower();
}
}
}
}
}
}
}
float fLocalPowerRatio = (float)iBullyLocalPower / (float)iMinorLocalPower;
if(fLocalPowerRatio >= 3.0)
{
iScore += 100;
}
else if(fLocalPowerRatio >= 2.0)
{
iScore += 80;
}
else if(fLocalPowerRatio >= 1.5)
{
iScore += 60;
}
else if(fLocalPowerRatio >= 1.0)
{
iScore += 40;
}
else if(fLocalPowerRatio >= 0.5)
{
iScore += 20;
}
// **************************
// Local military threat from major
// **************************
//antonjs: consider: count the number of threatening units (similar to counting threatening barbarians), and/or their power
// **************************
// Proximity to major
// **************************
// **************************
// Previous bully attempts
//
// InstantFail ~ -0
// **************************
int iLastBullyTurn = GetTurnLastBulliedByMajor(eBullyPlayer);
if(iLastBullyTurn >= 0)
{
if(iLastBullyTurn + 10 >= GC.getGame().getGameTurn())
{
return iFailScore;
}
else if(iLastBullyTurn + 20 >= GC.getGame().getGameTurn())
{
iScore += -40;
}
}
// **************************
// Current influence of major
//
// InstantFail ~ -0
// **************************
if(GetEffectiveFriendshipWithMajor(eBullyPlayer) < GC.getFRIENDSHIP_THRESHOLD_CAN_BULLY())
{
return iFailScore;
}
/*antonjs: consider: scale based on how low the influence is, like:
float iNegativeInfluenceMultiplier = 1.2f;
if (GetFriendshipWithMajor(eBullyPlayer) < 0)
{
iScore += (int) (iNegativeInfluenceMultiplier * GetFriendshipWithMajor(eBullyPlayer));
//antonjs: consider: exponent, like influence^1.1
}
*/
// **************************
// Diplomatic reputation of major
// **************************
// **************************
// Passive Support from other majors
//
// -10 ~ -0
// **************************
if(GetAlly() != NO_PLAYER && GetAlly() != eBullyPlayer)
{
iScore += -10;
}
//antonjs: todo: fulfilled support quests, esp. gold gift
//antonjs: todo: friendly support units
// **************************
// Pledges of Protection from other majors
//
// -20 ~ -0
// **************************
//antonjs: consider: don't have PtP be a factor here, only have it factor in major civ diplo reaction
for(int iMajorLoop = 0; iMajorLoop < MAX_MAJOR_CIVS; iMajorLoop++)
{
eMajorLoop = (PlayerTypes) iMajorLoop;
if(eMajorLoop != eBullyPlayer && IsProtectedByMajor(eMajorLoop))
{
iScore += -20;
break;
}
}
// **************************
// Major Civ UA, policies
// **************************
// **************************
// Minor Civ Type
//
// -20 ~ -0
// **************************
if(GetPersonality() == MINOR_CIV_PERSONALITY_HOSTILE)
{
iScore += -10;
}
if(GetTrait() == MINOR_CIV_TRAIT_MILITARISTIC)
{
iScore += -10;
}
return iScore;
}
Т.е. в основном - войска могут дать от 0 до 100 очков давления, для получения 100 нужно иметь армию в 3 раза сильнее, чем у гг. При расчёте мощь юнитов на морских клетках сокращается в 2 раза, кроме того учитываются очки передвижения и юниты ближнего боя дают больше влияния. Сила столицы гг тоже учитывается. От ядерного оружия гг серят кирпичами . Но "ифов" там много.