Pārlūkot izejas kodu

Merge remote-tracking branch 'upstream/develop' into develop

Xilmi 1 gadu atpakaļ
vecāks
revīzija
388d6e297f

+ 0 - 3
CI/msvc/before_install.sh

@@ -5,6 +5,3 @@ curl -LfsS -o "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z" \
 #rm -r -f vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug
 #mkdir -p vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
 #cp vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/bin/* vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
-
-DUMPBIN_DIR=$(vswhere -latest -find **/dumpbin.exe | head -n 1)
-dirname "$DUMPBIN_DIR" > $GITHUB_PATH

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

@@ -238,6 +238,8 @@
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Skip Intro Music}\n\nAllow actions during the intro music that plays at the beginning of each battle.",	
 	"vcmi.battleOptions.endWithAutocombat.hover": "Ends battle",
 	"vcmi.battleOptions.endWithAutocombat.help": "{Ends battle}\n\nAuto-Combat plays battle to end instant",
+	"vcmi.battleOptions.showQuickSpell.hover": "Show Quickspell panel",
+	"vcmi.battleOptions.showQuickSpell.help": "{Show Quickspell panel}\n\nShow panel for quick selecting spells",
 
 	"vcmi.adventureMap.revisitObject.hover" : "Revisit Object",
 	"vcmi.adventureMap.revisitObject.help" : "{Revisit Object}\n\nIf a hero currently stands on a Map Object, he can revisit the location.",

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

@@ -238,6 +238,8 @@
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Intro-Musik überspringen}\n\n Überspringe die kurze Musik, die zu Beginn eines jeden Kampfes gespielt wird, bevor die Action beginnt. Kann auch durch Drücken der ESC-Taste übersprungen werden.",	
 	"vcmi.battleOptions.endWithAutocombat.hover": "Kampf beenden",
 	"vcmi.battleOptions.endWithAutocombat.help": "{Kampf beenden}\n\nAutokampf spielt den Kampf sofort zu Ende",
+	"vcmi.battleOptions.showQuickSpell.hover": "Schnellzauber-Panel anzeigen",
+	"vcmi.battleOptions.showQuickSpell.help": "{Schnellzauber-Panel anzeigen}\n\nZeigt ein Panel, auf dem schnell Zauber ausgewählt werden können",
 
 	"vcmi.adventureMap.revisitObject.hover" : "Objekt erneut besuchen",
 	"vcmi.adventureMap.revisitObject.help" : "{Objekt erneut besuchen}\n\nSteht ein Held gerade auf einem Kartenobjekt, kann er den Ort erneut aufsuchen.",

+ 14 - 0
client/adventureMap/AdventureMapShortcuts.cpp

@@ -71,6 +71,8 @@ std::vector<AdventureMapShortcutState> AdventureMapShortcuts::getShortcuts()
 		{ EShortcut::ADVENTURE_QUEST_LOG,        optionCanViewQuests(),  [this]() { this->showQuestlog(); } },
 		{ EShortcut::ADVENTURE_TOGGLE_SLEEP,     optionHeroSelected(),   [this]() { this->toggleSleepWake(); } },
 		{ EShortcut::ADVENTURE_TOGGLE_GRID,      optionInMapView(),      [this]() { this->toggleGrid(); } },
+		{ EShortcut::ADVENTURE_TOGGLE_VISITABLE, optionInMapView(),      [this]() { this->toggleVisitable(); } },
+		{ EShortcut::ADVENTURE_TOGGLE_BLOCKED,   optionInMapView(),      [this]() { this->toggleBlocked(); } },
 		{ EShortcut::ADVENTURE_TRACK_HERO,       optionInMapView(),      [this]() { this->toggleTrackHero(); } },
 		{ EShortcut::ADVENTURE_SET_HERO_ASLEEP,  optionHeroAwake(),      [this]() { this->setHeroSleeping(); } },
 		{ EShortcut::ADVENTURE_SET_HERO_AWAKE,   optionHeroSleeping(),   [this]() { this->setHeroAwake(); } },
@@ -168,6 +170,18 @@ void AdventureMapShortcuts::toggleGrid()
 	s["showGrid"].Bool() = !settings["gameTweaks"]["showGrid"].Bool();
 }
 
+void AdventureMapShortcuts::toggleVisitable()
+{
+	Settings s = settings.write["session"];
+	s["showVisitable"].Bool() = !settings["session"]["showVisitable"].Bool();
+}
+
+void AdventureMapShortcuts::toggleBlocked()
+{
+	Settings s = settings.write["session"];
+	s["showBlocked"].Bool() = !settings["session"]["showBlocked"].Bool();
+}
+
 void AdventureMapShortcuts::toggleSleepWake()
 {
 	if (!optionHeroSelected())

+ 2 - 0
client/adventureMap/AdventureMapShortcuts.h

@@ -42,6 +42,8 @@ class AdventureMapShortcuts
 	void showQuestlog();
 	void toggleTrackHero();
 	void toggleGrid();
+	void toggleVisitable();
+	void toggleBlocked();
 	void toggleSleepWake();
 	void setHeroSleeping();
 	void setHeroAwake();

+ 6 - 4
client/battle/BattleFieldController.cpp

@@ -356,9 +356,6 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForSpellRange()
 	std::set<BattleHex> result;
 	auto hoveredHex = getHoveredHex();
 
-	if(!settings["battle"]["mouseShadow"].Bool())
-		return result;
-
 	const spells::Caster *caster = nullptr;
 	const CSpell *spell = nullptr;
 
@@ -549,6 +546,8 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
 	std::set<BattleHex> hoveredMoveHexes  = getHighlightedHexesForMovementTarget();
 
 	BattleHex hoveredHex = getHoveredHex();
+	std::set<BattleHex> hoveredMouseHex = hoveredHex.isValid() ? std::set<BattleHex>({ hoveredHex }) : std::set<BattleHex>();
+
 	const CStack * hoveredStack = getHoveredStack();
 	if(!hoveredStack && hoveredHex == BattleHex::INVALID)
 		return;
@@ -565,7 +564,10 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
 		calculateRangeLimitAndHighlightImages(shootingRangeDistance, shootingRangeLimitImages, shootingRangeLimitHexes, shootingRangeLimitHexesHighlights);
 	}
 
-	auto const & hoveredMouseHexes = hoveredHex != BattleHex::INVALID && owner.actionsController->currentActionSpellcasting(getHoveredHex()) ? hoveredSpellHexes : hoveredMoveHexes;
+	bool useSpellRangeForMouse = hoveredHex != BattleHex::INVALID && owner.actionsController->currentActionSpellcasting(getHoveredHex());
+	bool useMoveRangeForMouse = !hoveredMoveHexes.empty() || !settings["battle"]["mouseShadow"].Bool();
+
+	const auto & hoveredMouseHexes = useSpellRangeForMouse ? hoveredSpellHexes : ( useMoveRangeForMouse ? hoveredMoveHexes : hoveredMouseHex);
 
 	for(int hex = 0; hex < GameConstants::BFIELD_SIZE; ++hex)
 	{

+ 7 - 0
client/battle/BattleInterface.cpp

@@ -860,3 +860,10 @@ void BattleInterface::setStickyHeroWindowsVisibility(bool visible)
 	if(visible)
 		windowObject->showStickyHeroWindows();
 }
+
+void BattleInterface::setStickyQuickSpellWindowVisibility(bool visible)
+{
+	windowObject->hideStickyQuickSpellWindow();
+	if(visible)
+		windowObject->showStickyQuickSpellWindow();
+}

+ 1 - 0
client/battle/BattleInterface.h

@@ -184,6 +184,7 @@ public:
 
 	void setBattleQueueVisibility(bool visible);
 	void setStickyHeroWindowsVisibility(bool visible);
+	void setStickyQuickSpellWindowVisibility(bool visible);
 
 	void endNetwork();
 	void executeStagedAnimations();

+ 9 - 2
client/battle/BattleInterfaceClasses.cpp

@@ -425,7 +425,7 @@ QuickSpellPanel::QuickSpellPanel(BattleInterface & owner)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 
-	addUsedEvents(LCLICK | SHOW_POPUP | MOVE);
+	addUsedEvents(LCLICK | SHOW_POPUP | MOVE | INPUT_MODE_CHANGE);
 
 	pos = Rect(0, 0, 52, 600);
 	background = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), pos);
@@ -481,7 +481,8 @@ void QuickSpellPanel::create()
 		{
 			buttonsDisabled.push_back(std::make_shared<TransparentFilledRectangle>(Rect(2, 7 + 50 * i, 48, 36), ColorRGBA(0, 0, 0, 172)));
 		}
-		labels.push_back(std::make_shared<CLabel>(7, 10 + 50 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, config["keyboard"]["battleSpellShortcut" + std::to_string(i)].String()));
+		if(GH.input().getCurrentInputMode() == InputMode::KEYBOARD_AND_MOUSE)
+			labels.push_back(std::make_shared<CLabel>(7, 10 + 50 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, config["keyboard"]["battleSpellShortcut" + std::to_string(i)].String()));
 
 		buttons.push_back(button);
 	}
@@ -493,6 +494,12 @@ void QuickSpellPanel::show(Canvas & to)
 	CIntObject::show(to);
 }
 
+void QuickSpellPanel::inputModeChanged(InputMode modi)
+{
+	create();
+	redraw();
+}
+
 HeroInfoBasicPanel::HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground)
 	: CIntObject(0)
 {

+ 1 - 0
client/battle/BattleInterfaceClasses.h

@@ -167,6 +167,7 @@ public:
 	void create();
 
 	void show(Canvas & to) override;
+	void inputModeChanged(InputMode modi) override;
 };
 
 class HeroInfoBasicPanel : public CIntObject //extracted from InfoWindow to fit better as non-popup embed element

+ 36 - 1
client/eventsSDL/InputHandler.cpp

@@ -37,6 +37,7 @@ InputHandler::InputHandler()
 	: enableMouse(settings["input"]["enableMouse"].Bool())
 	, enableTouch(settings["input"]["enableTouch"].Bool())
 	, enableController(settings["input"]["enableController"].Bool())
+	, currentInputMode(InputMode::KEYBOARD_AND_MOUSE)
 	, mouseHandler(std::make_unique<InputSourceMouse>())
 	, keyboardHandler(std::make_unique<InputSourceKeyboard>())
 	, fingerHandler(std::make_unique<InputSourceTouch>())
@@ -52,6 +53,7 @@ void InputHandler::handleCurrentEvent(const SDL_Event & current)
 	switch (current.type)
 	{
 		case SDL_KEYDOWN:
+			setCurrentInputMode(InputMode::KEYBOARD_AND_MOUSE);
 			keyboardHandler->handleEventKeyDown(current.key);
 			return;
 		case SDL_KEYUP:
@@ -60,11 +62,17 @@ void InputHandler::handleCurrentEvent(const SDL_Event & current)
 #ifndef VCMI_EMULATE_TOUCHSCREEN_WITH_MOUSE
 		case SDL_MOUSEMOTION:
 			if (enableMouse)
+			{
+				setCurrentInputMode(InputMode::KEYBOARD_AND_MOUSE);
 				mouseHandler->handleEventMouseMotion(current.motion);
+			}
 			return;
 		case SDL_MOUSEBUTTONDOWN:
 			if (enableMouse)
+			{
+				setCurrentInputMode(InputMode::KEYBOARD_AND_MOUSE);
 				mouseHandler->handleEventMouseButtonDown(current.button);
+			}
 			return;
 		case SDL_MOUSEBUTTONUP:
 			if (enableMouse)
@@ -83,11 +91,17 @@ void InputHandler::handleCurrentEvent(const SDL_Event & current)
 			return;
 		case SDL_FINGERMOTION:
 			if (enableTouch)
+			{
+				setCurrentInputMode(InputMode::TOUCH);
 				fingerHandler->handleEventFingerMotion(current.tfinger);
+			}
 			return;
 		case SDL_FINGERDOWN:
 			if (enableTouch)
+			{
+				setCurrentInputMode(InputMode::TOUCH);
 				fingerHandler->handleEventFingerDown(current.tfinger);
+			}
 			return;
 		case SDL_FINGERUP:
 			if (enableTouch)
@@ -95,11 +109,17 @@ void InputHandler::handleCurrentEvent(const SDL_Event & current)
 			return;
 		case SDL_CONTROLLERAXISMOTION:
 			if (enableController)
+			{
+				setCurrentInputMode(InputMode::CONTROLLER);
 				gameControllerHandler->handleEventAxisMotion(current.caxis);
+			}
 			return;
 		case SDL_CONTROLLERBUTTONDOWN:
 			if (enableController)
+			{
+				setCurrentInputMode(InputMode::CONTROLLER);
 				gameControllerHandler->handleEventButtonDown(current.cbutton);
+			}
 			return;
 		case SDL_CONTROLLERBUTTONUP:
 			if (enableController)
@@ -108,6 +128,20 @@ void InputHandler::handleCurrentEvent(const SDL_Event & current)
 	}
 }
 
+void InputHandler::setCurrentInputMode(InputMode modi)
+{
+	if(currentInputMode != modi)
+	{
+		currentInputMode = modi;
+		GH.events().dispatchInputModeChanged(modi);
+	}
+}
+
+InputMode InputHandler::getCurrentInputMode()
+{
+	return currentInputMode;
+}
+
 std::vector<SDL_Event> InputHandler::acquireEvents()
 {
 	boost::unique_lock<boost::mutex> lock(eventsMutex);
@@ -335,7 +369,8 @@ void InputHandler::stopTextInput()
 
 void InputHandler::hapticFeedback()
 {
-	fingerHandler->hapticFeedback();
+	if(currentInputMode == InputMode::TOUCH)
+		fingerHandler->hapticFeedback();
 }
 
 uint32_t InputHandler::getTicks()

+ 12 - 0
client/eventsSDL/InputHandler.h

@@ -23,6 +23,13 @@ class InputSourceTouch;
 class InputSourceText;
 class InputSourceGameController;
 
+enum class InputMode
+{
+	KEYBOARD_AND_MOUSE,
+	TOUCH,
+	CONTROLLER
+};
+
 class InputHandler
 {
 	std::vector<SDL_Event> eventsQueue;
@@ -34,6 +41,9 @@ class InputHandler
 	const bool enableTouch;
 	const bool enableController;
 
+	InputMode currentInputMode;
+	void setCurrentInputMode(InputMode modi);
+
 	std::vector<SDL_Event> acquireEvents();
 
 	void preprocessEvent(const SDL_Event & event);
@@ -91,4 +101,6 @@ public:
 	bool isKeyboardCmdDown() const;
 	bool isKeyboardCtrlDown() const;
 	bool isKeyboardShiftDown() const;
+
+	InputMode getCurrentInputMode();
 };

+ 10 - 0
client/gui/EventDispatcher.cpp

@@ -19,6 +19,7 @@
 
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/Rect.h"
+#include "../eventsSDL/InputHandler.h"
 
 template<typename Functor>
 void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
@@ -40,6 +41,7 @@ void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
 	processList(AEventsReceiver::DOUBLECLICK, doubleClickInterested);
 	processList(AEventsReceiver::TEXTINPUT, textInterested);
 	processList(AEventsReceiver::GESTURE, panningInterested);
+	processList(AEventsReceiver::INPUT_MODE_CHANGE, inputModeChangeInterested);
 }
 
 void EventDispatcher::activateElement(AEventsReceiver * elem, ui16 activityFlag)
@@ -316,6 +318,14 @@ void EventDispatcher::dispatchTextEditing(const std::string & text)
 	}
 }
 
+void EventDispatcher::dispatchInputModeChanged(const InputMode & modi)
+{
+	for(auto it : inputModeChangeInterested)
+	{
+		it->inputModeChanged(modi);
+	}
+}
+
 void EventDispatcher::dispatchGesturePanningStarted(const Point & initialPosition)
 {
 	auto copied = panningInterested;

+ 4 - 0
client/gui/EventDispatcher.h

@@ -16,6 +16,7 @@ VCMI_LIB_NAMESPACE_END
 class AEventsReceiver;
 enum class MouseButton;
 enum class EShortcut;
+enum class InputMode;
 
 /// Class that receives events from event producers and dispatches it to UI elements that are interested in this event
 class EventDispatcher
@@ -34,6 +35,7 @@ class EventDispatcher
 	EventReceiversList doubleClickInterested;
 	EventReceiversList textInterested;
 	EventReceiversList panningInterested;
+	EventReceiversList inputModeChangeInterested;
 
 	void handleLeftButtonClick(const Point & position, int tolerance, bool isPressed);
 	void handleDoubleButtonClick(const Point & position, int tolerance);
@@ -76,4 +78,6 @@ public:
 	/// Text input events
 	void dispatchTextInput(const std::string & text);
 	void dispatchTextEditing(const std::string & text);
+
+	void dispatchInputModeChanged(const InputMode & modi);
 };

+ 4 - 0
client/gui/EventsReceiver.h

@@ -16,6 +16,7 @@ VCMI_LIB_NAMESPACE_END
 
 class EventDispatcher;
 enum class EShortcut;
+enum class InputMode;
 
 /// Class that is capable of subscribing and receiving input events
 /// Acts as base class for all UI elements
@@ -75,6 +76,8 @@ public:
 
 	virtual void tick(uint32_t msPassed) {}
 
+	virtual void inputModeChanged(InputMode modi) {}
+
 public:
 	AEventsReceiver();
 	virtual ~AEventsReceiver() = default;
@@ -94,6 +97,7 @@ public:
 		TEXTINPUT = 512,
 		GESTURE = 1024,
 		DRAG = 2048,
+		INPUT_MODE_CHANGE = 4096
 	};
 
 	/// Returns true if element is currently hovered by mouse

+ 2 - 0
client/gui/Shortcut.h

@@ -120,6 +120,8 @@ enum class EShortcut
 	// Adventure map screen
 	ADVENTURE_GAME_OPTIONS, // 'o', Open CAdventureOptions window
 	ADVENTURE_TOGGLE_GRID,  // F6, Toggles map grid
+	ADVENTURE_TOGGLE_VISITABLE,  // Toggles visitable tiles overlay
+	ADVENTURE_TOGGLE_BLOCKED,  // Toggles blocked tiles overlay
 	ADVENTURE_TOGGLE_SLEEP, // Toggles hero sleep status
 	ADVENTURE_SET_HERO_ASLEEP, // Moves hero to sleep state
 	ADVENTURE_SET_HERO_AWAKE, // Move hero to awake state

+ 2 - 0
client/gui/ShortcutHandler.cpp

@@ -171,6 +171,8 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
 		{"gameActivateConsole",      EShortcut::GAME_ACTIVATE_CONSOLE     },
 		{"adventureGameOptions",     EShortcut::ADVENTURE_GAME_OPTIONS    },
 		{"adventureToggleGrid",      EShortcut::ADVENTURE_TOGGLE_GRID     },
+		{"adventureToggleVisitable", EShortcut::ADVENTURE_TOGGLE_VISITABLE},
+		{"adventureToggleBlocked",   EShortcut::ADVENTURE_TOGGLE_BLOCKED  },
 		{"adventureToggleSleep",     EShortcut::ADVENTURE_TOGGLE_SLEEP    },
 		{"adventureSetHeroAsleep",   EShortcut::ADVENTURE_SET_HERO_ASLEEP },
 		{"adventureSetHeroAwake",    EShortcut::ADVENTURE_SET_HERO_AWAKE  },

+ 9 - 7
client/mainmenu/CreditsScreen.cpp

@@ -22,13 +22,15 @@
 #include "../../AUTHORS.h"
 
 CreditsScreen::CreditsScreen(Rect rect)
-	: CIntObject(LCLICK), positionCounter(0)
+	: CIntObject(LCLICK), timePassed(0)
 {
 	pos.w = rect.w;
 	pos.h = rect.h;
 	setRedrawParent(true);
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
 
+	addUsedEvents(TIME);
+
 	std::string contributorsText = "";
 	std::string contributorsTask = "";
 	for (auto & element : contributors) 
@@ -48,15 +50,15 @@ CreditsScreen::CreditsScreen(Rect rect)
 	credits->scrollTextTo(-600); // move all text below the screen
 }
 
-void CreditsScreen::show(Canvas & to)
+void CreditsScreen::tick(uint32_t msPassed)
 {
-	CIntObject::show(to);
-	positionCounter++;
-	if(positionCounter % 2 == 0)
-		credits->scrollTextBy(1);
+	static const int timeToScrollByOnePx = 20;
+	timePassed += msPassed;
+	int scrollPosition = timePassed / timeToScrollByOnePx - 600;
+	credits->scrollTextTo(scrollPosition);
 
 	//end of credits, close this screen
-	if(credits->textSize.y + 600 < positionCounter / 2)
+	if(credits->textSize.y < scrollPosition)
 		clickPressed(GH.getCursorPosition());
 }
 

+ 2 - 2
client/mainmenu/CreditsScreen.h

@@ -15,11 +15,11 @@ class CMultiLineLabel;
 
 class CreditsScreen : public CIntObject
 {
-	int positionCounter;
+	int timePassed;
 	std::shared_ptr<CMultiLineLabel> credits;
 
 public:
 	CreditsScreen(Rect rect);
-	void show(Canvas & to) override;
+	void tick(uint32_t msPassed) override;
 	void clickPressed(const Point & cursorPosition) override;
 };

+ 1 - 1
client/windows/CSpellWindow.cpp

@@ -288,7 +288,7 @@ void CSpellWindow::processSpells()
 
 		if(onSpellSelect)
 		{
-			if(spell->isCombat() == openOnBattleSpells && !spell->isSpecial() && !spell->isCreatureAbility())
+			if(spell->isCombat() == openOnBattleSpells && !spell->isSpecial() && !spell->isCreatureAbility() && searchTextFound)
 				mySpells.push_back(spell.get());
 			continue;
 		}

+ 1 - 1
client/windows/CTutorialWindow.cpp

@@ -67,7 +67,7 @@ void CTutorialWindow::setContent()
 
 void CTutorialWindow::openWindowFirstTime(const TutorialMode & m)
 {
-	if(GH.input().hasTouchInputDevice() && !persistentStorage["gui"]["tutorialCompleted" + std::to_string(m)].Bool())
+	if(GH.input().getCurrentInputMode() == InputMode::TOUCH && !persistentStorage["gui"]["tutorialCompleted" + std::to_string(m)].Bool())
 	{
 		if(LOCPLINT)
 			LOCPLINT->showingDialog->setBusy();

+ 1 - 1
client/windows/InfoWindows.cpp

@@ -248,7 +248,7 @@ void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p,
 CRClickPopupInt::CRClickPopupInt(const std::shared_ptr<CIntObject> & our)
 {
 	CCS->curh->hide();
-	defActions = SHOWALL | UPDATE;
+	defActions = 255-DISPOSE;
 	our->recActions = defActions;
 	inner = our;
 	addChild(our.get(), false);

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

@@ -64,6 +64,10 @@ BattleOptionsTab::BattleOptionsTab(BattleInterface * owner)
 	{
 		showStickyHeroWindowsChangedCallback(value, owner);
 	});
+	addCallback("showQuickSpellChanged", [this, owner](bool value)
+	{
+		showQuickSpellChangedCallback(value, owner);
+	});
 	addCallback("enableAutocombatSpellsChanged", [this](bool value)
 	{
 		enableAutocombatSpellsChangedCallback(value);
@@ -95,6 +99,9 @@ BattleOptionsTab::BattleOptionsTab(BattleInterface * owner)
 	std::shared_ptr<CToggleButton> showStickyHeroInfoWindowsCheckbox = widget<CToggleButton>("showStickyHeroInfoWindowsCheckbox");
 	showStickyHeroInfoWindowsCheckbox->setSelected(settings["battle"]["stickyHeroInfoWindows"].Bool());
 
+	std::shared_ptr<CToggleButton> showQuickSpellCheckbox = widget<CToggleButton>("showQuickSpellCheckbox");
+	showQuickSpellCheckbox->setSelected(settings["battle"]["enableQuickSpellPanel"].Bool());
+
 	std::shared_ptr<CToggleButton> mouseShadowCheckbox = widget<CToggleButton>("mouseShadowCheckbox");
 	mouseShadowCheckbox->setSelected(settings["battle"]["mouseShadow"].Bool());
 
@@ -228,6 +235,19 @@ void BattleOptionsTab::showStickyHeroWindowsChangedCallback(bool value, BattleIn
 	}
 }
 
+void BattleOptionsTab::showQuickSpellChangedCallback(bool value, BattleInterface * parentBattleInterface)
+{
+	if(!parentBattleInterface)
+	{
+		Settings showQuickSpell = settings.write["battle"]["enableQuickSpellPanel"];
+		showQuickSpell->Bool() = value;
+	}
+	else
+	{
+		parentBattleInterface->setStickyQuickSpellWindowVisibility(value);
+	}
+}
+
 void BattleOptionsTab::queueSizeChangedCallback(int value, BattleInterface * parentBattleInterface)
 {
 	if (value == -1)

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

@@ -32,6 +32,7 @@ private:
 	void queueSizeChangedCallback(int value, BattleInterface * parentBattleInterface);
 	void skipBattleIntroMusicChangedCallback(bool value);
 	void showStickyHeroWindowsChangedCallback(bool value, BattleInterface * parentBattleInterface);
+	void showQuickSpellChangedCallback(bool value, BattleInterface * parentBattleInterface);
 	void enableAutocombatSpellsChangedCallback(bool value);
 	void endWithAutocombatChangedCallback(bool value);
 public:

+ 2 - 0
config/shortcutsConfig.json

@@ -40,6 +40,8 @@
 		"adventureSetHeroAwake":    "W",
 		"adventureThievesGuild":    "G",
 		"adventureToggleGrid":      "F6",
+		"adventureToggleVisitable": [],
+		"adventureToggleBlocked":   [],
 		"adventureToggleMapLevel":  "U",
 		"adventureToggleSleep":     [],
 		"adventureTrackHero":       "F5",

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

@@ -6,11 +6,6 @@
 
 	"items":
 	[
-		{
-			"name": "lineCreatureInfo",
-			"type": "horizontalLine",
-			"rect": { "x" : 5, "y" : 289, "w": 365, "h": 3}
-		},
 		{
 			"name": "lineAnimationSpeed",
 			"type": "horizontalLine",
@@ -21,11 +16,6 @@
 			"text": "core.genrltxt.396", // Auto-combat options
 			"position": {"x": 380, "y": 55}
 		},
-		{
-			"type" : "labelTitle",
-			"text": "core.genrltxt.397", // Creature info
-			"position": {"x": 10, "y": 265}
-		},
 /////////////////////////////////////// Right section - Auto-combat settings (NOT IMPLEMENTED)
 		{
 			"name": "autoCombatLabels",
@@ -105,32 +95,6 @@
 			]
 		},
 /////////////////////////////////////// Left section - checkboxes
-		{
-			"name": "creatureInfoLabels",
-			"type" : "verticalLayout",
-			"customType" : "labelDescription",
-			"position": {"x": 45, "y": 295},
-			"items":
-			[
-				{
-					"text": "core.genrltxt.402",
-				},
-				{
-					"text": "core.genrltxt.403",
-				}
-			]
-		},
-		{
-			"name": "creatureInfoCheckboxes",
-			"type" : "verticalLayout",
-			"customType" : "checkboxFake",
-			"position": {"x": 10, "y": 293},
-			"items":
-			[
-				{},
-				{}
-			]
-		},
 		{
 			"name": "generalOptionsLabels",
 			"type" : "verticalLayout",
@@ -153,6 +117,9 @@
 				{
 					"text": "vcmi.battleOptions.showStickyHeroInfoWindows.hover",
 				},
+				{
+					"text": "vcmi.battleOptions.showQuickSpell.hover",
+				},
 				{
 					"text": "core.genrltxt.406",
 				},
@@ -192,6 +159,11 @@
 					"help": "vcmi.battleOptions.showStickyHeroInfoWindows",
 					"callback": "showStickyHeroWindowsChanged"
 				},
+				{
+					"name": "showQuickSpellCheckbox",
+					"help": "vcmi.battleOptions.showQuickSpell",
+					"callback": "showQuickSpellChanged"
+				},
 				{
 					"name": "mouseShadowCheckbox",
 					"help": "core.help.429",

+ 1 - 0
launcher/main.cpp

@@ -113,6 +113,7 @@ void startExecutable(QString name, const QStringList & args)
 	// QProcess::start() causes the launcher window to freeze while the child process is running, so we hide it in
 	// MainWindow::on_startGameButton_clicked() and MainWindow::on_startEditorButton_clicked()
 	QProcess process;
+	process.setProcessChannelMode(QProcess::ForwardedChannels);
 	process.start(name, args);
 	process.waitForFinished(-1);
 

+ 6 - 3
server/battles/BattleResultProcessor.cpp

@@ -425,10 +425,13 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
 		if(finishingBattle->loserHero)
 		{
 			packHero.srcArtHolder = finishingBattle->loserHero->id;
-			for(const auto & artSlot : finishingBattle->loserHero->artifactsWorn)
+			for(const auto & slot : ArtifactUtils::commonWornSlots())
 			{
-				if(ArtifactUtils::isArtRemovable(artSlot))
-					addArtifactToTransfer(packHero, artSlot.first, artSlot.second.getArt());
+				if(const auto artSlot = finishingBattle->loserHero->artifactsWorn.find(slot);
+					artSlot != finishingBattle->loserHero->artifactsWorn.end() && ArtifactUtils::isArtRemovable(*artSlot))
+				{
+					addArtifactToTransfer(packHero, artSlot->first, artSlot->second.getArt());
+				}
 			}
 			for(const auto & artSlot : finishingBattle->loserHero->artifactsInBackpack)
 			{