Sfoglia il codice sorgente

Sacrificing artifacts. [Code is one giant workaround but should work good enough.] Updated changelog.

Michał W. Urbańczyk 15 anni fa
parent
commit
8487653a8e

+ 34 - 0
ChangeLog

@@ -1,3 +1,37 @@
+0.8 -> 0.81 (Jun 01 2010)
+GENERAL:
+
+ADVENTURE MAP:
+* Neutral armies growth implemented (%10 weekly)
+* Power rating of neutral stack
+* Favourable Winds reduce sailing cost
+
+BATTLES:
+
+HERO:
+
+TOWNS:
+* Support for new town structures:
+- Artifact Merchant
+- Castle Gates
+- Magic University
+- Portal of Summoning 
+- Skeleton transformer
+- Veil of Darkness
+
+OBJECTS:
+* Stables will now upgrade Cavaliers to Champions.
+New object supported:
+- Altar of Sacrifice
+- Aurora Borealis
+- Black Market
+- Cover of Darkness
+- Hill Fort
+- Sanctuary
+- Tavern
+- University
+- Whirlpool
+
 0.8 -> 0.81 (Jun 01 2010)
 GENERAL:
 * It's possible to start campaign

+ 1 - 1
client/CHeroWindow.cpp

@@ -203,7 +203,7 @@ void CHeroWindow::show(SDL_Surface *to)
 	garr->show(to);
 	ourBar->show(to);
 
-	artifs->show(to);
+	artifs->showAll(to);
 }
 
 void CHeroWindow::setHero(const CGHeroInstance *hero)

+ 1 - 1
client/CSpellWindow.cpp

@@ -609,7 +609,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 			owner->fexitb();
 			owner->myInt->battleInt->castThisSpell(spell);
 		}
-		else //adventure spell
+		else if(!sp->combatSpell && !owner->myInt->battleInt) //adventure spell
 		{
 			using namespace Spells;
 			int spell = mySpell;

+ 1 - 1
client/Client.h

@@ -90,7 +90,7 @@ 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=0; pos==-2 - default if available or backpack
-	void removeArtifact(int artid, int hid){};
+	bool removeArtifact(int artid, int hid){return false;};
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL){}; //use hero=NULL for no hero
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false){}; //if any of armies is hero, hero will be used
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false){}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle

+ 317 - 90
client/GUIClasses.cpp

@@ -2745,6 +2745,7 @@ void CTradeWindow::CTradeableItem::showAll(SDL_Surface * to)
 	case PLAYER:
 		posToSubCenter = Point(31, 76);
 		break;
+	case ARTIFACT_PLACEHOLDER:
 	case ARTIFACT:
 		posToSubCenter = Point(18, 57);
 		break;
@@ -2760,12 +2761,37 @@ void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState)
 {
 	CTradeWindow *mw = static_cast<CTradeWindow *>(parent);
 	assert(mw);
-	if(type == ARTIFACT_PLACEHOLDER)
-	{
-
-	}
 	if(down)
 	{
+
+		if(type == ARTIFACT_PLACEHOLDER)
+		{
+			CAltarWindow *aw = static_cast<CAltarWindow *>(mw);
+			const CArtifact *movedArt = aw->arts->commonInfo->srcArtifact;
+			if(movedArt)
+			{
+				aw->moveFromSlotToAltar(aw->arts->commonInfo->srcSlotID, this, movedArt->id);
+			}
+			else if(id >= 0)
+			{
+				movedArt = CGI->arth->artifacts[id];
+				aw->arts->commonInfo->srcAOH = aw->arts;
+				aw->arts->commonInfo->srcArtifact = movedArt;
+				aw->arts->commonInfo->srcSlotID = 19 + vstd::findPos(aw->hero->artifacts, movedArt->id);
+
+				aw->arts->commonInfo->destAOH = aw->arts;
+				CGI->curh->dragAndDropCursor(graphics->artDefs->ourImages[movedArt->id].bitmap);
+
+				id = -1;
+				subtitle = "";
+				aw->arts->artifactsOnAltar.erase(movedArt->id);
+				aw->deal->block(!aw->arts->artifactsOnAltar.size());
+				aw->arts->markPossibleSlots(movedArt);
+			}
+
+			aw->calcTotalExp();
+			return;
+		}
 		if(left)
 		{
 			if(mw->hLeft != this)
@@ -2793,7 +2819,8 @@ SDL_Surface * CTradeWindow::CTradeableItem::getSurface()
 	case PLAYER:
 		return graphics->flags->ourImages[id].bitmap;
 	case ARTIFACT:
-		return graphics->artDefs->ourImages[id].bitmap;
+	case ARTIFACT_PLACEHOLDER:
+		return id >= 0 ? graphics->artDefs->ourImages[id].bitmap : NULL;
 	case CREATURE:
 		return graphics->bigImgs[id];
 	default:
@@ -2841,12 +2868,20 @@ void CTradeWindow::CTradeableItem::hover(bool on)
 
 void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
 {
-	switch(type)
+	if(down)
 	{
-	case CREATURE:
-	case CREATURE_PLACEHOLDER:
-		//GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl));
-		break;
+		switch(type)
+		{
+		case CREATURE:
+		case CREATURE_PLACEHOLDER:
+			//GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl));
+			break;
+		case ARTIFACT:
+		case ARTIFACT_PLACEHOLDER:
+			if(id >= 0)
+				adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down);
+			break;
+		}
 	}
 }
 
@@ -2908,7 +2943,7 @@ void CTradeWindow::initItems(bool Left)
 	for(int j=0; j<amount; j++)
 	{
 		int id = (ids && ids->size()>j) ? (*ids)[j] : j;
-		if(id < 0) 
+		if(id < 0 && mode != ARTIFACT_EXP)  //when sacrificing artifacts we need to prepare empty slots
 			continue;
 
 		CTradeableItem *hlp = new CTradeableItem(itemsType[Left], id, Left, j);
@@ -2923,6 +2958,9 @@ std::vector<int> *CTradeWindow::getItemsIds(bool Left)
 {
 	std::vector<int> *ids = NULL;
 
+	if(mode == ARTIFACT_EXP)
+		return new std::vector<int>(22, -1);
+
 	if(Left)
 	{
 		switch(itemsType[1])
@@ -2974,8 +3012,8 @@ void CTradeWindow::getPositionsFor(std::vector<Rect> &poss, bool Left, EType typ
 			for (int j = 0; j < 5 ; j++)
 				poss += Rect(x + dx*j, y + dy*i, w, h);
 
-		poss += Rect(x + dx*2.5, y + dy*5, w, h);
-		poss += Rect(x + dx*2.5, y + dy*5, w, h);
+		poss += Rect(x + dx*1.5, y + dy*4, w, h);
+		poss += Rect(x + dx*2.5, y + dy*4, w, h);
 	}
 	else
 	{
@@ -3085,19 +3123,23 @@ void CTradeWindow::getEmptySlots(std::set<CTradeableItem *> &toRemove)
 
 void CTradeWindow::setMode(EMarketMode Mode)
 {
+	const IMarket *m = market;
+	const CGHeroInstance *h = hero;
 	CTradeWindow *nwindow = NULL;
+
+	GH.popIntTotally(this);
+	
 	switch(Mode)
 	{
 	case CREATURE_EXP:
 	case ARTIFACT_EXP:
-		nwindow = new CAltarWindow(market, hero, Mode);
+		nwindow = new CAltarWindow(m, h, Mode);
 		break;
 	default:
-		nwindow = new CMarketplaceWindow(market, hero, Mode);
+		nwindow = new CMarketplaceWindow(m, h, Mode);
 		break;
 	}
 
-	GH.popIntTotally(this);
 	GH.pushInt(nwindow);
 }
 
@@ -3490,15 +3532,19 @@ CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*=
 		sacrificeBackpack = new AdventureMapButton(CGI->generaltexth->zelp[570],boost::bind(&CAltarWindow::SacrificeBackpack,this),147,520,"ALTEMBK.DEF");
 		sacrificeBackpack->block(!hero->artifacts.size());
 
-		BLOCK_CAPTURING;
 		slider = NULL;
 		max = NULL;
-		arts = new CArtifactsOfHero(Point(-267,-10));
-		arts->commonInfo = new CArtifactsOfHero::SCommonPart;
-		arts->commonInfo->participants.insert(arts);
-		arts->setHero(Hero);
-		arts->recActions = 255;
-		addChild(arts);
+
+		{
+			BLOCK_CAPTURING;
+			arts = new CArtifactsOfHero(Point(-267,-10));
+			arts->commonInfo = new CArtifactsOfHero::SCommonPart;
+			arts->commonInfo->participants.insert(arts);
+			arts->setHero(Hero);
+			arts->recActions = 255;
+			arts->allowedAssembling = false;
+			addChild(arts);
+		}
 
 		initItems(false);
 	}
@@ -3512,9 +3558,9 @@ CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*=
 
 	deal = new AdventureMapButton(CGI->generaltexth->zelp[585],boost::bind(&CAltarWindow::makeDeal,this),269,520,"ALTSACR.DEF");
 
-	if(/*Hero->getAlignment() != EVIL && */Mode == CREATURE_EXP)
+	if(Hero->getAlignment() != EVIL && Mode == CREATURE_EXP)
 		new AdventureMapButton(CGI->generaltexth->zelp[580], boost::bind(&CTradeWindow::setMode,this, ARTIFACT_EXP), 516, 421, "ALTART.DEF");
-	if(/*Hero->getAlignment() != GOOD && */Mode == ARTIFACT_EXP)
+	if(Hero->getAlignment() != GOOD && Mode == ARTIFACT_EXP)
 		new AdventureMapButton(CGI->generaltexth->zelp[572], boost::bind(&CTradeWindow::setMode,this, CREATURE_EXP), 516, 421, "ALTSACC.DEF");
 
 	expPerUnit.resize(ARMY_SIZE, 0);
@@ -3555,23 +3601,44 @@ void CAltarWindow::sliderMoved(int to)
 
 void CAltarWindow::makeDeal()
 {
-	blockTrade();
-	slider->value = 0;
-
-	std::vector<int> toSacrifice = sacrificedUnits;
-	for (int i = 0; i < toSacrifice.size(); i++)
+	if(mode == CREATURE_EXP)
 	{
-		if(toSacrifice[i])
-			LOCPLINT->cb->trade(market->o, mode, i, 0, toSacrifice[i], hero);
-	}
+		blockTrade();
+		slider->value = 0;
 
-	BOOST_FOREACH(int& val, sacrificedUnits)
-		val = 0;
+		std::vector<int> toSacrifice = sacrificedUnits;
+		for (int i = 0; i < toSacrifice.size(); i++)
+		{
+			if(toSacrifice[i])
+				LOCPLINT->cb->trade(market->o, mode, i, 0, toSacrifice[i], hero);
+		}
 
-	BOOST_FOREACH(CTradeableItem *t, items[0])
+		BOOST_FOREACH(int& val, sacrificedUnits)
+			val = 0;
+
+		BOOST_FOREACH(CTradeableItem *t, items[0])
+		{
+			t->type = CREATURE_PLACEHOLDER;
+			t->subtitle = "";
+		}
+	}
+	else
 	{
-		t->type = CREATURE_PLACEHOLDER;
-		t->subtitle = "";
+		BOOST_FOREACH(int artID, arts->artifactsOnAltar) //sacrifice each artifact on the list
+		{
+			LOCPLINT->cb->trade(market->o, mode, artID, -1, 1, hero);
+		}
+		arts->artifactsOnAltar.clear();
+
+		BOOST_FOREACH(CTradeableItem *t, items[0])
+		{
+			t->id = -1;
+			t->subtitle = "";
+		}
+
+		arts->commonInfo->reset();
+		arts->scrollBackpack(0);
+		deal->block(true);
 	}
 
 	calcTotalExp();
@@ -3579,14 +3646,33 @@ void CAltarWindow::makeDeal()
 
 void CAltarWindow::SacrificeAll()
 {
-	BOOST_FOREACH(CTradeableItem *t, items[1])
-		sacrificedUnits[t->serial] = hero->getAmount(t->serial);
+	if(mode == CREATURE_EXP)
+	{
+		bool movedAnything = false;
+		BOOST_FOREACH(CTradeableItem *t, items[1])
+			sacrificedUnits[t->serial] = hero->getAmount(t->serial);
 
-	sacrificedUnits[items[1].front()->serial]--;
+		sacrificedUnits[items[1].front()->serial]--;
 
-	BOOST_FOREACH(CTradeableItem *t, items[0])
-		updateRight(t);
+		BOOST_FOREACH(CTradeableItem *t, items[0])
+		{
+			updateRight(t);
+			if(t->type == CREATURE)
+				movedAnything = true;
+		}
+		
+		deal->block(!movedAnything);
+		calcTotalExp();
+	}
+	else
+	{
+		for(std::map<ui16,ui32>::const_iterator i = hero->artifWorn.begin(); i != hero->artifWorn.end(); i++)
+		{
+			moveFromSlotToAltar(i->first, NULL, i->second);
+		}
 
+		SacrificeBackpack();
+	}
 	redraw();
 }
 
@@ -3599,7 +3685,13 @@ void CAltarWindow::selectionChanged(bool side)
 	CTradeableItem *&theOther = side ? hRight : hLeft;
 
 	theOther = *std::find_if(items[!side].begin(), items[!side].end(), boost::bind(&CTradeableItem::serial, _1) == selected->serial);
-	slider->setAmount(hero->getAmount(hLeft->serial));
+
+	int stackCount = 0;
+	for (int i = 0; i < ARMY_SIZE; i++)
+		if(hero->getAmount(i) > sacrificedUnits[i])
+			stackCount++;
+
+	slider->setAmount(hero->getAmount(hLeft->serial) - (stackCount == 1));
 	slider->block(!slider->amount);
 	slider->value = sacrificedUnits[hLeft->serial];
 	max->block(!slider->amount);
@@ -3672,16 +3764,28 @@ void CAltarWindow::getExpValues()
 void CAltarWindow::calcTotalExp()
 {
 	int val = 0;
-	for (int i = 0; i < sacrificedUnits.size(); i++)
+	if(mode == CREATURE_EXP)
 	{
-		val += expPerUnit[i] * sacrificedUnits[i];
+		for (int i = 0; i < sacrificedUnits.size(); i++)
+		{
+			val += expPerUnit[i] * sacrificedUnits[i];
+		}
+	}
+	else
+	{
+		for(std::multiset<int>::const_iterator i = arts->artifactsOnAltar.begin(); i != arts->artifactsOnAltar.end(); i++)
+		{
+			int dmp, valOfArt;
+			market->getOffer(*i, 0, dmp, valOfArt, mode);
+			val += valOfArt * arts->artifactsOnAltar.count(*i);
+		}
 	}
 	expOnAltar->setTxt(boost::lexical_cast<std::string>(val));
 }
 
 void CAltarWindow::setExpToLevel()
 {
-	expToLevel->setTxt(boost::lexical_cast<std::string>(CGI->heroh->reqExp(hero->level+1) - hero->exp));
+	expToLevel->setTxt(boost::lexical_cast<std::string>(CGI->heroh->reqExp(CGI->heroh->level(hero->exp)+1) - hero->exp));
 }
 
 void CAltarWindow::blockTrade()
@@ -3703,9 +3807,86 @@ void CAltarWindow::updateRight(CTradeableItem *toUpdate)
 	toUpdate->subtitle = val ? boost::str(boost::format(CGI->generaltexth->allTexts[122]) % boost::lexical_cast<std::string>(val * expPerUnit[toUpdate->serial])) : ""; //%s exp
 }
 
+int CAltarWindow::firstFreeSlot()
+{
+	int ret = -1;
+	while(items[0][++ret]->id >= 0  &&  ret + 1 < items[0].size());
+	return ret < items[0].size() ? ret : -1;
+}
+
 void CAltarWindow::SacrificeBackpack()
 {
+	std::multiset<int> toOmmit = arts->artifactsOnAltar;
 
+	for (int i = 0; i < hero->artifacts.size(); i++)
+	{
+		if(vstd::contains(toOmmit, hero->artifacts[i]))
+		{
+			toOmmit -= hero->artifacts[i];
+			continue;
+		}
+
+		putOnAltar(NULL, hero->artifacts[i]);
+	}
+	arts->scrollBackpack(0);
+	calcTotalExp();
+}
+
+void CAltarWindow::artifactPicked()
+{
+	redraw();
+}
+
+void CAltarWindow::showAll(SDL_Surface * to)
+{
+	CTradeWindow::showAll(to);
+	if(mode == ARTIFACT_EXP && arts && arts->commonInfo->srcArtifact)
+	{
+		blitAtLoc(graphics->artDefs->ourImages[arts->commonInfo->srcArtifact->id].bitmap, 281, 442, to);
+
+		int dmp, val;
+		market->getOffer(arts->commonInfo->srcArtifact->id, 0, dmp, val, ARTIFACT_EXP);
+		printAtMiddleLoc(boost::lexical_cast<std::string>(val), 304, 498, FONT_SMALL, zwykly, to);
+	}
+}
+
+bool CAltarWindow::putOnAltar(CTradeableItem* altarSlot, int artID)
+{
+	if(artID != 1 && artID < 7) //special art
+		return false;
+
+	if(!altarSlot)
+	{
+		int slotIndex = firstFreeSlot();
+		if(slotIndex < 0)
+		{
+			tlog2 << "No free slots on altar!\n";
+			return false;
+		}
+		altarSlot = items[0][slotIndex];
+	}
+
+	int dmp, val;
+	market->getOffer(artID, 0, dmp, val, ARTIFACT_EXP);
+
+	arts->artifactsOnAltar.insert(artID);
+	altarSlot->id = artID;
+	altarSlot->subtitle = boost::lexical_cast<std::string>(val);
+
+	deal->block(false);
+	return true;
+}
+
+void CAltarWindow::moveFromSlotToAltar(int slotID, CTradeableItem* altarSlot, int artID)
+{
+	if(arts->commonInfo->srcArtifact)
+	{
+		arts->commonInfo->destSlotID = 65500;
+		arts->commonInfo->destAOH = arts;
+	}
+
+	if(putOnAltar(altarSlot, artID))
+		LOCPLINT->cb->swapArtifacts(hero, slotID, hero, 65500);
 }
 
 CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface * owner)
@@ -4465,34 +4646,39 @@ void CArtPlace::clickRight(tribool down, bool previousState)
 		if (slotID < 19) 
 		{
 			selectedNo = false;
-
-			// If the artifact can be assembled, display dialog.
-			if (ourArt->constituentOf != NULL) {
-				BOOST_FOREACH(ui32 combination, *ourArt->constituentOf) {
-					if (ourArt->canBeAssembledTo(ourOwner->curHero->artifWorn, combination)) {
-						LOCPLINT->showArtifactAssemblyDialog(
-							ourArt->id,
-							combination,
-							true,
-							boost::bind(&CCallback::assembleArtifacts, LOCPLINT->cb, ourOwner->curHero, slotID, true, combination),
-							boost::bind(&CArtPlace::userSelectedNo, this));
-						if (!selectedNo)
-							return;
+			if(ourOwner->allowedAssembling)
+			{
+				// If the artifact can be assembled, display dialog.
+				if (ourArt->constituentOf != NULL) 
+				{
+					BOOST_FOREACH(ui32 combination, *ourArt->constituentOf) 
+					{
+						if (ourArt->canBeAssembledTo(ourOwner->curHero->artifWorn, combination)) 
+						{
+							LOCPLINT->showArtifactAssemblyDialog(
+								ourArt->id,
+								combination,
+								true,
+								boost::bind(&CCallback::assembleArtifacts, LOCPLINT->cb, ourOwner->curHero, slotID, true, combination),
+								boost::bind(&CArtPlace::userSelectedNo, this));
+							if (!selectedNo)
+								return;
+						}
 					}
 				}
-			}
 
-			// Otherwise if the artifact can be diasassembled, display dialog.
-			if (ourArt->constituents != NULL) 
-			{
-				LOCPLINT->showArtifactAssemblyDialog(
-					ourArt->id,
-					0,
-					false,
-					boost::bind(&CCallback::assembleArtifacts, LOCPLINT->cb, ourOwner->curHero, slotID, false, 0),
-					boost::bind(&CArtPlace::userSelectedNo, this));
-				if (!selectedNo)
-					return;
+				// Otherwise if the artifact can be diasassembled, display dialog.
+				if (ourArt->constituents != NULL) 
+				{
+					LOCPLINT->showArtifactAssemblyDialog(
+						ourArt->id,
+						0,
+						false,
+						boost::bind(&CCallback::assembleArtifacts, LOCPLINT->cb, ourOwner->curHero, slotID, false, 0),
+						boost::bind(&CArtPlace::userSelectedNo, this));
+					if (!selectedNo)
+						return;
+				}
 			}
 		}
 
@@ -4533,13 +4719,15 @@ void CArtPlace::select ()
 	ourOwner->markPossibleSlots(ourArt);
 	//ourOwner->curHero->recreateArtBonuses();
 
-	// Update the hero bonuses.
-	ourOwner->updateParentWindow();
 
 	if (slotID >= 19)
 		ourOwner->scrollBackpack(backpackCorrection);
 	else
 		ourOwner->eraseSlotData(this, slotID);
+
+	// Update the hero bonuses.
+	ourOwner->updateParentWindow();
+	ourOwner->safeRedraw();
 }
 
 /**
@@ -4559,7 +4747,7 @@ void CArtPlace::deactivate()
 	}
 }
 
-void CArtPlace::show(SDL_Surface *to)
+void CArtPlace::showAll(SDL_Surface *to)
 {
 	if (ourArt)
 		blitAt(graphics->artDefs->ourImages[ourArt->id].bitmap, pos.x, pos.y, to);
@@ -4703,6 +4891,7 @@ void CArtifactsOfHero::SCommonPart::reset()
 	destAOH = srcAOH = NULL;
 	destArtifact = srcArtifact = NULL;
 	destSlotID = srcSlotID = -1;
+	CGI->curh->dragAndDropCursor(NULL);
 }
 
 void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
@@ -4751,7 +4940,6 @@ void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
 			{
 				// Reset all parameters.
 				commonInfo->reset();
-				CGI->curh->dragAndDropCursor(NULL);
 				unmarkSlots();
 			}
 		}
@@ -4776,10 +4964,6 @@ void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
 	for (int g = 0; g < 19 ; g++)
 		setSlotData(artWorn[g], g);
 	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());
 }
 
 void CArtifactsOfHero::dispose()
@@ -4792,23 +4976,54 @@ void CArtifactsOfHero::scrollBackpack(int dir)
 	backpackPos += dir;
 	if (curHero->artifacts.size() > 0) 
 	{
-		if (backpackPos < 0) { // No guarantee of modulus behavior with negative operands.
-			do {
+		if (backpackPos < 0) // No guarantee of modulus behavior with negative operands.
+		{ 
+			do 
+			{
 				backpackPos += curHero->artifacts.size();
 			} while (backpackPos < 0);
-		} else {
+		} 
+		else 
+		{
 			backpackPos %= curHero->artifacts.size();
 		}
 	}
 
+	std::multiset<int> toOmmit = artifactsOnAltar;
+	int ommited = 0;
+
 	//set new data
-	for (size_t s = 0; s < backpack.size(); ++s) 
+	size_t s = 0;
+	for( ; s < curHero->artifacts.size(); ++s) 
 	{
+
 		if (s < curHero->artifacts.size())
-			setSlotData(backpack[s], 19 + (s + backpackPos)%curHero->artifacts.size());
-		else
-			eraseSlotData(backpack[s], 19 + s);
+		{
+			int slotID = 19 + (s + backpackPos)%curHero->artifacts.size();
+			const CArtifact *art = curHero->getArt(slotID);
+			assert(art);
+			if(!vstd::contains(toOmmit, art->id))
+			{
+				if(s - ommited < 5)
+					setSlotData(backpack[s-ommited], slotID);
+			}
+			else
+			{
+				toOmmit -= art->id;
+				ommited ++;
+				continue;
+			}
+		}
 	}
+	for( ; s - ommited < 5; s++)
+		eraseSlotData(backpack[s-ommited], 19 + s);
+
+	//blocking scrolling if there is not enough artifacts to scroll
+	leftArtRoll->block(curHero->artifacts.size() - ommited <= backpack.size());
+	rightArtRoll->block(curHero->artifacts.size() - ommited <= backpack.size());
+
+	safeRedraw();
+
 }
 
 /**
@@ -4830,6 +5045,8 @@ void CArtifactsOfHero::markPossibleSlots (const CArtifact* art)
 				(*it)->artWorn[i]->marked = false;
 		}
 	}
+
+	safeRedraw();
 }
 
 /**
@@ -4846,6 +5063,8 @@ void CArtifactsOfHero::unmarkSlots ()
 			(*it)->artWorn[i]->marked = false;
 		}
 	}
+
+	safeRedraw();
 }
 
 /**
@@ -4882,7 +5101,7 @@ void CArtifactsOfHero::eraseSlotData (CArtPlace* artPlace, int slotID)
 }
 
 CArtifactsOfHero::CArtifactsOfHero(const Point &position) :
-	backpackPos(0), updateState(false), commonInfo(NULL), curHero(NULL)
+	backpackPos(0), updateState(false), commonInfo(NULL), curHero(NULL), allowedAssembling(true)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	pos += position;
@@ -4970,6 +5189,14 @@ void CArtifactsOfHero::updateParentWindow()
 	}
 }
 
+void CArtifactsOfHero::safeRedraw()
+{
+	if(parent)
+		parent->redraw();
+	else 
+		redraw();
+}
+
 void CExchangeWindow::close()
 {
 	GH.popIntTotally(this);
@@ -5071,8 +5298,8 @@ void CExchangeWindow::show(SDL_Surface * to)
 	if(screen->w != 800 || screen->h !=600)
 		CMessage::drawBorder(LOCPLINT->playerID,to,828,628,pos.x-14,pos.y-15);
 
-	artifs[0]->show(to);
-	artifs[1]->show(to);
+	artifs[0]->showAll(to);
+	artifs[1]->showAll(to);
 
 	ourBar->show(to);
 

+ 13 - 1
client/GUIClasses.h

@@ -687,7 +687,11 @@ public:
 	void selectionChanged(bool side); //true == left
 	void SacrificeAll();
 	void SacrificeBackpack();
+
+	void putOnAltar(int backpackIndex);
+	bool putOnAltar(CTradeableItem* altarSlot, int artID);
 	void makeDeal();
+	void showAll(SDL_Surface * to);
 
 	void blockTrade();
 	void sliderMoved(int to);
@@ -701,6 +705,10 @@ public:
 	void calcTotalExp();
 	void setExpToLevel();
 	void updateRight(CTradeableItem *toUpdate);
+
+	void artifactPicked();
+	int firstFreeSlot();
+	void moveFromSlotToAltar(int slotID, CTradeableItem* altarSlot, int artID);
 };
 
 class CSystemOptionsWindow : public CIntObject
@@ -900,7 +908,7 @@ public:
 	void deselect ();
 	void activate();
 	void deactivate();
-	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
 	bool fitsHere (const CArtifact * art); //returns true if given artifact can be placed here
 	bool locked () const;
 	void userSelectedNo ();
@@ -937,10 +945,14 @@ public:
 	bool updateState; // Whether the commonInfo should be updated on setHero or not.
 
 	AdventureMapButton * leftArtRoll, * rightArtRoll;
+	bool allowedAssembling;
+	std::multiset<int> artifactsOnAltar; //artifacts id that are technically present in backpack but in GUI are moved to the altar - they'll be ommited in backpack slots
 
 	void setHero(const CGHeroInstance * hero);
 	void dispose(); //free resources not needed after closing windows and reset state
 	void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right
+
+	void safeRedraw();
 	void markPossibleSlots (const CArtifact* art);
 	void unmarkSlots ();
 	void setSlotData (CArtPlace* artPlace, int slotID);

+ 21 - 0
hch/CArtHandler.cpp

@@ -143,6 +143,27 @@ CArtifact::~CArtifact()
 {
 }
 
+int CArtifact::getArtClassSerial() const
+{
+	if(id == 1)
+		return 4;
+	switch(aClass)
+	{
+	case ART_TREASURE:
+		return 0;
+	case ART_MINOR:
+		return 1;
+	case ART_MAJOR:
+		return 2;
+	case ART_RELIC:
+		return 3;
+	case ART_SPECIAL:
+		return 5;
+	}
+
+	return -1;
+}
+
 CArtHandler::CArtHandler()
 {
 	VLC->arth = this;

+ 1 - 0
hch/CArtHandler.h

@@ -30,6 +30,7 @@ public:
 	bool canBeAssembledTo (const std::map<ui16, ui32> &artifWorn, ui32 artifactID) const;
 	void addBonusesTo (BonusList *otherBonuses) const;
 	void removeBonusesFrom (BonusList *otherBonuses) const;
+	int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other
 
 	ui32 price;
 	std::vector<ui16> possibleSlots; //ids of slots where artifact can be placed

+ 14 - 0
hch/CObjectHandler.cpp

@@ -6507,7 +6507,21 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
 			val2 = (VLC->creh->creatures[id1]->AIValue / 40) * 5;
 		}
 		break;
+	case ARTIFACT_EXP:
+		{
+			val1 = 1;
+
+			int givenClass = VLC->arth->artifacts[id1]->getArtClassSerial();
+			if(givenClass < 0 || givenClass > 3)
+			{
+				val2 = 0;
+				return false;
+			}
 
+			static const int expPerClass[] = {1000, 1500, 3000, 6000};
+			val2 = expPerClass[givenClass];
+		}
+		break;
 	default:
 		assert(0);
 		return false;

+ 1 - 1
hch/CVideoHandler.cpp

@@ -469,7 +469,7 @@ bool CVideoPlayer::openAndPlayVideo(std::string name, int x, int y, SDL_Surface
 
 void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, bool update )
 {
-	if(current)
+	if(!current)
 		return;
 
 	bool w = false;

+ 1 - 1
lib/CGameState.cpp

@@ -2635,7 +2635,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 
 	if(defendingHero)
 	{
-		multBonus *= (std::max(0, 100-attackerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 23))) / 100.0f;
+		multBonus *= (std::max(0, 100-defendingHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 23))) / 100.0f;
 	}
 
 	//handling hate effect

+ 1 - 1
lib/IGameCallback.h

@@ -91,7 +91,7 @@ public:
 	virtual void heroVisitCastle(int obj, int heroID)=0;
 	virtual void stopHeroVisitCastle(int obj, int heroID)=0;
 	virtual void giveHeroArtifact(int artid, int hid, int position)=0; //pos==-1 - first free slot in backpack=0; pos==-2 - default if available or backpack
-	virtual void removeArtifact(int artid, int hid) = 0;
+	virtual bool removeArtifact(int artid, int hid) = 0;
 	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL)=0; //use hero=NULL for no hero
 	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false)=0; //if any of armies is hero, hero will be used
 	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle

+ 1 - 1
lib/NetPacks.h

@@ -1409,7 +1409,7 @@ struct TradeOnMarketplace : public CPackForServer
 	const CGObjectInstance *market;
 	const CGHeroInstance *hero; //needed when trading artifacts / creatures
 	ui8 mode;//enum EMarketMode
-	ui32 r1, r2; //mode 0: r1 - sold resource, r2 - bought res
+	ui32 r1, r2; //mode 0: r1 - sold resource, r2 - bought res (exception: when sacrificing art r1 is art id [todo: make r2 preferred slot?]
 	ui32 val; //units of sold resource
 
 	bool applyGh(CGameHandler *gh);

+ 21 - 2
server/CGameHandler.cpp

@@ -2095,7 +2095,7 @@ void CGameHandler::giveHeroArtifact(int artid, int hid, int position) //pos==-1
 
 	sendAndApply(&sha);
 }
-void CGameHandler::removeArtifact(int artid, int hid)
+bool CGameHandler::removeArtifact(int artid, int hid)
 {
 	const CGHeroInstance* h = getHero(hid);
 
@@ -2109,7 +2109,8 @@ void CGameHandler::removeArtifact(int artid, int hid)
 		sha.artifacts.erase(it);
 	else //worn
 	{
-		for (std::map<ui16,ui32>::iterator itr = sha.artifWorn.begin(); itr != sha.artifWorn.end(); ++itr)
+		std::map<ui16,ui32>::iterator itr;
+		for (itr = sha.artifWorn.begin(); itr != sha.artifWorn.end(); ++itr)
 		{
 			if (itr->second == artid)
 			{
@@ -2117,8 +2118,15 @@ void CGameHandler::removeArtifact(int artid, int hid)
 				break;
 			}
 		}
+
+		if(itr == sha.artifWorn.end())
+		{
+			tlog2 << "Cannot find artifact to remove!\n";
+			return false;
+		}
 	}
 	sendAndApply(&sha);
+	return true;
 }
 
 void CGameHandler::startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town) //use hero=NULL for no hero
@@ -4930,5 +4938,16 @@ bool CGameHandler::sacrificeCreatures(const IMarket *market, const CGHeroInstanc
 	exp *= count;
 	changePrimSkill	(hero->id, 4, exp);
 
+	return true;
+}
+
+bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, ui32 artID)
+{
+	if(!removeArtifact(artID, hero->id))
+		COMPLAIN_RET("Cannot find artifact to sacrifice!");
+
+	int dmp, expToGive;
+	m->getOffer(artID, 0, dmp, expToGive, ARTIFACT_EXP);
+	changePrimSkill(hero->id, 4, expToGive);
 	return true;
 }

+ 2 - 1
server/CGameHandler.h

@@ -139,7 +139,7 @@ public:
 	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 artid, int hid);
+	bool removeArtifact(int artid, int hid);
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL); //use hero=NULL for no hero
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false); //if any of armies is hero, hero will be used
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false); //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army
@@ -216,6 +216,7 @@ public:
 	void run(bool resume, const StartInfo *si = NULL);
 	void newTurn();
 	void handleAfterAttackCasting( const BattleAttack & bat );
+	bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, ui32 artID);
 	friend class CVCMIServer;
 	friend class CScriptCallback;
 };

+ 2 - 0
server/NetPacksServer.cpp

@@ -162,6 +162,8 @@ bool TradeOnMarketplace::applyGh( CGameHandler *gh )
 		return gh->buySecSkill(m, hero, r2);
 	case CREATURE_EXP:
 		return gh->sacrificeCreatures(m, hero, r1, val);
+	case ARTIFACT_EXP:
+		return gh->sacrificeArtifact(m, hero, r1);
 	default:
 		COMPLAIN_AND_RETURN("Unknown exchange mode!");
 	}