void CvUnit::updateCombat(bool bQuick)
{
CvUnit* pDefender;
CvPlot* pPlot;
CvWString szBuffer;
int iAttackerStrength;
int iAttackerFirepower;
int iDefenderStrength;
int iDefenderFirepower;
int iStrengthFactor;
bool bFinish;
bool bAdvance;
bool bVisible;
bool bFocused;
bool bFirst;
int iExperience;
int iDefenderOdds;
int iDamage;
bFinish = false;
bVisible = false;
if (getCombatTimer() > 0)
{
changeCombatTimer(-1);
if (getCombatTimer() > 0)
{
return;
}
else
{
bFinish = true;
}
}
pPlot = getAttackPlot();
if (pPlot == NULL)
{
return;
}
if (getDomainType() == DOMAIN_AIR)
{
if (!bFinish)
{
if (!bQuick)
{
if (isHuman())
{
bVisible = !(GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_QUICK_ATTACK));
}
}
if (bVisible)
{
setCombatTimer(GC.getMissionInfo(MISSION_AIRSTRIKE).getTime());
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
}
airStrike(pPlot);
if (bVisible)
{
return;
}
}
setAttackPlot(NULL);
getGroup()->clearMissionQueue();
return;
}
if (bFinish)
{
pDefender = getCombatUnit();
}
else
{
pDefender = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
}
if (pDefender == NULL)
{
setAttackPlot(NULL);
setCombatUnit(NULL);
getGroup()->groupMove(pPlot, true, ((canAdvance(pPlot, 0)) ? this : NULL));
getGroup()->clearMissionQueue();
return;
}
if (!bQuick)
{
if (isHuman())
{
bVisible = !(GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_QUICK_ATTACK));
}
else if (pDefender->isHuman())
{
bVisible = !(GET_PLAYER(pDefender->getOwnerINLINE()).isOption(PLAYEROPTION_QUICK_DEFENSE));
}
}
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 (!bFinish)
{
if (!isFighting())
{
if (plot()->isFighting() || pPlot->isFighting())
{
return;
}
setMadeAttack(true);
setCombatUnit(pDefender, true);
pDefender->setCombatUnit(this, false);
pDefender->getGroup()->clearMissionQueue();
bFocused = (bVisible && isCombatFocus() && gDLL->getInterfaceIFace()->isCombatFocus());
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.getDefineFLOAT("PLOT_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);
}
gDLL->getInterfaceIFace()->addMessage(pDefender->getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), ((!bFocused) ? gDLL->getText("TXT_KEY_MISC_YOU_UNITS_UNDER_ATTACK", GET_PLAYER(getOwnerINLINE()).getNameKey()).GetCString() : NULL), ((!bFocused) ? "AS2D_COMBAT" : NULL), MESSAGE_TYPE_DISPLAY_ONLY, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), !bFocused);
}
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)
{
if (GET_PLAYER(getOwnerINLINE()).isBarbarian() && !(pDefender->isNoCapture()))
{
CvBattleDefinition kBattle;
kBattle.pkUnits[BDU_ATTACKER] = this;
kBattle.pkUnits[BDU_DEFENDER] = pDefender;
kBattle.iDamage[BDU_ATTACKER][BDT_BEGIN] = getDamage();
kBattle.iDamage[BDU_ATTACKER][BDT_RANGED] = getDamage();
kBattle.iDamage[BDU_ATTACKER][BDT_END] = getDamage();
kBattle.iDamage[BDU_DEFENDER][BDT_BEGIN] = pDefender->getDamage();
kBattle.iDamage[BDU_DEFENDER][BDT_RANGED] = pDefender->getDamage();
kBattle.iDamage[BDU_DEFENDER][BDT_END] = 100;
kBattle.iFirstStrikes[BDU_ATTACKER] = 0;
kBattle.iFirstStrikes[BDU_DEFENDER] = 0;
int iTurns = planBattle( kBattle );
kBattle.fMissionTime = (float)(iTurns * gDLL->getSecsPerTurn());
gDLL->getEntityIFace()->AddMission(kBattle);
setCombatTimer(iTurns);
}
else
{
CvMissionDefinition kMission;
kMission.fMissionTime = getCombatTimer() * gDLL->getSecsPerTurn();
kMission.eMissionType = MISSION_SURRENDER;
kMission.pkUnits[BDU_ATTACKER] = this;
kMission.pkUnits[BDU_DEFENDER] = pDefender;
kMission.pkPlot = pPlot;
gDLL->getEntityIFace()->AddMission(kMission);
// Surrender mission
setCombatTimer(GC.getMissionInfo(MISSION_SURRENDER).getTime());
}
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
}
else
{
bFinish = true;
}
// Kill them!
pDefender->setDamage(GC.getMAX_HIT_POINTS());
}
else
{
CvBattleDefinition kBattle;
kBattle.pkUnits[BDU_ATTACKER] = this;
kBattle.pkUnits[BDU_DEFENDER] = pDefender;
kBattle.iDamage[BDU_ATTACKER][BDT_BEGIN] = getDamage();
kBattle.iDamage[BDU_DEFENDER][BDT_BEGIN] = pDefender->getDamage();
//Added ST
CombatDetails cdAttackerDetails;
CombatDetails cdDefenderDetails;
iAttackerStrength = currCombatStr(NULL, NULL, &cdAttackerDetails);
iAttackerFirepower = currFirepower(NULL, NULL);
iDefenderStrength = pDefender->currCombatStr(pPlot, this, &cdDefenderDetails);
iDefenderFirepower = pDefender->currFirepower(pPlot, this);
FAssert((iAttackerStrength + iDefenderStrength) > 0);
FAssert((iAttackerFirepower + iDefenderFirepower) > 0);
iDefenderOdds = ((GC.getDefineINT("COMBAT_DIE_SIDES") * iDefenderStrength) / (iAttackerStrength + iDefenderStrength));
iStrengthFactor = ((iAttackerFirepower + iDefenderFirepower + 1) / 2);
bFirst = true;
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));
gDLL->getEventReporterIFace()->genericEvent("combatLogCalc", pyArgsCD.makeFunctionArgs());
}
if (pDefender->isBarbarian())
{
if (GET_PLAYER(getOwnerINLINE()).getWinsVsBarbs() < GC.getHandicapInfo(GET_PLAYER(getOwnerINLINE()).getHandicapType()).getFreeWinsVsBarbs())
{
iDefenderOdds = min(10, iDefenderOdds);
}
}
if (isBarbarian())
{
if (GET_PLAYER(pDefender->getOwnerINLINE()).getWinsVsBarbs() < GC.getHandicapInfo(GET_PLAYER(pDefender->getOwnerINLINE()).getHandicapType()).getFreeWinsVsBarbs())
{
iDefenderOdds = max(90, iDefenderOdds);
}
}
[b]while (true)
{
if (bFirst)
{
collateralCombat(pPlot, pDefender);
}
if (GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("COMBAT_DIE_SIDES"), "Combat") <= iDefenderOdds)
{
if (getCombatFirstStrikes() == 0)
{
iDamage = max(1, ((GC.getDefineINT("COMBAT_DAMAGE") * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)));
if (((getDamage() + iDamage) >= maxHitPoints()) && (GC.getGameINLINE().getSorenRandNum(100, "Withdrawal") < withdrawalProbability()))
{
changeExperience(GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL"), pDefender->maxXPValue());
break;
}
changeDamage(iDamage, pDefender->getOwnerINLINE());
if (pDefender->getCombatFirstStrikes() > 0)
{
kBattle.iFirstStrikes[BDU_DEFENDER]++;
kBattle.iDamage[BDU_ATTACKER][BDT_RANGED] += iDamage;
}
//Added ST
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(iDamage);
gDLL->getEventReporterIFace()->genericEvent("combatLogHit", pyArgs.makeFunctionArgs());
}
}
}
else
{
if (pDefender->getCombatFirstStrikes() == 0)
{
iDamage = max(1, ((GC.getDefineINT("COMBAT_DAMAGE") * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)));
pDefender->changeDamage(iDamage);
if (getCombatFirstStrikes() > 0)
{
kBattle.iFirstStrikes[BDU_ATTACKER]++;
kBattle.iDamage[BDU_DEFENDER][BDT_RANGED] += iDamage;
}
//Added ST
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(iDamage);
gDLL->getEventReporterIFace()->genericEvent("combatLogHit", pyArgs.makeFunctionArgs());
}
}
}
if (getCombatFirstStrikes() > 0)
{
changeCombatFirstStrikes(-1);
}
if (pDefender->getCombatFirstStrikes() > 0)
{
pDefender->changeCombatFirstStrikes(-1);
}
bFirst = false;
if (isDead() || pDefender->isDead())
{
if (isDead())
{
iExperience = defenseXPValue();
iExperience = ((iExperience * iAttackerStrength) / iDefenderStrength);
iExperience = range(iExperience, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));
pDefender->changeExperience(iExperience, maxXPValue());
}
else
{
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());
}
break;
}
}[/b]
if (bVisible)
{
kBattle.iDamage[BDU_ATTACKER][BDT_END] = getDamage();
kBattle.iDamage[BDU_DEFENDER][BDT_END] = pDefender->getDamage();
kBattle.bAdvanceSquare = canAdvance(pPlot, 1);
if (isRanged() && pDefender->isRanged())
{
kBattle.iDamage[BDU_ATTACKER][BDT_RANGED] = kBattle.iDamage[BDU_ATTACKER][BDT_END];
kBattle.iDamage[BDU_DEFENDER][BDT_RANGED] = kBattle.iDamage[BDU_DEFENDER][BDT_END];
}
else
{
kBattle.iDamage[BDU_ATTACKER][BDT_RANGED] += kBattle.iDamage[BDU_ATTACKER][BDT_BEGIN];
kBattle.iDamage[BDU_DEFENDER][BDT_RANGED] += kBattle.iDamage[BDU_DEFENDER][BDT_BEGIN];
}
int iTurns = planBattle( kBattle);
kBattle.fMissionTime = iTurns * gDLL->getSecsPerTurn();
setCombatTimer(iTurns);
GC.getGameINLINE().incrementTurnTimer(getCombatTimer());
if (pPlot->isActiveVisible(false))
{
ExecuteMove(0.0f);
gDLL->getEntityIFace()->AddMission(kBattle);
}
}
else
{
bFinish = true;
}
}
}
if (bFinish)
{
if (bVisible)
{
if (isCombatFocus() && gDLL->getInterfaceIFace()->isCombatFocus())
{
if (getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())
{
gDLL->getInterfaceIFace()->releaseLockedCamera();
}
}
}
setAttackPlot(NULL);
setCombatUnit(NULL);
pDefender->setCombatUnit(NULL);
if (isDead())
{
if (isBarbarian())
{
GET_PLAYER(pDefender->getOwnerINLINE()).changeWinsVsBarbs(1);
}
if (pPlot->findHighestCultureTeam() != getTeam())
{
GET_TEAM(getTeam()).changeWarWeariness(pDefender->getTeam(), GC.getDefineINT("WW_UNIT_KILLED_ATTACKING"));
}
if (pPlot->findHighestCultureTeam() != pDefender->getTeam())
{
GET_TEAM(pDefender->getTeam()).changeWarWeariness(getTeam(), GC.getDefineINT("WW_KILLED_UNIT_DEFENDING"));
}
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());
gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_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(), GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey());
gDLL->getInterfaceIFace()->addMessage(pDefender->getOwnerINLINE(), true, GC.getDefineINT("EVENT_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
gDLL->getEventReporterIFace()->combatResult(pDefender, this);
}
else if (pDefender->isDead())
{
if (pDefender->isBarbarian())
{
GET_PLAYER(getOwnerINLINE()).changeWinsVsBarbs(1);
}
if (pPlot->findHighestCultureTeam() != pDefender->getTeam())
{
GET_TEAM(pDefender->getTeam()).changeWarWeariness(getTeam(), GC.getDefineINT("WW_UNIT_KILLED_DEFENDING"));
}
if (pPlot->findHighestCultureTeam() != getTeam())
{
GET_TEAM(getTeam()).changeWarWeariness(pDefender->getTeam(), GC.getDefineINT("WW_KILLED_UNIT_ATTACKING"));
}
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());
gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_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_YOU_UNIT_WAS_DESTROYED", pDefender->getNameKey(), getNameKey(), GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey());
gDLL->getInterfaceIFace()->addMessage(pDefender->getOwnerINLINE(), true, GC.getDefineINT("EVENT_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
gDLL->getEventReporterIFace()->combatResult(this, pDefender);
bAdvance = canAdvance(pPlot, ((pDefender->canDefend()) ? 1 : 0));
if (bAdvance)
{
if (!isNoCapture())
{
pDefender->setCapturingPlayer(getOwnerINLINE());
}
}
pDefender->kill(false, getOwnerINLINE());
pDefender = NULL;
if (!bAdvance)
{
changeMoves(max(GC.getMOVE_DENOMINATOR(), pPlot->movementCost(this, plot())));
if (!canMove() || !isBlitz())
{
if (IsSelected())
{
if (gDLL->getInterfaceIFace()->getLengthSelectionList() > 1)
{
gDLL->getInterfaceIFace()->removeFromSelectionList(this);
}
}
}
}
if (pPlot->getNumVisibleEnemyDefenders(getOwnerINLINE()) == 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();
}
else
{
szBuffer = gDLL->getText("TXT_KEY_MISC_YOU_UNIT_WITHDRAW", getNameKey(), pDefender->getNameKey());
gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), true, GC.getDefineINT("EVENT_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());
gDLL->getInterfaceIFace()->addMessage(pDefender->getOwnerINLINE(), true, GC.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, "AS2D_THEIR_WITHDRAWL", MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE());
changeMoves(max(GC.getMOVE_DENOMINATOR(), pPlot->movementCost(this, plot())));
getGroup()->clearMissionQueue();
}
}
}