浏览代码

GetClosestTile refactor

MichalZr6 11 月之前
父节点
当前提交
78845706c9
共有 2 个文件被更改,包括 346 次插入311 次删除
  1. 103 71
      lib/battle/BattleHexArray.cpp
  2. 243 240
      lib/battle/Unit.cpp

+ 103 - 71
lib/battle/BattleHexArray.cpp

@@ -24,39 +24,71 @@ BattleHexArray::BattleHexArray(std::initializer_list<BattleHex> initList) noexce
 
 BattleHex BattleHexArray::getClosestTile(BattleSide side, BattleHex initialPos) const
 {
+	if(this->empty())
+		return BattleHex();
+
 	BattleHex initialHex = BattleHex(initialPos);
-	auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool
-	{
-		return initialHex.getDistance(initialHex, left) < initialHex.getDistance(initialHex, right);
-	};
-	BattleHexArray sortedTiles(*this);
-	boost::sort(sortedTiles, compareDistance); //closest tiles at front
-	int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away
-	auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool
-	{
-		return closestDistance < here.getDistance(initialPos, here);
-	};
-	vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
-	auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool
+	int closestDistance = std::numeric_limits<int>::max();
+	BattleHexArray closestTiles;
+
+	for(auto hex : internalStorage)
 	{
-		if(left.getX() != right.getX())
+		int distance = initialHex.getDistance(initialHex, hex);
+		if(distance < closestDistance)
 		{
-			if(side == BattleSide::ATTACKER)
-				return left.getX() > right.getX(); //find furthest right
-			else
-				return left.getX() < right.getX(); //find furthest left
+			closestDistance = distance;
+			closestTiles.clear();
+			closestTiles.insert(hex);
 		}
-		else
+		else if(distance == closestDistance)
+			closestTiles.insert(hex);
+	}
+
+	auto compareHorizontal = [side, initialPos](const BattleHex & left, const BattleHex & right)
+	{
+		if(left.getX() != right.getX())
 		{
-			//Prefer tiles in the same row.
-			return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
+			return (side == BattleSide::ATTACKER) ? (left.getX() > right.getX()) : (left.getX() < right.getX());
 		}
+		return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
 	};
-	boost::sort(sortedTiles, compareHorizontal);
-	return sortedTiles.front();
-}
-
-BattleHexArray::NeighbouringTilesCache BattleHexArray::calculateNeighbouringTiles()
+
+	auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal);
+	return (bestTile != closestTiles.end()) ? *bestTile : BattleHex();
+
+	//BattleHex initialHex = BattleHex(initialPos);
+	//auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool
+	//{
+	//	return initialHex.getDistance(initialHex, left) < initialHex.getDistance(initialHex, right);
+	//};
+	//BattleHexArray sortedTiles(*this);
+	//boost::sort(sortedTiles, compareDistance); //closest tiles at front
+	//int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away
+	//auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool
+	//{
+	//	return closestDistance < here.getDistance(initialPos, here);
+	//};
+	//vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
+	//auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool
+	//{
+	//	if(left.getX() != right.getX())
+	//	{
+	//		if(side == BattleSide::ATTACKER)
+	//			return left.getX() > right.getX(); //find furthest right
+	//		else
+	//			return left.getX() < right.getX(); //find furthest left
+	//	}
+	//	else
+	//	{
+	//		//Prefer tiles in the same row.
+	//		return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
+	//	}
+	//};
+	//boost::sort(sortedTiles, compareHorizontal);
+	//return sortedTiles.front();
+}
+
+BattleHexArray::NeighbouringTilesCache BattleHexArray::calculateNeighbouringTiles()
 {
 	BattleHexArray::NeighbouringTilesCache ret;
 
@@ -71,59 +103,59 @@ BattleHexArray::NeighbouringTilesCache BattleHexArray::calculateNeighbouringTile
 	}
 
 	return ret;
-}
-
-BattleHexArray BattleHexArray::generateNeighbouringTiles(BattleHex hex)
+}
+
+BattleHexArray BattleHexArray::generateNeighbouringTiles(BattleHex hex)
 {
 	BattleHexArray ret;
 	for(auto dir : BattleHex::hexagonalDirections())
 		ret.checkAndPush(hex.cloneInDirection(dir, false));
 
 	return ret;
-}
-
-BattleHexArray BattleHexArray::generateAttackerClosestTilesCache()
-{
-	assert(!neighbouringTilesCache.empty());
-
-	BattleHexArray ret;
-
-	ret.resize(GameConstants::BFIELD_SIZE);
-
-	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
-	{
-		ret.set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::ATTACKER, hex));
-	}
-
-	return ret;
-}
-
-BattleHexArray BattleHexArray::generateDefenderClosestTilesCache()
-{
-	assert(!neighbouringTilesCache.empty());
-
-	BattleHexArray ret;
-
-	ret.resize(GameConstants::BFIELD_SIZE);
-
-	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
-	{
-		ret.set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::DEFENDER, hex));
-	}
-
-	return ret;
 }
 
-BattleHex BattleHexArray::getClosestTileFromAllPossibleNeighbours(BattleSide side, BattleHex pos)
-{
-	if(side == BattleSide::ATTACKER)
-			return closestTilesCacheForAttacker[pos.hex];
-	else if(side == BattleSide::DEFENDER)
-		return closestTilesCacheForDefender[pos.hex];
-	else
-		assert(false);		// we should never be here
-}
-
+BattleHexArray BattleHexArray::generateAttackerClosestTilesCache()
+{
+	assert(!neighbouringTilesCache.empty());
+
+	BattleHexArray ret;
+
+	ret.resize(GameConstants::BFIELD_SIZE);
+
+	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
+	{
+		ret.set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::ATTACKER, hex));
+	}
+
+	return ret;
+}
+
+BattleHexArray BattleHexArray::generateDefenderClosestTilesCache()
+{
+	assert(!neighbouringTilesCache.empty());
+
+	BattleHexArray ret;
+
+	ret.resize(GameConstants::BFIELD_SIZE);
+
+	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
+	{
+		ret.set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::DEFENDER, hex));
+	}
+
+	return ret;
+}
+
+BattleHex BattleHexArray::getClosestTileFromAllPossibleNeighbours(BattleSide side, BattleHex pos)
+{
+	if(side == BattleSide::ATTACKER)
+			return closestTilesCacheForAttacker[pos.hex];
+	else if(side == BattleSide::DEFENDER)
+		return closestTilesCacheForDefender[pos.hex];
+	else
+		assert(false);		// we should never be here
+}
+
 void BattleHexArray::merge(const BattleHexArray & other) noexcept
 {
 	for(auto hex : other)

+ 243 - 240
lib/battle/Unit.cpp

@@ -1,240 +1,243 @@
-/*
- * Unit.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-#include "StdInc.h"
-
-#include "Unit.h"
-
-#include "../VCMI_Lib.h"
-#include "../texts/CGeneralTextHandler.h"
-
-#include "../serializer/JsonDeserializer.h"
-#include "../serializer/JsonSerializer.h"
-
-#include <vcmi/Faction.h>
-#include <vcmi/FactionService.h>
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-namespace battle
-{
-
-///Unit
-Unit::~Unit() = default;
-
-bool Unit::isDead() const
-{
-	return !alive() && !isGhost();
-}
-
-bool Unit::isTurret() const
-{
-	return creatureIndex() == CreatureID::ARROW_TOWERS;
-}
-
-std::string Unit::getDescription() const
-{
-	boost::format fmt("Unit %d of side %d");
-	fmt % unitId() % static_cast<int>(unitSide());
-	return fmt.str();
-}
-
-//TODO: deduplicate these functions
-const IBonusBearer* Unit::getBonusBearer() const
-{
-	return this;
-}
-
-BattleHexArray Unit::getSurroundingHexes(BattleHex assumedPosition) const
-{
-	BattleHex hex = (assumedPosition != BattleHex::INVALID) ? assumedPosition : getPosition(); //use hypothetical position
-
-	return getSurroundingHexes(hex, doubleWide(), unitSide());
-}
-
-BattleHexArray Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
-{
-	BattleHexArray hexes;
-	if(twoHex)
-	{
-		const BattleHex otherHex = occupiedHex(position, twoHex, side);
-
-		if(side == BattleSide::ATTACKER)
-		{
-			for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
-				hexes.checkAndPush(position.cloneInDirection(dir, false));
-
-			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
-			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false));
-			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
-		}
-		else
-		{
-			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
-
-			for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
-				hexes.checkAndPush(otherHex.cloneInDirection(dir, false));
-
-			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
-			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::LEFT, false));
-		}
-		return hexes;
-	}
-	else
-	{
-		return BattleHexArray::neighbouringTilesCache[position];
-	}
-}
-
-BattleHexArray Unit::getAttackableHexes(const Unit * attacker) const
-{
-	auto defenderHexes = battle::Unit::getHexes(
-		getPosition(),
-		doubleWide(),
-		unitSide());
-	
-	BattleHexArray targetableHexes;
-
-	for(auto defenderHex : defenderHexes)
-	{
-		auto hexes = battle::Unit::getHexes(
-			defenderHex,
-			attacker->doubleWide(),
-			unitSide());
-
-		if(hexes.size() == 2 && BattleHex::getDistance(hexes.front(), hexes.back()) != 1)
-			hexes.pop_back();
-
-		for(auto hex : hexes)
-			targetableHexes.merge(BattleHexArray::neighbouringTilesCache[hex]);
-	}
-
-	return targetableHexes;
-}
-
-bool Unit::coversPos(BattleHex pos) const
-{
-	return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));
-}
-
-BattleHexArray Unit::getHexes() const
-{
-	return getHexes(getPosition(), doubleWide(), unitSide());
-}
-
-BattleHexArray Unit::getHexes(BattleHex assumedPos) const
-{
-	return getHexes(assumedPos, doubleWide(), unitSide());
-}
-
-BattleHexArray Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side)
-{
-	BattleHexArray hexes;
-	hexes.insert(assumedPos);
-
-	if(twoHex)
-		hexes.insert(occupiedHex(assumedPos, twoHex, side));
-
-	return hexes;
-}
-
-BattleHex Unit::occupiedHex() const
-{
-	return occupiedHex(getPosition(), doubleWide(), unitSide());
-}
-
-BattleHex Unit::occupiedHex(BattleHex assumedPos) const
-{
-	return occupiedHex(assumedPos, doubleWide(), unitSide());
-}
-
-BattleHex Unit::occupiedHex(BattleHex assumedPos, bool twoHex, BattleSide side)
-{
-	if(twoHex)
-	{
-		if(side == BattleSide::ATTACKER)
-			return assumedPos - 1;
-		else
-			return assumedPos + 1;
-	}
-	else
-	{
-		return BattleHex::INVALID;
-	}
-}
-
-void Unit::addText(MetaString & text, EMetaText type, int32_t serial, const boost::logic::tribool & plural) const
-{
-	if(boost::logic::indeterminate(plural))
-		serial = VLC->generaltexth->pluralText(serial, getCount());
-	else if(plural)
-		serial = VLC->generaltexth->pluralText(serial, 2);
-	else
-		serial = VLC->generaltexth->pluralText(serial, 1);
-
-	text.appendLocalString(type, serial);
-}
-
-void Unit::addNameReplacement(MetaString & text, const boost::logic::tribool & plural) const
-{
-	if(boost::logic::indeterminate(plural))
-		text.replaceName(creatureId(), getCount());
-	else if(plural)
-		text.replaceNamePlural(creatureIndex());
-	else
-		text.replaceNameSingular(creatureIndex());
-}
-
-std::string Unit::formatGeneralMessage(const int32_t baseTextId) const
-{
-	const int32_t textId = VLC->generaltexth->pluralText(baseTextId, getCount());
-
-	MetaString text;
-	text.appendLocalString(EMetaText::GENERAL_TXT, textId);
-	text.replaceName(creatureId(), getCount());
-
-	return text.toString();
-}
-
-int Unit::getRawSurrenderCost() const
-{
-	//we pay for our stack that comes from our army slots - condition eliminates summoned cres and war machines
-	if(unitSlot().validSlot())
-		return creatureCost() * getCount();
-	else
-		return 0;
-}
-
-///UnitInfo
-void UnitInfo::serializeJson(JsonSerializeFormat & handler)
-{
-	handler.serializeInt("count", count);
-	handler.serializeId("type", type, CreatureID(CreatureID::NONE));
-	handler.serializeInt("side", side);
-	handler.serializeInt("position", position);
-	handler.serializeBool("summoned", summoned);
-}
-
-void UnitInfo::save(JsonNode & data)
-{
-	data.clear();
-	JsonSerializer ser(nullptr, data);
-	ser.serializeStruct("newUnitInfo", *this);
-}
-
-void UnitInfo::load(uint32_t id_, const JsonNode & data)
-{
-	id = id_;
-    JsonDeserializer deser(nullptr, data);
-    deser.serializeStruct("newUnitInfo", *this);
-}
-
-}
-
-VCMI_LIB_NAMESPACE_END
+/*
+ * Unit.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+
+#include "Unit.h"
+
+#include "../VCMI_Lib.h"
+#include "../texts/CGeneralTextHandler.h"
+
+#include "../serializer/JsonDeserializer.h"
+#include "../serializer/JsonSerializer.h"
+
+#include <vcmi/Faction.h>
+#include <vcmi/FactionService.h>
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+namespace battle
+{
+
+///Unit
+Unit::~Unit() = default;
+
+bool Unit::isDead() const
+{
+	return !alive() && !isGhost();
+}
+
+bool Unit::isTurret() const
+{
+	return creatureIndex() == CreatureID::ARROW_TOWERS;
+}
+
+std::string Unit::getDescription() const
+{
+	boost::format fmt("Unit %d of side %d");
+	fmt % unitId() % static_cast<int>(unitSide());
+	return fmt.str();
+}
+
+//TODO: deduplicate these functions
+const IBonusBearer* Unit::getBonusBearer() const
+{
+	return this;
+}
+
+BattleHexArray Unit::getSurroundingHexes(BattleHex assumedPosition) const
+{
+	BattleHex hex = (assumedPosition != BattleHex::INVALID) ? assumedPosition : getPosition(); //use hypothetical position
+
+	return getSurroundingHexes(hex, doubleWide(), unitSide());
+}
+
+BattleHexArray Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
+{
+	if(!position.isValid())
+		return { };
+
+	BattleHexArray hexes;
+	if(twoHex)
+	{
+		const BattleHex otherHex = occupiedHex(position, twoHex, side);
+
+		if(side == BattleSide::ATTACKER)
+		{
+			for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
+				hexes.checkAndPush(position.cloneInDirection(dir, false));
+
+			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
+			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false));
+			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
+		}
+		else
+		{
+			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
+
+			for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
+				hexes.checkAndPush(otherHex.cloneInDirection(dir, false));
+
+			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
+			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::LEFT, false));
+		}
+		return hexes;
+	}
+	else
+	{
+		return BattleHexArray::neighbouringTilesCache[position];
+	}
+}
+
+BattleHexArray Unit::getAttackableHexes(const Unit * attacker) const
+{
+	auto defenderHexes = battle::Unit::getHexes(
+		getPosition(),
+		doubleWide(),
+		unitSide());
+	
+	BattleHexArray targetableHexes;
+
+	for(auto defenderHex : defenderHexes)
+	{
+		auto hexes = battle::Unit::getHexes(
+			defenderHex,
+			attacker->doubleWide(),
+			unitSide());
+
+		if(hexes.size() == 2 && BattleHex::getDistance(hexes.front(), hexes.back()) != 1)
+			hexes.pop_back();
+
+		for(auto hex : hexes)
+			targetableHexes.merge(BattleHexArray::neighbouringTilesCache[hex]);
+	}
+
+	return targetableHexes;
+}
+
+bool Unit::coversPos(BattleHex pos) const
+{
+	return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));
+}
+
+BattleHexArray Unit::getHexes() const
+{
+	return getHexes(getPosition(), doubleWide(), unitSide());
+}
+
+BattleHexArray Unit::getHexes(BattleHex assumedPos) const
+{
+	return getHexes(assumedPos, doubleWide(), unitSide());
+}
+
+BattleHexArray Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side)
+{
+	BattleHexArray hexes;
+	hexes.insert(assumedPos);
+
+	if(twoHex)
+		hexes.insert(occupiedHex(assumedPos, twoHex, side));
+
+	return hexes;
+}
+
+BattleHex Unit::occupiedHex() const
+{
+	return occupiedHex(getPosition(), doubleWide(), unitSide());
+}
+
+BattleHex Unit::occupiedHex(BattleHex assumedPos) const
+{
+	return occupiedHex(assumedPos, doubleWide(), unitSide());
+}
+
+BattleHex Unit::occupiedHex(BattleHex assumedPos, bool twoHex, BattleSide side)
+{
+	if(twoHex)
+	{
+		if(side == BattleSide::ATTACKER)
+			return assumedPos - 1;
+		else
+			return assumedPos + 1;
+	}
+	else
+	{
+		return BattleHex::INVALID;
+	}
+}
+
+void Unit::addText(MetaString & text, EMetaText type, int32_t serial, const boost::logic::tribool & plural) const
+{
+	if(boost::logic::indeterminate(plural))
+		serial = VLC->generaltexth->pluralText(serial, getCount());
+	else if(plural)
+		serial = VLC->generaltexth->pluralText(serial, 2);
+	else
+		serial = VLC->generaltexth->pluralText(serial, 1);
+
+	text.appendLocalString(type, serial);
+}
+
+void Unit::addNameReplacement(MetaString & text, const boost::logic::tribool & plural) const
+{
+	if(boost::logic::indeterminate(plural))
+		text.replaceName(creatureId(), getCount());
+	else if(plural)
+		text.replaceNamePlural(creatureIndex());
+	else
+		text.replaceNameSingular(creatureIndex());
+}
+
+std::string Unit::formatGeneralMessage(const int32_t baseTextId) const
+{
+	const int32_t textId = VLC->generaltexth->pluralText(baseTextId, getCount());
+
+	MetaString text;
+	text.appendLocalString(EMetaText::GENERAL_TXT, textId);
+	text.replaceName(creatureId(), getCount());
+
+	return text.toString();
+}
+
+int Unit::getRawSurrenderCost() const
+{
+	//we pay for our stack that comes from our army slots - condition eliminates summoned cres and war machines
+	if(unitSlot().validSlot())
+		return creatureCost() * getCount();
+	else
+		return 0;
+}
+
+///UnitInfo
+void UnitInfo::serializeJson(JsonSerializeFormat & handler)
+{
+	handler.serializeInt("count", count);
+	handler.serializeId("type", type, CreatureID(CreatureID::NONE));
+	handler.serializeInt("side", side);
+	handler.serializeInt("position", position);
+	handler.serializeBool("summoned", summoned);
+}
+
+void UnitInfo::save(JsonNode & data)
+{
+	data.clear();
+	JsonSerializer ser(nullptr, data);
+	ser.serializeStruct("newUnitInfo", *this);
+}
+
+void UnitInfo::load(uint32_t id_, const JsonNode & data)
+{
+	id = id_;
+    JsonDeserializer deser(nullptr, data);
+    deser.serializeStruct("newUnitInfo", *this);
+}
+
+}
+
+VCMI_LIB_NAMESPACE_END