Browse Source

Locks for combination artifacts largely implemented, only one or two bugs when moving them around in a certain way. Artifact assembly still remaining.
To make sure these artifacts get (un)equipped properly the functions CArtHandler::equipArtifact and CArtHandler::unequipArtifact should be used instead of modifying artifWorn manually.

OnionKnight 15 years ago
parent
commit
790a77c280

+ 2 - 0
client/CHeroWindow.cpp

@@ -257,7 +257,9 @@ void CHeroWindow::setHero(const CGHeroInstance *hero)
 	sprintf(bufor, CGI->generaltexth->allTexts[205].c_str(), hero->name.c_str(), hero->mana, hero->manaLimit());
 	spellPointsArea->text = std::string(bufor);
 
+	artifs->updateState = true;
 	artifs->setHero(hero);
+	artifs->updateState = false;
 
 	//if we have exchange window with this hero open
 	bool noDismiss=false;

+ 2 - 0
client/CPlayerInterface.cpp

@@ -853,7 +853,9 @@ void CPlayerInterface::heroArtifactSetChanged(const CGHeroInstance*hero)
 		{
 			if(cew->heroInst[g] == hero)
 			{
+				cew->artifs[g]->updateState = true;
 				cew->artifs[g]->setHero(hero);
+				cew->artifs[g]->updateState = false;
 			}
 		}
 		cew->prepareBackground();

+ 36 - 22
client/GUIClasses.cpp

@@ -3657,9 +3657,20 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 				ourOwner->commonInfo->destSlotID = slotID;
 				ourOwner->commonInfo->destArtifact = ourArt;
 
+				// Special case when the dest artifact can't be fit into the src slot.
+				CGHeroInstance *destHero = const_cast<CGHeroInstance *>(ourOwner->curHero);
+				CGI->arth->unequipArtifact(destHero->artifWorn, slotID);
+				const CArtifactsOfHero* srcAOH = ourOwner->commonInfo->srcAOH;
+				ui16 srcSlotID = ourOwner->commonInfo->srcSlotID;
+				if (ourArt && srcSlotID < 19 && !ourArt->fitsAt(srcAOH->curHero->artifWorn, srcSlotID)) {
+					// Put dest artifact into owner's backpack.
+					ourOwner->commonInfo->srcAOH = ourOwner;
+					ourOwner->commonInfo->srcSlotID = ourOwner->curHero->artifacts.size() + 19;
+				}
+
 				LOCPLINT->cb->swapArtifacts(
-						ourOwner->commonInfo->srcAOH->curHero,
-						ourOwner->commonInfo->srcSlotID,
+						srcAOH->curHero,
+						srcSlotID,
 						ourOwner->curHero,
 						slotID);
 			}
@@ -3676,7 +3687,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 
 void CArtPlace::clickRight(tribool down, bool previousState)
 {
-	if(text.size()) //if there is no description, do nothing ;]
+	if(!locked() && text.size()) //if there is no description or it's a lock, do nothing ;]
 		LRClickableAreaWTextComp::clickRight(down, previousState);
 }
 
@@ -3685,8 +3696,10 @@ void CArtPlace::clickRight(tribool down, bool previousState)
  */
 void CArtPlace::select ()
 {
+	if (locked())
+		return;
+
 	CGI->curh->dragAndDropCursor(graphics->artDefs->ourImages[ourArt->id].bitmap);
-	ourOwner->markPossibleSlots(ourArt);
 
 	ourOwner->commonInfo->srcArtifact = ourArt;
 	ourOwner->commonInfo->srcSlotID = slotID;
@@ -3695,9 +3708,10 @@ void CArtPlace::select ()
 	// Temporarily remove artifact from hero.
 	CGHeroInstance* hero = const_cast<CGHeroInstance*>(ourOwner->curHero);
 	if (slotID < 19)
-		hero->artifWorn.erase(slotID);
+		CGI->arth->unequipArtifact(hero->artifWorn, slotID);
 	else
 		hero->artifacts.erase(hero->artifacts.begin() + (slotID - 19));
+	ourOwner->markPossibleSlots(ourArt);
 	hero->recreateArtBonuses();
 
 	// Update the hero bonuses.
@@ -3776,14 +3790,10 @@ bool CArtPlace::fitsHere(const CArtifact * art)
 		return true;
 
 	// Anything can but War Machines can be placed in backpack.
-	if (slotID >= 19) {
+	if (slotID >= 19)
 		return !CGI->arth->isBigArtifact(art->id);
-	} else if (vstd::contains(art->possibleSlots, slotID)) {
-		// TODO: Deal with combinational at dest and as src.
-		return true;
-	}
 
-	return false;
+	return art->fitsAt(ourOwner->curHero->artifWorn, slotID);
 }
 
 CArtPlace::~CArtPlace()
@@ -3988,21 +3998,20 @@ void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
 			backpackPos++;
 		}
 
-		if (commonInfo->srcAOH == this) {
+		if (updateState && commonInfo->srcAOH == this) {
+			curHero = hero;
 			// A swap was made, make the replaced artifact the current selected.
 			if (commonInfo->destSlotID < 19 && commonInfo->destArtifact) {
 				// Temporarily remove artifact from hero.
-				CGHeroInstance * hero = const_cast<CGHeroInstance *>(curHero);
+				CGHeroInstance * nonconstCurHero = const_cast<CGHeroInstance *>(curHero);
 				if (commonInfo->srcSlotID < 19)
-					hero->artifWorn.erase(commonInfo->srcSlotID);
+					CGI->arth->unequipArtifact(nonconstCurHero->artifWorn, commonInfo->srcSlotID);
 				else
-					hero->artifacts.erase(hero->artifacts.begin() + (commonInfo->srcSlotID - 19));
-				hero->recreateArtBonuses();
+					nonconstCurHero->artifacts.erase(nonconstCurHero->artifacts.begin() + (commonInfo->srcSlotID - 19));
+				nonconstCurHero->recreateArtBonuses();
 
 				// Source <- Dest
-				//commonInfo->srcAOH = commonInfo->destAOH;
 				commonInfo->srcArtifact = commonInfo->destArtifact;
-				//commonInfo->srcSlotID = commonInfo->destSlotID;
 
 				// Reset destination parameters.
 				commonInfo->destAOH = NULL;
@@ -4059,7 +4068,7 @@ void CArtifactsOfHero::rollback()
 			if (commonInfo->srcSlotID != -1) {
 				// Put a held artifact back to it's spot.
 				if (commonInfo->srcSlotID < 19)
-					hero->artifWorn[commonInfo->srcSlotID] = commonInfo->srcArtifact->id;
+					CGI->arth->equipArtifact(hero->artifWorn, commonInfo->srcSlotID, commonInfo->srcArtifact->id);
 				else
 					hero->artifacts.insert(hero->artifacts.begin() + (commonInfo->srcSlotID - 19), commonInfo->srcArtifact->id);
 			} else { // Held swapped artifact.
@@ -4069,7 +4078,7 @@ void CArtifactsOfHero::rollback()
 					if (artWorn[i]->fitsHere(commonInfo->srcArtifact)
 						&& curHero->artifWorn.find(i) == curHero->artifWorn.end())
 					{
-						hero->artifWorn[i] = commonInfo->srcArtifact->id;
+						CGI->arth->equipArtifact(hero->artifWorn, i, commonInfo->srcArtifact->id);
 						break;
 					}
 				}
@@ -4137,6 +4146,8 @@ void CArtifactsOfHero::markPossibleSlots (const CArtifact* art)
 		for (int i = 0; i < (*it)->artWorn.size(); i++) {
 			if ((*it)->artWorn[i]->fitsHere(art))
 				(*it)->artWorn[i]->marked = true;
+			else
+				(*it)->artWorn[i]->marked = false;
 		}
 	}
 }
@@ -4166,7 +4177,10 @@ void CArtifactsOfHero::setSlotData (CArtPlace* artPlace, int slotID)
 
 	if (artPlace->ourArt) {
 		artPlace->text = artPlace->ourArt->Description();
-		artPlace->hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1].c_str()) % artPlace->ourArt->Name().c_str());
+		if (artPlace->locked()) // Locks should appear as empty.
+			artPlace->hoverText = CGI->generaltexth->allTexts[507];
+		else
+			artPlace->hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1].c_str()) % artPlace->ourArt->Name().c_str());
 	} else {
 		eraseSlotData(artPlace, slotID);
 	}
@@ -4184,7 +4198,7 @@ void CArtifactsOfHero::eraseSlotData (CArtPlace* artPlace, int slotID)
 }
 
 CArtifactsOfHero::CArtifactsOfHero(const SDL_Rect & position) :
-	backpackPos(0)
+	backpackPos(0), updateState(false)
 {
 	pos = position;
 	artWorn.resize(19);

+ 10 - 2
client/GUIClasses.h

@@ -2,6 +2,7 @@
 #define __GUICLASSES_H__
 
 #include "../global.h"
+#include "../hch/CArtHandler.h"
 #include "SDL_framerate.h"
 #include "GUIBase.h"
 #include "FunctionList.h"
@@ -691,10 +692,15 @@ public:
 	void activate();
 	void deactivate();
 	void show(SDL_Surface * to);
-	bool fitsHere(const CArtifact * art); //returns true if given artifact can be placed here
+	bool fitsHere (const CArtifact * art); //returns true if given artifact can be placed here
+	bool locked () const;
 	~CArtPlace(); //d-tor
 };
 
+inline bool CArtPlace::locked () const
+{
+	return ourArt && ourArt->id == 145;
+}
 
 class CArtifactsOfHero : public CIntObject
 {
@@ -702,7 +708,7 @@ class CArtifactsOfHero : public CIntObject
 
 	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)
+	int backpackPos; //number of first art visible in backpack (in hero's vector)
 
 public:
 	struct SCommonPart
@@ -716,6 +722,8 @@ public:
 		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
 
+	bool updateState; // Whether the commonInfo should be updated on setHero or not.
+
 	AdventureMapButton * leftArtRoll, * rightArtRoll;
 
 	void activate();

+ 146 - 15
hch/CArtHandler.cpp

@@ -6,6 +6,7 @@
 #include <boost/assign/std/vector.hpp>
 #include <boost/assign/list_of.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
 #include "../lib/VCMI_Lib.h"
 extern CLodHandler *bitmaph;
 using namespace boost::assign;
@@ -36,11 +37,69 @@ const std::string & CArtifact::Description() const
 		return VLC->generaltexth->artifDescriptions[id];
 }
 
-bool CArtifact::isBig () const
+inline bool CArtifact::isBig () const
 {
 	return VLC->arth->isBigArtifact(id);
 }
 
+/**
+ * Checks whether the artifact fits at a given slot.
+ * @param artifWorn A hero's set of worn artifacts.
+ */
+bool CArtifact::fitsAt (const std::map<ui16, ui32> &artifWorn, ui16 slotID) const
+{
+	if (!vstd::contains(possibleSlots, slotID))
+		return false;
+
+	// Can't put an artifact in a locked slot.
+	std::map<ui16, ui32>::const_iterator it = artifWorn.find(slotID);
+	if (it != artifWorn.end() && it->second == 145)
+		return false;
+
+	// Check if a combination artifact fits.
+	// TODO: Might want a more general algorithm?
+	//       Assumes that misc & rings fits only in their slots, and others in only one slot and no duplicates.
+	if (constituents != NULL) {
+		std::map<ui16, ui32> tempArtifWorn = artifWorn;
+		const ui16 ringSlots[] = {6, 7};
+		const ui16 miscSlots[] = {9, 10, 11, 12, 18};
+		int rings = 0;
+		int misc = 0;
+
+		VLC->arth->unequipArtifact(tempArtifWorn, slotID);
+
+		BOOST_FOREACH(ui32 constituentID, *constituents) {
+			const CArtifact& constituent = VLC->arth->artifacts[constituentID];
+			const int slot = constituent.possibleSlots[0];
+
+			if (slot == 6 || slot == 7)
+				rings++;
+			else if (slot >= 9 && slot <= 12 || slot == 18)
+				misc++;
+			else if (tempArtifWorn.find(slot) != tempArtifWorn.end())
+				return false;
+		}
+
+		// Ensure enough ring slots are free
+		for (int i = 0; i < sizeof(ringSlots)/sizeof(*ringSlots); i++) {
+			if (tempArtifWorn.find(ringSlots[i]) == tempArtifWorn.end() || ringSlots[i] == slotID)
+				rings--;
+		}
+		if (rings > 0)
+			return false;
+
+		// Ensure enough misc slots are free.
+		for (int i = 0; i < sizeof(miscSlots)/sizeof(*miscSlots); i++) {
+			if (tempArtifWorn.find(miscSlots[i]) == tempArtifWorn.end() || miscSlots[i] == slotID)
+				misc--;
+		}
+		if (misc > 0)
+			return false;
+	}
+
+	return true;
+}
+
 CArtHandler::CArtHandler()
 {
 	VLC->arth = this;
@@ -52,7 +111,7 @@ CArtHandler::CArtHandler()
 void CArtHandler::loadArtifacts(bool onlyTxt)
 {
 	std::vector<ui16> slots;
-	slots += 17, 16, 15,14,13, 18, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0;
+	slots += 17, 16, 15, 14, 13, 18, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0;
 	static std::map<char, CArtifact::EartClass> classes = 
 	  map_list_of('S',CArtifact::ART_SPECIAL)('T',CArtifact::ART_TREASURE)('N',CArtifact::ART_MINOR)('J',CArtifact::ART_MAJOR)('R',CArtifact::ART_RELIC);
 	std::string buf = bitmaph->getTextFile("ARTRAITS.TXT"), dump, pom;
@@ -85,65 +144,65 @@ void CArtHandler::loadArtifacts(bool onlyTxt)
 		if(desc[0] == '\"' && desc[desc.size()-1] == '\"')
 			desc = desc.substr(1,desc.size()-2);
 
-		// Fill in information about combined artifacts.
+		// Fill in information about combined artifacts. Should perhaps be moved to a config file?
 		switch (nart.id) {
 			case 129: // Angelic Alliance
-				nart.constituents = new std::vector<ui32>(6);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 31, 32, 33, 34, 35, 36;
 				break;
 
 			case 130: // Cloak of the Undead King
-				nart.constituents = new std::vector<ui32>(3);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 54, 55, 56;
 				break;
 
 			case 131: // Elixir of Life
-				nart.constituents = new std::vector<ui32>(3);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 94, 95, 96;
 				break;
 
 			case 132: // Armor of the Damned
-				nart.constituents = new std::vector<ui32>(4);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 8, 14, 20, 26;
 				break;
 
 			case 133: // Statue of Legion
-				nart.constituents = new std::vector<ui32>(5);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 118, 119, 120, 121, 122;
 				break;
 
 			case 134: // Power of the Dragon Father
-				nart.constituents = new std::vector<ui32>(9);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 37, 38, 39, 40, 41, 42, 43, 44, 45;
 				break;
 
 			case 135: // Titan's Thunder
-				nart.constituents = new std::vector<ui32>(4);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 12, 18, 24, 30;
 				break;
 
 			case 136: // Admiral's Hat
-				nart.constituents = new std::vector<ui32>(2);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 71, 123;
 				break;
 
 			case 137: // Bow of the Sharpshooter
-				nart.constituents = new std::vector<ui32>(3);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 60, 61, 62;
 				break;
 
 			case 138: // Wizards' Well
-				nart.constituents = new std::vector<ui32>(3);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 73, 74, 75;
 				break;
 
 			case 139: // Ring of the Magi
-				nart.constituents = new std::vector<ui32>(3);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 76, 77, 78;
 				break;
 
 			case 140: // Cornucopia
-				nart.constituents = new std::vector<ui32>(4);
+				nart.constituents = new std::vector<ui32>();
 				*nart.constituents += 109, 110, 111, 113;
 				break;
 
@@ -484,4 +543,76 @@ void CArtHandler::clear()
 	minors.clear();
 	majors.clear();
 	relics.clear();
+}
+
+/**
+ * Locally equips an artifact to a hero's worn slots. Unequips an already present artifact.
+ * Does not test if the operation is legal.
+ * @param artifWorn A hero's set of worn artifacts.
+ */
+void CArtHandler::equipArtifact (std::map<ui16, ui32> &artifWorn, ui16 slotID, ui32 artifactID)
+{
+	unequipArtifact(artifWorn, slotID);
+
+	const CArtifact &artifact = artifacts[artifactID];
+
+	// Add artifact.
+	artifWorn[slotID] = artifactID;
+
+	// Add locks, in reverse order of being removed.
+	if (artifact.constituents != NULL) {
+		bool destConsumed = false; // Determines which constituent that will be counted for together with the artifact.
+
+		BOOST_FOREACH(ui32 constituentID, *artifact.constituents) {
+			const CArtifact &constituent = artifacts[constituentID];
+
+			if (!destConsumed && vstd::contains(constituent.possibleSlots, slotID)) {
+				destConsumed = true;
+			} else {
+				BOOST_FOREACH(ui16 slot, constituent.possibleSlots) {
+					if (artifWorn.find(slot) == artifWorn.end()) {
+						artifWorn[slot] = 145;
+						break;
+					}
+				}
+			}
+		}
+	}
+}
+
+/**
+ * Locally unequips an artifact from a hero's worn slots.
+ * Does not test if the operation is legal.
+ * @param artifWorn A hero's set of worn artifacts.
+ */
+void CArtHandler::unequipArtifact (std::map<ui16, ui32> &artifWorn, ui16 slotID)
+{
+	if (artifWorn.find(slotID) == artifWorn.end())
+		return;
+
+	const CArtifact &artifact = artifacts[artifWorn[slotID]];
+
+	// Remove artifact, if it's not already removed.
+	artifWorn.erase(slotID);
+
+	// Remove locks, in reverse order of being added.
+	if (artifact.constituents != NULL) {
+		bool destConsumed = false;
+
+		BOOST_FOREACH(ui32 constituentID, *artifact.constituents) {
+			const CArtifact &constituent = artifacts[constituentID];
+
+			if (!destConsumed && vstd::contains(constituent.possibleSlots, slotID)) {
+				destConsumed = true;
+			} else {
+				BOOST_REVERSE_FOREACH(ui16 slot, constituent.possibleSlots) {
+					if (artifWorn.find(slot) != artifWorn.end() && artifWorn[slot] == 145) {
+						artifWorn.erase(slot);
+						break;
+					}
+				}
+			}
+
+		}
+	}
 }

+ 3 - 0
hch/CArtHandler.h

@@ -26,6 +26,7 @@ public:
 	const std::string &Name() const; //getter
 	const std::string &Description() const; //getter
 	bool isBig () const;
+	bool fitsAt (const std::map<ui16, ui32> &artifWorn, ui16 slot) const;
 
 	ui32 price;
 	std::vector<ui16> possibleSlots; //ids of slots where artifact can be placed
@@ -53,6 +54,8 @@ public:
 	void addBonuses();
 	void clear();
 	bool isBigArtifact (ui32 artID) {return bigArtifacts.find(artID) != bigArtifacts.end();}
+	void equipArtifact (std::map<ui16, ui32> &artifWorn, ui16 slotID, ui32 artifactID);
+	void unequipArtifact (std::map<ui16, ui32> &artifWorn, ui16 slotID);
 	static int convertMachineID(int id, bool creToArt);
 	CArtHandler();
 

+ 5 - 5
hch/CObjectHandler.cpp

@@ -704,9 +704,9 @@ void CGHeroInstance::initHero()
 
 	if(!vstd::contains(artifWorn, 16) && type->startingSpell >= 0) //no catapult means we haven't read pre-existant set
 	{
-		artifWorn[17] = 0; //give spellbook
+		VLC->arth->equipArtifact(artifWorn, 17, 0); //give spellbook
 	}
-	artifWorn[16] = 3; //everyone has a catapult
+	VLC->arth->equipArtifact(artifWorn, 16, 3); //everyone has a catapult
 
 	if(portrait < 0 || portrait == 255)
 		portrait = subID;
@@ -754,10 +754,10 @@ void CGHeroInstance::initHero()
 				switch (pom)
 				{
 				case 145: //catapult
-					artifWorn[16] = 3;
+					VLC->arth->equipArtifact(artifWorn, 16, 3);
 					break;
 				default:
-					artifWorn[9+CArtHandler::convertMachineID(pom,true)] = CArtHandler::convertMachineID(pom,true);
+					VLC->arth->equipArtifact(artifWorn, 9+CArtHandler::convertMachineID(pom,true), CArtHandler::convertMachineID(pom,true));
 					break;
 				}
 				continue;
@@ -1220,7 +1220,7 @@ void CGHeroInstance::giveArtifact (ui32 aid)
 	if (artifact.isBig()) {
 		for (std::vector<ui16>::const_iterator it = artifact.possibleSlots.begin(); it != artifact.possibleSlots.end(); ++it) {
 			if (artifWorn.find(*it) == artifWorn.end()) {
-				artifWorn[*it] = aid;
+				VLC->arth->equipArtifact(artifWorn, *it, aid);
 				break;
 			}
 		}

+ 1 - 1
lib/CGameState.cpp

@@ -1490,7 +1490,7 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
 				std::vector<ui16>::iterator slot = vstd::findFirstNot(hero->artifWorn,toGive->possibleSlots);
 				if(slot!=toGive->possibleSlots.end())
 				{
-					hero->artifWorn[*slot] = toGive->id;
+					VLC->arth->equipArtifact(hero->artifWorn, *slot, toGive->id);
 					hero->recreateArtBonuses();
 				}
 				else

+ 1 - 1
lib/HeroBonus.h

@@ -31,7 +31,7 @@ struct DLL_EXPORT HeroBonus
 		NONEVIL_ALIGNMENT_MIX, //good and neutral creatures can be mixed without morale penalty
 		HP_REGENERATION, //regenerates a certain amount of hp for the top of each stack every turn, val - hp regained
 		LEVEL_SPELL_IMMUNITY, //val - spell level creatures become immune to and below
-		//not handled yet:
+		//might not be handled yet:
 		MAGIC_RESISTANCE, // %
 		SECONDARY_SKILL_PREMY, //%
 		SURRENDER_DISCOUNT, //%

+ 2 - 2
lib/NetPacksLib.cpp

@@ -464,14 +464,14 @@ DLL_EXPORT void SetHeroArtifacts::setArtAtPos(ui16 pos, int art)
 	if(art<0)
 	{
 		if(pos<19)
-			artifWorn.erase(pos);
+			VLC->arth->unequipArtifact(artifWorn, pos);
 		else
 			artifacts.erase(artifacts.begin() + (pos - 19));
 	}
 	else
 	{
 		if (pos < 19) {
-			artifWorn[pos] = art;
+			VLC->arth->equipArtifact(artifWorn, pos, (ui32) art);
 		} else { // Goes into the backpack.
 			if(pos - 19 < artifacts.size())
 				artifacts.insert(artifacts.begin() + (pos - 19), art);

+ 8 - 8
lib/map.cpp

@@ -964,27 +964,27 @@ void Mapa::loadHero( CGObjectInstance * &nobj, const unsigned char * bufor, int
 		{
 			int id = readNormalNr(bufor,i, artidlen); i+=artidlen;
 			if(id!=artmask)
-				nhi->artifWorn[pom] = id;
+				VLC->arth->equipArtifact(nhi->artifWorn, pom, id);
 		}
 		//misc5 art //17
 		if(version>=SoD)
 		{
 			int id = readNormalNr(bufor,i, artidlen); i+=artidlen;
 			if(id!=artmask)
-				nhi->artifWorn[16] = id;
+				VLC->arth->equipArtifact(nhi->artifWorn, 16, id);
 			else
-				nhi->artifWorn[16] = 3; //catapult by default
+				VLC->arth->equipArtifact(nhi->artifWorn, 16, 3); //catapult by default
 		}
 		//spellbook
 		int id = readNormalNr(bufor,i, artidlen); i+=artidlen;
 		if(id!=artmask)
-			nhi->artifWorn[17] = id;
+			VLC->arth->equipArtifact(nhi->artifWorn, 17, id);
 		//19 //???what is that? gap in file or what? - it's probably fifth slot..
 		if(version>RoE)
 		{
 			id = readNormalNr(bufor,i, artidlen); i+=artidlen;
 			if(id!=artmask)
-				nhi->artifWorn[18] = id;
+				VLC->arth->equipArtifact(nhi->artifWorn, 18, id);
 		}
 		else
 			i+=1;
@@ -1204,7 +1204,7 @@ void Mapa::readPredefinedHeroes( const unsigned char * bufor, int &i)
 					{
 						int id = readNormalNr(bufor,i, artidlen); i+=artidlen;
 						if(id!=artmask)
-							cgh->artifWorn[pom] = id;
+							VLC->arth->equipArtifact(cgh->artifWorn, pom, id);
 					}
 					//misc5 art //17
 					if(version>=SoD)
@@ -1217,13 +1217,13 @@ void Mapa::readPredefinedHeroes( const unsigned char * bufor, int &i)
 					//spellbook
 					int id = readNormalNr(bufor,i, artidlen); i+=artidlen;
 					if(id!=artmask)
-						cgh->artifWorn[17] = id;
+						VLC->arth->equipArtifact(cgh->artifWorn, 17, id);
 					//19 //???what is that? gap in file or what? - it's probably fifth slot..
 					if(version>RoE)
 					{
 						id = readNormalNr(bufor,i, artidlen); i+=artidlen;
 						if(id!=artmask)
-							cgh->artifWorn[18] = id;
+							VLC->arth->equipArtifact(cgh->artifWorn, 18, id);
 					}
 					else
 						i+=1;

+ 32 - 16
server/CGameHandler.cpp

@@ -1806,7 +1806,7 @@ void CGameHandler::giveHeroArtifact(int artid, int hid, int position) //pos==-1
 				if( !vstd::contains(sha.artifWorn,art.possibleSlots[i]) )
 				{
 					//we've found a free suitable slot
-					sha.artifWorn[art.possibleSlots[i]] = artid;
+					VLC->arth->equipArtifact(sha.artifWorn, art.possibleSlots[i], artid);
 					break;
 				}
 			}
@@ -1822,7 +1822,7 @@ void CGameHandler::giveHeroArtifact(int artid, int hid, int position) //pos==-1
 	{
 		if(!vstd::contains(sha.artifWorn,ui16(position)))
 		{
-			sha.artifWorn[position] = artid;
+			VLC->arth->equipArtifact(sha.artifWorn, position, artid);
 		}
 		else if (!art.isBig())
 		{
@@ -1850,7 +1850,7 @@ void CGameHandler::removeArtifact(int artid, int hid)
 		{
 			if (itr->second == artid)
 			{
-				sha.artifWorn.erase(itr);
+				VLC->arth->unequipArtifact(sha.artifWorn, itr->first);
 				break;
 			}
 		}
@@ -2536,6 +2536,7 @@ bool CGameHandler::garrisonSwap( si32 tid )
 	}
 }
 
+// With the amount of changes done to the function, it's more like transferArtifacts.
 bool CGameHandler::swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, ui16 destSlot)
 {
 	CGHeroInstance *srcHero = gs->getHero(srcHeroID);
@@ -2545,19 +2546,35 @@ bool CGameHandler::swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot,
 	if ((distance(srcHero->pos,destHero->pos) > 1.5 )|| (srcHero->tempOwner != destHero->tempOwner))
 		return false;
 
-	const CArtifact *srcArtifact = srcHero->getArt(srcSlot); 
+	const CArtifact *srcArtifact = srcHero->getArt(srcSlot);
 	const CArtifact *destArtifact = destHero->getArt(destSlot);
 
+	SetHeroArtifacts sha;
+	sha.hid = srcHeroID;
+	sha.artifacts = srcHero->artifacts;
+	sha.artifWorn = srcHero->artifWorn;
+
+	// Combinational artifacts needs to be removed first so they don't get denied movement because of their own locks.
+	if (srcHeroID == destHeroID && srcSlot < 19) {
+		VLC->arth->unequipArtifact(sha.artifWorn, srcSlot);
+		if (sha.artifWorn.find(destSlot) == sha.artifWorn.end())
+			destArtifact = NULL;
+	}
+
 	// Check if src/dest slots are appropriate for the artifacts exchanged.
 	// Moving to the backpack is always allowed.
 	if ((!srcArtifact || destSlot < 19)
-		&& (((srcArtifact && !vstd::contains(srcArtifact->possibleSlots, destSlot))
-			|| (destArtifact && srcSlot < 19 && !vstd::contains(destArtifact->possibleSlots, srcSlot)))))
+		&& (srcArtifact && !srcArtifact->fitsAt(srcHeroID == destHeroID ? sha.artifWorn : destHero->artifWorn, destSlot)))
 	{
 		complain("Cannot swap artifacts!");
 		return false;
 	}
 
+	if ((srcArtifact && srcArtifact->id == 145) || (destArtifact && destArtifact->id == 145)) {
+		complain("Cannot move artifact locks.");
+		return false;
+	}
+
 	if (destSlot >= 19 && srcArtifact->isBig()) {
 		complain("Cannot put big artifacts in backpack!");
 		return false;
@@ -2568,14 +2585,11 @@ bool CGameHandler::swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot,
 		return false;
 	}
 
-	// Perform the exchange.
-	SetHeroArtifacts sha;
-	sha.hid = srcHeroID;
-	sha.artifacts = srcHero->artifacts;
-	sha.artifWorn = srcHero->artifWorn;
+	// If dest does not fit in src, put it in dest's backpack instead.
+	bool destFits = !destArtifact || srcSlot >= 19 || destArtifact->fitsAt(sha.artifWorn, srcSlot);
 
 	sha.setArtAtPos(srcSlot, -1);
-	if (destSlot < 19 && (destArtifact || srcSlot < 19))
+	if (destSlot < 19 && (destArtifact || srcSlot < 19) && destFits)
 		sha.setArtAtPos(srcSlot, destHero->getArtAtPos(destSlot));
 
 	// Internal hero artifact arrangement.
@@ -2593,6 +2607,8 @@ bool CGameHandler::swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot,
 		sha.artifacts = destHero->artifacts;
 		sha.artifWorn = destHero->artifWorn;
 		sha.setArtAtPos(destSlot, srcArtifact ? srcArtifact->id : -1);
+		if (!destFits)
+			sha.setArtAtPos(sha.artifacts.size() + 19, destHero->getArtAtPos(destSlot));
 		sendAndApply(&sha);
 	}
 
@@ -3026,7 +3042,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 			sha.hid = h->id;
 			sha.artifacts = h->artifacts;
 			sha.artifWorn = h->artifWorn;
-			sha.artifWorn[17] = 0;
+			VLC->arth->equipArtifact(sha.artifWorn, 17, 0);
 			sendAndApply(&sha);
 		}
 
@@ -3063,9 +3079,9 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 		sha.hid = hero->id;
 		sha.artifacts = hero->artifacts;
 		sha.artifWorn = hero->artifWorn;
-		sha.artifWorn[13] = 4;
-		sha.artifWorn[14] = 5;
-		sha.artifWorn[15] = 6;
+		VLC->arth->equipArtifact(sha.artifWorn, 13, 4);
+		VLC->arth->equipArtifact(sha.artifWorn, 14, 5);
+		VLC->arth->equipArtifact(sha.artifWorn, 15, 6);
 		sendAndApply(&sha);
 	}
 	else if(message == "vcminahar") //1000000 movement points