瀏覽代碼

Merge pull request #3062 from Laserlicht/sort_towns

sort town/hero feature
Nordsoft91 2 年之前
父節點
當前提交
f3acc939b9

二進制
Mods/vcmi/Data/radialMenu/altDown.png


二進制
Mods/vcmi/Data/radialMenu/altDownBottom.png


二進制
Mods/vcmi/Data/radialMenu/altUp.png


二進制
Mods/vcmi/Data/radialMenu/altUpTop.png


二進制
Mods/vcmi/Data/radialMenu/itemEmptyAlt.png


二進制
Mods/vcmi/Data/radialMenu/itemInactiveAlt.png


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

@@ -49,6 +49,11 @@
 	"vcmi.radialWheel.heroSwapArtifacts" : "Swap artifacts with other hero",
 	"vcmi.radialWheel.heroDismiss" : "Dismiss hero",
 
+	"vcmi.radialWheel.moveTop" : "Move to top",
+	"vcmi.radialWheel.moveUp" : "Move up",
+	"vcmi.radialWheel.moveDown" : "Move down",
+	"vcmi.radialWheel.moveBottom" : "Move to bottom",
+
 	"vcmi.mainMenu.serverConnecting" : "Connecting...",
 	"vcmi.mainMenu.serverAddressEnter" : "Enter address:",
 	"vcmi.mainMenu.serverConnectionFailed" : "Failed to connect",

+ 14 - 0
client/PlayerLocalState.cpp

@@ -235,6 +235,12 @@ void PlayerLocalState::removeWanderingHero(const CGHeroInstance * hero)
 		setSelection(ownedTowns.front());
 }
 
+void PlayerLocalState::swapWanderingHero(int pos1, int pos2)
+{
+	assert(wanderingHeroes[pos1] && wanderingHeroes[pos2]);
+	std::swap(wanderingHeroes[pos1], wanderingHeroes[pos2]);
+}
+
 const std::vector<const CGTownInstance *> & PlayerLocalState::getOwnedTowns()
 {
 	return ownedTowns;
@@ -269,3 +275,11 @@ void PlayerLocalState::removeOwnedTown(const CGTownInstance * town)
 	if (currentSelection == nullptr && !ownedTowns.empty())
 		setSelection(ownedTowns.front());
 }
+
+void PlayerLocalState::swapOwnedTowns(int pos1, int pos2)
+{
+	assert(ownedTowns[pos1] && ownedTowns[pos2]);
+	std::swap(ownedTowns[pos1], ownedTowns[pos2]);
+
+	adventureInt->onTownOrderChanged();
+}

+ 2 - 0
client/PlayerLocalState.h

@@ -66,12 +66,14 @@ public:
 	const CGTownInstance * getOwnedTown(size_t index);
 	void addOwnedTown(const CGTownInstance * hero);
 	void removeOwnedTown(const CGTownInstance * hero);
+	void swapOwnedTowns(int pos1, int pos2);
 
 	const std::vector<const CGHeroInstance *> & getWanderingHeroes();
 	const CGHeroInstance * getWanderingHero(size_t index);
 	const CGHeroInstance * getNextWanderingHero(const CGHeroInstance * hero);
 	void addWanderingHero(const CGHeroInstance * hero);
 	void removeWanderingHero(const CGHeroInstance * hero);
+	void swapWanderingHero(int pos1, int pos2);
 
 	void setPath(const CGHeroInstance * h, const CGPath & path);
 	bool setPath(const CGHeroInstance * h, const int3 & destination);

+ 6 - 0
client/adventureMap/AdventureMapInterface.cpp

@@ -304,6 +304,7 @@ void AdventureMapInterface::onSelectionChanged(const CArmedInstance *sel)
 		auto town = dynamic_cast<const CGTownInstance*>(sel);
 
 		widget->getInfoBar()->showTownSelection(town);
+		widget->getTownList()->updateWidget();;
 		widget->getTownList()->select(town);
 		widget->getHeroList()->select(nullptr);
 		onHeroChanged(nullptr);
@@ -325,6 +326,11 @@ void AdventureMapInterface::onSelectionChanged(const CArmedInstance *sel)
 	widget->getTownList()->redraw();
 }
 
+void AdventureMapInterface::onTownOrderChanged()
+{
+	widget->getTownList()->updateWidget();
+}
+
 void AdventureMapInterface::onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions)
 {
 	if (positions)

+ 3 - 0
client/adventureMap/AdventureMapInterface.h

@@ -146,6 +146,9 @@ public:
 	/// Called when currently selected object changes
 	void onSelectionChanged(const CArmedInstance *sel);
 
+	/// Called when town order changes
+	void onTownOrderChanged();
+
 	/// Called when map audio should be paused, e.g. on combat or town screen access
 	void onAudioPaused();
 

+ 93 - 17
client/adventureMap/CList.cpp

@@ -16,11 +16,13 @@
 #include "../widgets/Images.h"
 #include "../widgets/Buttons.h"
 #include "../widgets/ObjectLists.h"
+#include "../widgets/RadialMenu.h"
 #include "../windows/InfoWindows.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../PlayerLocalState.h"
 #include "../gui/CGuiHandler.h"
+#include "../gui/WindowHandler.h"
 #include "../render/Canvas.h"
 #include "../render/Colors.h"
 
@@ -216,7 +218,8 @@ CHeroList::CEmptyHeroItem::CEmptyHeroItem()
 
 CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero)
 	: CListItem(parent),
-	hero(Hero)
+	hero(Hero),
+	parentList(parent)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	movement = std::make_shared<CAnimImage>(AnimationPath::builtin("IMOBIL"), 0, 0, 0, 1);
@@ -227,6 +230,8 @@ CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero)
 	pos.h = std::max(std::max<int>(movement->pos.h + 1, mana->pos.h + 1), portrait->pos.h);
 
 	update();
+
+	addUsedEvents(GESTURE);
 }
 
 void CHeroList::CHeroItem::update()
@@ -262,6 +267,43 @@ std::string CHeroList::CHeroItem::getHoverText()
 	return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->getNameTranslated() % hero->type->heroClass->getNameTranslated());
 }
 
+void CHeroList::CHeroItem::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
+{
+	if(!on)
+		return;
+
+	if(!hero)
+		return;
+
+	auto & heroes = LOCPLINT->localState->getWanderingHeroes();
+
+	if(heroes.size() < 2)
+		return;
+
+	int heroPos = vstd::find_pos(heroes, hero);
+	const CGHeroInstance * heroUpper = (heroPos < 1) ? nullptr : heroes[heroPos - 1];
+	const CGHeroInstance * heroLower = (heroPos > heroes.size() - 2) ? nullptr : heroes[heroPos + 1];
+
+	std::vector<RadialMenuConfig> menuElements = {
+		{ RadialMenuConfig::ITEM_ALT_NN, heroUpper != nullptr, "altUpTop", "vcmi.radialWheel.moveTop", [this, heroPos]()
+		{
+			for (int i = heroPos; i > 0; i--)
+				LOCPLINT->localState->swapWanderingHero(i, i - 1);
+			parentList->updateWidget();
+		} },
+		{ RadialMenuConfig::ITEM_ALT_NW, heroUpper != nullptr, "altUp", "vcmi.radialWheel.moveUp", [this, heroPos](){LOCPLINT->localState->swapWanderingHero(heroPos, heroPos - 1); parentList->updateWidget(); } },
+		{ RadialMenuConfig::ITEM_ALT_SW, heroLower != nullptr, "altDown", "vcmi.radialWheel.moveDown", [this, heroPos](){ LOCPLINT->localState->swapWanderingHero(heroPos, heroPos + 1); parentList->updateWidget(); } },
+		{ RadialMenuConfig::ITEM_ALT_SS, heroLower != nullptr, "altDownBottom", "vcmi.radialWheel.moveBottom", [this, heroPos, heroes]()
+		{
+			for (int i = heroPos; i < heroes.size() - 1; i++)
+				LOCPLINT->localState->swapWanderingHero(i, i + 1);
+			parentList->updateWidget();
+		} },
+	};
+
+	GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements, true);
+}
+
 std::shared_ptr<CIntObject> CHeroList::createItem(size_t index)
 {
 	if (LOCPLINT->localState->getWanderingHeroes().size() > index)
@@ -293,7 +335,7 @@ void CHeroList::updateWidget()
 
 	for (size_t i = 0; i < heroes.size(); ++i)
 	{
-		auto item =  std::dynamic_pointer_cast<CHeroItem>(listBox->getItem(i));
+		auto item = std::dynamic_pointer_cast<CHeroItem>(listBox->getItem(i));
 
 		if (!item)
 			continue;
@@ -324,12 +366,17 @@ std::shared_ptr<CIntObject> CTownList::createItem(size_t index)
 
 CTownList::CTownItem::CTownItem(CTownList *parent, const CGTownInstance *Town):
 	CListItem(parent),
-	town(Town)
+	parentList(parent)
 {
+	const std::vector<const CGTownInstance *> towns = LOCPLINT->localState->getOwnedTowns();
+	townIndex = std::distance(towns.begin(), std::find(towns.begin(), towns.end(), Town));
+
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	picture = std::make_shared<CAnimImage>(AnimationPath::builtin("ITPA"), 0);
 	pos = picture->pos;
 	update();
+
+	addUsedEvents(GESTURE);
 }
 
 std::shared_ptr<CIntObject> CTownList::CTownItem::genSelection()
@@ -339,6 +386,7 @@ std::shared_ptr<CIntObject> CTownList::CTownItem::genSelection()
 
 void CTownList::CTownItem::update()
 {
+	const CGTownInstance * town = LOCPLINT->localState->getOwnedTowns()[townIndex];
 	size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->settings()->getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
 
 	picture->setFrame(iconIndex + 2);
@@ -348,22 +396,58 @@ void CTownList::CTownItem::update()
 void CTownList::CTownItem::select(bool on)
 {
 	if(on)
-		LOCPLINT->localState->setSelection(town);
+		LOCPLINT->localState->setSelection(LOCPLINT->localState->getOwnedTowns()[townIndex]);
 }
 
 void CTownList::CTownItem::open()
 {
-	LOCPLINT->openTownWindow(town);
+	LOCPLINT->openTownWindow(LOCPLINT->localState->getOwnedTowns()[townIndex]);
 }
 
 void CTownList::CTownItem::showTooltip()
 {
-	CRClickPopup::createAndPush(town, GH.getCursorPosition());
+	CRClickPopup::createAndPush(LOCPLINT->localState->getOwnedTowns()[townIndex], GH.getCursorPosition());
+}
+
+void CTownList::CTownItem::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
+{
+	if(!on)
+		return;
+
+	const std::vector<const CGTownInstance *> towns = LOCPLINT->localState->getOwnedTowns();
+
+	if(townIndex < 0 || townIndex > towns.size() - 1 || !towns[townIndex])
+		return;
+
+	if(towns.size() < 2)
+		return;
+
+	int townUpperPos = (townIndex < 1) ? -1 : townIndex - 1;
+	int townLowerPos = (townIndex > towns.size() - 2) ? -1 : townIndex + 1;
+
+	std::vector<RadialMenuConfig> menuElements = {
+		{ RadialMenuConfig::ITEM_ALT_NN, townUpperPos > -1, "altUpTop", "vcmi.radialWheel.moveTop", [this]()
+		{
+			for (int i = townIndex; i > 0; i--)
+				LOCPLINT->localState->swapOwnedTowns(i, i - 1);
+			parentList->updateWidget();
+		} },
+		{ RadialMenuConfig::ITEM_ALT_NW, townUpperPos > -1, "altUp", "vcmi.radialWheel.moveUp", [this, townUpperPos](){LOCPLINT->localState->swapOwnedTowns(townIndex, townUpperPos); parentList->updateWidget(); } },
+		{ RadialMenuConfig::ITEM_ALT_SW, townLowerPos > -1, "altDown", "vcmi.radialWheel.moveDown", [this, townLowerPos](){ LOCPLINT->localState->swapOwnedTowns(townIndex, townLowerPos); parentList->updateWidget(); } },
+		{ RadialMenuConfig::ITEM_ALT_SS, townLowerPos > -1, "altDownBottom", "vcmi.radialWheel.moveBottom", [this, towns]()
+		{
+			for (int i = townIndex; i < towns.size() - 1; i++)
+				LOCPLINT->localState->swapOwnedTowns(i, i + 1);
+			parentList->updateWidget();
+		} },
+	};
+
+	GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements, true);
 }
 
 std::string CTownList::CTownItem::getHoverText()
 {
-	return town->getObjectName();
+	return LOCPLINT->localState->getOwnedTowns()[townIndex]->getObjectName();
 }
 
 CTownList::CTownList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount)
@@ -390,20 +474,12 @@ void CTownList::updateWidget()
 
 	for (size_t i = 0; i < towns.size(); ++i)
 	{
-		auto item =  std::dynamic_pointer_cast<CTownItem>(listBox->getItem(i));
+		auto item = std::dynamic_pointer_cast<CTownItem>(listBox->getItem(i));
 
 		if (!item)
 			continue;
 
-		if (item->town == towns[i])
-		{
-			item->update();
-		}
-		else
-		{
-			listBox->reset();
-			break;
-		}
+		listBox->reset();
 	}
 
 	if (LOCPLINT->localState->getCurrentTown())

+ 5 - 1
client/adventureMap/CList.h

@@ -117,6 +117,7 @@ class CHeroList	: public CList
 		std::shared_ptr<CAnimImage> movement;
 		std::shared_ptr<CAnimImage> mana;
 		std::shared_ptr<CAnimImage> portrait;
+		CHeroList *parentList;
 	public:
 		const CGHeroInstance * const hero;
 
@@ -127,6 +128,7 @@ class CHeroList	: public CList
 		void select(bool on) override;
 		void open() override;
 		void showTooltip() override;
+		void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
 		std::string getHoverText() override;
 	};
 
@@ -150,8 +152,9 @@ class CTownList	: public CList
 	class CTownItem : public CListItem
 	{
 		std::shared_ptr<CAnimImage> picture;
+		CTownList *parentList;
 	public:
-		const CGTownInstance * const town;
+		int townIndex;
 
 		CTownItem(CTownList *parent, const CGTownInstance * town);
 
@@ -160,6 +163,7 @@ class CTownList	: public CList
 		void select(bool on) override;
 		void open() override;
 		void showTooltip() override;
+		void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
 		std::string getHoverText() override;
 	};
 

+ 8 - 7
client/widgets/RadialMenu.cpp

@@ -21,14 +21,14 @@
 
 #include "../../lib/CGeneralTextHandler.h"
 
-RadialMenuItem::RadialMenuItem(const std::string & imageName, const std::string & hoverText, const std::function<void()> & callback)
+RadialMenuItem::RadialMenuItem(const std::string & imageName, const std::string & hoverText, const std::function<void()> & callback, bool alternativeLayout)
 	: callback(callback)
 	, hoverText(hoverText)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
 
-	inactiveImage = std::make_shared<CPicture>(ImagePath::builtin("radialMenu/itemInactive"), Point(0, 0));
-	selectedImage = std::make_shared<CPicture>(ImagePath::builtin("radialMenu/itemEmpty"), Point(0, 0));
+	inactiveImage = std::make_shared<CPicture>(ImagePath::builtin(alternativeLayout ? "radialMenu/itemInactiveAlt" : "radialMenu/itemInactive"), Point(0, 0));
+	selectedImage = std::make_shared<CPicture>(ImagePath::builtin(alternativeLayout ? "radialMenu/itemEmptyAlt" : "radialMenu/itemEmpty"), Point(0, 0));
 
 	iconImage = std::make_shared<CPicture>(ImagePath::builtin("radialMenu/" + imageName), Point(0, 0));
 
@@ -42,13 +42,13 @@ void RadialMenuItem::setSelected(bool selected)
 	inactiveImage->setEnabled(!selected);
 }
 
-RadialMenu::RadialMenu(const Point & positionToCenter, const std::vector<RadialMenuConfig> & menuConfig):
-	centerPosition(positionToCenter)
+RadialMenu::RadialMenu(const Point & positionToCenter, const std::vector<RadialMenuConfig> & menuConfig, bool alternativeLayout):
+	centerPosition(positionToCenter), alternativeLayout(alternativeLayout)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
 	pos += positionToCenter;
 
-	Point itemSize = Point(70, 80);
+	Point itemSize = alternativeLayout ? Point(80, 70) : Point(70, 80);
 	moveBy(-itemSize / 2);
 	pos.w = itemSize.x;
 	pos.h = itemSize.y;
@@ -60,6 +60,7 @@ RadialMenu::RadialMenu(const Point & positionToCenter, const std::vector<RadialM
 
 	for(const auto & item : items)
 		pos = pos.include(item->pos);
+	pos = pos.include(statusBar->pos);
 
 	fitToScreen(10);
 
@@ -71,7 +72,7 @@ void RadialMenu::addItem(const Point & offset, bool enabled, const std::string &
 	if (!enabled)
 		return;
 
-	auto item = std::make_shared<RadialMenuItem>(path, CGI->generaltexth->translate(hoverText), callback);
+	auto item = std::make_shared<RadialMenuItem>(path, CGI->generaltexth->translate(hoverText), callback, alternativeLayout);
 
 	item->moveBy(offset);
 

+ 11 - 2
client/widgets/RadialMenu.h

@@ -26,6 +26,13 @@ struct RadialMenuConfig
 	static constexpr Point ITEM_SW = Point(-40, +70);
 	static constexpr Point ITEM_SE = Point(+40, +70);
 
+	static constexpr Point ITEM_ALT_NN = Point(0,   -80);
+	static constexpr Point ITEM_ALT_SS = Point(0,   +80);
+	static constexpr Point ITEM_ALT_NW = Point(-70, -40);
+	static constexpr Point ITEM_ALT_SW = Point(-70, +40);
+	static constexpr Point ITEM_ALT_NE = Point(+70, -40);
+	static constexpr Point ITEM_ALT_SE = Point(+70, +40);
+
 	Point itemPosition;
 	bool enabled;
 	std::string imageName;
@@ -44,7 +51,7 @@ class RadialMenuItem : public CIntObject
 	std::string hoverText;
 
 public:
-	RadialMenuItem(const std::string & imageName, const std::string & hoverText, const std::function<void()> & callback);
+	RadialMenuItem(const std::string & imageName, const std::string & hoverText, const std::function<void()> & callback, bool alternativeLayout);
 
 	void setSelected(bool selected);
 };
@@ -60,8 +67,10 @@ class RadialMenu : public CIntObject
 	void addItem(const Point & offset, bool enabled, const std::string & path, const std::string & hoverText, const std::function<void()> & callback);
 
 	std::shared_ptr<RadialMenuItem> findNearestItem(const Point & cursorPosition) const;
+
+	bool alternativeLayout;
 public:
-	RadialMenu(const Point & positionToCenter, const std::vector<RadialMenuConfig> & menuConfig);
+	RadialMenu(const Point & positionToCenter, const std::vector<RadialMenuConfig> & menuConfig, bool alternativeLayout = false);
 
 	void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
 	void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;