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

Merge pull request #5224 from IvanSavenko/misc_fixes

Fixes for 1.6.3
Ivan Savenko пре 9 месеци
родитељ
комит
f8312737bb

+ 1 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -11,7 +11,7 @@
 #pragma once
 #pragma once
 
 
 #define NKAI_PATHFINDER_TRACE_LEVEL 0
 #define NKAI_PATHFINDER_TRACE_LEVEL 0
-constexpr int NKAI_GRAPH_TRACE_LEVEL = 0;
+constexpr int NKAI_GRAPH_TRACE_LEVEL = 0; // To actually enable graph visualization, enter `/vslog graph` in game chat
 #define NKAI_TRACE_LEVEL 0
 #define NKAI_TRACE_LEVEL 0
 
 
 #include "../../../lib/pathfinder/CGPathNode.h"
 #include "../../../lib/pathfinder/CGPathNode.h"

+ 8 - 11
client/mapView/MapOverlayLogVisualizer.cpp

@@ -17,6 +17,7 @@
 #include "../render/Colors.h"
 #include "../render/Colors.h"
 #include "../render/EFont.h"
 #include "../render/EFont.h"
 #include "../render/IFont.h"
 #include "../render/IFont.h"
+#include "../render/IScreenHandler.h"
 #include "../render/IRenderHandler.h"
 #include "../render/IRenderHandler.h"
 #include "../render/Graphics.h"
 #include "../render/Graphics.h"
 #include "../gui/TextAlignment.h"
 #include "../gui/TextAlignment.h"
@@ -30,24 +31,20 @@ MapOverlayLogVisualizer::MapOverlayLogVisualizer(Canvas & target, std::shared_pt
 
 
 void MapOverlayLogVisualizer::drawLine(int3 start, int3 end)
 void MapOverlayLogVisualizer::drawLine(int3 start, int3 end)
 {
 {
-	const Point offset = Point(30, 30);
-
 	auto level = model->getLevel();
 	auto level = model->getLevel();
 
 
 	if(start.z != level || end.z != level)
 	if(start.z != level || end.z != level)
 		return;
 		return;
 
 
-	auto pStart = model->getTargetTileArea(start).topLeft();
-	auto pEnd = model->getTargetTileArea(end).topLeft();
-	auto viewPort = target.getRenderArea();
-
-	pStart.x += 3;
-	pEnd.x -= 3;
+	int scaling = GH.screenHandler().getScalingFactor();
+	auto pStart = model->getTargetTileArea(start).center();
+	auto pEnd = model->getTargetTileArea(end).center();
+	Rect viewPortRaw = target.getRenderArea();
+	Rect viewPort(viewPortRaw.topLeft() / scaling, viewPortRaw.dimensions() / scaling );
 
 
-	pStart += offset;
-	pEnd += offset;
+	Point workaroundOffset(8,8); // not sure why it is needed. Removing leads to incorrect clipping near view edges
 
 
-	if(viewPort.isInside(pStart) && viewPort.isInside(pEnd))
+	if(viewPort.isInside(pStart + workaroundOffset) && viewPort.isInside(pEnd + workaroundOffset))
 	{
 	{
 		target.drawLine(pStart, pEnd, ColorRGBA(255, 255, 0), ColorRGBA(255, 0, 0));
 		target.drawLine(pStart, pEnd, ColorRGBA(255, 255, 0), ColorRGBA(255, 0, 0));
 	}
 	}

+ 1 - 1
client/mapView/MapView.cpp

@@ -65,7 +65,7 @@ void BasicMapView::render(Canvas & target, bool fullUpdate)
 	tilesCache->update(controller->getContext());
 	tilesCache->update(controller->getContext());
 	tilesCache->render(controller->getContext(), targetClipped, fullUpdate);
 	tilesCache->render(controller->getContext(), targetClipped, fullUpdate);
 
 
-	MapOverlayLogVisualizer r(target, model);
+	MapOverlayLogVisualizer r(targetClipped, model);
 	logVisual->visualize(r);
 	logVisual->visualize(r);
 }
 }
 
 

+ 32 - 7
client/media/CVideoHandler.cpp

@@ -59,8 +59,18 @@ static si64 lodSeek(void * opaque, si64 pos, int whence)
 	return data->seek(pos);
 	return data->seek(pos);
 }
 }
 
 
+static void logFFmpegError(int errorCode)
+{
+	std::array<char, AV_ERROR_MAX_STRING_SIZE> errorMessage{};
+	av_strerror(errorCode, errorMessage.data(), errorMessage.size());
+
+	logGlobal->warn("Failed to open video file! Reason: %s", errorMessage.data());
+}
+
 [[noreturn]] static void throwFFmpegError(int errorCode)
 [[noreturn]] static void throwFFmpegError(int errorCode)
 {
 {
+	logFFmpegError(errorCode);
+
 	std::array<char, AV_ERROR_MAX_STRING_SIZE> errorMessage{};
 	std::array<char, AV_ERROR_MAX_STRING_SIZE> errorMessage{};
 	av_strerror(errorCode, errorMessage.data(), errorMessage.size());
 	av_strerror(errorCode, errorMessage.data(), errorMessage.size());
 
 
@@ -95,7 +105,7 @@ bool FFMpegStream::openInput(const VideoPath & videoToOpen)
 	return input != nullptr;
 	return input != nullptr;
 }
 }
 
 
-void FFMpegStream::openContext()
+bool FFMpegStream::openContext()
 {
 {
 	static const int BUFFER_SIZE = 4096;
 	static const int BUFFER_SIZE = 4096;
 	input->seek(0);
 	input->seek(0);
@@ -109,13 +119,21 @@ void FFMpegStream::openContext()
 	int avfopen = avformat_open_input(&formatContext, "dummyFilename", nullptr, nullptr);
 	int avfopen = avformat_open_input(&formatContext, "dummyFilename", nullptr, nullptr);
 
 
 	if(avfopen != 0)
 	if(avfopen != 0)
-		throwFFmpegError(avfopen);
+	{
+		logFFmpegError(avfopen);
+		return false;
+	}
 
 
 	// Retrieve stream information
 	// Retrieve stream information
 	int findStreamInfo = avformat_find_stream_info(formatContext, nullptr);
 	int findStreamInfo = avformat_find_stream_info(formatContext, nullptr);
 
 
 	if(avfopen < 0)
 	if(avfopen < 0)
-		throwFFmpegError(findStreamInfo);
+	{
+		logFFmpegError(findStreamInfo);
+		return false;
+	}
+
+	return true;
 }
 }
 
 
 void FFMpegStream::openCodec(int desiredStreamIndex)
 void FFMpegStream::openCodec(int desiredStreamIndex)
@@ -169,10 +187,13 @@ const AVFrame * FFMpegStream::getCurrentFrame() const
 	return frame;
 	return frame;
 }
 }
 
 
-void CVideoInstance::openVideo()
+bool CVideoInstance::openVideo()
 {
 {
-	openContext();
+	if (!openContext())
+		return false;
+
 	openCodec(findVideoStream());
 	openCodec(findVideoStream());
+	return true;
 }
 }
 
 
 void CVideoInstance::prepareOutput(float scaleFactor, bool useTextureOutput)
 void CVideoInstance::prepareOutput(float scaleFactor, bool useTextureOutput)
@@ -526,7 +547,9 @@ std::pair<std::unique_ptr<ui8 []>, si64> CAudioInstance::extractAudio(const Vide
 {
 {
 	if (!openInput(videoToOpen))
 	if (!openInput(videoToOpen))
 		return { nullptr, 0};
 		return { nullptr, 0};
-	openContext();
+
+	if (!openContext())
+		return { nullptr, 0};
 
 
 	int audioStreamIndex = findAudioStream();
 	int audioStreamIndex = findAudioStream();
 	if (audioStreamIndex == -1)
 	if (audioStreamIndex == -1)
@@ -653,7 +676,9 @@ std::unique_ptr<IVideoInstance> CVideoPlayer::open(const VideoPath & name, float
 	if (!result->openInput(name))
 	if (!result->openInput(name))
 		return nullptr;
 		return nullptr;
 
 
-	result->openVideo();
+	if (!result->openVideo())
+		return nullptr;
+
 	result->prepareOutput(scaleFactor, false);
 	result->prepareOutput(scaleFactor, false);
 	result->loadNextFrame(); // prepare 1st frame
 	result->loadNextFrame(); // prepare 1st frame
 
 

+ 2 - 2
client/media/CVideoHandler.h

@@ -42,7 +42,7 @@ class FFMpegStream : boost::noncopyable
 	AVFrame * frame = nullptr;
 	AVFrame * frame = nullptr;
 
 
 protected:
 protected:
-	void openContext();
+	bool openContext();
 	void openCodec(int streamIndex);
 	void openCodec(int streamIndex);
 
 
 	int findVideoStream() const;
 	int findVideoStream() const;
@@ -91,7 +91,7 @@ public:
 	CVideoInstance();
 	CVideoInstance();
 	~CVideoInstance();
 	~CVideoInstance();
 
 
-	void openVideo();
+	bool openVideo();
 	bool loadNextFrame();
 	bool loadNextFrame();
 
 
 	double timeStamp() final;
 	double timeStamp() final;

+ 1 - 1
client/renderSDL/ScreenHandler.cpp

@@ -350,7 +350,7 @@ EUpscalingFilter ScreenHandler::loadUpscalingFilter() const
 	};
 	};
 
 
 	auto filterName = settings["video"]["upscalingFilter"].String();
 	auto filterName = settings["video"]["upscalingFilter"].String();
-	auto filter = upscalingFilterTypes.at(filterName);
+	auto filter = upscalingFilterTypes.count(filterName) ? upscalingFilterTypes.at(filterName) : EUpscalingFilter::AUTO;
 
 
 	if (filter != EUpscalingFilter::AUTO)
 	if (filter != EUpscalingFilter::AUTO)
 		return filter;
 		return filter;

+ 9 - 3
client/windows/CMessage.cpp

@@ -262,10 +262,16 @@ void CMessage::drawIWindow(CInfoWindow * ret, std::string text, PlayerColor play
 	{
 	{
 		// use more compact form for right-click popup with no buttons / components
 		// use more compact form for right-click popup with no buttons / components
 		if(ret->text->slider)
 		if(ret->text->slider)
+		{
 			ret->text->resize(Point(ret->text->pos.w, std::min(ret->text->label->textSize.y, RIGHT_CLICK_POPUP_MAX_HEIGHT_TEXTONLY)));
 			ret->text->resize(Point(ret->text->pos.w, std::min(ret->text->label->textSize.y, RIGHT_CLICK_POPUP_MAX_HEIGHT_TEXTONLY)));
-
-		ret->pos.w = std::max(RIGHT_CLICK_POPUP_MIN_SIZE, ret->text->pos.w + 2 * SIDE_MARGIN);
-		ret->pos.h = std::max(RIGHT_CLICK_POPUP_MIN_SIZE, ret->text->pos.h + TOP_MARGIN + BOTTOM_MARGIN);
+			ret->pos.w = std::max(RIGHT_CLICK_POPUP_MIN_SIZE, ret->text->pos.w + 2 * SIDE_MARGIN);
+			ret->pos.h = std::max(RIGHT_CLICK_POPUP_MIN_SIZE, ret->text->pos.h + TOP_MARGIN + BOTTOM_MARGIN);
+		}
+		else
+		{
+			ret->pos.w = std::max(RIGHT_CLICK_POPUP_MIN_SIZE, ret->text->label->textSize.x + 2 * SIDE_MARGIN);
+			ret->pos.h = std::max(RIGHT_CLICK_POPUP_MIN_SIZE, ret->text->label->textSize.y + TOP_MARGIN + BOTTOM_MARGIN);
+		}
 	}
 	}
 	else
 	else
 	{
 	{

+ 3 - 1
config/schemas/gameSettings.json

@@ -44,7 +44,9 @@
 				"startingStackChances" :      { "type" : "array" },
 				"startingStackChances" :      { "type" : "array" },
 				"backpackSize" :              { "type" : "number" },
 				"backpackSize" :              { "type" : "number" },
 				"tavernInvite" :              { "type" : "boolean" },
 				"tavernInvite" :              { "type" : "boolean" },
-				"minimalPrimarySkills" :      { "type" : "array" }
+				"minimalPrimarySkills" :      { "type" : "array" },
+				"movementPointsLand" :        { "type" : "array" },
+				"movementPointsSea" :         { "type" : "array" }
 			}
 			}
 		},
 		},
 		"towns" : {
 		"towns" : {

+ 2 - 0
lib/battle/CUnitState.cpp

@@ -359,6 +359,8 @@ CUnitState & CUnitState::operator=(const CUnitState & other)
 	waiting = other.waiting;
 	waiting = other.waiting;
 	waitedThisTurn = other.waitedThisTurn;
 	waitedThisTurn = other.waitedThisTurn;
 	casts = other.casts;
 	casts = other.casts;
+	counterAttacks = other.counterAttacks;
+	shots = other.shots;
 	health = other.health;
 	health = other.health;
 	cloneID = other.cloneID;
 	cloneID = other.cloneID;
 	position = other.position;
 	position = other.position;

+ 13 - 0
lib/bonuses/BonusCache.h

@@ -34,6 +34,19 @@ protected:
 	{
 	{
 		std::atomic<int64_t> version = 0;
 		std::atomic<int64_t> version = 0;
 		std::atomic<int64_t> value = 0;
 		std::atomic<int64_t> value = 0;
+
+		BonusCacheEntry() = default;
+		BonusCacheEntry(const BonusCacheEntry & other)
+			: version(other.version.load())
+			, value(other.value.load())
+		{
+		}
+		BonusCacheEntry & operator =(const BonusCacheEntry & other)
+		{
+			version = other.version.load();
+			value = other.value.load();
+			return *this;
+		}
 	};
 	};
 
 
 	int getBonusValueImpl(BonusCacheEntry & currentValue, const CSelector & selector, BonusCacheMode) const;
 	int getBonusValueImpl(BonusCacheEntry & currentValue, const CSelector & selector, BonusCacheMode) const;

+ 19 - 1
lib/json/JsonRandom.cpp

@@ -35,6 +35,21 @@
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
+std::string JsonRandomizationException::cleanupJson(const JsonNode & value)
+{
+	std::string result = value.toCompactString();
+	for (size_t i = 0; i < result.size(); ++i)
+		if (result[i] == '\n')
+			result[i] = ' ';
+
+	return result;
+}
+
+JsonRandomizationException::JsonRandomizationException(const std::string & message, const JsonNode & input)
+	: std::runtime_error(message + " Input was: " + cleanupJson(input))
+{}
+
+
 	si32 JsonRandom::loadVariable(const std::string & variableGroup, const std::string & value, const Variables & variables, si32 defaultValue)
 	si32 JsonRandom::loadVariable(const std::string & variableGroup, const std::string & value, const Variables & variables, si32 defaultValue)
 	{
 	{
 		if (value.empty() || value[0] != '@')
 		if (value.empty() || value[0] != '@')
@@ -483,7 +498,10 @@ VCMI_LIB_NAMESPACE_BEGIN
 		if (!potentialPicks.empty())
 		if (!potentialPicks.empty())
 			pickedCreature = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
 			pickedCreature = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
 		else
 		else
-			logMod->warn("Failed to select suitable random creature!");
+			throw JsonRandomizationException("No potential creatures to pick!", value);
+
+		if (!pickedCreature.hasValue())
+			throw JsonRandomizationException("Invalid creature picked!", value);
 
 
 		stack.setType(pickedCreature.toCreature());
 		stack.setType(pickedCreature.toCreature());
 		stack.count = loadValue(value, rng, variables);
 		stack.count = loadValue(value, rng, variables);

+ 7 - 0
lib/json/JsonRandom.h

@@ -27,6 +27,13 @@ struct Bonus;
 struct Component;
 struct Component;
 class CStackBasicDescriptor;
 class CStackBasicDescriptor;
 
 
+class JsonRandomizationException : public std::runtime_error
+{
+	std::string cleanupJson(const JsonNode & value);
+public:
+	JsonRandomizationException(const std::string & message, const JsonNode & input);
+};
+
 class JsonRandom : public GameCallbackHolder
 class JsonRandom : public GameCallbackHolder
 {
 {
 public:
 public:

+ 9 - 1
lib/mapObjectConstructors/CRewardableConstructor.cpp

@@ -11,6 +11,7 @@
 #include "CRewardableConstructor.h"
 #include "CRewardableConstructor.h"
 
 
 #include "../json/JsonUtils.h"
 #include "../json/JsonUtils.h"
+#include "../json/JsonRandom.h"
 #include "../mapObjects/CRewardableObject.h"
 #include "../mapObjects/CRewardableObject.h"
 #include "../texts/CGeneralTextHandler.h"
 #include "../texts/CGeneralTextHandler.h"
 #include "../IGameCallback.h"
 #include "../IGameCallback.h"
@@ -49,7 +50,14 @@ Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCal
 {
 {
 	Rewardable::Configuration result;
 	Rewardable::Configuration result;
 	result.variables.preset = presetVariables;
 	result.variables.preset = presetVariables;
-	objectInfo.configureObject(result, rand, cb);
+
+	try {
+		objectInfo.configureObject(result, rand, cb);
+	}
+	catch (const JsonRandomizationException & e)
+	{
+		throw std::runtime_error("Failed to generate configuration for object '" + getJsonKey() + "'! Reason: " + e.what());
+	}
 
 
 	for(auto & rewardInfo : result.info)
 	for(auto & rewardInfo : result.info)
 	{
 	{

+ 1 - 1
lib/modding/IdentifierStorage.cpp

@@ -500,7 +500,7 @@ std::vector<std::string> CIdentifierStorage::getModsWithFailedRequests() const
 	std::vector<std::string> result;
 	std::vector<std::string> result;
 
 
 	for (const auto & request : failedRequests)
 	for (const auto & request : failedRequests)
-		if (!vstd::contains(result, request.localScope))
+		if (!vstd::contains(result, request.localScope) && ModScope::isScopeReserved(request.localScope))
 			result.push_back(request.localScope);
 			result.push_back(request.localScope);
 
 
 	return result;
 	return result;