Browse Source

Merge pull request #4340 from IvanSavenko/mp_fixes

[1.5.6?] Fixes for issues with multiplayer
Ivan Savenko 1 year ago
parent
commit
8d9f690ae4

+ 30 - 25
client/CPlayerInterface.cpp

@@ -169,40 +169,44 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
 	adventureInt.reset(new AdventureMapInterface());
 }
 
-void CPlayerInterface::playerEndsTurn(PlayerColor player)
+void CPlayerInterface::closeAllDialogs()
 {
-	EVENT_HANDLER_CALLED_BY_CLIENT;
-	if (player == playerID)
+	// remove all active dialogs that do not expect query answer
+	for (;;)
 	{
-		makingTurn = false;
+		auto adventureWindow = GH.windows().topWindow<AdventureMapInterface>();
+		auto infoWindow = GH.windows().topWindow<CInfoWindow>();
 
-		// remove all active dialogs that do not expect query answer
-		for (;;)
-		{
-			auto adventureWindow = GH.windows().topWindow<AdventureMapInterface>();
-			auto infoWindow = GH.windows().topWindow<CInfoWindow>();
+		if(adventureWindow != nullptr)
+			break;
 
-			if(adventureWindow != nullptr)
-				break;
+		if(infoWindow && infoWindow->ID != QueryID::NONE)
+			break;
 
-			if(infoWindow && infoWindow->ID != QueryID::NONE)
-				break;
+		if (infoWindow)
+			infoWindow->close();
+		else
+			GH.windows().popWindows(1);
+	}
 
-			if (infoWindow)
-				infoWindow->close();
-			else
-				GH.windows().popWindows(1);
-		}
+	if(castleInt)
+		castleInt->close();
 
-		if(castleInt)
-			castleInt->close();
+	castleInt = nullptr;
 
-		castleInt = nullptr;
+	// remove all pending dialogs that do not expect query answer
+	vstd::erase_if(dialogs, [](const std::shared_ptr<CInfoWindow> & window){
+		return window->ID == QueryID::NONE;
+	});
+}
 
-		// remove all pending dialogs that do not expect query answer
-		vstd::erase_if(dialogs, [](const std::shared_ptr<CInfoWindow> & window){
-			return window->ID == QueryID::NONE;
-		});
+void CPlayerInterface::playerEndsTurn(PlayerColor player)
+{
+	EVENT_HANDLER_CALLED_BY_CLIENT;
+	if (player == playerID)
+	{
+		makingTurn = false;
+		closeAllDialogs();
 	}
 }
 
@@ -284,6 +288,7 @@ void CPlayerInterface::gamePause(bool pause)
 
 void CPlayerInterface::yourTurn(QueryID queryID)
 {
+	closeAllDialogs();
 	CTutorialWindow::openWindowFirstTime(TutorialMode::TOUCH_ADVENTUREMAP);
 
 	EVENT_HANDLER_CALLED_BY_CLIENT;

+ 1 - 0
client/CPlayerInterface.h

@@ -203,6 +203,7 @@ public: // public interface for use by client via LOCPLINT access
 	void performAutosave();
 	void gamePause(bool pause);
 	void endNetwork();
+	void closeAllDialogs();
 
 	///returns true if all events are processed internally
 	bool capturedAllEvents();

+ 2 - 2
client/adventureMap/AdventureMapShortcuts.cpp

@@ -553,12 +553,12 @@ bool AdventureMapShortcuts::optionSpellcasting()
 
 bool AdventureMapShortcuts::optionInMapView()
 {
-	return state == EAdventureState::MAKING_TURN;
+	return state == EAdventureState::MAKING_TURN || state == EAdventureState::OTHER_HUMAN_PLAYER_TURN;
 }
 
 bool AdventureMapShortcuts::optionInWorldView()
 {
-	return state == EAdventureState::WORLD_VIEW;
+	return state == EAdventureState::WORLD_VIEW || state == EAdventureState::OTHER_HUMAN_PLAYER_TURN;
 }
 
 bool AdventureMapShortcuts::optionSidePanelActive()

+ 6 - 2
client/battle/BattleActionsController.cpp

@@ -499,9 +499,12 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
 		case PossiblePlayerBattleAction::WALK_AND_ATTACK:
 		case PossiblePlayerBattleAction::ATTACK_AND_RETURN: //TODO: allow to disable return
 			{
+				const auto * attacker = owner.stacksController->getActiveStack();
 				BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex);
+				int distance = attacker->position.isValid() ? owner.getBattle()->battleGetDistances(attacker, attacker->getPosition())[attackFromHex] : 0;
 				DamageEstimation retaliation;
-				DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(owner.stacksController->getActiveStack(), targetStack, attackFromHex, &retaliation);
+				BattleAttackInfo attackInfo(attacker, targetStack, distance, false );
+				DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(attackInfo, &retaliation);
 				estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
 				estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
 				bool enemyMayBeKilled = estimation.kills.max == targetStack->getCount();
@@ -514,7 +517,8 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
 			const auto * shooter = owner.stacksController->getActiveStack();
 
 			DamageEstimation retaliation;
-			DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(shooter, targetStack, shooter->getPosition(), &retaliation);
+			BattleAttackInfo attackInfo(shooter, targetStack, 0, true );
+			DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(attackInfo, &retaliation);
 			estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
 			estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
 			return formatRangedAttack(estimation, targetStack->getName(), shooter->shots.available());

+ 4 - 1
client/widgets/CComponent.cpp

@@ -290,7 +290,10 @@ std::string CComponent::getSubtitle() const
 			return CGI->artifacts()->getById(data.subType.as<ArtifactID>())->getNameTranslated();
 		case ComponentType::SPELL_SCROLL:
 		case ComponentType::SPELL:
-			return CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated();
+			if (data.value < 0)
+				return "{#A9A9A9|" + CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated() + "}";
+			else
+				return CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated();
 		case ComponentType::NONE:
 		case ComponentType::MORALE:
 		case ComponentType::LUCK:

+ 3 - 0
config/objects/moddables.json

@@ -155,18 +155,21 @@
 		"types" : {
 			"boatNecropolis" : { 
 				"index" : 0,
+				"compatibilityIdentifiers" : [ "evil" ],
 				"actualAnimation" : "AB01_.def",
 				"overlayAnimation" : "ABM01_.def",
 				"flagAnimations" : ["ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"]
 			},
 			"boatCastle" : { 
 				"index" : 1, 
+				"compatibilityIdentifiers" : [ "good" ],
 				"actualAnimation" : "AB02_.def",
 				"overlayAnimation" : "ABM02_.def",
 				"flagAnimations" : ["ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"]
 			},
 			"boatFortress" : {
 				"index" : 2, 
+				"compatibilityIdentifiers" : [ "neutral" ],
 				"actualAnimation" : "AB03_.def",
 				"overlayAnimation" : "ABM03_.def",
 				"flagAnimations" : ["ABF03L", "ABF03G", "ABF03R", "ABF03D", "ABF03B", "ABF03P", "ABF03W", "ABF03K"]

+ 4 - 4
lib/battle/CBattleInfoCallback.cpp

@@ -742,15 +742,15 @@ DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit *
 {
 	RETURN_IF_NOT_BATTLE({});
 	auto reachability = battleGetDistances(attacker, attacker->getPosition());
-	int getMovementRange = attackerPosition.isValid() ? reachability[attackerPosition] : 0;
-	return battleEstimateDamage(attacker, defender, getMovementRange, retaliationDmg);
+	int movementRange = attackerPosition.isValid() ? reachability[attackerPosition] : 0;
+	return battleEstimateDamage(attacker, defender, movementRange, retaliationDmg);
 }
 
-DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int getMovementRange, DamageEstimation * retaliationDmg) const
+DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int movementRange, DamageEstimation * retaliationDmg) const
 {
 	RETURN_IF_NOT_BATTLE({});
 	const bool shooting = battleCanShoot(attacker, defender->getPosition());
-	const BattleAttackInfo bai(attacker, defender, getMovementRange, shooting);
+	const BattleAttackInfo bai(attacker, defender, movementRange, shooting);
 	return battleEstimateDamage(bai, retaliationDmg);
 }
 

+ 7 - 4
lib/constants/VariantIdentifier.h

@@ -32,18 +32,14 @@ public:
 	int32_t getNum() const
 	{
 		int32_t result;
-
 		std::visit([&result] (const auto& v) { result = v.getNum(); }, value);
-
 		return result;
 	}
 
 	std::string toString() const
 	{
 		std::string result;
-
 		std::visit([&result] (const auto& v) { result = v.encode(v.getNum()); }, value);
-
 		return result;
 	}
 
@@ -58,6 +54,13 @@ public:
 			return IdentifierType();
 	}
 
+	bool hasValue() const
+	{
+		bool result = false;
+		std::visit([&result] (const auto& v) { result = v.hasValue(); }, value);
+		return result;
+	}
+
 	template <typename Handler> void serialize(Handler &h)
 	{
 		h & value;

+ 1 - 1
lib/mapObjects/CGDwelling.cpp

@@ -424,7 +424,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
 		if(count) //there are available creatures
 		{
 
-			if (VLC->settings()->getBoolean(EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED))
+			if (VLC->settings()->getBoolean(EGameSettings::DWELLINGS_MERGE_ON_RECRUIT))
 			{
 				SlotID testSlot = h->getSlotFor(crid);
 				if(!testSlot.validSlot()) //no available slot - try merging army of visiting hero

+ 4 - 2
lib/rewardable/Reward.cpp

@@ -115,8 +115,10 @@ void Rewardable::Reward::loadComponents(std::vector<Component> & comps, const CG
 		comps.emplace_back(ComponentType::ARTIFACT, entry);
 
 	for(const auto & entry : spells)
-		if (!h || h->canLearnSpell(entry.toEntity(VLC), true))
-			comps.emplace_back(ComponentType::SPELL, entry);
+	{
+		bool learnable = !h || h->canLearnSpell(entry.toEntity(VLC), true);
+		comps.emplace_back(ComponentType::SPELL, entry, learnable ?	0 : -1);
+	}
 
 	for(const auto & entry : creatures)
 		comps.emplace_back(ComponentType::CREATURE, entry.type->getId(), entry.count);

+ 2 - 2
server/battles/BattleActionProcessor.cpp

@@ -494,7 +494,7 @@ bool BattleActionProcessor::doHealAction(const CBattleInfoCallback & battle, con
 	else
 		destStack = battle.battleGetUnitByPos(target.at(0).hexValue);
 
-	if(stack == nullptr || destStack == nullptr || !healerAbility || healerAbility->subtype == BonusSubtypeID())
+	if(stack == nullptr || destStack == nullptr || !healerAbility || !healerAbility->subtype.hasValue())
 	{
 		gameHandler->complain("There is either no healer, no destination, or healer cannot heal :P");
 	}
@@ -971,7 +971,7 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const
 	}
 
 	std::shared_ptr<const Bonus> bonus = attacker->getFirstBonus(Selector::type()(BonusType::SPELL_LIKE_ATTACK));
-	if(bonus && ranged) //TODO: make it work in melee?
+	if(bonus && ranged && bonus->subtype.hasValue()) //TODO: make it work in melee?
 	{
 		//this is need for displaying hit animation
 		bat.flags |= BattleAttack::SPELL_LIKE;