浏览代码

* resting in town with mage guild will replenih all the mana points
* battle will end when one side has only war machines
* fixed crasbug occurring on revisiting objects (by pressing space)
* started writing support for artifacts

Michał W. Urbańczyk 16 年之前
父节点
当前提交
1983aee1b2
共有 16 个文件被更改,包括 445 次插入151 次删除
  1. 1 1
      CAdvmapInterface.h
  2. 1 1
      CCallback.cpp
  3. 20 48
      CHeroWindow.cpp
  4. 2 3
      CHeroWindow.h
  5. 3 3
      CPlayerInterface.cpp
  6. 15 3
      client/NetPacksClient.cpp
  7. 1 1
      global.h
  8. 197 11
      hch/CArtHandler.cpp
  9. 8 5
      hch/CArtHandler.h
  10. 41 33
      hch/CObjectHandler.cpp
  11. 2 1
      hch/CObjectHandler.h
  12. 43 4
      lib/HeroBonus.h
  13. 3 0
      lib/NetPacks.h
  14. 63 3
      lib/NetPacksLib.cpp
  15. 43 34
      server/CGameHandler.cpp
  16. 2 0
      server/CGameHandler.h

+ 1 - 1
CAdvmapInterface.h

@@ -101,6 +101,7 @@ public:
 /*****************************/
 class CAdvMapInt : public CMainInterface, public KeyInterested //adventure map interface
 {
+	void hide(); //deactivates advmap interface
 public:
 	CAdvMapInt(int Player);
 	~CAdvMapInt();
@@ -161,7 +162,6 @@ public:
 	void deactivate();
 
 	void show(SDL_Surface * to=NULL); //shows and activates adv. map interface
-	void hide(); //deactivates advmap interface
 	void update(); //redraws terrain
 
 	void select(const CArmedInstance *sel);

+ 1 - 1
CCallback.cpp

@@ -384,7 +384,7 @@ bool CCallback::swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGH
 	if(player!=hero1->tempOwner || player!=hero2->tempOwner)
 		return false;
 
-	ExchangeArtifacts ea(hero1->id, pos1, hero2->id, pos2);
+	ExchangeArtifacts ea(hero1->id, hero2->id, pos1, pos2);
 	*cl->serv << &ea;
 	return true;
 }

+ 20 - 48
CHeroWindow.cpp

@@ -57,8 +57,8 @@ CHeroWindow::CHeroWindow(int playerColor):
 	gar2button = new CHighlightableButton(0, 0, map_list_of(0,CGI->generaltexth->heroscrn[26])(3,CGI->generaltexth->heroscrn[25]), CGI->generaltexth->heroscrn[31], false, "hsbtns8.def", NULL, pos.x+604, pos.y+491, SDLK_b);
 	gar4button = new AdventureMapButton(CGI->generaltexth->allTexts[256], CGI->generaltexth->heroscrn[32], boost::function<void()>(), pos.x+604, pos.y+527, "hsbtns9.def", false, NULL, false);
 	boost::algorithm::replace_first(gar4button->hoverTexts[0],"%s",CGI->generaltexth->allTexts[43]);
-	leftArtRoll = new AdventureMapButton(std::string(), std::string(), boost::bind(&CHeroWindow::leftArtRoller,this), pos.x+379, pos.y+364, "hsbtns3.def", SDLK_LEFT);
-	rightArtRoll = new AdventureMapButton(std::string(), std::string(), boost::bind(&CHeroWindow::rightArtRoller,this), pos.x+632, pos.y+364, "hsbtns5.def", SDLK_RIGHT);
+	leftArtRoll = new AdventureMapButton(std::string(), std::string(), boost::bind(&CHeroWindow::scrollBackpack,this,-1), pos.x+379, pos.y+364, "hsbtns3.def", SDLK_LEFT);
+	rightArtRoll = new AdventureMapButton(std::string(), std::string(), boost::bind(&CHeroWindow::scrollBackpack,this,+1), pos.x+632, pos.y+364, "hsbtns5.def", SDLK_RIGHT);
 
 
 	for(int g=0; g<8; ++g)
@@ -268,6 +268,7 @@ void CHeroWindow::setHero(const CGHeroInstance *Hero)
 	backpack.clear();
 
 	std::vector<SDL_Rect> slotPos;
+	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), 
@@ -501,56 +502,26 @@ void CHeroWindow::dismissCurrent()
 void CHeroWindow::questlog()
 {
 }
-void CHeroWindow::leftArtRoller()
+
+void CHeroWindow::scrollBackpack(int dir)
 {
-	if(curHero->artifacts.size()>5) //if it is <=5, we have nothing to scroll
-	{
-		backpackPos+=curHero->artifacts.size()-1; //set new offset
+	backpackPos += dir + curHero->artifacts.size();
+	backpackPos %= curHero->artifacts.size();
 
-		for(size_t s=0; s<5 && s<curHero->artifacts.size(); ++s) //set new data
-		{
-			backpack[s]->ourArt = &CGI->arth->artifacts[curHero->artifacts[(s+backpackPos) % curHero->artifacts.size() ]];
-			if(backpack[s]->ourArt)
-				backpack[s]->text = backpack[s]->ourArt->Description();
-			else
-				backpack[s]->text = std::string();
-		}
-	}
-}
 
-void CHeroWindow::rightArtRoller()
-{
-	if(curHero->artifacts.size()>5) //if it is <=5, we have nothing to scroll
+	for(size_t s=0; s<5 && s<curHero->artifacts.size(); ++s) //set new data
 	{
-		backpackPos+=1; //set new offset
+		CArtPlace *cur = backpack[s];
+		cur->slotID = 19+((s+backpackPos)%curHero->artifacts.size());
+		cur->ourArt = curHero->getArt(cur->slotID);
 
-		for(size_t s=0; s<5 && s<curHero->artifacts.size(); ++s) //set new data
-		{
-			backpack[s]->ourArt = &CGI->arth->artifacts[curHero->artifacts[(s+backpackPos) % curHero->artifacts.size() ] ];
-			if(backpack[s]->ourArt)
-				backpack[s]->text = backpack[s]->ourArt->Description();
-			else
-				backpack[s]->text = std::string();
-		}
+		if(cur->ourArt)
+			cur->text = cur->ourArt->Description();
+		else
+			cur->text = std::string();
 	}
 }
 
-void CHeroWindow::switchHero()
-{
-	//int y;
-	//SDL_GetMouseState(NULL, &y);
-	//for(int g=0; g<heroListMi.size(); ++g)
-	//{
-	//	if(y>=94+54*g)
-	//	{
-	//		//quit();
-	//		setHero(LOCPLINT->cb->getHeroInfo(player, g, false));
-	//		//LOCPLINT->openHeroWindow(curHero);
-	//		redrawCurBack();
-	//	}
-	//}
-}
-
 void CHeroWindow::redrawCurBack()
 {
 	if(curBack)
@@ -754,7 +725,10 @@ void CArtPlace::clickLeft(boost::logic::tribool down)
 			//chceck if swap is possible
 			if(this->fitsHere(ourWindow->activeArtPlace->ourArt) && ourWindow->activeArtPlace->fitsHere(this->ourArt))
 			{
-				LOCPLINT->cb->swapArtifacts(ourWindow->curHero,slotID,ourWindow->curHero,ourWindow->activeArtPlace->slotID);
+				int destSlot = slotID,
+					srcSlot = ourWindow->activeArtPlace->slotID;
+
+				LOCPLINT->cb->swapArtifacts(ourWindow->curHero,destSlot,ourWindow->curHero,srcSlot);
 
 				const CArtifact * pmh = ourArt;
 				ourArt = ourWindow->activeArtPlace->ourArt;
@@ -786,9 +760,7 @@ void CArtPlace::clickLeft(boost::logic::tribool down)
 				}
 				if(backID>=0) //put to backpack
 				{
-					/*ourWindow->activeArtPlace->ourArt = NULL;
-					ourWindow->activeArtPlace->clicked = false;
-					ourWindow->activeArtPlace = NULL;*/
+					LOCPLINT->cb->swapArtifacts(ourWindow->curHero,ourWindow->activeArtPlace->slotID,ourWindow->curHero,ourWindow->curHero->artifacts.size()+19);
 				}
 			}
 		}

+ 2 - 3
CHeroWindow.h

@@ -123,9 +123,8 @@ public:
 	void redrawCurBack(); //redraws curBAck from scratch
 	void quit(); //stops displaying hero window
 	void dismissCurrent(); //dissmissed currently displayed hero (curHero)
-	void questlog(); //show quest log in hero window
-	void leftArtRoller(); //scrolls artifacts in bag left
-	void rightArtRoller(); //scrolls artifacts in bag right
+	void questlog(); //show quest log in hero window
+	void scrollBackpack(int dir); //dir==-1 => to left; dir==-2 => to right
 	void switchHero(); //changes displayed hero
 
 	//friends

+ 3 - 3
CPlayerInterface.cpp

@@ -1214,7 +1214,7 @@ void CPlayerInterface::yourTurn()
 		pim->unlock();
 		SDL_framerateDelay(mainFPSmng);
 	}
-	adventureInt->hide();
+	adventureInt->deactivate();
 	cb->endTurn();
 }
 
@@ -1785,7 +1785,7 @@ void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
 }
 void CPlayerInterface::openTownWindow(const CGTownInstance * town)
 {
-	adventureInt->hide();
+	adventureInt->deactivate();
 	//timeHandler t;
 	//t.getDif();
 	castleInt = new CCastleInterface(town,true);
@@ -2035,7 +2035,7 @@ void CPlayerInterface::showSelDialog(const std::string &text, const std::vector<
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	LOCPLINT->showingDialog->setn(true);
-	adventureInt->hide(); //dezaktywacja starego interfejsu
+	adventureInt->deactivate(); //dezaktywacja starego interfejsu
 	std::vector<CSelectableComponent*> intComps;
 	for(int i=0;i<components.size();i++)
 		intComps.push_back(new CSelectableComponent(*components[i])); //will be deleted by CSelWindow::close

+ 15 - 3
client/NetPacksClient.cpp

@@ -211,9 +211,21 @@ void SetHeroesInTown::applyCl( CClient *cl )
 
 void SetHeroArtifacts::applyCl( CClient *cl )
 {
-	CGHeroInstance *t = GS(cl)->getHero(hid);
-	if(vstd::contains(cl->playerint,t->tempOwner))
-		cl->playerint[t->tempOwner]->heroArtifactSetChanged(t);
+	CGHeroInstance *h = GS(cl)->getHero(hid);
+	CGameInterface *player = (vstd::contains(cl->playerint,h->tempOwner) ? cl->playerint[h->tempOwner] : NULL);
+	if(!player)
+		return;
+
+	player->heroArtifactSetChanged(h);
+
+	BOOST_FOREACH(HeroBonus *bonus, gained)
+	{
+		player->heroBonusChanged(h,*bonus,true);
+	}
+	BOOST_FOREACH(HeroBonus *bonus, lost)
+	{
+		player->heroBonusChanged(h,*bonus,false);
+	}
 }
 
 void HeroRecruited::applyCl( CClient *cl )

+ 1 - 1
global.h

@@ -19,7 +19,7 @@ typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 #define THC
 #endif
 
-#define NAME_VER ("VCMI 0.7d2")
+#define NAME_VER ("VCMI 0.71b")
 #define CONSOLE_LOGGING_LEVEL 5
 #define FILE_LOGGING_LEVEL 6
 

+ 197 - 11
hch/CArtHandler.cpp

@@ -8,6 +8,22 @@
 #include "../lib/VCMI_Lib.h"
 extern CLodHandler *bitmaph;
 using namespace boost::assign;
+
+const std::string & CArtifact::Name() const
+{
+	if(name.size())
+		return name;
+	else
+		return VLC->generaltexth->artifNames[id];
+}
+
+const std::string & CArtifact::Description() const
+{
+	if(description.size())
+		return description;
+	else
+		return VLC->generaltexth->artifDescriptions[id];
+}
 CArtHandler::CArtHandler()
 {
 	VLC->arth = this;
@@ -46,6 +62,7 @@ void CArtHandler::loadArtifacts(bool onlyTxt)
 			artifacts.push_back(nart);
 	}
 	sortArts();
+	addBonuses();
 }
 
 int CArtHandler::convertMachineID(int id, bool creToArt )
@@ -101,18 +118,187 @@ void CArtHandler::sortArts()
 	}
 }
 
-const std::string & CArtifact::Name() const
+void CArtHandler::giveArtBonus( int aid, HeroBonus::BonusType type, int val, int subtype )
 {
-	if(name.size())
-		return name;
-	else
-		return VLC->generaltexth->artifNames[id];
+	artifacts[aid].bonuses.push_back(HeroBonus(HeroBonus::PERMANENT,type,HeroBonus::ARTIFACT,val,aid,subtype));
 }
 
-const std::string & CArtifact::Description() const
+void CArtHandler::addBonuses()
 {
-	if(description.size())
-		return description;
-	else
-		return VLC->generaltexth->artifDescriptions[id];
-}
+	#define ART_PRIM_SKILL(ID, whichSkill, val) giveArtBonus(ID,HeroBonus::PRIMARY_SKILL,val,whichSkill)
+	#define ART_MORALE(ID, val) giveArtBonus(ID,HeroBonus::MORALE,val)
+	#define ART_LUCK(ID, val) giveArtBonus(ID,HeroBonus::LUCK,val)
+	#define ART_MORALE_AND_LUCK(ID, val) giveArtBonus(ID,HeroBonus::MORALE_AND_LUCK,val)
+	#define ART_ALL_PRIM_SKILLS(ID, val) ART_PRIM_SKILL(ID,0,val); ART_PRIM_SKILL(ID,1,val); ART_PRIM_SKILL(ID,2,val); ART_PRIM_SKILL(ID,3,val)
+	#define ART_ATTACK_AND_DEFENSE(ID, val) ART_PRIM_SKILL(ID,0,val); ART_PRIM_SKILL(ID,1,val)
+	#define ART_POWER_AND_KNOWLEDGE(ID, val) ART_PRIM_SKILL(ID,2,val); ART_PRIM_SKILL(ID,3,val)
+
+	//Attack bonus artifacts (Weapons)
+	ART_PRIM_SKILL(7,0,+2); //Centaur Axe
+	ART_PRIM_SKILL(8,0,+3);
+	ART_PRIM_SKILL(9,0,+4);
+	ART_PRIM_SKILL(10,0,+5);
+	ART_PRIM_SKILL(11,0,+6);
+	ART_PRIM_SKILL(12,0,+12);
+	ART_PRIM_SKILL(12,1,-3);
+
+	//Defense bonus artifacts (Shields)
+	ART_PRIM_SKILL(13,1,+2); //Shield of the Dwarven Lords
+	ART_PRIM_SKILL(14,1,+3); 
+	ART_PRIM_SKILL(15,1,+4); 
+	ART_PRIM_SKILL(16,1,+5); 
+	ART_PRIM_SKILL(17,1,+6); 
+	ART_PRIM_SKILL(18,1,+12); 
+	ART_PRIM_SKILL(18,0,-3); 
+
+	//Knowledge bonus artifacts (Helmets)
+	ART_PRIM_SKILL(19,3,+1); //Helm of the Alabaster Unicorn 
+	ART_PRIM_SKILL(20,3,+2); 
+	ART_PRIM_SKILL(21,3,+3); 
+	ART_PRIM_SKILL(22,3,+4); 
+	ART_PRIM_SKILL(23,3,+5); 
+	ART_PRIM_SKILL(24,3,+10); 
+	ART_PRIM_SKILL(24,2,-2); 
+
+	//Spell power bonus artifacts (Armours)
+	ART_PRIM_SKILL(25,2,+1); //Breastplate of Petrified Wood
+	ART_PRIM_SKILL(26,2,+2); 
+	ART_PRIM_SKILL(27,2,+3); 
+	ART_PRIM_SKILL(28,2,+4); 
+	ART_PRIM_SKILL(29,2,+5); 
+	ART_PRIM_SKILL(30,2,+10); 
+	ART_PRIM_SKILL(30,3,-2); 
+
+	//All primary skills (various)
+	ART_ALL_PRIM_SKILLS(31,+1); //Armor of Wonder
+	ART_ALL_PRIM_SKILLS(32,+2); 
+	ART_ALL_PRIM_SKILLS(33,+3); 
+	ART_ALL_PRIM_SKILLS(34,+4); 
+	ART_ALL_PRIM_SKILLS(35,+5); 
+	ART_ALL_PRIM_SKILLS(36,+6); //Helm of Heavenly Enlightenment
+
+	//Attack and Defense (various)
+	ART_ATTACK_AND_DEFENSE(37,+1); //Quiet Eye of the Dragon
+	ART_ATTACK_AND_DEFENSE(38,+2); 
+	ART_ATTACK_AND_DEFENSE(39,+3); 
+	ART_ATTACK_AND_DEFENSE(40,+4); 
+
+	//Spell power and Knowledge (various)
+	ART_POWER_AND_KNOWLEDGE(41,+1); //Dragonbone Greaves
+	ART_POWER_AND_KNOWLEDGE(42,+2); 
+	ART_POWER_AND_KNOWLEDGE(43,+3); 
+	ART_POWER_AND_KNOWLEDGE(44,+4); 
+
+	//Luck and morale 
+	ART_MORALE(45,+1); //Still Eye of the Dragon
+	ART_LUCK(46,+1); //Clover of Fortune
+	ART_LUCK(47,+1); //Cards of Prophecy
+	ART_LUCK(48,+1); //Ladybird of Luck
+	ART_MORALE(49,+1); //Badge of Courage
+	ART_MORALE(50,+1); //Crest of Valor
+	ART_MORALE(51,+1); //Glyph of Gallantry
+
+	giveArtBonus(52,HeroBonus::SIGHT_RADIOUS,+1);//Speculum
+	giveArtBonus(53,HeroBonus::SIGHT_RADIOUS,+1);//Spyglass
+
+	//necromancy bonus
+	giveArtBonus(54,HeroBonus::SECONDARY_SKILL_PREMY,+5,12);//Amulet of the Undertaker
+	giveArtBonus(55,HeroBonus::SECONDARY_SKILL_PREMY,+10,12);//Vampire's Cowl
+	giveArtBonus(56,HeroBonus::SECONDARY_SKILL_PREMY,+15,12);//Dead Man's Boots
+
+	giveArtBonus(57,HeroBonus::MAGIC_RESISTANCE,+5);//Garniture of Interference
+	giveArtBonus(58,HeroBonus::MAGIC_RESISTANCE,+10);//Surcoat of Counterpoise
+	giveArtBonus(59,HeroBonus::MAGIC_RESISTANCE,+15);//Boots of Polarity
+
+	//archery bonus
+	giveArtBonus(60,HeroBonus::SECONDARY_SKILL_PREMY,+5,1);//Bow of Elven Cherrywood
+	giveArtBonus(61,HeroBonus::SECONDARY_SKILL_PREMY,+10,1);//Bowstring of the Unicorn's Mane
+	giveArtBonus(62,HeroBonus::SECONDARY_SKILL_PREMY,+15,1);//Angel Feather Arrows
+
+	//eagle eye bonus
+	giveArtBonus(63,HeroBonus::SECONDARY_SKILL_PREMY,+5,11);//Bird of Perception
+	giveArtBonus(64,HeroBonus::SECONDARY_SKILL_PREMY,+10,11);//Stoic Watchman
+	giveArtBonus(65,HeroBonus::SECONDARY_SKILL_PREMY,+15,11);//Emblem of Cognizance
+
+	//reducing cost of surrendering
+	giveArtBonus(66,HeroBonus::SURRENDER_DISCOUNT,+10);//Statesman's Medal
+	giveArtBonus(67,HeroBonus::SURRENDER_DISCOUNT,+10);//Diplomat's Ring
+	giveArtBonus(68,HeroBonus::SURRENDER_DISCOUNT,+10);//Ambassador's Sash
+
+	giveArtBonus(69,HeroBonus::STACKS_SPEED,+1);//Ring of the Wayfarer
+
+	giveArtBonus(70,HeroBonus::LAND_MOVEMENT,+300);//Equestrian's Gloves
+	giveArtBonus(71,HeroBonus::SEA_MOVEMENT,+1000);//Necklace of Ocean Guidance
+	giveArtBonus(72,HeroBonus::FLYING_MOVEMENT,+1);//Angel Wings
+
+	giveArtBonus(73,HeroBonus::MANA_REGENERATION,+1);//Charm of Mana
+	giveArtBonus(74,HeroBonus::MANA_REGENERATION,+2);//Talisman of Mana
+	giveArtBonus(75,HeroBonus::MANA_REGENERATION,+3);//Mystic Orb of Mana
+
+	giveArtBonus(76,HeroBonus::SPELL_DURATION,+1);//Collar of Conjuring
+	giveArtBonus(77,HeroBonus::SPELL_DURATION,+2);//Ring of Conjuring
+	giveArtBonus(78,HeroBonus::SPELL_DURATION,+3);//Cape of Conjuring
+
+	giveArtBonus(79,HeroBonus::AIR_SPELL_DMG_PREMY,+50);//Orb of the Firmament
+	giveArtBonus(80,HeroBonus::EARTH_SPELL_DMG_PREMY,+50);//Orb of Silt
+	giveArtBonus(81,HeroBonus::FIRE_SPELL_DMG_PREMY,+50);//Orb of Tempestuous Fire
+	giveArtBonus(82,HeroBonus::WATER_SPELL_DMG_PREMY,+50);//Orb of Driving Rain
+
+	giveArtBonus(83,HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL,3);//Recanter's Cloak
+	giveArtBonus(84,HeroBonus::BLOCK_MORALE,0);//Spirit of Oppression
+	giveArtBonus(85,HeroBonus::BLOCK_LUCK,0);//Hourglass of the Evil Hour
+
+	giveArtBonus(86,HeroBonus::FIRE_SPELLS,0);//Tome of Fire Magic
+	giveArtBonus(87,HeroBonus::AIR_SPELLS,0);//Tome of Air Magic
+	giveArtBonus(88,HeroBonus::WATER_SPELLS,0);//Tome of Water Magic
+	giveArtBonus(89,HeroBonus::EARTH_SPELLS,0);//Tome of Earth Magic
+
+	giveArtBonus(90,HeroBonus::WATER_WALKING,0);//Boots of Levitation
+	giveArtBonus(91,HeroBonus::NO_SHOTING_PENALTY,0);//Golden Bow
+	giveArtBonus(92,HeroBonus::DISPEL_IMMUNITY,0);//Sphere of Permanence
+	giveArtBonus(93,HeroBonus::NEGATE_ALL_NATURAL_IMMUNITIES,0);//Orb of Vulnerability
+
+	giveArtBonus(94,HeroBonus::STACK_HEALTH,+1);//Ring of Vitality
+	giveArtBonus(95,HeroBonus::STACK_HEALTH,+1);//Ring of Life
+	giveArtBonus(96,HeroBonus::STACK_HEALTH,+2);//Vial of Lifeblood
+
+	giveArtBonus(97,HeroBonus::STACKS_SPEED,+1);//Necklace of Swiftness
+	giveArtBonus(98,HeroBonus::LAND_MOVEMENT,+600);//Boots of Speed
+	giveArtBonus(99,HeroBonus::STACKS_SPEED,+1);//Cape of Velocity
+
+	giveArtBonus(100,HeroBonus::SPELL_IMMUNITY,59);//Pendant of Dispassion
+	giveArtBonus(101,HeroBonus::SPELL_IMMUNITY,62);//Pendant of Second Sight
+	giveArtBonus(102,HeroBonus::SPELL_IMMUNITY,42);//Pendant of Holiness
+	giveArtBonus(103,HeroBonus::SPELL_IMMUNITY,24);//Pendant of Life
+	giveArtBonus(104,HeroBonus::SPELL_IMMUNITY,25);//Pendant of Death
+	giveArtBonus(105,HeroBonus::SPELL_IMMUNITY,60);//Pendant of Free Will
+	giveArtBonus(106,HeroBonus::SPELL_IMMUNITY,17);//Pendant of Negativity
+	giveArtBonus(107,HeroBonus::SPELL_IMMUNITY,61);//Pendant of Total Recall
+	giveArtBonus(108,HeroBonus::MORALE_AND_LUCK,+3);//Pendant of Courage
+
+	giveArtBonus(109,HeroBonus::GENERATE_RESOURCE,+1,4); //Everflowing Crystal Cloak
+	giveArtBonus(110,HeroBonus::GENERATE_RESOURCE,+1,5); //Ring of Infinite Gems
+	giveArtBonus(111,HeroBonus::GENERATE_RESOURCE,+1,1); //Everpouring Vial of Mercury
+	giveArtBonus(112,HeroBonus::GENERATE_RESOURCE,+1,2); //Inexhaustible Cart of Ore
+	giveArtBonus(113,HeroBonus::GENERATE_RESOURCE,+1,3); //Eversmoking Ring of Sulfur
+	giveArtBonus(114,HeroBonus::GENERATE_RESOURCE,+1,0); //Inexhaustible Cart of Lumber
+	giveArtBonus(115,HeroBonus::GENERATE_RESOURCE,+1000,6); //Endless Sack of Gold
+	giveArtBonus(116,HeroBonus::GENERATE_RESOURCE,+750,6); //Endless Bag of Gold
+	giveArtBonus(117,HeroBonus::GENERATE_RESOURCE,+500,6); //Endless Purse of Gold
+
+	giveArtBonus(118,HeroBonus::CREATURE_GROWTH,+5,2); //Legs of Legion
+	giveArtBonus(119,HeroBonus::CREATURE_GROWTH,+4,3); //Loins of Legion
+	giveArtBonus(120,HeroBonus::CREATURE_GROWTH,+3,4); //Torso of Legion
+	giveArtBonus(121,HeroBonus::CREATURE_GROWTH,+2,5); //Arms of Legion
+	giveArtBonus(122,HeroBonus::CREATURE_GROWTH,+1,6); //Head of Legion
+
+	//Sea Captain's Hat 
+	giveArtBonus(123,HeroBonus::WHIRLPOOL_PROTECTION,0); 
+	giveArtBonus(123,HeroBonus::SEA_MOVEMENT,+500); 
+	giveArtBonus(123,HeroBonus::SPELL,3,0); 
+	giveArtBonus(123,HeroBonus::SPELL,3,1); 
+
+	giveArtBonus(124,HeroBonus::SPELLS_OF_LEVEL,3,1); //Spellbinder's Hat
+	giveArtBonus(125,HeroBonus::ENEMY_CANT_ESCAPE,0); //Shackles of War
+	giveArtBonus(126,HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL,0);//Orb of Inhibition
+}

+ 8 - 5
hch/CArtHandler.h

@@ -1,6 +1,8 @@
 #ifndef __CARTHANDLER_H__
 #define __CARTHANDLER_H__
 #include "../global.h"
+#include "../lib/HeroBonus.h"
+#include <list>
 #include <string>
 #include <vector>
 
@@ -13,28 +15,29 @@ class DLL_EXPORT CArtifact //container for artifacts
 public:
 	const std::string &Name() const;
 	const std::string &Description() const;
-	bool isAllowed; //true if we can use this artifact (map information)
-	//std::string desc2;
-	unsigned int price;
+
+	ui32 price;
 	std::vector<ui16> possibleSlots; //ids of slots where artifact can be placed
-	//bool spellBook, warMachine1, warMachine2, warMachine3, warMachine4, misc1, misc2, misc3, misc4, misc5, feet, lRing, rRing, torso, lHand, rHand, neck, shoulders, head;
 	EartClass aClass;
 	int id;
+	std::list<HeroBonus> bonuses; //bonuses given by artifact
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & isAllowed & name & description & price & possibleSlots & aClass & id ;
+		h & name & description & price & possibleSlots & aClass & id & bonuses;
 	}
 };
 
 class DLL_EXPORT CArtHandler //handles artifacts
 {
+	void giveArtBonus(int aid, HeroBonus::BonusType type, int val, int subtype = -1);
 public:
 	std::vector<CArtifact*> treasures, minors, majors, relics;
 	std::vector<CArtifact> artifacts;
 
 	void loadArtifacts(bool onlyTxt);
 	void sortArts();
+	void addBonuses();
 	static int convertMachineID(int id, bool creToArt);
 	CArtHandler();
 

+ 41 - 33
hch/CObjectHandler.cpp

@@ -348,7 +348,11 @@ bool CGHeroInstance::canWalkOnSea() const
 }
 int CGHeroInstance::getPrimSkillLevel(int id) const
 {
-	return primSkills[id];
+	int ret = primSkills[id];
+	for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
+		if(i->type == HeroBonus::PRIMARY_SKILL && i->subtype==id)
+			ret += i->val;
+	return ret;
 }
 ui8 CGHeroInstance::getSecSkillLevel(const int & ID) const
 {
@@ -359,24 +363,23 @@ ui8 CGHeroInstance::getSecSkillLevel(const int & ID) const
 }
 int CGHeroInstance::maxMovePoints(bool onLand) const
 {
-	int ret = 1270+70*lowestSpeed(this);
-	if (ret>2000) 
-		ret=2000;
+	int ret = std::max(2000, 1270+70*lowestSpeed(this)),
+		bonus = valOfBonuses(HeroBonus::MOVEMENT) + (onLand ? valOfBonuses(HeroBonus::LAND_MOVEMENT) : valOfBonuses(HeroBonus::SEA_MOVEMENT));
 
-	double bonus = 0;
+	double modifier = 0;
 	if(onLand)
 	{
 		//logistics:
 		switch(getSecSkillLevel(2))
 		{
 		case 1:
-			bonus = 0.1;
+			modifier = 0.1;
 			break;
 		case 2:
-			bonus = 0.2;
+			modifier = 0.2;
 			break;
 		case 3:
-			bonus = 0.3;
+			modifier = 0.3;
 			break;
 		}
 	}
@@ -386,18 +389,19 @@ int CGHeroInstance::maxMovePoints(bool onLand) const
 		switch(getSecSkillLevel(2))
 		{
 		case 1:
-			bonus = 0.5;
+			modifier = 0.5;
 			break;
 		case 2:
-			bonus = 1.0;
+			modifier = 1.0;
 			break;
 		case 3:
-			bonus = 1.5;
+			modifier = 1.5;
 			break;
 		}
 	}
-	return int(ret + ret*bonus);
+	return int(ret + ret*modifier) + bonus;
 }
+
 ui32 CGHeroInstance::getArtAtPos(ui16 pos) const
 {
 	if(pos<19)
@@ -411,26 +415,7 @@ ui32 CGHeroInstance::getArtAtPos(ui16 pos) const
 		else 
 			return -1;
 }
-void CGHeroInstance::setArtAtPos(ui16 pos, int art)
-{
-	if(art<0)
-	{
-		if(pos<19)
-			artifWorn.erase(pos);
-		else
-			artifacts -= artifacts[pos-19];
-	}
-	else
-	{
-		if(pos<19)
-			artifWorn[pos] = art;
-		else
-			if(pos-19 < artifacts.size())
-				artifacts[pos-19] = art;
-			else
-				artifacts.push_back(art);
-	}
-}
+
 const CArtifact * CGHeroInstance::getArt(int pos) const
 {
 	int id = getArtAtPos(pos);
@@ -554,6 +539,15 @@ void CGHeroInstance::initHero()
 	hoverName = VLC->generaltexth->allTexts[15];
 	boost::algorithm::replace_first(hoverName,"%s",name);
 	boost::algorithm::replace_first(hoverName,"%s", type->heroClass->name);
+
+	//clear all bonuses from artifacts and give those from artifacts
+	std::remove_if(bonuses.begin(), bonuses.end(), boost::bind(HeroBonus::IsFrom,_1,HeroBonus::ARTIFACT,0xffffff));
+	for (std::map<ui16,ui32>::iterator ari = artifWorn.begin(); ari != artifWorn.end(); ari++)
+	{
+		CArtifact &art = VLC->arth->artifacts[ari->second];
+		for(std::list<HeroBonus>::iterator i = art.bonuses.begin(); i != art.bonuses.end(); i++)
+			bonuses.push_back(*i);
+	}
 }
 
 void CGHeroInstance::initHeroDefInfo()
@@ -766,7 +760,21 @@ int3 CGHeroInstance::getSightCenter() const
 
 int CGHeroInstance::getSightRadious() const
 {
-	return 5 + getSecSkillLevel(3); //default + scouting
+	return 5 + getSecSkillLevel(3) + valOfBonuses(HeroBonus::SIGHT_RADIOUS); //default + scouting
+}
+
+si32 CGHeroInstance::manaRegain() const
+{
+	return 1 + getSecSkillLevel(8) + valOfBonuses(HeroBonus::MANA_REGENERATION); //1 + Mysticism level 
+}
+
+int CGHeroInstance::valOfBonuses( HeroBonus::BonusType type ) const
+{
+	int ret = 0;
+	for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
+		if(i->type == type)
+			ret += i->val;
+	return ret;
 }
 
 int CGTownInstance::getSightRadious() const //returns sight distance

+ 2 - 1
hch/CObjectHandler.h

@@ -229,6 +229,7 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 	const HeroBonus *getBonus(int from, int id) const;
+	int valOfBonuses(HeroBonus::BonusType type) const;
 	const std::string &getBiography() const;
 	bool needsLastStack()const;
 	unsigned int getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
@@ -237,6 +238,7 @@ public:
 	float getMultiplicativeMoveBonus() const;
 	int3 getPosition(bool h3m) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
 	si32 manaLimit() const; //maximum mana value for this hero (basically 10*knowledge)
+	si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
 	bool canWalkOnSea() const;
 	int getCurrentLuck(int stack=-1, bool town=false) const;
 	std::vector<std::pair<int,std::string> > getCurrentLuckModifiers(int stack=-1, bool town=false) const; //args as above
@@ -246,7 +248,6 @@ public:
 	ui8 getSecSkillLevel(const int & ID) const; //0 - no skill
 	int maxMovePoints(bool onLand) const;
 	ui32 getArtAtPos(ui16 pos) const; //-1 - no artifact
-	void setArtAtPos(ui16 pos, int art);
 	const CArtifact * getArt(int pos) const;
 	int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
 	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest

+ 43 - 4
lib/HeroBonus.h

@@ -4,21 +4,56 @@
 
 struct DLL_EXPORT HeroBonus
 {
-	enum BonusType{NONE, MOVEMENT, LAND_MOVEMENT, SEA_MOVEMENT, MORALE, LUCK, MORALE_AND_LUCK};
+	enum BonusType
+	{
+		//handled
+		NONE, 
+		MOVEMENT, //both water/land
+		LAND_MOVEMENT, 
+		SEA_MOVEMENT, 
+		MORALE, 
+		LUCK, 
+		MORALE_AND_LUCK, 
+		PRIMARY_SKILL, //uses subtype to pick skill
+		SIGHT_RADIOUS, 
+		MANA_REGENERATION, //points per turn apart from normal (1 + mysticism)
+		//not handled yet:
+		MAGIC_RESISTANCE, // %
+		SECONDARY_SKILL_PREMY, //%
+		SURRENDER_DISCOUNT, //%
+		STACKS_SPEED,
+		FLYING_MOVEMENT, SPELL_DURATION, AIR_SPELL_DMG_PREMY, EARTH_SPELL_DMG_PREMY, FIRE_SPELL_DMG_PREMY, 
+		WATER_SPELL_DMG_PREMY, BLOCK_SPELLS_ABOVE_LEVEL, WATER_WALKING, NO_SHOTING_PENALTY, DISPEL_IMMUNITY, 
+		NEGATE_ALL_NATURAL_IMMUNITIES, STACK_HEALTH, SPELL_IMMUNITY, BLOCK_MORALE, BLOCK_LUCK, FIRE_SPELLS,
+		AIR_SPELLS, WATER_SPELLS, EARTH_SPELLS, 
+		GENERATE_RESOURCE, //daily value, uses subtype (resource type)
+		CREATURE_GROWTH, //for legion artifacts: value - week growth bonus, subtype - monster level
+		WHIRLPOOL_PROTECTION, //hero won't lose army when teleporting through whirlpool
+		SPELL, //hero knows spell, val - skill level (0 - 3), subtype - spell id
+		SPELLS_OF_LEVEL, //hero knows all spells of given level, val - skill level; subtype - level
+		ENEMY_CANT_ESCAPE //for shackles of war
+	};
 	enum BonusDuration{PERMANENT, ONE_BATTLE, ONE_DAY, ONE_WEEK};
 	enum BonusSource{ARTIFACT, OBJECT};
 
 	ui8 duration; //uses BonusDuration values
 	ui8 type; //uses BonusType values - says to what is this bonus
+	si32 subtype; //-1 if not applicable
 	ui8 source;//uses BonusSource values - what gave that bonus
 	si32 val;//for morale/luck [-3,+3], others any
 	ui32 id; //id of object/artifact
 	std::string description; 
 
-	HeroBonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, std::string Desc)
-		:duration(Dur), type(Type), source(Src), val(Val), id(ID), description(Desc)
+	HeroBonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1)
+		:duration(Dur), type(Type), source(Src), val(Val), id(ID), description(Desc), subtype(Subtype)
+	{}
+	HeroBonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype=-1)
+		:duration(Dur), type(Type), source(Src), val(Val), id(ID), subtype(Subtype)
 	{}
-	HeroBonus(){};
+	HeroBonus()
+	{
+		subtype = -1;
+	}
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & duration & type & source & val & id & description;
@@ -36,4 +71,8 @@ struct DLL_EXPORT HeroBonus
 	{
 		return hb.duration==HeroBonus::ONE_BATTLE;
 	}
+	static bool IsFrom(const HeroBonus &hb, ui8 source, ui32 id) //if id==0xffffff then id doesn't matter
+	{
+		return hb.source==source && (id==0xffffff  ||  hb.id==id);
+	}
 };

+ 3 - 0
lib/NetPacks.h

@@ -404,6 +404,7 @@ struct SetHeroArtifacts : public CPackForClient //509
 	SetHeroArtifacts(){type = 509;};
 	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
+	DLL_EXPORT void setArtAtPos(ui16 pos, int art);
 
 	si32 hid;
 	std::vector<ui32> artifacts; //hero's artifacts from bag
@@ -413,6 +414,8 @@ struct SetHeroArtifacts : public CPackForClient //509
 	{
 		h & hid & artifacts & artifWorn;
 	}
+
+	std::vector<HeroBonus*> gained, lost; //used locally as hlp when applying
 };   
 
 struct HeroRecruited : public CPackForClient //515

+ 63 - 3
lib/NetPacksLib.cpp

@@ -2,6 +2,7 @@
 #include "../lib/NetPacks.h"
 #include "../hch/CGeneralTextHandler.h"
 #include "../hch/CDefObjInfoHandler.h"
+#include "../hch/CArtHandler.h"
 #include "../hch/CHeroHandler.h"
 #include "../hch/CObjectHandler.h"
 #include "../lib/VCMI_Lib.h"
@@ -272,10 +273,68 @@ DLL_EXPORT void SetHeroesInTown::applyGs( CGameState *gs )
 DLL_EXPORT void SetHeroArtifacts::applyGs( CGameState *gs )
 {
 	CGHeroInstance *h = gs->getHero(hid);
+	std::vector<ui32> equiped, unequiped;
+	for(std::map<ui16,ui32>::const_iterator i = h->artifWorn.begin(); i != h->artifWorn.end(); i++)
+		if(!vstd::contains(artifWorn,i->first)  ||  artifWorn[i->first] != i->second)
+			unequiped.push_back(i->second);
+
+	for(std::map<ui16,ui32>::const_iterator i = artifWorn.begin(); i != artifWorn.end(); i++)
+		if(!vstd::contains(h->artifWorn,i->first)  ||  h->artifWorn[i->first] != i->second)
+			equiped.push_back(i->second);
+
 	h->artifacts = artifacts;
 	h->artifWorn = artifWorn;
+
+	BOOST_FOREACH(ui32 id, unequiped)
+	{
+		while(1)
+		{
+			std::list<HeroBonus>::iterator hlp = std::find_if(h->bonuses.begin(),h->bonuses.end(),boost::bind(HeroBonus::IsFrom,_1,HeroBonus::ARTIFACT,id));
+			if(hlp != h->bonuses.end())
+			{
+				lost.push_back(&*hlp);
+				h->bonuses.erase(hlp);
+			}
+			else
+			{
+				break;
+			}
+		}
+	}
+
+	BOOST_FOREACH(ui32 id, equiped)
+	{
+		CArtifact &art = VLC->arth->artifacts[id];
+		for(std::list<HeroBonus>::iterator i = art.bonuses.begin(); i != art.bonuses.end(); i++)
+		{
+			gained.push_back(&*i);
+			h->bonuses.push_back(*i);
+		}
+	}
+}
+
+DLL_EXPORT void SetHeroArtifacts::setArtAtPos(ui16 pos, int art)
+{
+	if(art<0)
+	{
+		if(pos<19)
+			artifWorn.erase(pos);
+		else
+			artifacts -= artifacts[pos-19];
+	}
+	else
+	{
+		if(pos<19)
+			artifWorn[pos] = art;
+		else
+			if(pos-19 < artifacts.size())
+				artifacts[pos-19] = art;
+			else
+				artifacts.push_back(art);
+	}
 }
 
+
 DLL_EXPORT void HeroRecruited::applyGs( CGameState *gs )
 {
 	CGHeroInstance *h = gs->hpool.heroesPool[hid];
@@ -320,8 +379,9 @@ DLL_EXPORT void NewTurn::applyGs( CGameState *gs )
 	gs->day = day;
 	BOOST_FOREACH(NewTurn::Hero h, heroes) //give mana/movement point
 	{
-		static_cast<CGHeroInstance*>(gs->map->objects[h.id])->movement = h.move;
-		static_cast<CGHeroInstance*>(gs->map->objects[h.id])->mana = h.mana;
+		CGHeroInstance *hero = gs->getHero(h.id);
+		hero->movement = h.move;
+		hero->mana = h.mana;
 	}
 
 	BOOST_FOREACH(SetResources h, res) //give resources
@@ -339,7 +399,7 @@ DLL_EXPORT void NewTurn::applyGs( CGameState *gs )
 
 	if(gs->getDate(1) == 7) //new week
 		BOOST_FOREACH(CGHeroInstance *h, gs->map->heroes)
-		h->bonuses.remove_if(HeroBonus::OneWeek);
+			h->bonuses.remove_if(HeroBonus::OneWeek);
 }
 
 DLL_EXPORT void SetObjectProperty::applyGs( CGameState *gs )

+ 43 - 34
server/CGameHandler.cpp

@@ -646,7 +646,12 @@ void CGameHandler::newTurn()
 			NewTurn::Hero hth;
 			hth.id = h->id;
 			hth.move = h->maxMovePoints(true); //TODO: check if hero is really on the land
-			hth.mana = std::max(h->mana,std::min(h->mana+1+h->getSecSkillLevel(8), h->manaLimit())); //hero regains 1 mana point + mysticism lvel
+
+			if(h->visitedTown && vstd::contains(h->visitedTown->builtBuildings,0)) //if hero starts turn in town with mage guild
+				hth.mana = h->manaLimit(); //restore all mana
+			else
+				hth.mana = std::max(si32(0), std::min(h->mana + h->manaRegain(), h->manaLimit()) ); 
+
 			n.heroes.insert(hth);
 			
 			switch(h->getSecSkillLevel(13)) //handle estates - give gold
@@ -1018,7 +1023,7 @@ void CGameHandler::checkForBattleEnd( std::vector<CStack*> &stacks )
 	hasStack[0] = hasStack[1] = false;
 	for(int b = 0; b<stacks.size(); ++b)
 	{
-		if(stacks[b]->alive())
+		if(stacks[b]->alive() && !vstd::contains(stacks[b]->abilities,SIEGE_WEAPON))
 		{
 			hasStack[1-stacks[b]->attackerOwned] = true;
 		}
@@ -1242,28 +1247,31 @@ void CGameHandler::stopHeroVisitCastle(int obj, int heroID)
 void CGameHandler::giveHeroArtifact(int artid, int hid, int position) //pos==-1 - first free slot in backpack
 {
 	const CGHeroInstance* h = getHero(hid);
+	const CArtifact &art = VLC->arth->artifacts[artid];
 
 	SetHeroArtifacts sha;
 	sha.hid = hid;
 	sha.artifacts = h->artifacts;
 	sha.artifWorn = h->artifWorn;
+
 	if(position<0)
 	{
 		if(position == -2)
 		{
 			int i;
-			for(i=0; i<VLC->arth->artifacts[artid].possibleSlots.size(); i++) //try to put artifact into first avaialble slot
+			for(i=0; i<art.possibleSlots.size(); i++) //try to put artifact into first available slot
 			{
-				if( !vstd::contains(sha.artifWorn,VLC->arth->artifacts[artid].possibleSlots[i]) )
+				if( !vstd::contains(sha.artifWorn,art.possibleSlots[i]) )
 				{
-					sha.artifWorn[VLC->arth->artifacts[artid].possibleSlots[i]] = artid;
+					//we've found a free suitable slot
+					sha.artifWorn[art.possibleSlots[i]] = artid;
 					break;
 				}
 			}
-			if(i==VLC->arth->artifacts[artid].possibleSlots.size()) //if haven't find proper slot, use backpack
+			if(i == art.possibleSlots.size()) //if haven't find proper slot, use backpack
 				sha.artifacts.push_back(artid);
 		}
-		else //should be -1 => putartifact into backpack
+		else //should be -1 => put artifact into backpack
 		{
 			sha.artifacts.push_back(artid);
 		}
@@ -1271,10 +1279,15 @@ void CGameHandler::giveHeroArtifact(int artid, int hid, int position) //pos==-1
 	else
 	{
 		if(!vstd::contains(sha.artifWorn,ui16(position)))
+		{
 			sha.artifWorn[position] = artid;
+		}
 		else
+		{
 			sha.artifacts.push_back(artid);
+		}
 	}
+
 	sendAndApply(&sha);
 }
 
@@ -1763,20 +1776,32 @@ void CGameHandler::swapArtifacts( si32 hid1, si32 hid2, ui16 slot1, ui16 slot2 )
 	CGHeroInstance *h1 = gs->getHero(hid1), *h2 = gs->getHero(hid2);
 	if((distance(h1->pos,h2->pos) > 1.0)   ||   (h1->tempOwner != h2->tempOwner))
 		return;
-	int a1=h1->getArtAtPos(slot1), a2=h2->getArtAtPos(slot2);
+	const CArtifact *a1 = h1->getArt(slot1), 
+		*a2=h2->getArt(slot2);
+
+	if(a1 && slot2<19 && !vstd::contains(a1->possibleSlots,slot2)
+		|| a2 && slot1<19 && !vstd::contains(a2->possibleSlots,slot1)
+	)
+	{
+		//artifact doesn't fit dst slot
+		complain("Cannot swap artifacts!");
+		return;
+	}
+
 
-	h2->setArtAtPos(slot2,a1);
-	h1->setArtAtPos(slot1,a2);
 	SetHeroArtifacts sha;
 	sha.hid = hid1;
 	sha.artifacts = h1->artifacts;
 	sha.artifWorn = h1->artifWorn;
+	sha.setArtAtPos(slot1,h2->getArtAtPos(slot2));
+	if(h1 == h2) sha.setArtAtPos(slot2,h1->getArtAtPos(slot1));
 	sendAndApply(&sha);
 	if(hid1 != hid2)
 	{
 		sha.hid = hid2;
 		sha.artifacts = h2->artifacts;
 		sha.artifWorn = h2->artifWorn;
+		sha.setArtAtPos(slot2,h1->getArtAtPos(slot1));
 		sendAndApply(&sha);
 	}
 }
@@ -1787,21 +1812,14 @@ void CGameHandler::buyArtifact( ui32 hid, si32 aid )
 	CGTownInstance *town = hero->visitedTown;
 	if(aid==0) //spellbook
 	{
-		if(!vstd::contains(town->builtBuildings,si32(0)))
+		if(!vstd::contains(town->builtBuildings,si32(0)) && complain("Cannot buy a spellbook, no mage guild in the town!")
+			|| getResource(hero->getOwner(),6)<500 && complain("Cannot buy a spellbook, not enough gold!") 
+			|| hero->getArt(17) && complain("Cannot buy a spellbook, hero already has a one!")
+			)
 			return;
-		SetResource sr;
-		sr.player = hero->tempOwner;
-		sr.resid = 6;
-		sr.val = gs->getPlayer(hero->getOwner())->resources[6] - 500;
-		sendAndApply(&sr);
-
-		SetHeroArtifacts sha;
-		sha.hid = hid;
-		sha.artifacts = hero->artifacts;
-		sha.artifWorn = hero->artifWorn;
-		sha.artifWorn[17] = 0;
-		sendAndApply(&sha);
 
+		giveResource(hero->getOwner(),6,-500);
+		giveHeroArtifact(0,hid,17);
 		giveSpells(town,hero);
 	}
 	else if(aid < 7  &&  aid > 3) //war machine
@@ -1814,18 +1832,9 @@ void CGameHandler::buyArtifact( ui32 hid, si32 aid )
 		{
 			return;
 		}
-		SetResource sr;
-		sr.player = hero->tempOwner;
-		sr.resid = 6;
-		sr.val = gs->getPlayer(hero->getOwner())->resources[6] - price;
-		sendAndApply(&sr);
 
-		SetHeroArtifacts sha;
-		sha.hid = hid;
-		sha.artifacts = hero->artifacts;
-		sha.artifWorn = hero->artifWorn;
-		sha.artifWorn[9+aid] = aid;
-		sendAndApply(&sha);
+		giveResource(hero->getOwner(),6,-price);
+		giveHeroArtifact(aid,hid,9+aid);
 	}
 }
 

+ 2 - 0
server/CGameHandler.h

@@ -100,6 +100,8 @@ public:
 	void heroVisitCastle(int obj, int heroID);
 	void stopHeroVisitCastle(int obj, int heroID);
 	void giveHeroArtifact(int artid, int hid, int position); //pos==-1 - first free slot in backpack; pos==-2 - default if available or backpack
+	void moveArtifact(int hid, int oldPosition, int destPos);
+	void removeArtifact(int hid, int pos);
 	void startBattleI(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
 	void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army
 	void setAmount(int objid, ui32 val);