فهرست منبع

BattleAI: take into account defender dragon breath and other mutitarget attacks

Andrii Danylchenko 1 سال پیش
والد
کامیت
da46d5d01b
2فایلهای تغییر یافته به همراه85 افزوده شده و 38 حذف شده
  1. 73 26
      AI/BattleAI/AttackPossibility.cpp
  2. 12 12
      client/render/CAnimation.cpp

+ 73 - 26
AI/BattleAI/AttackPossibility.cpp

@@ -93,6 +93,8 @@ int64_t DamageCache::getOriginalDamage(const battle::Unit * attacker, const batt
 AttackPossibility::AttackPossibility(BattleHex from, BattleHex dest, const BattleAttackInfo & attack)
 	: from(from), dest(dest), attack(attack)
 {
+	this->attack.attackerPos = from;
+	this->attack.defenderPos = dest;
 }
 
 float AttackPossibility::damageDiff() const
@@ -261,37 +263,59 @@ AttackPossibility AttackPossibility::evaluate(
 		if (!attackInfo.shooting)
 			ap.attackerState->setPosition(hex);
 
-		std::vector<const battle::Unit*> units;
+		std::vector<const battle::Unit *> defenderUnits;
+		std::vector<const battle::Unit *> retaliatedUnits = {attacker};
+		std::vector<const battle::Unit *> affectedUnits;
 
 		if (attackInfo.shooting)
-			units = state->getAttackedBattleUnits(attacker, defHex, true, BattleHex::INVALID);
+			defenderUnits = state->getAttackedBattleUnits(attacker, defHex, true, BattleHex::INVALID);
 		else
-			units = state->getAttackedBattleUnits(attacker, defHex, false, hex);
-
-		// ensure the defender is also affected
-		bool addDefender = true;
-		for(auto unit : units)
 		{
-			if (unit->unitId() == defender->unitId())
+			defenderUnits = state->getAttackedBattleUnits(attacker, defHex, false, hex);
+			retaliatedUnits = state->getAttackedBattleUnits(defender, hex, false, defender->getPosition());
+
+			// attacker can not melle-attack itself but still can hit that place where it was before moving
+			vstd::erase_if(defenderUnits, [attacker](const battle::Unit * u) -> bool { return u->unitId() == attacker->unitId(); });
+
+			if(!vstd::contains_if(retaliatedUnits, [attacker](const battle::Unit * u) -> bool { return u->unitId() == attacker->unitId(); }))
 			{
-				addDefender = false;
-				break;
+				retaliatedUnits.push_back(attacker);
 			}
 		}
 
-		if(addDefender)
-			units.push_back(defender);
+		// ensure the defender is also affected
+		if(!vstd::contains_if(defenderUnits, [defender](const battle::Unit * u) -> bool { return u->unitId() == defender->unitId(); }))
+		{
+			defenderUnits.push_back(defender);
+		}
+
+		affectedUnits = defenderUnits;
+		vstd::concatenate(affectedUnits, retaliatedUnits);
+
+		logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex.hex, defHex.hex);
+
+		std::map<uint32_t, std::shared_ptr<battle::CUnitState>> defenderStates;
 
-		for(auto u : units)
+		for(auto u : affectedUnits)
 		{
-			if(!ap.attackerState->alive())
-				break;
+			if(u->unitId() == attacker->unitId())
+				continue;
 
 			auto defenderState = u->acquireState();
+
 			ap.affectedUnits.push_back(defenderState);
+			defenderStates[u->unitId()] = defenderState;
+		}
+
+		for(int i = 0; i < totalAttacks; i++)
+		{
+			if(!ap.attackerState->alive() || !defenderStates[defender->unitId()]->alive())
+				break;
 
-			for(int i = 0; i < totalAttacks; i++)
+			for(auto u : defenderUnits)
 			{
+				auto defenderState = defenderStates.at(u->unitId());
+
 				int64_t damageDealt;
 				int64_t damageReceived;
 				float defenderDamageReduce;
@@ -307,17 +331,36 @@ AttackPossibility AttackPossibility::evaluate(
 				vstd::amin(retaliation.damage.max, ap.attackerState->getAvailableHealth());
 
 				damageDealt = averageDmg(attackDmg.damage);
-				defenderDamageReduce = calculateDamageReduce(attacker, defender, damageDealt, damageCache, state);
+				defenderDamageReduce = calculateDamageReduce(attacker, u, damageDealt, damageCache, state);
 				ap.attackerState->afterAttack(attackInfo.shooting, false);
 
 				//FIXME: use ranged retaliation
 				damageReceived = 0;
 				attackerDamageReduce = 0;
 
-				if (!attackInfo.shooting && defenderState->ableToRetaliate() && !counterAttacksBlocked)
+				if (!attackInfo.shooting && u->unitId() == defender->unitId() && defenderState->ableToRetaliate() && !counterAttacksBlocked)
 				{
-					damageReceived = averageDmg(retaliation.damage);
-					attackerDamageReduce = calculateDamageReduce(defender, attacker, damageReceived, damageCache, state);
+					for(auto retaliated : retaliatedUnits)
+					{
+						damageReceived = averageDmg(retaliation.damage);
+						
+						if(retaliated->unitId() == attacker->unitId())
+						{
+							attackerDamageReduce = calculateDamageReduce(defender, retaliated, damageReceived, damageCache, state);
+							ap.attackerState->damage(damageReceived);
+						}
+						else
+						{
+							if(state->battleMatchOwner(defender, u))
+								defenderDamageReduce += calculateDamageReduce(defender, retaliated, damageReceived, damageCache, state);
+							else
+								ap.collateralDamageReduce += calculateDamageReduce(defender, retaliated, damageReceived, damageCache, state);
+
+							defenderStates.at(retaliated->unitId())->damage(damageReceived);
+						}
+						
+					}
+
 					defenderState->afterAttack(attackInfo.shooting, true);
 				}
 
@@ -331,21 +374,25 @@ AttackPossibility AttackPossibility::evaluate(
 				if(attackerSide == u->unitSide())
 					ap.collateralDamageReduce += defenderDamageReduce;
 
-				if(u->unitId() == defender->unitId() || 
-					(!attackInfo.shooting && CStack::isMeleeAttackPossible(u, attacker, hex)))
+				if(u->unitId() == defender->unitId()
+					|| (!attackInfo.shooting && CStack::isMeleeAttackPossible(u, attacker, hex)))
 				{
 					//FIXME: handle RANGED_RETALIATION ?
 					ap.attackerDamageReduce += attackerDamageReduce;
 				}
 
-				ap.attackerState->damage(damageReceived);
 				defenderState->damage(damageDealt);
-
-				if (!ap.attackerState->alive() || !defenderState->alive())
-					break;
 			}
 		}
 
+#if BATTLE_TRACE_LEVEL>=2
+		logAi->trace("BattleAI AP: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
+			attackInfo.attacker->unitType()->getJsonKey(),
+			attackInfo.defender->unitType()->getJsonKey(),
+			(int)ap.dest, (int)ap.from, (int)ap.affectedUnits.size(),
+			ap.defenderDamageReduce, ap.attackerDamageReduce, ap.collateralDamageReduce, ap.shootersBlockedDmg);
+#endif
+
 		if(!bestAp.dest.isValid() || ap.attackValue() > bestAp.attackValue())
 			bestAp = ap;
 	}

+ 12 - 12
client/render/CAnimation.cpp

@@ -161,13 +161,13 @@ void CAnimation::verticalFlip()
 
 void CAnimation::horizontalFlip(size_t frame, size_t group)
 {
-	try
+	auto i1 = images.find(group);
+	if(i1 != images.end())
 	{
-		images.at(group).at(frame) = nullptr;
-	}
-	catch (const std::out_of_range &)
-	{
-		// ignore - image not loaded
+		auto i2 = i1->second.find(frame);
+
+		if(i2 != i1->second.end())
+			i2->second = nullptr;
 	}
 
 	auto locator = getImageLocator(frame, group);
@@ -177,13 +177,13 @@ void CAnimation::horizontalFlip(size_t frame, size_t group)
 
 void CAnimation::verticalFlip(size_t frame, size_t group)
 {
-	try
+	auto i1 = images.find(group);
+	if(i1 != images.end())
 	{
-		images.at(group).at(frame) = nullptr;
-	}
-	catch (const std::out_of_range &)
-	{
-		// ignore - image not loaded
+		auto i2 = i1->second.find(frame);
+
+		if(i2 != i1->second.end())
+			i2->second = nullptr;
 	}
 
 	auto locator = getImageLocator(frame, group);