浏览代码

Artifact Merchant: selling artifacts. Messy, but completes H3 town structures functionalities.

BTW updating screen after closing marketplace is broken.
Michał W. Urbańczyk 14 年之前
父节点
当前提交
d092eaf9d1

+ 1 - 1
AI/StupidAI/StupidAI.cpp

@@ -6,7 +6,7 @@
 #include <boost/bind.hpp>
 #include "../../lib/CCreatureHandler.h"
 #include <algorithm>
-#include <boost/thread.hpp>
+//#include <boost/thread.hpp>
 
 IBattleCallback * cbc;
 

+ 8 - 0
client/CPlayerInterface.cpp

@@ -2171,6 +2171,14 @@ void CPlayerInterface::artifactPut(const ArtifactLocation &al)
 void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	BOOST_FOREACH(IShowActivable *isa, GH.listInt)
+	{
+		if(isa->type & IShowActivable::WITH_ARTIFACTS)
+		{
+			BOOST_FOREACH(CArtifactsOfHero *aoh, (dynamic_cast<CWindowWithArtifacts*>(isa))->artSets)
+				aoh->artifactRemoved(al);
+		}
+	}
 }
 
 void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)

+ 194 - 68
client/GUIClasses.cpp

@@ -2586,6 +2586,7 @@ void CTradeWindow::CTradeableItem::showAll(SDL_Surface * to)
 		posToSubCenter = Point(31, 76);
 		break;
 	case ARTIFACT_PLACEHOLDER:
+	case ARTIFACT_INSTANCE:
 		posToSubCenter = Point(19, 55);
 		if(downSelection)
 			posToSubCenter.y += 8;
@@ -2662,6 +2663,7 @@ SDL_Surface * CTradeWindow::CTradeableItem::getSurface()
 		return graphics->flags->ourImages[id].bitmap;
 	case ARTIFACT_TYPE:
 	case ARTIFACT_PLACEHOLDER:
+	case ARTIFACT_INSTANCE:
 		return id >= 0 ? graphics->artDefs->ourImages[id].bitmap : NULL;
 	case CREATURE:
 		return graphics->bigImgs[id];
@@ -2741,6 +2743,7 @@ std::string CTradeWindow::CTradeableItem::getName(int number /*= -1*/) const
 		else
 			return CGI->creh->creatures[id]->namePl;
 	case ARTIFACT_TYPE:
+	case ARTIFACT_INSTANCE:
 		return CGI->arth->artifacts[id]->Name();
 	}
 	assert(0);
@@ -2759,20 +2762,15 @@ const CArtifactInstance * CTradeWindow::CTradeableItem::getArtInstance() const
 	}
 }
 
-// const CArtifact * CTradeWindow::CTradeableItem::getArt() const
-// {
-// 	return NULL;
-// }
-// 
-// void CTradeWindow::CTradeableItem::setArtInstance(const CArtifactInstance *art) const
-// {
-// 
-// }
-// 
-// void CTradeWindow::CTradeableItem::setArt(const CArtifact *artT) const
-// {
-// 
-// }
+void CTradeWindow::CTradeableItem::setArtInstance(const CArtifactInstance *art)
+{
+	assert(type == ARTIFACT_PLACEHOLDER || type == ARTIFACT_INSTANCE);
+	hlp = art;
+	if(art)
+		id = art->artType->id;
+	else
+		id = -1;
+}
 
 CTradeWindow::CTradeWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode Mode)
 	: market(Market), hero(Hero),  arts(NULL), hLeft(NULL), hRight(NULL), readyToTrade(false)
@@ -2802,6 +2800,10 @@ void CTradeWindow::initTypes()
 		itemsType[1] = RESOURCE;
 		itemsType[0] = ARTIFACT_TYPE;
 		break;
+	case ARTIFACT_RESOURCE:
+		itemsType[1] = ARTIFACT_INSTANCE;
+		itemsType[0] = RESOURCE;
+		break;
 	case CREATURE_EXP:
 		itemsType[1] = CREATURE;
 		itemsType[0] = CREATURE_PLACEHOLDER;
@@ -2815,6 +2817,40 @@ void CTradeWindow::initTypes()
 
 void CTradeWindow::initItems(bool Left)
 {
+	if(Left && (itemsType[1] == ARTIFACT_TYPE || itemsType[1] == ARTIFACT_INSTANCE))
+	{
+		int xOffset = 0, yOffset = 0;
+		if(mode == ARTIFACT_RESOURCE)
+		{
+			xOffset = -361;
+			yOffset = +46;
+
+			CTradeableItem *hlp = new CTradeableItem(itemsType[Left], -1, 1, 0);
+			hlp->recActions &= ~(UPDATE | SHOWALL);
+			hlp->pos += Rect(137, 469, 42, 42);
+			items[Left].push_back(hlp);
+		}
+		else //ARTIFACT_EXP
+		{
+			xOffset = -363;
+			yOffset = -12;
+		}
+
+		BLOCK_CAPTURING;
+		arts = new CArtifactsOfHero(Point(pos.x+xOffset, pos.y+yOffset));
+		arts->commonInfo = new CArtifactsOfHero::SCommonPart;
+		arts->commonInfo->participants.insert(arts);
+		arts->recActions = 255;
+		arts->setHero(hero);
+		arts->allowedAssembling = false;
+		addChild(arts);
+		artSets.push_back(arts);
+
+		if(mode == ARTIFACT_RESOURCE)
+			arts->highlightModeCallback = boost::bind(&CTradeWindow::artifactSelected, this, _1);
+		return;
+	}
+
 	std::vector<int> *ids = getItemsIds(Left);
 	std::vector<Rect> pos;
 	int amount = -1;
@@ -2975,7 +3011,7 @@ void CTradeWindow::showAll(SDL_Surface * to)
 
 	if(hRight)
 		CSDL_Ext::drawBorder(to,hRight->pos.x-1,hRight->pos.y-1,hRight->pos.w+2,hRight->pos.h+2,int3(255,231,148));
-	if(hLeft)
+	if(hLeft && hLeft->type != ARTIFACT_INSTANCE)
 		CSDL_Ext::drawBorder(to,hLeft->pos.x-1,hLeft->pos.y-1,hLeft->pos.w+2,hLeft->pos.h+2,int3(255,231,148));
 
 	if(readyToTrade)
@@ -3034,6 +3070,18 @@ void CTradeWindow::setMode(EMarketMode Mode)
 	GH.pushInt(nwindow);
 }
 
+void CTradeWindow::artifactSelected(CArtPlace *slot)
+{
+	assert(mode == ARTIFACT_RESOURCE);
+	items[1][0]->setArtInstance(slot->ourArt);
+	if(slot->ourArt)
+		hLeft = items[1][0];
+	else
+		hLeft = NULL;
+
+	selectionChanged(true);
+}
+
 CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode Mode)
 	: CTradeWindow(Market, Hero, Mode)
 {
@@ -3063,6 +3111,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 
 	case ARTIFACT_RESOURCE:
 		bgName = "TPMRKASS.bmp";
+		sliderNeeded = false;
 		break;
 	}
 
@@ -3121,7 +3170,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 	{
 		slider = NULL;
 		max = NULL;
-		deal->pos.x -= 38;
+		deal->moveBy(Point(-30, 0));
 	}
 
 	Rect traderTextRect;
@@ -3137,6 +3186,9 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 	case CREATURE_RESOURCE: 
 		printAtMiddle(boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name), 152, 102, FONT_SMALL, zwykly, *bg); //%s's Creatures
 		break;
+	case ARTIFACT_RESOURCE: 
+		printAtMiddle(boost::str(boost::format(CGI->generaltexth->allTexts[271]) % hero->name), 152, 57, FONT_SMALL, zwykly, *bg); //%s's Artifacts
+		break;
 	}
 
 	//right side
@@ -3145,6 +3197,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 	case RESOURCE_RESOURCE:
 	case CREATURE_RESOURCE:
 	case RESOURCE_ARTIFACT:
+	case ARTIFACT_RESOURCE:
 		printAtMiddle(CGI->generaltexth->allTexts[168],445,148,FONT_SMALL,zwykly,*bg); //available for trade
 		traderTextRect = Rect(316, 48, 260, 75);
 		break;
@@ -3155,17 +3208,18 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 	}
 
 	traderText = new CTextBox("", traderTextRect, 0, FONT_SMALL, CENTER);
+	int specialOffset = mode == ARTIFACT_RESOURCE ? 35 : 0; //in selling artifacts mode we need to move res-res and art-res buttons down
 
 	if(printButtonFor(RESOURCE_PLAYER))
 		new AdventureMapButton(CGI->generaltexth->zelp[612],boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF");
 	if(printButtonFor(RESOURCE_RESOURCE))
-		new AdventureMapButton(CGI->generaltexth->zelp[605],boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_RESOURCE), 516, 450,"TPMRKBU5.DEF");
+		new AdventureMapButton(CGI->generaltexth->zelp[605],boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_RESOURCE), 516, 450 + specialOffset,"TPMRKBU5.DEF");
 	if(printButtonFor(CREATURE_RESOURCE))
 		new AdventureMapButton(CGI->generaltexth->zelp[599],boost::bind(&CMarketplaceWindow::setMode,this, CREATURE_RESOURCE), 516, 485,"TPMRKBU4.DEF"); //was y=450, changed to not overlap res-res in some conditions
 	if(printButtonFor(RESOURCE_ARTIFACT))
-		new AdventureMapButton(CGI->generaltexth->zelp[598],boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_ARTIFACT), 18, 450,"TPMRKBU2.DEF");
-	if(printButtonFor(ARTIFACT_RESOURCE))																				//unblock when support for art-res is ready
-		(new AdventureMapButton(CGI->generaltexth->zelp[613],boost::bind(&CMarketplaceWindow::setMode,this, ARTIFACT_RESOURCE), 18, 485,"TPMRKBU3.DEF"))->block(true); //was y=450, changed to not overlap res-art in some conditions
+		new AdventureMapButton(CGI->generaltexth->zelp[598],boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_ARTIFACT), 18, 450 + specialOffset,"TPMRKBU2.DEF");
+	if(printButtonFor(ARTIFACT_RESOURCE))																				
+		new AdventureMapButton(CGI->generaltexth->zelp[613],boost::bind(&CMarketplaceWindow::setMode,this, ARTIFACT_RESOURCE), 18, 485,"TPMRKBU3.DEF"); //was y=450, changed to not overlap res-art in some conditions
 
 	updateTraderText();
 }
@@ -3205,10 +3259,12 @@ void CMarketplaceWindow::makeDeal()
 	int leftIdToSend = -1;
 	if(mode == CREATURE_RESOURCE)
 		leftIdToSend = hLeft->serial;
+	else if(mode == ARTIFACT_RESOURCE)
+		leftIdToSend = hLeft->getArtInstance()->id;
 	else
 		leftIdToSend = hLeft->id;
 
-	if(mode != RESOURCE_ARTIFACT)
+	if(slider)
 	{
 		LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero);
 		slider->moveTo(0);
@@ -3231,31 +3287,39 @@ void CMarketplaceWindow::sliderMoved( int to )
 
 void CMarketplaceWindow::selectionChanged(bool side)
 {
-	readyToTrade = (hLeft && hRight && (hLeft->id!= hRight->id || mode != RESOURCE_RESOURCE));
+	readyToTrade = hLeft && hRight;
+	if(mode == RESOURCE_RESOURCE)
+		readyToTrade = readyToTrade && (hLeft->id != hRight->id); //for resource trade, two DIFFERENT resources must be selected 
 
+ 	if(mode == ARTIFACT_RESOURCE && !hLeft) 
+		arts->unmarkSlots(false);
+ 
 	if(readyToTrade)
 	{
-		int newAmount = -1;
-		market->getOffer(hLeft->id, hRight->id, r1, r2, mode);
-
-		if(itemsType[1] == RESOURCE)
-			newAmount = LOCPLINT->cb->getResourceAmount(hLeft->id);
-		else if(itemsType[1] ==  CREATURE)
-			newAmount = hero->getStackCount(hLeft->serial) - (hero->Slots().size() == 1  &&  hero->needsLastStack());
-		else
-			assert(0);
+		int soldItemId = hLeft->id;
+		market->getOffer(soldItemId, hRight->id, r1, r2, mode);
 
 		if(slider)
 		{
+			int newAmount = -1;
+			if(itemsType[1] == RESOURCE)
+				newAmount = LOCPLINT->cb->getResourceAmount(soldItemId);
+			else if(itemsType[1] ==  CREATURE)
+				newAmount = hero->getStackCount(hLeft->serial) - (hero->Slots().size() == 1  &&  hero->needsLastStack());
+			else
+				assert(0);
+
 			slider->setAmount(newAmount / r1);
 			slider->moveTo(0);
 			max->block(false);
 			deal->block(false);
 		}
-		else
+		else if(itemsType[1] == RESOURCE) //buying -> check if we can afford transaction
 		{
-			deal->block(LOCPLINT->cb->getResourceAmount(hLeft->id) < r1);
+			deal->block(LOCPLINT->cb->getResourceAmount(soldItemId) < r1);
 		}
+		else
+			deal->block(false);
 	}
 	else
 	{
@@ -3313,19 +3377,30 @@ std::string CMarketplaceWindow::selectionSubtitle(bool Left) const
 {
 	if(Left)
 	{
-		assert(itemsType[1] == CREATURE || itemsType[1] == RESOURCE);
-		int val = slider 
-			? slider->value * r1 
-			: (((deal->isBlocked())) ? 0 : r1);
+		switch(itemsType[1])
+		{
+		case RESOURCE:
+		case CREATURE:
+			{
+				int val = slider 
+					? slider->value * r1 
+					: (((deal->isBlocked())) ? 0 : r1);
 
-		return boost::lexical_cast<std::string>(val);
+				return boost::lexical_cast<std::string>(val);
+			}
+		case ARTIFACT_INSTANCE:
+			return ((deal->isBlocked()) ? "0" : "1");
+		}
 	}
 	else
 	{
 		switch(itemsType[0])
 		{
 		case RESOURCE:
-			return boost::lexical_cast<std::string>( slider->value * r2 );
+			if(slider)
+				return boost::lexical_cast<std::string>( slider->value * r2 );
+			else
+				return boost::lexical_cast<std::string>(r2);
 		case ARTIFACT_TYPE:
 			return ((deal->isBlocked()) ? "0" : "1");
 		case PLAYER:
@@ -3346,6 +3421,8 @@ Point CMarketplaceWindow::selectionOffset(bool Left) const
 			return Point(122, 446);
 		case CREATURE:
 			return Point(128, 450);
+		case ARTIFACT_INSTANCE:
+			return Point(134, 466);
 		}
 	}
 	else
@@ -3353,7 +3430,10 @@ Point CMarketplaceWindow::selectionOffset(bool Left) const
 		switch(itemsType[0])
 		{
 		case RESOURCE:
-			return Point(410, 446);
+			if(mode == ARTIFACT_RESOURCE)
+				return Point(410, 469);
+			else
+				return Point(410, 446);
 		case ARTIFACT_TYPE:
 			return Point(425, 447);
 		case PLAYER:
@@ -3437,6 +3517,11 @@ void CMarketplaceWindow::updateTraderText()
 			//I can offer you %d %s of %s for %d %s.
 			traderText->setTxt(boost::str(boost::format(CGI->generaltexth->allTexts[269]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % hLeft->getName(r1)));
 		}
+		else if(mode == ARTIFACT_RESOURCE)
+		{
+			//I can offer you %d %s of %s for your %s.
+			traderText->setTxt(boost::str(boost::format(CGI->generaltexth->allTexts[268]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % hLeft->getName(r1)));
+		}
 		return;
 	}
 
@@ -3462,7 +3547,7 @@ CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*=
 	:CTradeWindow(Market, Hero, Mode)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	bg = new CPicture(Mode == CREATURE_EXP ? "ALTARMON.bmp" : "ALTARART.bmp");
+	bg = new CPicture(Mode == CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp");
 	bg->colorizeAndConvert(LOCPLINT->playerID);
 	pos = bg->center();
 
@@ -3498,19 +3583,8 @@ CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*=
 
 		slider = NULL;
 		max = NULL;
-
-		{
-			BLOCK_CAPTURING;
-			arts = new CArtifactsOfHero(Point(pos.x-363, pos.y-12));
-			arts->commonInfo = new CArtifactsOfHero::SCommonPart;
-			arts->commonInfo->participants.insert(arts);
-			arts->setHero(Hero);
-			arts->recActions = 255;
-			arts->allowedAssembling = false;
-			addChild(arts);
-			artSets.push_back(arts);
-		}
-
+		
+		initItems(true);
 		initItems(false);
 	}
 
@@ -3843,9 +3917,8 @@ bool CAltarWindow::putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance
 	market->getOffer(artID, 0, dmp, val, ARTIFACT_EXP);
 
 	arts->artifactsOnAltar.insert(art);
-	altarSlot->id = artID;
+	altarSlot->setArtInstance(art);
 	altarSlot->subtitle = boost::lexical_cast<std::string>(val);
-	altarSlot->hlp = art;
 
 	deal->block(false);
 	return true;
@@ -4532,6 +4605,24 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 		srcInBackpack = ourOwner->commonInfo->src.slotID >= Arts::BACKPACK_START,
 		srcInSameHero = ourOwner->commonInfo->src.AOH == ourOwner;
 	
+	if(ourOwner->highlightModeCallback && ourArt)
+	{
+		if(down)
+		{
+			if(ourArt->artType->id < 7) //War Machine or Spellbook
+			{
+				LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); //This item can't be traded.
+			}
+			else
+			{
+				ourOwner->unmarkSlots(false);
+				marked = true;
+				ourOwner->highlightModeCallback(this);
+			}
+		}
+		return;
+	}
+
 	// If clicked on spellbook, open it only if no artifact is held at the moment.
 	if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH)
 	{
@@ -5077,6 +5168,19 @@ void CArtifactsOfHero::scrollBackpack(int dir)
 	for( ; s - omitedSoFar < 5; s++)
 		eraseSlotData(backpack[s-omitedSoFar], 19 + s);
 
+	//in artifact merchant selling artifacts we may have highlight on one of backpack artifacts -> market needs update, cause artifact under highlight changed
+	if(highlightModeCallback)
+	{
+		for(int i = 0; i < 5; i++)
+		{
+			if(backpack[i]->marked)
+			{
+				highlightModeCallback(backpack[i]);
+				break;
+			}
+		}
+	}
+
 	//blocking scrolling if there is not enough artifacts to scroll
 	bool scrollingPossible = artsInBackpack - omitedSoFar > backpack.size();
 	leftArtRoll->block(!scrollingPossible);
@@ -5106,16 +5210,21 @@ void CArtifactsOfHero::markPossibleSlots(const CArtifactInstance* art)
 void CArtifactsOfHero::unmarkSlots(bool withRedraw /*= true*/)
 {
 	if(commonInfo)
-	{
 		BOOST_FOREACH(CArtifactsOfHero *aoh, commonInfo->participants)
-			BOOST_FOREACH(CArtPlace *place, aoh->artWorn)
-			place->marked = false;
-	}
+			aoh->unmarkLocalSlots(false);
 	else
-	{
-		BOOST_FOREACH(CArtPlace *place, artWorn)
-			place->marked = false;
-	}
+		unmarkLocalSlots(false);\
+
+	if(withRedraw)
+		safeRedraw();
+}
+
+void CArtifactsOfHero::unmarkLocalSlots(bool withRedraw /*= true*/)
+{
+	BOOST_FOREACH(CArtPlace *place, artWorn)
+		place->marked = false;
+	BOOST_FOREACH(CArtPlace *place, backpack)
+		place->marked = false;
 
 	if(withRedraw)
 		safeRedraw();
@@ -5154,7 +5263,7 @@ void CArtifactsOfHero::eraseSlotData (CArtPlace* artPlace, int slotID)
 }
 
 CArtifactsOfHero::CArtifactsOfHero(const Point& position, bool createCommonPart /*= false*/)
- : curHero(NULL), backpackPos(0), commonInfo(NULL), updateState(false), allowedAssembling(true)
+ : curHero(NULL), backpackPos(0), commonInfo(NULL), updateState(false), allowedAssembling(true), highlightModeCallback(0)
 {
 	if(createCommonPart)
 	{
@@ -5263,9 +5372,9 @@ void CArtifactsOfHero::realizeCurrentTransaction()
 void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
 {
 	if(src.hero == curHero && src.slot >= Arts::BACKPACK_START)
-		setSlotData(getArtPlace(src.slot), src.slot);
+		updateSlot(src.slot);
 	if(dst.hero == curHero && dst.slot >= Arts::BACKPACK_START)
-		setSlotData(getArtPlace(dst.slot), dst.slot);
+		updateSlot(dst.slot);
 	if(src.hero == curHero  ||  dst.hero == curHero) //we need to update all slots, artifact might be combined and affect more slots
 		updateWornSlots(false);
 
@@ -5332,6 +5441,17 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact
 		scrollBackpack(shift); //update backpack slots
 }
 
+void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al)
+{
+	if(al.hero == curHero)
+	{
+		if(al.slot < Arts::BACKPACK_START)
+			updateWornSlots(0);
+		else
+			scrollBackpack(0); //update backpack slots
+	}
+}
+
 CArtPlace * CArtifactsOfHero::getArtPlace(int slot)
 {
 	if(slot < Arts::BACKPACK_START)
@@ -5363,7 +5483,8 @@ void CArtifactsOfHero::artifactDisassembled(const ArtifactLocation &al)
 void CArtifactsOfHero::updateWornSlots(bool redrawParent /*= true*/)
 {
 	for(int i = 0; i < Arts::BACKPACK_START; i++)
-		setSlotData(getArtPlace(i), i);
+		updateSlot(i);
+
 
 	if(redrawParent)
 		updateParentWindow();
@@ -5374,6 +5495,11 @@ const CGHeroInstance * CArtifactsOfHero::getHero() const
 	return curHero;
 }
 
+void CArtifactsOfHero::updateSlot(int slotID)
+{
+	setSlotData(getArtPlace(slotID), slotID);
+}
+
 void CExchangeWindow::close()
 {
 	GH.popIntTotally(this);

+ 11 - 3
client/GUIClasses.h

@@ -71,6 +71,7 @@ class IMarket;
 class CTextBox;
 class CArtifactInstance;
 class IBonusBearer;
+class CArtPlace;
 
 extern SDL_Color tytulowy, tlo, zwykly ;
 
@@ -622,6 +623,7 @@ public:
 	};
 	class CTradeableItem : public CIntObject
 	{
+		const CArtifactInstance *hlp; //holds ptr to artifact instance id type artifact 
 	public:
 		EType type;
 		int id; 
@@ -629,10 +631,9 @@ public:
 		bool left;
 		std::string subtitle; //empty if default
 
-		const CArtifactInstance *hlp; //holds ptr to artifact instance id type artifact 
 		const CArtifactInstance *getArtInstance() const;
+		void setArtInstance(const CArtifactInstance *art);
 // 		const CArtifact *getArt() const;
-// 		void setArtInstance(const CArtifactInstance *art) const;
 // 		void setArt(const CArtifact *artT) const;
 
 		CFunctionList<void()> callback;
@@ -678,6 +679,8 @@ public:
 	void getEmptySlots(std::set<CTradeableItem *> &toRemove);
 	void setMode(EMarketMode Mode); //mode setter
 
+	void artifactSelected(CArtPlace *slot); //used when selling artifacts -> called when user clicked on artifact slot
+
 	virtual void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const = 0;
 	virtual void selectionChanged(bool side) = 0; //true == left
 	virtual Point selectionOffset(bool Left) const = 0;
@@ -1011,9 +1014,11 @@ public:
 	AdventureMapButton * leftArtRoll, * rightArtRoll;
 	bool allowedAssembling;
 	std::multiset<const CArtifactInstance*> artifactsOnAltar; //artifacts id that are technically present in backpack but in GUI are moved to the altar - they'll be ommited in backpack slots
+	boost::function<void(CArtPlace*)> highlightModeCallback; //if set, clicking on art place doesn't pick artifact but highlights the slot and calls this function
 
 	void realizeCurrentTransaction(); //calls callback with parameters stored in commonInfo
 	void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst);
+	void artifactRemoved(const ArtifactLocation &al);
 	void artifactAssembled(const ArtifactLocation &al);
 	void artifactDisassembled(const ArtifactLocation &al);
 	CArtPlace *getArtPlace(int slot);
@@ -1025,9 +1030,12 @@ public:
 
 	void safeRedraw();
 	void markPossibleSlots(const CArtifactInstance* art);
-	void unmarkSlots(bool withRedraw = true);
+	void unmarkSlots(bool withRedraw = true); //unmarks slots in all visible AOHs
+	void unmarkLocalSlots(bool withRedraw = true); //unmarks slots in that particular AOH
 	void setSlotData (CArtPlace* artPlace, int slotID);
 	void updateWornSlots (bool redrawParent = true);
+
+	void updateSlot(int i);
 	void eraseSlotData (CArtPlace* artPlace, int slotID);
 
 	CArtifactsOfHero(const Point& position, bool createCommonPart = false); //c-tor

+ 26 - 1
lib/CObjectHandler.cpp

@@ -6798,7 +6798,19 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
 			val2 = 1;
 		}
 		break;
+	case ARTIFACT_RESOURCE:
+		{
+			float effectiveness = std::min(((float)getMarketEfficiency()+3.0f) / 20.0f, 0.6f);
+			float r = VLC->arth->artifacts[id1]->price * effectiveness,
+				g = VLC->objh->resVals[id2]; 
+
+// 			if(id2 != 6) //non-gold prices are doubled
+// 				r /= 2; 
 
+			val1 = 1;
+			val2 = (r / g) + 0.5f;
+		}
+		break;
 	case CREATURE_EXP:
 		{
 			val1 = 1;
@@ -7081,6 +7093,19 @@ si32 CArtifactSet::getArtPos(const CArtifactInstance *art) const
 	return -1;
 }
 
+const CArtifactInstance * CArtifactSet::getArtByInstanceId(int artInstId) const
+{
+	for(std::map<ui16, ArtSlotInfo>::const_iterator i = artifactsWorn.begin(); i != artifactsWorn.end(); i++)
+		if(i->second.artifact->id == artInstId)
+			return i->second.artifact;
+
+	for(int i = 0; i < artifactsInBackpack.size(); i++)
+		if(artifactsInBackpack[i].artifact->id == artInstId)
+			return artifactsInBackpack[i].artifact;
+
+	return NULL;
+}
+
 bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/) const
 {
 	return getArtPos(aid, onlyWorn) != -1;
@@ -7154,4 +7179,4 @@ void CArtifactSet::eraseArtSlot(ui16 slot)
 		slot -= Arts::BACKPACK_START;
 		artifactsInBackpack.erase(artifactsInBackpack.begin() + slot);
 	}
-}
+}

+ 1 - 0
lib/CObjectHandler.h

@@ -278,6 +278,7 @@ public:
 	CArtifactInstance* getArt(ui16 pos, bool excludeLocked = true); //NULL - no artifact
 	si32 getArtPos(int aid, bool onlyWorn = true) const; //looks for equipped artifact with given ID and returns its slot ID or -1 if none(if more than one such artifact lower ID is returned)
 	si32 getArtPos(const CArtifactInstance *art) const;
+	const CArtifactInstance *getArtByInstanceId(int artInstId) const;
 	bool hasArt(ui32 aid, bool onlyWorn = false) const; //checks if hero possess artifact of given id (either in backack or worn)
 	bool isPositionFree(ui16 pos, bool onlyLockCheck = false) const;
 	si32 getArtTypeId(ui16 pos) const;

+ 2 - 0
lib/VCMI_lib.vcxproj

@@ -181,6 +181,7 @@
     <ClCompile Include="Connection.cpp" />
     <ClCompile Include="CSpellHandler.cpp" />
     <ClCompile Include="CTownHandler.cpp" />
+    <ClCompile Include="ERMInterpreter.cpp" />
     <ClCompile Include="ERMParser.cpp" />
     <ClCompile Include="HeroBonus.cpp" />
     <ClCompile Include="IGameCallback.cpp" />
@@ -213,6 +214,7 @@
     <ClInclude Include="ConstTransitivePtr.h" />
     <ClInclude Include="CSpellHandler.h" />
     <ClInclude Include="CTownHandler.h" />
+    <ClInclude Include="ERMInterpreter.h" />
     <ClInclude Include="ERMParser.h" />
     <ClInclude Include="HeroBonus.h" />
     <ClInclude Include="IGameCallback.h" />

+ 17 - 1
server/CGameHandler.cpp

@@ -1571,7 +1571,7 @@ void CGameHandler::giveResource(int player, int which, int val)
 	SetResource sr;
 	sr.player = player;
 	sr.resid = which;
-	sr.val = gs->players.find(player)->second.resources[which]+val;
+	sr.val = gs->players.find(player)->second.resources[which] + val;
 	sendAndApply(&sr);
 }
 
@@ -2658,6 +2658,22 @@ bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, int ri
 	return true;
 }
 
+bool CGameHandler::sellArtifact(const IMarket *m, const CGHeroInstance *h, int aid, int rid)
+{
+	const CArtifactInstance *art = h->getArtByInstanceId(aid);
+	if(!art)
+		COMPLAIN_RET("There is no artifact to sell!");
+	if(art->artType->id < 7)
+		COMPLAIN_RET("Cannot sell a war machine or spellbook!");
+
+	int resVal = 0, dump = 1;
+	m->getOffer(art->artType->id, rid, dump, resVal, ARTIFACT_RESOURCE);
+
+	removeArtifact(ArtifactLocation(h, h->getArtPos(art)));
+	giveResource(h->tempOwner, rid, resVal);
+	return true;
+}
+
 bool CGameHandler::buySecSkill( const IMarket *m, const CGHeroInstance *h, int skill)
 {
 	if (!h)

+ 1 - 0
server/CGameHandler.h

@@ -211,6 +211,7 @@ public:
 	bool assembleArtifacts (si32 heroID, ui16 artifactSlot, bool assemble, ui32 assembleTo);
 	bool buyArtifact( ui32 hid, si32 aid ); //for blacksmith and mage guild only -> buying for gold in common buildings
 	bool buyArtifact( const IMarket *m, const CGHeroInstance *h, int rid, int aid); //for artifact merchant and black market -> buying for any resource in special building / advobject
+	bool sellArtifact( const IMarket *m, const CGHeroInstance *h, int aid, int rid); //for artifact merchant selling
 	bool buySecSkill( const IMarket *m, const CGHeroInstance *h, int skill);
 	bool moveArtifact(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, ui16 destSlot);
 	bool garrisonSwap(si32 tid);

+ 4 - 0
server/NetPacksServer.cpp

@@ -164,6 +164,10 @@ bool TradeOnMarketplace::applyGh( CGameHandler *gh )
 		if(!hero)
 			COMPLAIN_AND_RETURN("Only hero can buy artifacts!");
 		return gh->buyArtifact(m, hero, r1, r2);
+	case ARTIFACT_RESOURCE:
+		if(!hero)
+			COMPLAIN_AND_RETURN("Only hero can sell artifacts!");
+		return gh->sellArtifact(m, hero, r1, r2);
 	case CREATURE_UNDEAD:
 		return gh->transformInUndead(m, hero, r1);
 	case RESOURCE_SKILL: