Browse Source

Merge pull request #4407 from vcmi/visual-logger

Visual logger
Andrii Danylchenko 1 year ago
parent
commit
9b914bb4db

+ 14 - 0
AI/BattleAI/BattleAI.cpp

@@ -26,6 +26,7 @@
 #include "../../lib/CStack.h" // TODO: remove
                               // Eventually only IBattleInfoCallback and battle::Unit should be used,
                               // CUnitState should be private and CStack should be removed completely
+#include "../../lib/logging/VisualLogger.h"
 
 #define LOGL(text) print(text)
 #define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
@@ -47,6 +48,17 @@ CBattleAI::~CBattleAI()
 	}
 }
 
+void logHexNumbers()
+{
+#if BATTLE_TRACE_LEVEL >= 1
+	logVisual->updateWithLock("hexes", [](IVisualLogBuilder & b)
+		{
+			for(BattleHex hex = BattleHex(0); hex < GameConstants::BFIELD_SIZE; hex = BattleHex(hex + 1))
+				b.addText(hex, std::to_string(hex.hex));
+		});
+#endif
+}
+
 void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB)
 {
 	env = ENV;
@@ -57,6 +69,8 @@ void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::share
 	CB->waitTillRealize = false;
 	CB->unlockGsWhenWaiting = false;
 	movesSkippedByDefense = 0;
+
+	logHexNumbers();
 }
 
 void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences)

+ 38 - 0
AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp

@@ -13,6 +13,7 @@
 #include "../Engine/Nullkiller.h"
 #include "../pforeach.h"
 #include "../../../lib/CRandomGenerator.h"
+#include "../../../lib/logging/VisualLogger.h"
 
 namespace NKAI
 {
@@ -24,6 +25,41 @@ double HitMapInfo::value() const
 	return danger / std::sqrt(turn / 3.0f + 1);
 }
 
+void logHitmap(PlayerColor playerID, DangerHitMapAnalyzer & data)
+{
+#if NKAI_TRACE_LEVEL >= 1
+	logVisual->updateWithLock(playerID.toString() + ".danger.max", [&data](IVisualLogBuilder & b)
+		{
+			foreach_tile_pos([&b, &data](const int3 & pos)
+				{
+					auto & treat = data.getTileThreat(pos).maximumDanger;
+					b.addText(pos, std::to_string(treat.danger));
+
+					if(treat.hero.validAndSet())
+					{
+						b.addText(pos, std::to_string(treat.turn));
+						b.addText(pos, treat.hero->getNameTranslated());
+					}
+				});
+		});
+
+	logVisual->updateWithLock(playerID.toString() + ".danger.fast", [&data](IVisualLogBuilder & b)
+		{
+			foreach_tile_pos([&b, &data](const int3 & pos)
+				{
+					auto & treat = data.getTileThreat(pos).fastestDanger;
+					b.addText(pos, std::to_string(treat.danger));
+
+					if(treat.hero.validAndSet())
+					{
+						b.addText(pos, std::to_string(treat.turn));
+						b.addText(pos, treat.hero->getNameTranslated());
+					}
+				});
+		});
+#endif
+}
+
 void DangerHitMapAnalyzer::updateHitMap()
 {
 	if(hitMapUpToDate)
@@ -144,6 +180,8 @@ void DangerHitMapAnalyzer::updateHitMap()
 	}
 
 	logAi->trace("Danger hit map updated in %ld", timeElapsed(start));
+
+	logHitmap(ai->playerID, *this);
 }
 
 void DangerHitMapAnalyzer::calculateTileOwners()

+ 4 - 0
client/CMakeLists.txt

@@ -27,6 +27,7 @@ set(client_SRCS
 	battle/BattleStacksController.cpp
 	battle/BattleWindow.cpp
 	battle/CreatureAnimation.cpp
+	battle/BattleOverlayLogVisualizer.cpp
 
 	eventsSDL/NotificationHandler.cpp
 	eventsSDL/InputHandler.cpp
@@ -74,6 +75,7 @@ set(client_SRCS
 	mapView/MapViewController.cpp
 	mapView/MapViewModel.cpp
 	mapView/mapHandler.cpp
+	mapView/MapOverlayLogVisualizer.cpp
 
 	media/CAudioBase.cpp
 	media/CMusicHandler.cpp
@@ -214,6 +216,7 @@ set(client_HEADERS
 	battle/BattleStacksController.h
 	battle/BattleWindow.h
 	battle/CreatureAnimation.h
+	battle/BattleOverlayLogVisualizer.h
 
 	eventsSDL/NotificationHandler.h
 	eventsSDL/InputHandler.h
@@ -266,6 +269,7 @@ set(client_HEADERS
 	mapView/MapViewController.h
 	mapView/MapViewModel.h
 	mapView/mapHandler.h
+	mapView/MapOverlayLogVisualizer.h
 
 	media/CAudioBase.h
 	media/CEmptyVideoPlayer.h

+ 38 - 0
client/battle/BattleOverlayLogVisualizer.cpp

@@ -0,0 +1,38 @@
+/*
+ * BattleOverlayLogVisualizer.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 "BattleOverlayLogVisualizer.h"
+#include "BattleInterface.h"
+#include "BattleFieldController.h"
+
+#include "../render/Canvas.h"
+#include "../render/Colors.h"
+#include "../render/EFont.h"
+#include "../render/IFont.h"
+#include "../gui/TextAlignment.h"
+#include "../render/Graphics.h"
+
+BattleOverlayLogVisualizer::BattleOverlayLogVisualizer(
+	BattleRenderer::RendererRef & target,
+	BattleInterface & owner)
+	: target(target), owner(owner)
+{
+}
+
+void BattleOverlayLogVisualizer::drawText(BattleHex hex, int lineNumber, std::string text)
+{
+	Point offset = owner.fieldController->hexPositionLocal(hex).topLeft() + Point(20, 20);
+	int h = graphics->fonts[EFonts::FONT_TINY]->getLineHeight();
+
+	offset.y += h * lineNumber;
+
+	target.drawText(offset, EFonts::FONT_TINY, Colors::YELLOW, ETextAlignment::TOPCENTER, text);
+}

+ 28 - 0
client/battle/BattleOverlayLogVisualizer.h

@@ -0,0 +1,28 @@
+/*
+ * BattleOverlayLogVisualizer.h, 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
+ *
+ */
+#pragma once
+
+#include "../../lib/logging/VisualLogger.h"
+#include "BattleRenderer.h"
+
+class Canvas;
+class BattleInterface;
+
+class BattleOverlayLogVisualizer : public IBattleOverlayLogVisualizer
+{
+private:
+	BattleRenderer::RendererRef & target;
+	BattleInterface & owner;
+
+public:
+	BattleOverlayLogVisualizer(BattleRenderer::RendererRef & target, BattleInterface & owner);
+
+	void drawText(BattleHex hex, int lineNumber, std::string text) override;
+};

+ 4 - 0
client/battle/BattleRenderer.cpp

@@ -17,6 +17,7 @@
 #include "BattleSiegeController.h"
 #include "BattleStacksController.h"
 #include "BattleObstacleController.h"
+#include "BattleOverlayLogVisualizer.h"
 
 void BattleRenderer::collectObjects()
 {
@@ -73,4 +74,7 @@ void BattleRenderer::execute(BattleRenderer::RendererRef targetCanvas)
 	collectObjects();
 	sortObjects();
 	renderObjects(targetCanvas);
+
+	BattleOverlayLogVisualizer r(targetCanvas, owner);
+	logVisual->visualize(r);
 }

+ 94 - 0
client/mapView/MapOverlayLogVisualizer.cpp

@@ -0,0 +1,94 @@
+/*
+ * MapOverlayLogVisualizer.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 "MapOverlayLogVisualizer.h"
+#include "MapViewModel.h"
+
+#include "../../lib/logging/VisualLogger.h"
+#include "../render/Canvas.h"
+#include "../render/Colors.h"
+#include "../render/EFont.h"
+#include "../render/IFont.h"
+#include "../render/Graphics.h"
+#include "../gui/TextAlignment.h"
+
+
+MapOverlayLogVisualizer::MapOverlayLogVisualizer(Canvas & target, std::shared_ptr<MapViewModel> model)
+	: target(target), model(model)
+{
+}
+
+void MapOverlayLogVisualizer::drawLine(int3 start, int3 end)
+{
+	const Point offset = Point(30, 30);
+
+	auto level = model->getLevel();
+
+	if(start.z != level || end.z != level)
+		return;
+
+	auto pStart = model->getTargetTileArea(start).topLeft();
+	auto pEnd = model->getTargetTileArea(end).topLeft();
+	auto viewPort = target.getRenderArea();
+
+	pStart.x += 3;
+	pEnd.x -= 3;
+
+	pStart += offset;
+	pEnd += offset;
+
+	if(viewPort.isInside(pStart) && viewPort.isInside(pEnd))
+	{
+		target.drawLine(pStart, pEnd, ColorRGBA(255, 255, 0), ColorRGBA(255, 0, 0));
+	}
+}
+
+void MapOverlayLogVisualizer::drawText(
+	int3 tile,
+	int lineNumber,
+	std::string text,
+	std::optional<ColorRGBA> background)
+{
+	const Point offset = Point(6, 6);
+
+	auto level = model->getLevel();
+
+	if(tile.z != level)
+		return;
+
+	auto pStart = offset + model->getTargetTileArea(tile).topLeft();
+	auto viewPort = target.getRenderArea();
+
+	ColorRGBA color = Colors::YELLOW;
+	
+	if(background)
+	{
+		color = ((background->b + background->r + background->g) < 300)
+			? Colors::WHITE
+			: Colors::BLACK;
+	}
+
+	if(viewPort.isInside(pStart))
+	{
+		int w = graphics->fonts[EFonts::FONT_TINY]->getStringWidth(text);
+		int h = graphics->fonts[EFonts::FONT_TINY]->getLineHeight();
+
+		pStart.y += h * lineNumber;
+
+		if(background)
+		{
+			target.drawColor(Rect(pStart, Point(w + 4, h)), *background);
+			pStart.x += 2;
+		}
+
+		target.drawText(pStart, EFonts::FONT_TINY, color, ETextAlignment::TOPLEFT, text);
+	}
+}

+ 33 - 0
client/mapView/MapOverlayLogVisualizer.h

@@ -0,0 +1,33 @@
+/*
+ * MapOverlayLogVisualizer.h, 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
+ *
+ */
+#pragma once
+
+#include "../../lib/logging/VisualLogger.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class int3;
+
+VCMI_LIB_NAMESPACE_END
+
+class Canvas;
+class MapViewModel;
+
+class MapOverlayLogVisualizer : public IMapOverlayLogVisualizer
+{
+private:
+	Canvas & target;
+	std::shared_ptr<MapViewModel> model;
+
+public:
+	MapOverlayLogVisualizer(Canvas & target, std::shared_ptr<MapViewModel> model);
+	void drawLine(int3 start, int3 end) override;
+	void drawText(int3 tile, int lineNumber, std::string text, std::optional<ColorRGBA> color) override;
+};

+ 3 - 39
client/mapView/MapView.cpp

@@ -31,7 +31,8 @@
 
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
-#include "../../lib/logging/VisualLogger.h"
+
+#include "MapOverlayLogVisualizer.h"
 
 BasicMapView::~BasicMapView() = default;
 
@@ -58,50 +59,13 @@ BasicMapView::BasicMapView(const Point & offset, const Point & dimensions)
 	pos.h = dimensions.y;
 }
 
-class VisualLoggerRenderer : public ILogVisualizer
-{
-private:
-	Canvas & target;
-	std::shared_ptr<MapViewModel> model;
-
-public:
-	VisualLoggerRenderer(Canvas & target, std::shared_ptr<MapViewModel> model) : target(target), model(model)
-	{
-	}
-
-	virtual void drawLine(int3 start, int3 end) override
-	{
-		const Point offset = Point(30, 30);
-
-		auto level = model->getLevel();
-
-		if(start.z != level || end.z != level)
-			return;
-
-		auto pStart = model->getTargetTileArea(start).topLeft();
-		auto pEnd = model->getTargetTileArea(end).topLeft();
-		auto viewPort = target.getRenderArea();
-
-		pStart.x += 3;
-		pEnd.x -= 3;
-
-		pStart += offset;
-		pEnd += offset;
-
-		if(viewPort.isInside(pStart) && viewPort.isInside(pEnd))
-		{
-			target.drawLine(pStart, pEnd, ColorRGBA(255, 255, 0), ColorRGBA(255, 0, 0));
-		}
-	}
-};
-
 void BasicMapView::render(Canvas & target, bool fullUpdate)
 {
 	Canvas targetClipped(target, pos);
 	tilesCache->update(controller->getContext());
 	tilesCache->render(controller->getContext(), targetClipped, fullUpdate);
 
-	VisualLoggerRenderer r(target, model);
+	MapOverlayLogVisualizer r(target, model);
 	logVisual->visualize(r);
 }
 

+ 73 - 2
lib/logging/VisualLogger.cpp

@@ -20,13 +20,15 @@ void VisualLogger::updateWithLock(std::string channel, std::function<void(IVisua
 	std::lock_guard<std::mutex> lock(mutex);
 
 	mapLines[channel].clear();
+	mapTexts[channel].clear();
+	battleTexts[channel].clear();
 
-	VisualLogBuilder builder(mapLines[channel]);
+	VisualLogBuilder builder(mapLines[channel], mapTexts[channel], battleTexts[channel]);
 	
 	func(builder);
 }
 
-void VisualLogger::visualize(ILogVisualizer & visulizer)
+void VisualLogger::visualize(IMapOverlayLogVisualizer & visulizer)
 {
 	std::lock_guard<std::mutex> lock(mutex);
 
@@ -34,6 +36,40 @@ void VisualLogger::visualize(ILogVisualizer & visulizer)
 	{
 		visulizer.drawLine(line.start, line.end);
 	}
+
+	std::map<int3, std::vector<Text<int3>>> textMap;
+
+	for(auto line : mapTexts[keyToShow])
+	{
+		textMap[line.tile].push_back(line);
+	}
+
+	for(auto & pair : textMap)
+	{
+		for(int i = 0; i < pair.second.size(); i++)
+		{
+			visulizer.drawText(pair.first, i, pair.second[i].text, pair.second[i].background);
+		}
+	}
+}
+
+void VisualLogger::visualize(IBattleOverlayLogVisualizer & visulizer)
+{
+	std::lock_guard<std::mutex> lock(mutex);
+	std::map<BattleHex, std::vector<std::string>> textMap;
+
+	for(auto line : battleTexts[keyToShow])
+	{
+		textMap[line.tile].push_back(line.text);
+	}
+
+	for(auto & pair : textMap)
+	{
+		for(int i = 0; i < pair.second.size(); i++)
+		{
+			visulizer.drawText(pair.first, i, pair.second[i]);
+		}
+	}
 }
 
 void VisualLogger::setKey(std::string key)
@@ -41,4 +77,39 @@ void VisualLogger::setKey(std::string key)
 	keyToShow = key;
 }
 
+void IVisualLogBuilder::addText(int3 tile, std::string text, PlayerColor background)
+{
+	std::optional<ColorRGBA> rgbColor;
+
+	switch(background)
+	{
+	case 0:
+		rgbColor = ColorRGBA(255, 0, 0);
+		break;
+	case 1:
+		rgbColor = ColorRGBA(0, 0, 255);
+		break;
+	case 2:
+		rgbColor = ColorRGBA(128, 128, 128);
+		break;
+	case 3:
+		rgbColor = ColorRGBA(0, 255, 0);
+		break;
+	case 4:
+		rgbColor = ColorRGBA(255, 128, 0);
+		break;
+	case 5:
+		rgbColor = ColorRGBA(128, 0, 128);
+		break;
+	case 6:
+		rgbColor = ColorRGBA(0, 255, 255);
+		break;
+	case 7:
+		rgbColor = ColorRGBA(255, 128, 255);
+		break;
+	}
+
+	addText(tile, text, rgbColor);
+}
+
 VCMI_LIB_NAMESPACE_END

+ 58 - 13
lib/logging/VisualLogger.h

@@ -11,62 +11,107 @@
 
 #include "../int3.h"
 #include "../constants/EntityIdentifiers.h"
+#include "../battle/BattleHex.h"
+#include "../Color.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class ILogVisualizer
+class IMapOverlayLogVisualizer
 {
 public:
 	virtual void drawLine(int3 start, int3 end) = 0;
+	virtual void drawText(int3 tile, int lineNumber, std::string text, std::optional<ColorRGBA> background) = 0;
 };
 
-class IVisualLogBuilder
+class IBattleOverlayLogVisualizer
+{
+public:
+	virtual void drawText(BattleHex tile, int lineNumber, std::string text) = 0;
+};
+
+class DLL_LINKAGE IVisualLogBuilder
 {
 public:
 	virtual void addLine(int3 start, int3 end) = 0;
+	virtual void addText(int3 tile, std::string text, std::optional<ColorRGBA> color = {}) = 0;
+	virtual void addText(BattleHex tile, std::string text) = 0;
+
+	void addText(int3 tile, std::string text, PlayerColor background);
 };
 
 /// The logger is used to show screen overlay
 class DLL_LINKAGE VisualLogger
 {
 private:
-	struct MapLine
+	template<typename T>
+	struct Line
 	{
-		int3 start;
-		int3 end;
+		T start;
+		T end;
 
-		MapLine(int3 start, int3 end)
+		Line(T start, T end)
 			:start(start), end(end)
 		{
 		}
 	};
 
+	template<typename T>
+	struct Text
+	{
+		T tile;
+		std::string text;
+		std::optional<ColorRGBA> background;
+
+		Text(T tile, std::string text, std::optional<ColorRGBA> background)
+			:tile(tile), text(text), background(background)
+		{
+		}
+	};
+
 	class VisualLogBuilder : public IVisualLogBuilder
 	{
 	private:
-		std::vector<MapLine> & mapLines;
+		std::vector<Line<int3>> & mapLines;
+		std::vector<Text<BattleHex>> & battleTexts;
+		std::vector<Text<int3>> & mapTexts;
 
 	public:
-		VisualLogBuilder(std::vector<MapLine> & mapLines)
-			:mapLines(mapLines)
+		VisualLogBuilder(
+			std::vector<Line<int3>> & mapLines,
+			std::vector<Text<int3>> & mapTexts,
+			std::vector<Text<BattleHex>> & battleTexts)
+			:mapLines(mapLines), mapTexts(mapTexts), battleTexts(battleTexts)
+		{
+		}
+
+		void addLine(int3 start, int3 end) override
+		{
+			mapLines.emplace_back(start, end);
+		}
+
+		void addText(BattleHex tile, std::string text) override
 		{
+			battleTexts.emplace_back(tile, text, std::optional<ColorRGBA>());
 		}
 
-		virtual void addLine(int3 start, int3 end) override
+		void addText(int3 tile, std::string text, std::optional<ColorRGBA> background) override
 		{
-			mapLines.push_back(MapLine(start, end));
+			mapTexts.emplace_back(tile, text, background);
 		}
 	};
 
 private:
-	std::map<std::string, std::vector<MapLine>> mapLines;
+	std::map<std::string, std::vector<Line<int3>>> mapLines;
+	std::map<std::string, std::vector<Text<int3>>> mapTexts;
+	std::map<std::string, std::vector<Text<BattleHex>>> battleTexts;
 	std::mutex mutex;
 	std::string keyToShow;
 
 public:
 
 	void updateWithLock(std::string channel, std::function<void(IVisualLogBuilder & logBuilder)> func);
-	void visualize(ILogVisualizer & visulizer);
+	void visualize(IMapOverlayLogVisualizer & visulizer);
+	void visualize(IBattleOverlayLogVisualizer & visulizer);
 	void setKey(std::string key);
 };