From 0a96decf872ea9ad01c50ee31379d6393f7e79d5 Mon Sep 17 00:00:00 2001
From: OnionKnight <onionknigh@gmail.com>
Date: Tue, 1 Dec 2009 23:19:43 +0000
Subject: [PATCH] * Fixed the artifact screen completely, hopefully. Structural
 overhaul of the CArtPlace and CArtifactsOfHero classes, and I had to add a
 new server message SetArtifact to deal with things properly. The only thing
 that differs from H3 now is that you can't have gaps of empty slots in the
 backpack. * Preliminary work on bug #112, a bigArtifacts set is added to
 CArtHandler.

---
 CCallback.cpp             |  10 ++
 CCallback.h               |   1 +
 client/CHeroWindow.cpp    |   3 +-
 client/GUIClasses.cpp     | 284 ++++++++++++++++++++++----------------
 client/GUIClasses.h       |   7 +-
 hch/CArtHandler.cpp       |   4 +
 hch/CArtHandler.h         |   3 +
 lib/CGameState.cpp        |   2 +-
 lib/NetPacks.h            |  16 +++
 lib/RegisterTypes.cpp     |   1 +
 server/CGameHandler.cpp   |  23 +++
 server/CGameHandler.h     |   1 +
 server/NetPacksServer.cpp |   6 +
 13 files changed, 236 insertions(+), 125 deletions(-)

diff --git a/CCallback.cpp b/CCallback.cpp
index 217f8a4dd..59e14e758 100644
--- a/CCallback.cpp
+++ b/CCallback.cpp
@@ -416,6 +416,16 @@ bool CCallback::swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGH
 	return true;
 }
 
+bool CCallback::setArtifact(const CGHeroInstance * hero, ui16 pos, int artID)
+{
+	if(player != hero->tempOwner)
+		return false;
+
+	SetArtifact sa(hero->id, pos, artID);
+	sendRequest(&sa);
+	return true;
+}
+
 bool CCallback::buildBuilding(const CGTownInstance *town, si32 buildingID)
 {
 	CGTownInstance * t = const_cast<CGTownInstance *>(town);
diff --git a/CCallback.h b/CCallback.h
index 809302222..f9899d382 100644
--- a/CCallback.h
+++ b/CCallback.h
@@ -224,6 +224,7 @@ public:
 	int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val);
 	bool dismissHero(const CGHeroInstance * hero);
 	bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2);
+	bool setArtifact(const CGHeroInstance * hero, ui16 pos, int artID);
 	bool buildBuilding(const CGTownInstance *town, si32 buildingID);
 	void recruitCreatures(const CGObjectInstance *obj, ui32 ID, ui32 amount);
 	bool dismissCreature(const CArmedInstance *obj, int stackPos);
diff --git a/client/CHeroWindow.cpp b/client/CHeroWindow.cpp
index fabc8bc41..1fa43c39d 100644
--- a/client/CHeroWindow.cpp
+++ b/client/CHeroWindow.cpp
@@ -55,7 +55,6 @@ CHeroWindow::CHeroWindow(int playerColor):
 	artifs = new CArtifactsOfHero(pos);
 	artifs->commonInfo = new CArtifactsOfHero::SCommonPart;
 	artifs->commonInfo->participants.insert(artifs);
-	artifs->commonInfo->activeArtPlace = NULL;
 
 	garr = NULL;
 	ourBar = new CStatusBar(pos.x+72, pos.y+567, "ADROLLVR.bmp", 660);
@@ -146,6 +145,7 @@ CHeroWindow::~CHeroWindow()
 	delete garr;
 	delete ourBar;
 
+	artifs->rollback();
 	delete artifs->commonInfo;
 	artifs->commonInfo = NULL; //to prevent heap corruption
 	delete artifs;
@@ -506,6 +506,7 @@ void CHeroWindow::dispose()
 	curBack = NULL;
 	curHero = NULL;
 
+	artifs->rollback();
 	artifs->dispose();
 }
 
diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp
index 3614da8da..0e137238a 100644
--- a/client/GUIClasses.cpp
+++ b/client/GUIClasses.cpp
@@ -3497,7 +3497,7 @@ CRClickPopupInt::~CRClickPopupInt()
 	CGI->curh->show();
 }
 
-CArtPlace::CArtPlace(const CArtifact* Art): active(false), clicked(false), marked(false), ourArt(Art)/*,
+CArtPlace::CArtPlace(const CArtifact* Art): active(false), marked(false), ourArt(Art)/*,
 	spellBook(false), warMachine1(false), warMachine2(false), warMachine3(false),
 	warMachine4(false),misc1(false), misc2(false), misc3(false), misc4(false),
 	misc5(false), feet(false), lRing(false), rRing(false), torso(false),
@@ -3520,7 +3520,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 	//LRClickableAreaWTextComp::clickLeft(down);
 	
 	// If clicked on spellbook, open it only if no artifact is held at the moment.
-	if(ourArt && !down && previousState && !ourOwner->commonInfo->activeArtPlace)
+	if(ourArt && !down && previousState && !ourOwner->commonInfo->srcAOH)
 	{
 		if(ourArt->id == 0)
 		{
@@ -3528,11 +3528,11 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 			GH.pushInt(spellWindow);
 		}
 	}
-	if(!down && (!clicked || ourOwner->commonInfo->srcSlotID >= 19) && previousState) //not clicked before
+	if (!down && previousState)
 	{
 		if(ourArt && ourArt->id == 0)
 			return; //this is handled separately
-		if(!ourOwner->commonInfo->activeArtPlace) //nothing has been clicked
+		if(!ourOwner->commonInfo->srcAOH) //nothing has been clicked
 		{
 			if(ourArt) //to prevent selecting empty slots (bugfix to what GrayFace reported)
 			{
@@ -3567,39 +3567,36 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 				default:
 					ourOwner->commonInfo->destAOH = ourOwner;
 					ourOwner->commonInfo->destSlotID = slotID;
+					ourOwner->commonInfo->destArtifact = NULL;
 
-					LOCPLINT->cb->swapArtifacts(
-						ourOwner->commonInfo->activeArtPlace->ourOwner->curHero,
-						ourOwner->commonInfo->srcSlotID,
+					LOCPLINT->cb->setArtifact(
 						ourOwner->curHero,
-						slotID);
+						slotID,
+						ourOwner->commonInfo->srcArtifact->id);
 
-					ourOwner->commonInfo->activeArtPlace->deselect();
 					break;
 				}
 			}
 			//check if swap is possible
-			else if(this->fitsHere(ourOwner->commonInfo->srcArtifact) && ourOwner->commonInfo->activeArtPlace->fitsHere(this->ourArt))
+			else if (this->fitsHere(ourOwner->commonInfo->srcArtifact))
 			{
 				ourOwner->commonInfo->destAOH = ourOwner;
 				ourOwner->commonInfo->destSlotID = slotID;
+				ourOwner->commonInfo->destArtifact = ourArt;
 
-				LOCPLINT->cb->swapArtifacts(
-					ourOwner->commonInfo->activeArtPlace->ourOwner->curHero,
-					ourOwner->commonInfo->srcSlotID,
+				LOCPLINT->cb->setArtifact(
 					ourOwner->curHero,
-					slotID);
-
-				ourOwner->commonInfo->activeArtPlace->deselect();
+					slotID,
+					ourOwner->commonInfo->srcArtifact->id);
 			}
 		}
 	}
-	else if(!down && clicked)
+	/*else if(!down && clicked)
 	{
 		if(ourArt && ourArt->id == 0)
 			return; //this is handled separately
 		deselect();
-	}
+	}*/
 	//ClickableL::clickLeft(down);
 }
 
@@ -3615,20 +3612,19 @@ void CArtPlace::clickRight(tribool down, bool previousState)
 void CArtPlace::select ()
 {
 	CGI->curh->dragAndDropCursor(graphics->artDefs->ourImages[ourArt->id].bitmap);
-	clicked = true;
 	ourOwner->markPossibleSlots(ourArt);
 
-	ourOwner->commonInfo->activeArtPlace = this;
 	ourOwner->commonInfo->srcArtifact = ourArt;
 	ourOwner->commonInfo->srcSlotID = slotID;
 	ourOwner->commonInfo->srcAOH = ourOwner;
 
 	if (slotID >= 19) {
-		// Updates backpack, possibly correcting the position.
+		// Correcting position in backpack.
 		ourOwner->scrollBackpack(-(slotID - 19 < ourOwner->backpackPos));
 	} else {
 		ourOwner->eraseSlotData(this, slotID);
 	}
+	LOCPLINT->cb->setArtifact(ourOwner->curHero, slotID, -1);
 }
 
 /**
@@ -3636,14 +3632,8 @@ void CArtPlace::select ()
  */
 void CArtPlace::deselect ()
 {
-	clicked = false;
 	CGI->curh->dragAndDropCursor(NULL);
 	ourOwner->unmarkSlots();
-	ourOwner->commonInfo->activeArtPlace = NULL;
-
-	// If a worn artifact is deselected, restore it's picture.
-	if (slotID < 19 && !ourOwner->commonInfo->destAOH)
-		ourOwner->setSlotData(this, slotID);
 }
 
 void CArtPlace::deactivate()
@@ -3658,10 +3648,9 @@ void CArtPlace::deactivate()
 
 void CArtPlace::show(SDL_Surface *to)
 {
-	if(ourArt && (!clicked || slotID >= 19))
-	{
+	if (ourArt)
 		blitAt(graphics->artDefs->ourImages[ourArt->id].bitmap, pos.x, pos.y, to);
-	}
+
 	if(marked && active)
 	{
 		// Draw vertical bars.
@@ -3685,8 +3674,8 @@ bool CArtPlace::fitsHere(const CArtifact * art)
 		return true;
 
 	// Anything can be placed in the backpack, except War Machines.
-	if(slotID > 18 && !(art->id >= 3 && art->id <= 6)
-	  || vstd::contains(art->possibleSlots,slotID))
+	if (slotID >= 19 && !CGI->arth->isBigArtifact(art->id)
+		|| vstd::contains(art->possibleSlots, slotID))
 	{
 		return true;
 	}
@@ -3697,10 +3686,6 @@ bool CArtPlace::fitsHere(const CArtifact * art)
 CArtPlace::~CArtPlace()
 {
 	deactivate();
-
-	// Make sure a currently held artifact does not affect the outside.
-	if (clicked)
-		CGI->curh->dragAndDropCursor(NULL);
 }
 
 void LClickableArea::activate()
@@ -3867,124 +3852,141 @@ void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
 {
 	// An update is made, rather than initialization.
 	if (curHero == hero) {
-		// Compensate backpack pos if an artifact is insertad before it.
+		// Compensate backpack pos if an artifact was insertad before it.
 		if (commonInfo->destSlotID >= 19 && commonInfo->destAOH == this
 			&& commonInfo->destSlotID - 19 < backpackPos)
 		{
 			backpackPos++;
 		}
+
+		// A swap was made, make the replaced artifact into current selected.
+		if (commonInfo->destSlotID < 19 && commonInfo->destArtifact) {
+			// Source <- Dest
+			commonInfo->srcAOH = commonInfo->destAOH;
+			commonInfo->srcArtifact = commonInfo->destArtifact;
+			commonInfo->srcSlotID = -1; // The artifact's original place is taken now.
+
+			// Reset destination parameters.
+			commonInfo->destAOH = NULL;
+			commonInfo->destArtifact = NULL;
+			commonInfo->destSlotID = -1;
+
+			CGI->curh->dragAndDropCursor(
+				graphics->artDefs->ourImages[commonInfo->srcArtifact->id].bitmap);
+			markPossibleSlots(commonInfo->srcArtifact);
+		} else if (commonInfo->destAOH != NULL) {
+			// Reset all parameters.
+			commonInfo->srcAOH = NULL;
+			commonInfo->srcArtifact = NULL;
+			commonInfo->srcSlotID = -1;
+			commonInfo->destAOH = NULL;
+			commonInfo->destArtifact = NULL;
+			commonInfo->destSlotID = -1;
+
+			CGI->curh->dragAndDropCursor(NULL);
+			unmarkSlots();
+		}
+	} else {
+		rollback();
 	}
 
-	commonInfo->srcAOH = NULL;
-	commonInfo->srcArtifact = NULL;
-	commonInfo->srcSlotID = 0; // Can be anything that's not in backpack range.
-	commonInfo->destAOH = NULL;
-	commonInfo->destSlotID = 0;
-
 	curHero = hero;
-	backpackSize = curHero->artifacts.size();
 
-	// Remove any previously allocated slots.
-	for(size_t g=0; g<artWorn.size(); ++g)
-		delete artWorn[g];
-	for(size_t g=0; g<backpack.size(); ++g)
-		delete backpack[g];
-	backpack.clear();
-
-	std::vector<SDL_Rect> slotPos;
 	if (curHero->artifacts.size() > 0)
 		backpackPos %= curHero->artifacts.size();
 	else
 		backpackPos = 0;
 
-	slotPos += genRect(44,44,pos.x+509,pos.y+30), genRect(44,44,pos.x+567,pos.y+240), genRect(44,44,pos.x+509,pos.y+80), 
-		genRect(44,44,pos.x+383,pos.y+68), genRect(44,44,pos.x+564,pos.y+183), genRect(44,44,pos.x+509,pos.y+130), 
-		genRect(44,44,pos.x+431,pos.y+68), genRect(44,44,pos.x+610,pos.y+183), genRect(44,44,pos.x+515,pos.y+295), 
-		genRect(44,44,pos.x+383,pos.y+143), genRect(44,44,pos.x+399,pos.y+194), genRect(44,44,pos.x+415,pos.y+245),
-		genRect(44,44,pos.x+431,pos.y+296), genRect(44,44,pos.x+564,pos.y+30), genRect(44,44,pos.x+610,pos.y+30), 
-		genRect(44,44,pos.x+610,pos.y+76), genRect(44,44,pos.x+610,pos.y+122), genRect(44,44,pos.x+610,pos.y+310),	
-		genRect(44,44,pos.x+381,pos.y+296);
-
-	// Fill the slots for worn artifacts.
+	// Fill the slots for worn artifacts and backpack.
 	for (int g = 0; g < 19 ; g++)
-	{	
-		artWorn[g] = new CArtPlace(hero->getArt(g));
-		artWorn[g]->pos = slotPos[g];
-		artWorn[g]->ourOwner = this;
 		setSlotData(artWorn[g], g);
-	}
-
-	// Fill the slots for the backpack.
-	for(size_t s=0; s<5; ++s)
-	{
-		CArtPlace * add = new CArtPlace(NULL);
-
-		add->ourOwner = this;
-		add->pos.x = pos.x + 403 + 46*s;
-		add->pos.y = pos.y + 365;
-		add->pos.h = add->pos.w = 44;
-
-		if (s < curHero->artifacts.size())
-			setSlotData(add, 19 + (s + backpackPos)%curHero->artifacts.size());
-		else
-			setSlotData(add, 19 + s);
-		backpack.push_back(add);
-	}
-	commonInfo->activeArtPlace = NULL;
+	scrollBackpack(0);
 
 	//blocking scrolling if there is not enough artifacts to scroll
 	leftArtRoll->block(curHero->artifacts.size() <= backpack.size());
 	rightArtRoll->block(curHero->artifacts.size() <= backpack.size());
 }
 
+/**
+ * Any held artifacts, marked slots etc. will be restored to it's original way.
+ */
+void CArtifactsOfHero::rollback()
+{
+	if (curHero != NULL) {
+		// Restore any held artifact to it's original position.
+		if (commonInfo->destArtifact && commonInfo->destAOH == this) {
+			// For an unlikely race condition scenario, put swapped artifact into backpack.
+			LOCPLINT->cb->setArtifact(
+				curHero,
+				19 + curHero->artifacts.size(),
+				commonInfo->destArtifact->id);
+		}
+		else if (commonInfo->srcArtifact && commonInfo->srcAOH == this) {
+			if (commonInfo->srcSlotID != -1) { // Held artifact, just put it back to it's spot.
+				LOCPLINT->cb->setArtifact(
+					curHero,
+					commonInfo->srcSlotID,
+					commonInfo->srcArtifact->id);
+			} else { // Swapped artifact.
+				// Wear the artifact in a suitable spot.
+				ui16 i = 0;
+				for (; i < 19; i++) {
+					if (artWorn[i]->fitsHere(commonInfo->srcArtifact)
+						&& curHero->artifWorn.find(i) == curHero->artifWorn.end())
+					{
+						LOCPLINT->cb->setArtifact(
+							curHero, i, commonInfo->srcArtifact->id);
+						break;
+					}
+				}
+
+				// If it can't be worn, put it in the backpack.
+				if (i == 19)
+					LOCPLINT->cb->setArtifact(
+						curHero,
+						19 + curHero->artifacts.size(),
+						commonInfo->srcArtifact->id);
+			}
+		}
+	}
+		unmarkSlots();
+	backpackPos = 0;
+
+	commonInfo->srcAOH = NULL;
+	commonInfo->srcArtifact = NULL;
+	commonInfo->srcSlotID = -1;
+	commonInfo->destAOH = NULL;
+	commonInfo->destArtifact = NULL;
+	commonInfo->destSlotID = -1;
+
+	CGI->curh->dragAndDropCursor(NULL);
+}
+
 void CArtifactsOfHero::dispose()
 {
 	curHero = NULL;
-	for(size_t g=0; g<artWorn.size(); ++g)
-	{
-		delete artWorn[g];
-		artWorn[g] = NULL;
-	}
-	for(size_t g=0; g<backpack.size(); ++g)
-	{
-		delete backpack[g];
-		backpack[g] = NULL;
-	}
-	backpack.clear();
-	if(commonInfo)
-		commonInfo->activeArtPlace = NULL;
 }
 
 void CArtifactsOfHero::scrollBackpack(int dir)
 {
 	backpackPos += dir;
-	if (backpackPos < 0) { // No guarantee of modulus behavior with negative operands.
-		do {
-			backpackPos += curHero->artifacts.size();
-		} while (backpackPos < 0);
-	} else {
-		backpackPos %= curHero->artifacts.size();
+	if (curHero->artifacts.size() > 0) {
+		if (backpackPos < 0) { // No guarantee of modulus behavior with negative operands.
+			do {
+				backpackPos += curHero->artifacts.size();
+			} while (backpackPos < 0);
+		} else {
+			backpackPos %= curHero->artifacts.size();
+		}
 	}
 
-	const int tempBackpackSize = curHero->artifacts.size() - (commonInfo->srcSlotID >= 19);
-
 	//set new data
 	for (size_t s = 0; s < backpack.size(); ++s) {
-		int slotID = 19 + (s + backpackPos)%tempBackpackSize;
-
-		// Don't show the held artifact, skip it.
-		if (commonInfo->srcAOH == this && commonInfo->srcSlotID >= 19 && slotID >= commonInfo->srcSlotID)
-			slotID++;
-
-		if (s < tempBackpackSize)
-			setSlotData(backpack[s], slotID);
+		if (s < curHero->artifacts.size())
+			setSlotData(backpack[s], 19 + (s + backpackPos)%curHero->artifacts.size());
 		else
-			eraseSlotData(backpack[s], slotID);
+			eraseSlotData(backpack[s], 19 + s);
 	}
-
-	// Activate/deactivate sliders.
-	leftArtRoll->block(tempBackpackSize <= backpack.size());
-	rightArtRoll->block(tempBackpackSize <= backpack.size());
 }
 
 /**
@@ -4053,6 +4055,38 @@ CArtifactsOfHero::CArtifactsOfHero(const SDL_Rect & position) :
 	pos = position;
 	artWorn.resize(19);
 	
+	std::vector<SDL_Rect> slotPos;
+	slotPos += genRect(44,44,pos.x+509,pos.y+30), genRect(44,44,pos.x+567,pos.y+240), genRect(44,44,pos.x+509,pos.y+80), 
+		genRect(44,44,pos.x+383,pos.y+68), genRect(44,44,pos.x+564,pos.y+183), genRect(44,44,pos.x+509,pos.y+130), 
+		genRect(44,44,pos.x+431,pos.y+68), genRect(44,44,pos.x+610,pos.y+183), genRect(44,44,pos.x+515,pos.y+295), 
+		genRect(44,44,pos.x+383,pos.y+143), genRect(44,44,pos.x+399,pos.y+194), genRect(44,44,pos.x+415,pos.y+245),
+		genRect(44,44,pos.x+431,pos.y+296), genRect(44,44,pos.x+564,pos.y+30), genRect(44,44,pos.x+610,pos.y+30), 
+		genRect(44,44,pos.x+610,pos.y+76), genRect(44,44,pos.x+610,pos.y+122), genRect(44,44,pos.x+610,pos.y+310),	
+		genRect(44,44,pos.x+381,pos.y+296);
+
+	// Create slots for worn artifacts.
+	for (int g = 0; g < 19 ; g++)
+	{	
+		artWorn[g] = new CArtPlace(NULL);
+		artWorn[g]->pos = slotPos[g];
+		artWorn[g]->ourOwner = this;
+		eraseSlotData(artWorn[g], g);
+	}
+
+	// Create slots for the backpack.
+	for(size_t s=0; s<5; ++s)
+	{
+		CArtPlace * add = new CArtPlace(NULL);
+
+		add->ourOwner = this;
+		add->pos.x = pos.x + 403 + 46*s;
+		add->pos.y = pos.y + 365;
+		add->pos.h = add->pos.w = 44;
+		eraseSlotData(add, 19 + s);
+
+		backpack.push_back(add);
+	}
+
 	leftArtRoll = new AdventureMapButton(std::string(), std::string(), boost::bind(&CArtifactsOfHero::scrollBackpack,this,-1), pos.x+379, pos.y+364, "hsbtns3.def", SDLK_LEFT);
 	rightArtRoll = new AdventureMapButton(std::string(), std::string(), boost::bind(&CArtifactsOfHero::scrollBackpack,this,+1), pos.x+632, pos.y+364, "hsbtns5.def", SDLK_RIGHT);
 }
@@ -4060,6 +4094,17 @@ CArtifactsOfHero::CArtifactsOfHero(const SDL_Rect & position) :
 CArtifactsOfHero::~CArtifactsOfHero()
 {
 	dispose();
+	for(size_t g=0; g<artWorn.size(); ++g)
+	{
+		delete artWorn[g];
+		artWorn[g] = NULL;
+	}
+	for(size_t g=0; g<backpack.size(); ++g)
+	{
+		delete backpack[g];
+		backpack[g] = NULL;
+	}
+	backpack.clear();
 	artWorn.clear();
 
 	delete leftArtRoll;
@@ -4261,7 +4306,6 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) : bg(NULL)
 	artifs[0] = new CArtifactsOfHero(genRect(600, 800, pos.x + -334, pos.y + 150));
 	artifs[0]->commonInfo = new CArtifactsOfHero::SCommonPart;
 	artifs[0]->commonInfo->participants.insert(artifs[0]);
-	artifs[0]->commonInfo->activeArtPlace = NULL;
 	artifs[0]->setHero(heroInst[0]);
 	artifs[1] = new CArtifactsOfHero(genRect(600, 800, pos.x + 96, pos.y + 150));
 	artifs[1]->commonInfo = artifs[0]->commonInfo;
@@ -4353,6 +4397,8 @@ CExchangeWindow::~CExchangeWindow() //d-tor
 	delete quit;
 
 	//warning: don't experiment with these =NULL lines, they prevent heap corruption!
+	artifs[0]->rollback();
+	artifs[1]->rollback();
 	delete artifs[0]->commonInfo;
 	artifs[0]->commonInfo = NULL;
 	delete artifs[0];
diff --git a/client/GUIClasses.h b/client/GUIClasses.h
index 8ee56260f..1050636eb 100644
--- a/client/GUIClasses.h
+++ b/client/GUIClasses.h
@@ -645,7 +645,6 @@ public:
 	//	lHand, rHand, neck, shoulders, head; //my types
 	ui16 slotID; //0   	head	1 	shoulders		2 	neck		3 	right hand		4 	left hand		5 	torso		6 	right ring		7 	left ring		8 	feet		9 	misc. slot 1		10 	misc. slot 2		11 	misc. slot 3		12 	misc. slot 4		13 	ballista (war machine 1)		14 	ammo cart (war machine 2)		15 	first aid tent (war machine 3)		16 	catapult		17 	spell book		18 	misc. slot 5		19+ 	backpack slots
 
-	bool clicked;
 	bool marked;
 	CArtifactsOfHero * ourOwner;
 	const CArtifact * ourArt;
@@ -666,7 +665,6 @@ class CArtifactsOfHero : public CIntObject
 {
 	const CGHeroInstance * curHero;
 
-	size_t backpackSize; // Used to check differences in backpack sizes.
 	std::vector<CArtPlace *> artWorn; // 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
 	std::vector<CArtPlace *> backpack; //hero's visible backpack (only 5 elements!)
 	int backpackPos; //unmber of first art visible in backpack (in hero's vector)
@@ -675,12 +673,12 @@ public:
 	struct SCommonPart
 	{
 		std::set<CArtifactsOfHero *> participants; // Needed to mark slots.
-		CArtPlace * activeArtPlace;
-		const CArtifact * srcArtifact;    // Held artifact., technically superfluous right now.
+		const CArtifact * srcArtifact;    // Held artifact.
 		const CArtifactsOfHero * srcAOH;    // Following two needed to uniquely identify the source.
 		int srcSlotID;                      //
 		const CArtifactsOfHero * destAOH; // For swapping. (i.e. changing what is held)
 		int destSlotID;	                     // Needed to determine what kind of action was last taken in setHero
+		const CArtifact * destArtifact;    // For swapping.
 	} * commonInfo; //when we have more than one CArtifactsOfHero in one window with exchange possibility, we use this (eg. in exchange window); to be provided externally
 
 	AdventureMapButton * leftArtRoll, * rightArtRoll;
@@ -691,6 +689,7 @@ public:
 
 	void setHero(const CGHeroInstance * hero);
 	void dispose(); //free resources not needed after closing windows and reset state
+	void rollback();
 	void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right
 	void markPossibleSlots (const CArtifact* art);
 	void unmarkSlots ();
diff --git a/hch/CArtHandler.cpp b/hch/CArtHandler.cpp
index 62faec8b6..b14a3d9e1 100644
--- a/hch/CArtHandler.cpp
+++ b/hch/CArtHandler.cpp
@@ -38,6 +38,10 @@ const std::string & CArtifact::Description() const
 CArtHandler::CArtHandler()
 {
 	VLC->arth = this;
+
+	// War machines are the default big artifacts.
+	for (ui32 i = 3; i <= 6; i++)
+		bigArtifacts.insert(i);
 }
 void CArtHandler::loadArtifacts(bool onlyTxt)
 {
diff --git a/hch/CArtHandler.h b/hch/CArtHandler.h
index caabf6902..31be33856 100644
--- a/hch/CArtHandler.h
+++ b/hch/CArtHandler.h
@@ -2,6 +2,7 @@
 #define __CARTHANDLER_H__
 #include "../global.h"
 #include "../lib/HeroBonus.h"
+#include <set>
 #include <list>
 #include <string>
 #include <vector>
@@ -43,11 +44,13 @@ class DLL_EXPORT CArtHandler //handles artifacts
 public:
 	std::vector<CArtifact*> treasures, minors, majors, relics;
 	std::vector<CArtifact> artifacts;
+	std::set<ui32> bigArtifacts; // Artifacts that cannot be moved to backpack, e.g. war machines.
 
 	void loadArtifacts(bool onlyTxt);
 	void sortArts();
 	void addBonuses();
 	void clear();
+	bool isBigArtifact (ui32 artID) {return bigArtifacts.find(artID) != bigArtifacts.end();}
 	static int convertMachineID(int id, bool creToArt);
 	CArtHandler();
 
diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp
index 7adf2e929..d05e49987 100644
--- a/lib/CGameState.cpp
+++ b/lib/CGameState.cpp
@@ -1307,7 +1307,7 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
 		players.insert(ins);
 	}
 	/******************RESOURCES****************************************************/
-	//TODO: computer player should receive other amount of resource than computer (depending on difficulty)
+	//TODO: computer player should receive other amount of resource than player (depending on difficulty)
 	std::vector<int> startres;
 	std::ifstream tis(DATA_DIR "/config/startres.txt");
 	int k;
diff --git a/lib/NetPacks.h b/lib/NetPacks.h
index a5d06c0f3..a7592095f 100644
--- a/lib/NetPacks.h
+++ b/lib/NetPacks.h
@@ -1226,6 +1226,22 @@ struct ExchangeArtifacts : public CPackForServer
 	}
 };
 
+struct SetArtifact : public CPackForServer
+{
+	SetArtifact () {};
+	SetArtifact (si32 hid, ui16 slot, int artID)
+		:hid(hid), slot(slot), artID(artID) {};
+	si32 hid;
+	ui16 slot;
+	int artID;
+
+	bool applyGh(CGameHandler *gh);
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & hid & slot & artID;
+	}
+};
+
 struct BuyArtifact : public CPackForServer
 {
 	BuyArtifact(){};
diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp
index 7f3828504..a5b3e8945 100644
--- a/lib/RegisterTypes.cpp
+++ b/lib/RegisterTypes.cpp
@@ -137,6 +137,7 @@ void registerTypes3(Serializer &s)
 	s.template registerType<UpgradeCreature>();
 	s.template registerType<GarrisonHeroSwap>();
 	s.template registerType<ExchangeArtifacts>();
+	s.template registerType<SetArtifact>();
 	s.template registerType<BuyArtifact>();
 	s.template registerType<TradeOnMarketplace>();
 	s.template registerType<SetFormation>();
diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp
index 304901170..e0e1abe28 100644
--- a/server/CGameHandler.cpp
+++ b/server/CGameHandler.cpp
@@ -2326,6 +2326,7 @@ bool CGameHandler::swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot,
 		return false;
 	}
 
+	// TODO: This relates to bug #112, fix later.
 	// Make sure the artifacts are not war machines.
 	if ((srcSlot>=13 && srcSlot<=16) || (destSlot>=13 && destSlot<=16)) {
 		complain("Cannot move war machine!");
@@ -2363,6 +2364,28 @@ bool CGameHandler::swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot,
 	return true;
 }
 
+/**
+ * Sets a hero artifact slot to contain a specific artifact.
+ *
+ * @param artID ID of an artifact or -1 for no artifact.
+ */
+bool CGameHandler::setArtifact(si32 heroID, ui16 slot, int artID)
+{
+	CGHeroInstance *hero = gs->getHero(heroID);
+
+	// TODO: Deal with war machine placement.
+
+	// Perform the exchange.
+	SetHeroArtifacts sha;
+	sha.hid = heroID;
+	sha.artifacts = hero->artifacts;
+	sha.artifWorn = hero->artifWorn;
+	sha.setArtAtPos(slot, artID);
+	sendAndApply(&sha);
+
+	return true;
+}
+
 bool CGameHandler::buyArtifact( ui32 hid, si32 aid )
 {
 	CGHeroInstance *hero = gs->getHero(hid);
diff --git a/server/CGameHandler.h b/server/CGameHandler.h
index 1c69045a0..d2d974fe1 100644
--- a/server/CGameHandler.h
+++ b/server/CGameHandler.h
@@ -155,6 +155,7 @@ public:
 	bool tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 );
 	bool buyArtifact( ui32 hid, si32 aid );
 	bool swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, ui16 destSlot);
+	bool setArtifact(si32 heroID, ui16 slot, int artID);
 	bool garrisonSwap(si32 tid);
 	bool upgradeCreature( ui32 objid, ui8 pos, ui32 upgID );
 	bool recruitCreatures(si32 objid, ui32 crid, ui32 cram);
diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp
index 6d39fbc99..35f3e3ade 100644
--- a/server/NetPacksServer.cpp
+++ b/server/NetPacksServer.cpp
@@ -98,6 +98,12 @@ bool ExchangeArtifacts::applyGh( CGameHandler *gh )
 	return gh->swapArtifacts(hid1,hid2,slot1,slot2);
 }
 
+bool SetArtifact::applyGh( CGameHandler *gh )
+{
+	ERROR_IF_NOT_OWNS(hid);
+	return gh->setArtifact(hid, slot, artID);
+}
+
 bool BuyArtifact::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(hid);