瀏覽代碼

Add option for persistent hero info window as in HD mod

Dydzio 2 年之前
父節點
當前提交
48a72f90f7

+ 2 - 0
Mods/vcmi/config/vcmi/english.json

@@ -113,6 +113,8 @@
 	"vcmi.battleOptions.movementHighlightOnHover.help": "{Movement Highlight on Hover}\n\nHighlight unit's movement range when you hover over it.",
 	"vcmi.battleOptions.rangeLimitHighlightOnHover.hover": "Show range limits for shooters",
 	"vcmi.battleOptions.rangeLimitHighlightOnHover.help": "{Show range limits for shooters on Hover}\n\nShow shooter's range limits when you hover over it.",
+	"vcmi.battleOptions.showStickyHeroInfoWindows.hover": "Permanently show heroes statistics windows",
+	"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{Permanently show heroes statistics windows}\n\nPermanently toggle on heroes statistics windows that show primary stats and spell points.",
 	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Skip Intro Music",
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Skip Intro Music}\n\nAllow actions during the intro music that plays at the beginning of each battle",
 	"vcmi.battleWindow.pressKeyToSkipIntro" : "Press any key to start battle immediately",

+ 7 - 0
client/battle/BattleInterface.cpp

@@ -810,3 +810,10 @@ void BattleInterface::setBattleQueueVisibility(bool visible)
 	if(visible)
 		windowObject->showQueue();
 }
+
+void BattleInterface::setStickyHeroWindowsVisibility(bool visible)
+{
+	windowObject->hideStickyHeroWindows();
+	if(visible)
+		windowObject->showStickyHeroWindows();
+}

+ 1 - 0
client/battle/BattleInterface.h

@@ -178,6 +178,7 @@ public:
 	void tacticPhaseEnd();
 
 	void setBattleQueueVisibility(bool visible);
+	void setStickyHeroWindowsVisibility(bool visible);
 
 	void executeStagedAnimations();
 	void executeAnimationStage( EAnimationEvents event);

+ 40 - 5
client/battle/BattleInterfaceClasses.cpp

@@ -296,9 +296,20 @@ void BattleHero::heroLeftClicked()
 
 void BattleHero::heroRightClicked()
 {
+	if(settings["battle"]["stickyHeroInfoWindows"].Bool())
+		return;
+
 	Point windowPosition;
-	windowPosition.x = (!defender) ? owner.fieldController->pos.left() + 1 : owner.fieldController->pos.right() - 79;
-	windowPosition.y = owner.fieldController->pos.y + 135;
+	if(GH.screenDimensions().x < 1000)
+	{
+		windowPosition.x = (!defender) ? owner.fieldController->pos.left() + 1 : owner.fieldController->pos.right() - 79;
+		windowPosition.y = owner.fieldController->pos.y + 135;
+	}
+	else
+	{
+		windowPosition.x = (!defender) ? owner.fieldController->pos.left() - 93 : owner.fieldController->pos.right() + 15;
+		windowPosition.y = owner.fieldController->pos.y;
+	}
 
 	InfoAboutHero targetHero;
 	if(owner.makingTurn() || settings["session"]["spectate"].Bool())
@@ -364,13 +375,19 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her
 	addUsedEvents(TIME);
 }
 
-HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)
-	: CWindowObject(RCLICK_POPUP | SHADOW_DISABLED, "CHRPOP")
+HeroInfoBasicPanel::HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground)
+	: CIntObject(0)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	if (position != nullptr)
 		moveTo(*position);
-	background->colorize(hero.owner); //maybe add this functionality to base class?
+
+	if(initializeBackground)
+	{
+		background = std::make_shared<CPicture>("CHRPOP");
+		background->getSurface()->setBlitMode(EImageBlitMode::OPAQUE);
+		background->colorize(hero.owner);
+	}
 
 	auto attack = hero.details->primskills[0];
 	auto defense = hero.details->primskills[1];
@@ -406,6 +423,24 @@ HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)
 	labels.push_back(std::make_shared<CLabel>(39, 186, EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(currentSpellPoints) + "/" + std::to_string(maxSpellPoints)));
 }
 
+void HeroInfoBasicPanel::show(Canvas & to)
+{
+	showAll(to);
+	CIntObject::show(to);
+}
+
+HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)
+	: CWindowObject(RCLICK_POPUP | SHADOW_DISABLED, "CHRPOP")
+{
+	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	if (position != nullptr)
+		moveTo(*position);
+
+	background->colorize(hero.owner); //maybe add this functionality to base class?
+
+	content = std::make_shared<HeroInfoBasicPanel>(hero, nullptr, false);
+}
+
 BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay)
 	: owner(_owner)
 {

+ 12 - 1
client/battle/BattleInterfaceClasses.h

@@ -127,11 +127,22 @@ public:
 	BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender);
 };
 
-class HeroInfoWindow : public CWindowObject
+class HeroInfoBasicPanel : public CIntObject //extracted from InfoWindow to fit better as non-popup embed element
 {
 private:
+	std::shared_ptr<CPicture> background;
 	std::vector<std::shared_ptr<CLabel>> labels;
 	std::vector<std::shared_ptr<CAnimImage>> icons;
+public:
+	HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground = true);
+
+	void show(Canvas & to) override;
+};
+
+class HeroInfoWindow : public CWindowObject
+{
+private:
+	std::shared_ptr<HeroInfoBasicPanel> content;
 public:
 	HeroInfoWindow(const InfoAboutHero & hero, Point * position);
 };

+ 80 - 0
client/battle/BattleWindow.cpp

@@ -67,6 +67,7 @@ BattleWindow::BattleWindow(BattleInterface & owner):
 	addShortcut(EShortcut::BATTLE_SELECT_ACTION, std::bind(&BattleWindow::bSwitchActionf, this));
 
 	addShortcut(EShortcut::BATTLE_TOGGLE_QUEUE, [this](){ this->toggleQueueVisibility();});
+	addShortcut(EShortcut::BATTLE_TOGGLE_HEROES_STATS, [this](){ this->toggleStickyHeroWindowsVisibility();});
 	addShortcut(EShortcut::BATTLE_USE_CREATURE_SPELL, [this](){ this->owner.actionsController->enterCreatureCastingMode(); });
 	addShortcut(EShortcut::GLOBAL_CANCEL, [this](){ this->owner.actionsController->endCastingSpell(); });
 
@@ -80,6 +81,7 @@ BattleWindow::BattleWindow(BattleInterface & owner):
 	owner.fieldController->createHeroes();
 
 	createQueue();
+	createStickyHeroInfoWindows();
 
 	if ( owner.tacticsMode )
 		tacticPhaseStarted();
@@ -116,6 +118,41 @@ void BattleWindow::createQueue()
 		queue->disable();
 }
 
+void BattleWindow::createStickyHeroInfoWindows()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+
+	if(owner.defendingHeroInstance)
+	{
+		InfoAboutHero info;
+		info.initFromHero(owner.defendingHeroInstance, InfoAboutHero::EInfoLevel::INBATTLE);
+		Point position = (GH.screenDimensions().x >= 1000)
+				? Point(pos.x + pos.w + 15, pos.y)
+				: Point(pos.x + pos.w -79, pos.y + 135);
+		defenderHeroWindow = std::make_shared<HeroInfoBasicPanel>(info, &position);
+	}
+	if(owner.attackingHeroInstance)
+	{
+		InfoAboutHero info;
+		info.initFromHero(owner.attackingHeroInstance, InfoAboutHero::EInfoLevel::INBATTLE);
+		Point position = (GH.screenDimensions().x >= 1000)
+				? Point(pos.x - 93, pos.y)
+				: Point(pos.x + 1, pos.y + 135);
+		attackerHeroWindow = std::make_shared<HeroInfoBasicPanel>(info, &position);
+	}
+
+	bool showInfoWindows = settings["battle"]["stickyHeroInfoWindows"].Bool();
+
+	if(!showInfoWindows)
+	{
+		if(attackerHeroWindow)
+			attackerHeroWindow->disable();
+
+		if(defenderHeroWindow)
+			defenderHeroWindow->disable();
+	}
+}
+
 BattleWindow::~BattleWindow()
 {
 	CPlayerInterface::battleInt = nullptr;
@@ -170,6 +207,49 @@ void BattleWindow::showQueue()
 	GH.windows().totalRedraw();
 }
 
+void BattleWindow::toggleStickyHeroWindowsVisibility()
+{
+	if(settings["battle"]["stickyHeroInfoWindows"].Bool())
+		hideStickyHeroWindows();
+	else
+		showStickyHeroWindows();
+}
+
+void BattleWindow::hideStickyHeroWindows()
+{
+	if(settings["battle"]["stickyHeroInfoWindows"].Bool() == false)
+		return;
+
+	Settings showStickyHeroInfoWindows = settings.write["battle"]["stickyHeroInfoWindows"];
+	showStickyHeroInfoWindows->Bool() = false;
+
+	if(attackerHeroWindow)
+		attackerHeroWindow->disable();
+
+	if(defenderHeroWindow)
+		defenderHeroWindow->disable();
+
+	GH.windows().totalRedraw();
+}
+
+void BattleWindow::showStickyHeroWindows()
+{
+	if(settings["battle"]["stickyHeroInfoWindows"].Bool() == true)
+		return;
+
+	Settings showStickyHeroInfoWIndows = settings.write["battle"]["stickyHeroInfoWindows"];
+	showStickyHeroInfoWIndows->Bool() = true;
+
+//	if(attackerHeroWindow)
+//		attackerHeroWindow->enable();
+//
+//	if(defenderHeroWindow)
+//		defenderHeroWindow->enable();
+
+	createStickyHeroInfoWindows();
+	GH.windows().totalRedraw();
+}
+
 void BattleWindow::updateQueue()
 {
 	queue->update();

+ 9 - 0
client/battle/BattleWindow.h

@@ -24,6 +24,7 @@ class BattleInterface;
 class BattleConsole;
 class BattleRenderer;
 class StackQueue;
+class HeroInfoBasicPanel;
 
 /// GUI object that handles functionality of panel at the bottom of combat screen
 class BattleWindow : public InterfaceObjectConfigurable
@@ -32,6 +33,8 @@ class BattleWindow : public InterfaceObjectConfigurable
 
 	std::shared_ptr<StackQueue> queue;
 	std::shared_ptr<BattleConsole> console;
+	std::shared_ptr<HeroInfoBasicPanel> attackerHeroWindow;
+	std::shared_ptr<HeroInfoBasicPanel> defenderHeroWindow;
 
 	/// button press handling functions
 	void bOptionsf();
@@ -60,6 +63,9 @@ class BattleWindow : public InterfaceObjectConfigurable
 	void toggleQueueVisibility();
 	void createQueue();
 
+	void toggleStickyHeroWindowsVisibility();
+	void createStickyHeroInfoWindows();
+
 	std::shared_ptr<BattleConsole> buildBattleConsole(const JsonNode &) const;
 
 public:
@@ -73,6 +79,9 @@ public:
 	void hideQueue();
 	void showQueue();
 
+	void hideStickyHeroWindows();
+	void showStickyHeroWindows();
+
 	/// block all UI elements when player is not allowed to act, e.g. during enemy turn
 	void blockUI(bool on);
 

+ 1 - 0
client/gui/Shortcut.h

@@ -133,6 +133,7 @@ enum class EShortcut
 	BATTLE_TACTICS_NEXT,
 	BATTLE_TACTICS_END,
 	BATTLE_SELECT_ACTION, // Alternative actions toggle
+	BATTLE_TOGGLE_HEROES_STATS,
 
 	// Town screen
 	TOWN_OPEN_TAVERN,

+ 1 - 0
client/gui/ShortcutHandler.cpp

@@ -134,6 +134,7 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
 		{SDLK_RETURN,    EShortcut::BATTLE_TACTICS_END        },
 		{SDLK_KP_ENTER,  EShortcut::BATTLE_TACTICS_END        },
 		{SDLK_s,         EShortcut::BATTLE_SELECT_ACTION      },
+		{SDLK_i,         EShortcut::BATTLE_TOGGLE_HEROES_STATS},
 		{SDLK_t,         EShortcut::TOWN_OPEN_TAVERN          },
 		{SDLK_SPACE,     EShortcut::TOWN_SWAP_ARMIES          },
 		{SDLK_END,       EShortcut::RECRUITMENT_MAX           },

+ 20 - 0
client/windows/settings/BattleOptionsTab.cpp

@@ -60,6 +60,10 @@ BattleOptionsTab::BattleOptionsTab(BattleInterface * owner)
 	{
 		skipBattleIntroMusicChangedCallback(value);
 	});
+	addCallback("showStickyHeroWindowsChanged", [this, owner](bool value)
+	{
+		showStickyHeroWindowsChangedCallback(value, owner);
+	});
 	build(config);
 
 	std::shared_ptr<CToggleGroup> animationSpeedToggle = widget<CToggleGroup>("animationSpeedPicker");
@@ -80,6 +84,9 @@ BattleOptionsTab::BattleOptionsTab(BattleInterface * owner)
 	std::shared_ptr<CToggleButton> rangeLimitHighlightOnHoverCheckbox = widget<CToggleButton>("rangeLimitHighlightOnHoverCheckbox");
 	rangeLimitHighlightOnHoverCheckbox->setSelected(settings["battle"]["rangeLimitHighlightOnHover"].Bool());
 
+	std::shared_ptr<CToggleButton> showStickyHeroInfoWindowsCheckbox = widget<CToggleButton>("showStickyHeroInfoWindowsCheckbox");
+	showStickyHeroInfoWindowsCheckbox->setSelected(settings["battle"]["stickyHeroInfoWindows"].Bool());
+
 	std::shared_ptr<CToggleButton> mouseShadowCheckbox = widget<CToggleButton>("mouseShadowCheckbox");
 	mouseShadowCheckbox->setSelected(settings["battle"]["mouseShadow"].Bool());
 
@@ -194,6 +201,19 @@ void BattleOptionsTab::showQueueChangedCallback(bool value, BattleInterface * pa
 	}
 }
 
+void BattleOptionsTab::showStickyHeroWindowsChangedCallback(bool value, BattleInterface * parentBattleInterface)
+{
+	if(!parentBattleInterface)
+	{
+		Settings showStickyWindows = settings.write["battle"]["stickyHeroInfoWindows"];
+		showStickyWindows->Bool() = value;
+	}
+	else
+	{
+		parentBattleInterface->setStickyHeroWindowsVisibility(value);
+	}
+}
+
 void BattleOptionsTab::queueSizeChangedCallback(int value, BattleInterface * parentBattleInterface)
 {
 	if (value == -1)

+ 1 - 0
client/windows/settings/BattleOptionsTab.h

@@ -31,6 +31,7 @@ private:
 	void showQueueChangedCallback(bool value, BattleInterface * parentBattleInterface);
 	void queueSizeChangedCallback(int value, BattleInterface * parentBattleInterface);
 	void skipBattleIntroMusicChangedCallback(bool value);
+	void showStickyHeroWindowsChangedCallback(bool value, BattleInterface * parentBattleInterface);
 public:
 	BattleOptionsTab(BattleInterface * owner = nullptr);
 };

+ 5 - 1
config/schemas/settings.json

@@ -238,7 +238,7 @@
 			"type" : "object",
 			"additionalProperties" : false,
 			"default" : {},
-			"required" : [ "speedFactor", "mouseShadow", "cellBorders", "stackRange", "movementHighlightOnHover", "rangeLimitHighlightOnHover", "showQueue", "swipeAttackDistance", "queueSize" ],
+			"required" : [ "speedFactor", "mouseShadow", "cellBorders", "stackRange", "movementHighlightOnHover", "rangeLimitHighlightOnHover", "showQueue", "swipeAttackDistance", "queueSize", "stickyHeroInfoWindows" ],
 			"properties" : {
 				"speedFactor" : {
 					"type" : "number",
@@ -276,6 +276,10 @@
 					"type" : "string",
 					"default" : "auto",
 					"enum" : [ "auto", "small", "big" ]
+				},
+				"stickyHeroInfoWindows" : {
+					"type" : "boolean",
+					"default" : true
 				}
 			}
 		},

+ 8 - 0
config/widgets/settings/battleOptionsTab.json

@@ -110,6 +110,9 @@
 				{
 					"text": "vcmi.battleOptions.rangeLimitHighlightOnHover.hover",
 				},
+				{
+					"text": "vcmi.battleOptions.showStickyHeroInfoWindows.hover",
+				},
 				{
 					"text": "core.genrltxt.406",
 				},
@@ -144,6 +147,11 @@
 					"help": "vcmi.battleOptions.rangeLimitHighlightOnHover",
 					"callback": "rangeLimitHighlightOnHoverChanged"
 				},
+				{
+					"name": "showStickyHeroInfoWindowsCheckbox",
+					"help": "vcmi.battleOptions.showStickyHeroInfoWindows",
+					"callback": "showStickyHeroWindowsChanged"
+				},
 				{
 					"name": "mouseShadowCheckbox",
 					"help": "core.help.429",