/// Get center of mass of units in army (account for world wrap!) CvPlot* CvArmyAI::GetCenterOfMass(float* pfVarX, float* pfVarY) { int iTotalX = 0; int iTotalY = 0; int iNumUnits = 0; UnitHandle pUnit = GetFirstUnit(); if (!pUnit) return NULL; int iTotalX2 = 0; int iTotalY2 = 0; int iWorldWidth = GC.getMap().getGridWidth(); int iWorldHeight = GC.getMap().getGridHeight(); //the first unit is our reference ... int iRefX = pUnit->getX(); int iRefY = pUnit->getY(); iNumUnits++; pUnit = GetNextUnit(); while(pUnit) { int iDX = pUnit->getX() - iRefX; int iDY = pUnit->getY() - iRefY; if (GC.getMap().isWrapX()) { if( iDX > +(iWorldWidth / 2)) iDX -= iWorldWidth; if( iDX < -(iWorldWidth / 2)) iDX += iWorldWidth; } if (GC.getMap().isWrapY()) { if( iDY > +(iWorldHeight / 2)) iDY -= iWorldHeight; if( iDY < -(iWorldHeight / 2)) iDY += iWorldHeight; } iTotalX += iDX; iTotalY += iDY; iTotalX2 += iDX*iDX; iTotalY2 += iDY*iDY; iNumUnits++; pUnit = GetNextUnit(); } if (iNumUnits==0) return NULL; //this is for debugging float fVarX = (iTotalX2 / (float)iNumUnits) - (iTotalX/(float)iNumUnits)*(iTotalX/(float)iNumUnits); float fVarY = (iTotalY2 / (float)iNumUnits) - (iTotalY/(float)iNumUnits)*(iTotalY/(float)iNumUnits); //finally, compute average (with rounding) int iAvgX = (iTotalX + (iNumUnits / 2)) / iNumUnits + iRefX; int iAvgY = (iTotalY + (iNumUnits / 2)) / iNumUnits + iRefY; if (fVarX > 64 || fVarY > 64) { CvString msg = CvString::format("Warning: Army %d with %d units Center of Mass (%d,%d) has a large variance (%.2f,%.2f)\n", GetID(), iNumUnits, iAvgX, iAvgY, fVarX, fVarY); OutputDebugString( msg.c_str() ); } //this handles wrapped coordinates CvPlot* pCOM = GC.getMap().plot(iAvgX, iAvgY); if (!pCOM) return NULL; if (pfVarX) *pfVarX = fVarX; if (pfVarY) *pfVarY = fVarY; //don't return it directly but use the plot of the closest unit pUnit = GetFirstUnit(); std::vector<SPlotWithScore> vPlots; while (pUnit) { if (pUnit->plot()->getDomain()==GetDomainType()) { int iDistToCOM = plotDistance(*pUnit->plot(),*pCOM); int iDistToTarget = plotDistance(pUnit->getX(),pUnit->getY(),GetGoalX(),GetGoalY()); vPlots.push_back( SPlotWithScore(pUnit->plot(),iDistToCOM*100+iDistToTarget) ); } pUnit = GetNextUnit(); } if (vPlots.empty()) return NULL; //this sorts ascending! std::sort(vPlots.begin(),vPlots.end()); return vPlots.front().pPlot; }
/// 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; }