Преглед изворни кода

Merge pull request #5864 from IvanSavenko/crashfixes

Fixes for recently reported crashes
Ivan Savenko пре 3 месеци
родитељ
комит
559ea7d003

+ 1 - 0
client/adventureMap/AdventureMapInterface.cpp

@@ -362,6 +362,7 @@ void AdventureMapInterface::onHotseatWaitStarted(PlayerColor playerID)
 {
 	backgroundDimLevel = 255;
 
+	widget->getMinimap()->setAIRadar(true);
 	onCurrentPlayerChanged(playerID);
 	setState(EAdventureState::HOTSEAT_WAIT);
 }

+ 9 - 6
client/battle/BattleActionsController.cpp

@@ -731,15 +731,18 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, c
 		case PossiblePlayerBattleAction::MOVE_STACK:
 		{
 			const auto * activeStack = owner.stacksController->getActiveStack();
-			const bool backwardsMove = activeStack->unitSide() == BattleSide::ATTACKER ?
-				targetHex.getX() < activeStack->getPosition().getX():
-				targetHex.getX() > activeStack->getPosition().getX();
 
-			if(activeStack->doubleWide() && backwardsMove)
+			if(activeStack->doubleWide())
 			{
-				BattleHexArray acc = owner.getBattle()->battleGetAvailableHexes(activeStack, false);
+				BattleHexArray availableHexes = owner.getBattle()->battleGetAvailableHexes(activeStack, false);
 				BattleHex shiftedDest = targetHex.cloneInDirection(activeStack->destShiftDir(), false);
-				if(acc.contains(shiftedDest))
+				const bool canMoveHeadHere = availableHexes.contains(targetHex);
+				const bool canMoveTailHere = availableHexes.contains(shiftedDest);
+				const bool backwardsMove = activeStack->unitSide() == BattleSide::ATTACKER ?
+											   targetHex.getX() < activeStack->getPosition().getX():
+											   targetHex.getX() > activeStack->getPosition().getX();
+
+				if(canMoveTailHere && (backwardsMove || !canMoveHeadHere))
 					owner.giveCommand(EActionType::WALK, shiftedDest);
 				else
 					owner.giveCommand(EActionType::WALK, targetHex);

+ 7 - 5
client/battle/BattleFieldController.cpp

@@ -392,14 +392,16 @@ BattleHexArray BattleFieldController::getHighlightedHexesForMovementTarget()
 
 	if (stack->doubleWide())
 	{
+		const bool canMoveHeadHere = hoveredHex.isAvailable() && availableHexes.contains(hoveredHex);
+		const bool canMoveTailHere = hoveredHex.isAvailable() && availableHexes.contains(hoveredHex.cloneInDirection(stack->destShiftDir()));
 		const bool backwardsMove = stack->unitSide() == BattleSide::ATTACKER ?
-			hoveredHex.getX() < stack->getPosition().getX():
-			hoveredHex.getX() > stack->getPosition().getX();
+									   hoveredHex.getX() < stack->getPosition().getX():
+									   hoveredHex.getX() > stack->getPosition().getX();
 
-		if (backwardsMove && availableHexes.contains(hoveredHex.cloneInDirection(stack->destShiftDir())))
+		if(canMoveTailHere && (backwardsMove || !canMoveHeadHere))
 			return {hoveredHex, hoveredHex.cloneInDirection(stack->destShiftDir())};
 
-		if (availableHexes.contains(hoveredHex))
+		if (canMoveHeadHere)
 			return {hoveredHex, stack->occupiedHex(hoveredHex)};
 
 		return {};
@@ -820,7 +822,7 @@ bool BattleFieldController::isTileAttackable(const BattleHex & number) const
 
 	for (auto & elem : occupiableHexes)
 	{
-		if (BattleHex::mutualPosition(elem, number) != -1 || elem == number)
+		if (BattleHex::mutualPosition(elem, number) != BattleHex::EDir::NONE || elem == number)
 			return true;
 	}
 	return false;

+ 39 - 6
config/schemas/creature.json

@@ -4,15 +4,47 @@
 	"title" : "VCMI creature format",
 	"description" : "Json format for defining new creatures in VCMI",
 	"required" : [ "faction" ],
-	"anyOf" : [
+	"allOf" : [
 		{
-			"disabled" : { "enum" : [ true ] }
+			"if" : {
+				"properties" : {
+					"disabled" : {
+						"const" : false
+					}
+				}
+			},
+			"then" : {
+				"required" : [
+					"name", "level", "cost", "speed", "hitPoints", "attack", "defense", "damage", "fightValue", "aiValue", "graphics", "sound"
+				]
+			}
 		},
 		{
-			"required" : [
-				"name", "level", "cost", "speed", "hitPoints", "attack", "defense", "damage",
-				"fightValue", "aiValue", "advMapAmount", "graphics", "sound"
-			]
+			"if" : {
+				"properties" : {
+					"special" : {
+						"const" : false
+					},
+					"disabled" : {
+						"const" : false 
+					}
+				}
+			},
+			"then" : {
+				"required" : [ "advMapAmount" ],
+				"properties" : {
+					"advMapAmount" : {
+						"type" : "object",
+						"additionalProperties" : false,
+						"description" : "Initial size of random stacks on adventure map",
+						"required" : [ "min", "max" ],
+						"properties" : {
+							"min" : { "type" : "number", "minimum" : 1 },
+							"max" : { "type" : "number", "minimum" : 1 }
+						}
+					}
+				}
+			}
 		}
 	],
 	"additionalProperties" : false,
@@ -80,6 +112,7 @@
 		"damage" : {
 			"type" : "object",
 			"additionalProperties" : false,
+			"required" : [ "min", "max" ],
 			"properties" : {
 				"max" : { "type" : "number" },
 				"min" : { "type" : "number" }

+ 11 - 2
lib/battle/BattleHex.h

@@ -212,8 +212,17 @@ public:
 	[[nodiscard]] static EDir mutualPosition(const BattleHex & hex1, const BattleHex & hex2)
 	{
 		for(auto dir : hexagonalDirections())
-			if(hex2 == hex1.cloneInDirection(dir, false))
-				return dir;
+		{
+			try
+			{
+				if(hex2 == hex1.cloneInDirection(dir, true))
+					return dir;
+			}
+			catch (const std::out_of_range &)
+			{
+				continue;
+			}
+		}
 		return NONE;
 	}
 

+ 1 - 0
lib/gameState/CGameStateCampaign.cpp

@@ -389,6 +389,7 @@ void CGameStateCampaign::replaceHeroesPlaceholders()
 		heroToPlace->setAnchorPos(heroPlaceholder->anchorPos());
 		heroToPlace->setHeroType(heroToPlace->getHeroTypeID());
 		heroToPlace->appearance = heroToPlace->getObjectHandler()->getTemplates().front();
+		heroToPlace->instanceName = heroPlaceholder->instanceName;
 
 		gameState->map->replaceObject(campaignHeroReplacement.heroPlaceholderId, heroToPlace);
 	}

+ 1 - 1
lib/gameState/GameStatePackVisitor.cpp

@@ -420,8 +420,8 @@ void GameStatePackVisitor::visitRemoveObject(RemoveObject & pack)
 
 		if (town->getGarrisonHero())
 		{
-			town->setGarrisonedHero(nullptr);
 			gs.getMap().showObject(gs.getHero(town->getGarrisonHero()->id));
+			town->setGarrisonedHero(nullptr);
 		}
 	}
 

+ 1 - 1
lib/mapping/MapFormatH3M.cpp

@@ -2127,7 +2127,7 @@ std::shared_ptr<CGObjectInstance> CMapLoaderH3M::readHero(const int3 & mapPositi
 		bool hasCustomPrimSkills = reader->readBool();
 		if(hasCustomPrimSkills)
 		{
-			auto ps = object->getAllBonuses(Selector::type()(BonusType::PRIMARY_SKILL).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)), nullptr);
+			auto ps = object->getAllBonuses(Selector::type()(BonusType::PRIMARY_SKILL).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)), "");
 			if(ps->size())
 			{
 				logGlobal->debug("Hero %s has set primary skills twice (in map properties and on adventure map instance). Using the latter set...", object->getHeroTypeID().getNum() );

+ 7 - 0
lib/modding/ModManager.cpp

@@ -285,6 +285,13 @@ void ModsPresetState::removeOldMods(const TModList & modsToKeep)
 	vstd::erase_if(currentPreset["settings"].Struct(), [&](const auto & entry){
 		return !vstd::contains(modsToKeep, entry.first);
 	});
+
+	for (auto & modSettings : currentPreset["settings"].Struct())
+	{
+		vstd::erase_if(modSettings.second.Struct(), [&](const auto & entry){
+			return !vstd::contains(modsToKeep, modSettings.first + "." + entry.first);
+		});
+	}
 }
 
 void ModsPresetState::eraseRootMod(const TModID & modName)