Hooks
static bool BeforeCvUnitCombat_Attack(CvUnit& kAttacker, CvPlot& targetPlot, ATTACK_OPTION eOption, CvUnitCombat::ATTACK_RESULT& result);static void AfterCvUnitCombat_Attack(CvUnit& kAttacker, CvPlot& targetPlot, ATTACK_OPTION eOption, CvUnitCombat::ATTACK_RESULT& result);static CvUnitCombat::ATTACK_RESULT ReplaceCvUnitCombat_Attack(CvUnitCombat::ATTACK_RESULT (*original)(CvUnit&, CvPlot&, ATTACK_OPTION), CvUnit& kAttacker, CvPlot& targetPlot, ATTACK_OPTION eOption);// CivHooks.h gives this hook point no types, so nothing checks these hooks' parameters.host->RegisterHook(host->pluginIndex, CivHooks::CvUnitCombat_Attack::ID, CIV_HOOK_BEFORE, (void*)&BeforeCvUnitCombat_Attack);host->RegisterHook(host->pluginIndex, CivHooks::CvUnitCombat_Attack::ID, CIV_HOOK_AFTER, (void*)&AfterCvUnitCombat_Attack);host->RegisterHook(host->pluginIndex, CivHooks::CvUnitCombat_Attack::ID, CIV_HOOK_REPLACE, (void*)&ReplaceCvUnitCombat_Attack);
Code
3000CvUnitCombat::ATTACK_RESULT CvUnitCombat::Attack(CvUnit& kAttacker, CvPlot& targetPlot, ATTACK_OPTION eOption)3001{3002 CvString strBuffer;30033004 //VALIDATE_OBJECT3005 CvAssert(kAttacker.canMoveInto(targetPlot, CvUnit::MOVEFLAG_ATTACK | CvUnit::MOVEFLAG_PRETEND_CORRECT_EMBARK_STATE));3006 CvAssert(kAttacker.getCombatTimer() == 0);30073008 CvUnitCombat::ATTACK_RESULT eResult = CvUnitCombat::ATTACK_ABORTED;30093010 CvAssert(kAttacker.getCombatTimer() == 0);3011 // CvAssert(pDefender != NULL);3012 CvAssert(!kAttacker.isFighting());30133014 // Unit that attacks loses his Fort bonus3015 kAttacker.setFortifyTurns(0);30163017 UnitHandle pDefender;3018 pDefender = targetPlot.getBestDefender(NO_PLAYER, kAttacker.getOwner(), &kAttacker, true);30193020 // JAR - without pDefender, nothing in here is going to work, just crash3021 if(!pDefender)3022 {3023 return eResult;3024 }30253026 kAttacker.SetAutomateType(NO_AUTOMATE);3027 pDefender->SetAutomateType(NO_AUTOMATE);3028#if defined(MOD_BUGFIX_UNITS_AWAKE_IN_DANGER)3029 // We want to "wake up" a unit that is being attacked by a hidden enemy (probably being bombed, indirect naval fire,3030 // or sneaky long-range archers) so the player can consider what to do with them, but without removing any fortification bonus!3031 if (MOD_BUGFIX_UNITS_AWAKE_IN_DANGER) {3032 pDefender->SetActivityType(ACTIVITY_AWAKE, false); 3033 }3034#endif30353036#if !defined(NO_TUTORIALS)3037 // slewis - tutorial'd3038 if(kAttacker.getOwner() == GC.getGame().getActivePlayer())3039 {3040 GC.getGame().SetEverAttackedTutorial(true);3041 }3042 // end tutorial'd3043#endif30443045 // handle the Zulu special thrown spear first attack3046 ATTACK_RESULT eFireSupportResult = ATTACK_ABORTED;3047 if (kAttacker.isRangedSupportFire() && pDefender->IsCanDefend())3048 {3049 eFireSupportResult = AttackRanged(kAttacker, pDefender->getX(), pDefender->getY(), CvUnitCombat::ATTACK_OPTION_NO_DEFENSIVE_SUPPORT);3050 if (pDefender->isDelayedDeath())3051 {3052 // Killed him, move to the plot if we can.3053 if(targetPlot.getNumVisibleEnemyDefenders(&kAttacker) == 0)3054 {3055 if (kAttacker.UnitMove(&targetPlot, true, &kAttacker, true))3056 kAttacker.finishMoves(); // Burn all the moves we have3057 }3058 return eFireSupportResult;3059 }3060 }30613062 CvCombatInfo kCombatInfo;3063 GenerateMeleeCombatInfo(kAttacker, pDefender.pointer(), targetPlot, &kCombatInfo);30643065 CvAssertMsg(!kAttacker.isDelayedDeath() && !pDefender->isDelayedDeath(), "Trying to battle and one of the units is already dead!");30663067 if(pDefender->getExtraWithdrawal() > 0 && pDefender->CanWithdrawFromMelee(kAttacker))3068 {3069 pDefender->DoWithdrawFromMelee(kAttacker);30703071 if(kAttacker.getOwner() == GC.getGame().getActivePlayer())3072 {3073 strBuffer = GetLocalizedText("TXT_KEY_MISC_ENEMY_UNIT_WITHDREW", pDefender->getNameKey());3074 GC.GetEngineUserInterface()->AddMessage(0, kAttacker.getOwner(), true, GC.getEVENT_MESSAGE_TIME(), strBuffer);3075 MILITARYLOG(kAttacker.getOwner(), strBuffer.c_str(), kAttacker.plot(), pDefender->getOwner());3076 }3077 else if(pDefender->getOwner() == GC.getGame().getActivePlayer())3078 {3079 strBuffer = GetLocalizedText("TXT_KEY_MISC_FRIENDLY_UNIT_WITHDREW", pDefender->getNameKey());3080 GC.GetEngineUserInterface()->AddMessage(0, pDefender->getOwner(), true, GC.getEVENT_MESSAGE_TIME(), strBuffer);3081 MILITARYLOG(pDefender->getOwner(), strBuffer.c_str(), pDefender->plot(), kAttacker.getOwner());3082 }30833084 // Move forward3085 if(targetPlot.getNumVisibleEnemyDefenders(&kAttacker) == 0)3086 {3087 kAttacker.UnitMove(&targetPlot, true, &kAttacker);3088 }30893090// kAttacker.setMadeAttack(true); /* EFB: Doesn't work, causes tactical AI to not dequeue this attack; but we've decided you don't lose your attack anyway */3091 eResult = ATTACK_COMPLETED;3092 }30933094 else if(!pDefender->IsCanDefend())3095 {3096 CvMissionDefinition kMission;3097 kMission.setMissionTime(kAttacker.getCombatTimer() * gDLL->getSecsPerTurn());3098 kMission.setMissionType(CvTypes::getMISSION_SURRENDER());3099 kMission.setUnit(BATTLE_UNIT_ATTACKER, &kAttacker);3100 kMission.setUnit(BATTLE_UNIT_DEFENDER, pDefender.pointer());3101 kMission.setPlot(&targetPlot);31023103 // Surrender mission3104 CvMissionInfo* pkSurrenderMission = GC.getMissionInfo(CvTypes::getMISSION_SURRENDER());3105 if(pkSurrenderMission == NULL)3106 {3107 CvAssert(false);3108 }3109 else3110 {3111 kAttacker.setCombatTimer(pkSurrenderMission->getTime());3112 }31133114 // Kill them!3115#if defined(MOD_UNITS_MAX_HP)3116 pDefender->setDamage(pDefender->GetMaxHitPoints());3117#else3118 pDefender->setDamage(GC.getMAX_HIT_POINTS());3119#endif31203121 Localization::String strMessage;3122 Localization::String strSummary;31233124 // Some units can't capture civilians. Embarked units are also not captured, they're simply killed. And some aren't a type that gets captured.3125 if(!kAttacker.isNoCapture() && (!pDefender->isEmbarked() || pDefender->getUnitInfo().IsCaptureWhileEmbarked()) && pDefender->getCaptureUnitType(GET_PLAYER(pDefender->getOwner()).getCivilizationType()) != NO_UNIT)3126 {3127 pDefender->setCapturingPlayer(kAttacker.getOwner());31283129 if(kAttacker.isBarbarian())3130 {3131 strMessage = Localization::Lookup("TXT_KEY_UNIT_CAPTURED_BARBS_DETAILED");3132 strMessage << pDefender->getUnitInfo().GetTextKey() << GET_PLAYER(kAttacker.getOwner()).getNameKey();3133 strSummary = Localization::Lookup("TXT_KEY_UNIT_CAPTURED_BARBS");3134 }3135 else3136 {3137 strMessage = Localization::Lookup("TXT_KEY_UNIT_CAPTURED_DETAILED");3138 strMessage << pDefender->getUnitInfo().GetTextKey();3139 strSummary = Localization::Lookup("TXT_KEY_UNIT_CAPTURED");3140 }3141 }3142 // Unit was killed instead3143 else3144 {3145 strMessage = Localization::Lookup("TXT_KEY_UNIT_LOST");3146 strSummary = strMessage;3147 }31483149 CvNotifications* pNotification = GET_PLAYER(pDefender->getOwner()).GetNotifications();3150 if(pNotification)3151 pNotification->Add(NOTIFICATION_UNIT_DIED, strMessage.toUTF8(), strSummary.toUTF8(), pDefender->getX(), pDefender->getY(), (int) pDefender->getUnitType(), pDefender->getOwner());31523153 bool bAdvance;3154 bAdvance = kAttacker.canAdvance(targetPlot, ((pDefender->IsCanDefend()) ? 1 : 0));31553156 // Move forward3157 if(targetPlot.getNumVisibleEnemyDefenders(&kAttacker) == 0)3158 {3159 kAttacker.UnitMove(&targetPlot, true, ((bAdvance) ? &kAttacker : NULL));3160 }31613162 // KWG: Should this be called? The defender is killed above and the unit.3163 // If anything, the above code should be put in the ResolveCombat method.3164 ResolveCombat(kCombatInfo);3165 eResult = ATTACK_COMPLETED;3166 }3167 else3168 {3169 ATTACK_RESULT eSupportResult = ATTACK_ABORTED;3170 if(eOption != ATTACK_OPTION_NO_DEFENSIVE_SUPPORT)3171 {3172 // Ranged fire support from artillery units3173 CvUnit* pFireSupportUnit = GetFireSupportUnit(pDefender->getOwner(), pDefender->getX(), pDefender->getY(), kAttacker.getX(), kAttacker.getY());3174 if(pFireSupportUnit != NULL)3175 {3176 CvAssertMsg(!pFireSupportUnit->isDelayedDeath(), "Supporting battle unit is already dead!");3177 eSupportResult = AttackRanged(*pFireSupportUnit, kAttacker.getX(), kAttacker.getY(), CvUnitCombat::ATTACK_OPTION_NO_DEFENSIVE_SUPPORT);3178 // Turn off Fortify Turns, as this is the trigger for whether or not a ranged Unit can provide support fire (in addition to hasMadeAttack)3179 pFireSupportUnit->setFortifyTurns(0);3180 }31813182 if(eSupportResult == ATTACK_QUEUED)3183 {3184 // The supporting unit has queued their attack (against the attacker), we must have the attacker queue its attack.3185 // Also, flag the current mission that the next time through, the defender doesn't get any defensive support.3186 const_cast<MissionData*>(kAttacker.GetHeadMissionData())->iFlags |= MISSION_MODIFIER_NO_DEFENSIVE_SUPPORT;3187 CvUnitMission::WaitFor(&kAttacker, pFireSupportUnit);3188 eResult = ATTACK_QUEUED;3189 }3190 }31913192 if(eResult != ATTACK_QUEUED)3193 {3194 kAttacker.setMadeAttack(true);31953196 uint uiParentEventID = 0;3197 // Send the combat message if the target plot is visible.3198 bool isTargetVisibleToActivePlayer = targetPlot.isActiveVisible(false);3199 bool quickCombat = CvPreGame::quickCombat();3200 if(!quickCombat)3201 {3202 // Center camera here!3203 if(isTargetVisibleToActivePlayer)3204 {3205 auto_ptr<ICvPlot1> pDefenderPlot = GC.WrapPlotPointer(pDefender->plot());3206 GC.GetEngineUserInterface()->lookAt(pDefenderPlot.get(), CAMERALOOKAT_NORMAL);3207 }3208 kCombatInfo.setVisualizeCombat(isTargetVisibleToActivePlayer);32093210 auto_ptr<ICvCombatInfo1> pDllCombatInfo(new CvDllCombatInfo(&kCombatInfo));3211 uiParentEventID = gDLL->GameplayUnitCombat(pDllCombatInfo.get());32123213 // Set the combat units so that other missions do not continue until combat is over.3214 kAttacker.setCombatUnit(pDefender.pointer(), true);3215 pDefender->setCombatUnit(&kAttacker, false);32163217 eResult = ATTACK_QUEUED;3218 }3219 else3220 eResult = ATTACK_COMPLETED;32213222 // Resolve combat here.3223 ResolveCombat(kCombatInfo, uiParentEventID);32243225 }3226 }32273228 return eResult;3229}