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

Merge pull request #88 from ArseniyShestakov/improvedQuestLog

Awesome, thanks!
DjWarmonger пре 10 година
родитељ
комит
200bcd7da6

BIN
Mods/vcmi/Data/questDialog.png


+ 2 - 2
client/widgets/CComponent.cpp

@@ -34,7 +34,7 @@ CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize):
 	init(Type, Subtype, Val, imageSize);
 }
 
-CComponent::CComponent(const Component &c):
+CComponent::CComponent(const Component &c, ESize imageSize):
 	image(nullptr),
 	perDay(false)
 {
@@ -43,7 +43,7 @@ CComponent::CComponent(const Component &c):
 	if(c.id == Component::RESOURCE && c.when==-1)
 		perDay = true;
 
-	init((Etype)c.id,c.subtype,c.val, large);
+	init((Etype)c.id,c.subtype,c.val, imageSize);
 }
 
 void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize)

+ 1 - 1
client/widgets/CComponent.h

@@ -55,7 +55,7 @@ public:
 	std::string getSubtitle();
 
 	CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large);//c-tor
-	CComponent(const Component &c); //c-tor
+	CComponent(const Component &c, ESize imageSize=large); //c-tor
 
 	void clickRight(tribool down, bool previousState); //call-in
 };

+ 1 - 0
client/windows/CAdvmapInterface.cpp

@@ -560,6 +560,7 @@ void CAdvMapInt::restoreState()
 	changeMode(EAdvMapMode::NORMAL);
 
 	underground->block(!CGI->mh->map->twoLevel);
+	questlog->block(!CGI->mh->map->quests.size());
 	worldViewUnderground->block(!CGI->mh->map->twoLevel);
 	
 	terrain.currentPath = nullptr; // invalidate previously visible path after game reload

+ 1 - 0
client/windows/CHeroWindow.cpp

@@ -302,6 +302,7 @@ void CHeroWindow::dismissCurrent()
 
 void CHeroWindow::questlog()
 {
+	LOCPLINT->showQuestLog();
 }
 
 void CHeroWindow::commanderWindow()

+ 149 - 35
client/windows/CQuestLog.cpp

@@ -11,6 +11,7 @@
 
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
+#include "../widgets/CComponent.h"
 
 #include "../../CCallback.h"
 #include "../../lib/CArtHandler.h"
@@ -71,23 +72,21 @@ CMinimap (position),
 void CQuestMinimap::addQuestMarks (const QuestInfo * q)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	for (auto icon : icons)
-		delete icon;
 	icons.clear();
 
 	int3 tile;
 	if (q->obj)
-	{
 		tile = q->obj->pos;
-	}
 	else
-	{
 		tile = q->tile;
-	}
+
 	int x,y;
 	minimap->tileToPixels (tile, x, y);
 
-	CQuestIcon * pic = new CQuestIcon ("VwSymbol.def", 3, x, y);
+	if (level != tile.z)
+		setLevel(tile.z);
+
+	auto pic = make_shared<CQuestIcon>("VwSymbol.def", 3, x, y);
 
 	pic->moveBy (Point ( -pic->pos.w/2, -pic->pos.h/2));
 	pic->callback = std::bind (&CQuestMinimap::iconClicked, this);
@@ -117,10 +116,12 @@ void CQuestMinimap::showAll(SDL_Surface * to)
 }
 
 CQuestLog::CQuestLog (const std::vector<QuestInfo> & Quests) :
-	CWindowObject(PLAYER_COLORED | BORDERED, "questDialog.pcx"),
+	CWindowObject(PLAYER_COLORED | BORDERED, "questDialog"),
 	questIndex(0),
 	currentQuest(nullptr),
+	componentsBox(nullptr),
 	quests (Quests),
+	hideComplete(false),
 	slider(nullptr)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
@@ -129,69 +130,173 @@ CQuestLog::CQuestLog (const std::vector<QuestInfo> & Quests) :
 
 void CQuestLog::init()
 {
-	minimap = new CQuestMinimap (Rect (33, 18, 144, 144));
-	description = new CTextBox ("", Rect(221, 18, 350, 355), 1, FONT_MEDIUM, TOPLEFT, Colors::WHITE);
-	ok = new CButton(Point(533, 386), "IOKAY.DEF", CGI->generaltexth->zelp[445], boost::bind(&CQuestLog::close,this), SDLK_RETURN);
+	const JsonNode & texts = CGI->generaltexth->localizedTexts["questLog"];
 
-	if (quests.size() > QUEST_COUNT)
-		slider = new CSlider(Point(189, 184), 230, std::bind (&CQuestLog::sliderMoved, this, _1), QUEST_COUNT, quests.size(), false, CSlider::BROWN);
+	minimap = new CQuestMinimap (Rect (12, 12, 169, 169));
+	// TextBox have it's own 4 pixel padding from top at least for English. To achieve 10px from both left and top only add 6px margin
+	description = new CTextBox ("", Rect(205, 18, 385, DESCRIPTION_HEIGHT_MAX), CSlider::BROWN, FONT_MEDIUM, TOPLEFT, Colors::WHITE);
+	ok = new CButton(Point(539, 398), "IOKAY.DEF", CGI->generaltexth->zelp[445], boost::bind(&CQuestLog::close,this), SDLK_RETURN);
+	// Both button and lable are shifted to -2px by x and y to not make them actually look like they're on same line with quests list and ok button
+	hideCompleteButton = new CToggleButton(Point(10, 396), "sysopchk.def", CButton::tooltip(texts["hideComplete"]), std::bind(&CQuestLog::toggleComplete, this, _1));
+	hideCompleteLabel = new CLabel(46, 398, FONT_MEDIUM, TOPLEFT, Colors::WHITE, texts["hideComplete"]["label"].String());
+	slider = new CSlider(Point(166, 195), 191, std::bind(&CQuestLog::sliderMoved, this, _1), QUEST_COUNT, 0, false, CSlider::BROWN);
 
+	recreateLabelList();
+	recreateQuestList (0);
+}
+
+void CQuestLog::recreateLabelList()
+{
+	if (labels.size())
+		labels.clear();
+
+	bool completeMissing = true;
+	int currentLabel = 0;
 	for (int i = 0; i < quests.size(); ++i)
 	{
+		// Quests with MISSION_NONE type don't have text for them and can't be displayed
+		if (quests[i].quest->missionType == CQuest::MISSION_NONE)
+			continue;
+
+		if (quests[i].quest->progress == CQuest::COMPLETE)
+		{
+			completeMissing = false;
+			if (hideComplete)
+				continue;
+		}
+
 		MetaString text;
 		quests[i].quest->getRolloverText (text, false);
 		if (quests[i].obj)
-			text.addReplacement (quests[i].obj->getObjectName()); //get name of the object
-		CQuestLabel * label = new CQuestLabel (Rect(14, 184 + i * 24, 172,30), FONT_SMALL, TOPLEFT, Colors::WHITE, text.toString());
-		label->callback = boost::bind(&CQuestLog::selectQuest, this, i);
+		{
+			if (auto seersHut = dynamic_cast<const CGSeerHut *>(quests[i].obj))
+			{
+				MetaString toSeer;
+				toSeer << VLC->generaltexth->allTexts[347];
+				toSeer.addReplacement(seersHut->seerName);
+				text.addReplacement(toSeer.toString());
+			}
+			else
+				text.addReplacement(quests[i].obj->getObjectName()); //get name of the object
+		}
+		auto label = make_shared<CQuestLabel>(Rect(13, 195, 149,31), FONT_SMALL, TOPLEFT, Colors::WHITE, text.toString());
+		label->disable();
+
+		label->callback = boost::bind(&CQuestLog::selectQuest, this, i, currentLabel);
 		labels.push_back(label);
+
+		// Select latest active quest
+		if (quests[i].quest->progress != CQuest::COMPLETE)
+			selectQuest(i, currentLabel);
+
+		currentLabel = labels.size();
 	}
 
-	recreateQuestList (0);
+	if (completeMissing) // We can't use block(completeMissing) because if false button state reset to NORMAL
+		hideCompleteButton->block(true);
+
+	slider->setAmount(currentLabel);
+	if (currentLabel > QUEST_COUNT)
+	{
+		slider->block(false);
+		slider->moveToMax();
+	}
+	else
+		slider->block(true);
 }
 
 void CQuestLog::showAll(SDL_Surface * to)
 {
 	CWindowObject::showAll (to);
-	for (auto label : labels)
-	{
-		label->show(to); //shows only if active
-	}
 	if (labels.size() && labels[questIndex]->active)
 	{
-		CSDL_Ext::drawBorder(to, Rect::around(labels[questIndex]->pos), int3(Colors::METALLIC_GOLD.r, Colors::METALLIC_GOLD.g, Colors::METALLIC_GOLD.b));
+		Rect rect = Rect::around(labels[questIndex]->pos);
+		rect.x -= 2; // Adjustment needed as we want selection box on top of border in graphics
+		rect.w += 2;
+		CSDL_Ext::drawBorder(to, rect, int3(Colors::METALLIC_GOLD.r, Colors::METALLIC_GOLD.g, Colors::METALLIC_GOLD.b));
 	}
-	description->show(to);
-	minimap->show(to);
 }
 
 void CQuestLog::recreateQuestList (int newpos)
 {
 	for (int i = 0; i < labels.size(); ++i)
 	{
-		labels[i]->pos = Rect (pos.x + 14, pos.y + 192 + (i-newpos) * 25, 173, 23);
+		labels[i]->pos = Rect (pos.x + 14, pos.y + 195 + (i-newpos) * 32, 151, 31);
 		if (i >= newpos && i < newpos + QUEST_COUNT)
-		{
-			labels[i]->activate();
-		}
+			labels[i]->enable();
 		else
-		{
-			labels[i]->deactivate();
-		}
+			labels[i]->disable();
 	}
 	minimap->update();
 }
 
-void CQuestLog::selectQuest (int which)
+void CQuestLog::selectQuest (int which, int labelId)
 {
-	questIndex = which;
+	questIndex = labelId;
 	currentQuest = &quests[which];
 	minimap->currentQuest = currentQuest;
 
 	MetaString text;
-	std::vector<Component> components; //TODO: display them
-	currentQuest->quest->getVisitText (text, components , currentQuest->quest->isCustomFirst, true);
+	std::vector<Component> components;
+	currentQuest->quest->getVisitText (text, components, currentQuest->quest->isCustomFirst, true);
+	if (description->slider)
+		description->slider->moveToMin(); // scroll text to start position
 	description->setText (text.toString()); //TODO: use special log entry text
+
+	vstd::clear_pointer(componentsBox);
+	int componentsSize = components.size();
+	int descriptionHeight = DESCRIPTION_HEIGHT_MAX;
+	if (componentsSize)
+	{
+		descriptionHeight -= 15;
+		CComponent::ESize imageSize = CComponent::large;
+		switch (currentQuest->quest->missionType)
+		{
+			case CQuest::MISSION_ARMY:
+			{
+				if (componentsSize > 4)
+					descriptionHeight -= 195;
+				else
+					descriptionHeight -= 100;
+
+				break;
+			}
+			case CQuest::MISSION_ART:
+			{
+				if (componentsSize > 4)
+					descriptionHeight -= 190;
+				else
+					descriptionHeight -= 90;
+
+				break;
+			}
+			case CQuest::MISSION_PRIMARY_STAT:
+			case CQuest::MISSION_RESOURCES:
+			{
+				if (componentsSize > 4)
+				{
+					imageSize = CComponent::small; // Only small icons can be used for resources as 4+ icons take too much space
+					descriptionHeight -= 140;
+				}
+				else
+					descriptionHeight -= 125;
+
+				break;
+			}
+			default:
+				descriptionHeight -= 115;
+				break;
+		}
+
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		std::vector<CComponent *> comps;
+		for (auto & component : components)
+			comps.push_back(new CComponent(component, imageSize));
+
+		componentsBox = new CComponentBox(comps, Rect(202, 20+descriptionHeight+15, 391, DESCRIPTION_HEIGHT_MAX-(20+descriptionHeight)));
+	}
+	description->resize(Point(385, descriptionHeight));
+
 	minimap->update();
 	redraw();
 }
@@ -201,3 +306,12 @@ void CQuestLog::sliderMoved (int newpos)
 	recreateQuestList (newpos); //move components
 	redraw();
 }
+
+void CQuestLog::toggleComplete(bool on)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	hideComplete = on;
+	recreateLabelList();
+	recreateQuestList(0);
+	redraw();
+}

+ 13 - 6
client/windows/CQuestLog.h

@@ -19,8 +19,9 @@
 class CCreature;
 class CStackInstance;
 class CButton;
+class CToggleButton;
 class CGHeroInstance;
-class CComponent;
+class CComponentBox;
 class LRClickableAreaWText;
 class CButton;
 class CPicture;
@@ -30,7 +31,8 @@ class CSlider;
 class CLabel;
 struct QuestInfo;
 
-const int QUEST_COUNT = 9;
+const int QUEST_COUNT = 6;
+const int DESCRIPTION_HEIGHT_MAX = 355;
 
 class CQuestLabel : public LRClickableAreaWText, public CMultiLineLabel
 {
@@ -56,7 +58,7 @@ public:
 
 class CQuestMinimap : public CMinimap
 {
-	std::vector <CQuestIcon *> icons;
+	std::vector <shared_ptr<CQuestIcon>> icons;
 
 	void clickLeft(tribool down, bool previousState){}; //minimap ignores clicking on its surface
 	void iconClicked();
@@ -69,7 +71,6 @@ public:
 	CQuestMinimap (const Rect & position);
 	//should be called to invalidate whole map - different player or level
 	void update();
-	void setLevel(int level);
 	void addQuestMarks (const QuestInfo * q);
 
 	void showAll(SDL_Surface * to);
@@ -79,9 +80,13 @@ class CQuestLog : public CWindowObject
 {
 	int questIndex;
 	const QuestInfo * currentQuest;
+	CComponentBox * componentsBox;
+	bool hideComplete;
+	CToggleButton * hideCompleteButton;
+	CLabel * hideCompleteLabel;
 
 	const std::vector<QuestInfo> quests;
-	std::vector<CQuestLabel *> labels;
+	std::vector <shared_ptr<CQuestLabel>> labels;
 	CTextBox * description;
 	CQuestMinimap * minimap;
 	CSlider * slider; //scrolls quests
@@ -94,10 +99,12 @@ public:
 
 	~CQuestLog(){};
 
-	void selectQuest (int which);
+	void selectQuest (int which, int labelId);
 	void updateMinimap (int which){};
 	void printDescription (int which){};
 	void sliderMoved (int newpos);
+	void recreateLabelList();
 	void recreateQuestList (int pos);
+	void toggleComplete(bool on);
 	void showAll (SDL_Surface * to);
 };

+ 1 - 0
client/windows/GUIClasses.cpp

@@ -851,6 +851,7 @@ void CTavernWindow::HeroPortrait::hover( bool on )
 void CExchangeWindow::questlog(int whichHero)
 {
 	CCS->curh->dragAndDropCursor(nullptr);
+	LOCPLINT->showQuestLog();
 }
 
 void CExchangeWindow::prepareBackground()

+ 8 - 0
config/translate.json

@@ -74,5 +74,13 @@
 			"label" : "Give back artifact",
 			"help" : "Use this button to return stack artifact back into hero backpack"
 		}
+	},
+	"questLog" :
+	{
+		"hideComplete" :
+		{
+			"label" : "Hide complete quests",
+			"help" : "Hide all quests that already completed"
+		}
 	}
 }

+ 4 - 1
lib/mapObjects/CQuest.cpp

@@ -223,6 +223,9 @@ void CQuest::getVisitText (MetaString &iwText, std::vector<Component> &component
 
 void CQuest::getRolloverText (MetaString &ms, bool onHover) const
 {
+	// Quests with MISSION_NONE type don't have a text for them
+	assert(missionType != MISSION_NONE);
+
 	if (onHover)
 		ms << "\n\n";
 
@@ -231,7 +234,7 @@ void CQuest::getRolloverText (MetaString &ms, bool onHover) const
 	switch (missionType)
 	{
 		case MISSION_LEVEL:
-			ms.addReplacement(m13489val);
+			ms.addReplacement(boost::lexical_cast<std::string>(m13489val));
 			break;
 		case MISSION_PRIMARY_STAT:
 			{