瀏覽代碼

Black Market and Artifact Merchant (only buying artifacts) support.

Michał W. Urbańczyk 15 年之前
父節點
當前提交
21a05d73cb

+ 2 - 0
CGameInterface.h

@@ -27,6 +27,7 @@ struct TryMoveHero;
 class CGHeroInstance;
 class CGTownInstance;
 class CGObjectInstance;
+class CGBlackMarket;
 class CGDwelling;
 class CCreatureSet;
 class CArmedInstance;
@@ -87,6 +88,7 @@ public:
 	virtual void tileHidden(const std::set<int3> &pos){};
 	virtual void tileRevealed(const std::set<int3> &pos){};
 	virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard
+	virtual void availableArtifactsChanged(const CGBlackMarket *bm = NULL){}; //bm may be NULL, then artifacts are changed in the global pool (used by merchants in towns)
 	virtual void yourTurn(){};
 	virtual void centerView (int3 pos, int focusTime){};
 	virtual void availableCreaturesChanged(const CGDwelling *town){};

+ 3 - 1
client/AdventureMapButton.cpp

@@ -513,7 +513,9 @@ void CSlider::moveTo(int to)
 		else
 			slider->pos.y = pos.y+16;
 	}
-	moved(to);
+
+	if(moved)
+		moved(to);
 }
 
 void CSlider::clickLeft(tribool down, bool previousState)

+ 6 - 0
client/CPlayerInterface.cpp

@@ -1989,4 +1989,10 @@ void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInsta
 {
 	CMarketplaceWindow *cmw = new CMarketplaceWindow(market, visitor, market->availableModes().front());
 	GH.pushInt(cmw);
+}
+
+void CPlayerInterface::availableArtifactsChanged(const CGBlackMarket *bm /*= NULL*/)
+{
+	if(CMarketplaceWindow *cmw = dynamic_cast<CMarketplaceWindow*>(GH.topInt()))
+		cmw->artifactsChanged(false);
 }

+ 1 - 0
client/CPlayerInterface.h

@@ -167,6 +167,7 @@ public:
 	void tileHidden(const std::set<int3> &pos); //called when given tiles become hidden under fog of war
 	void tileRevealed(const std::set<int3> &pos); //called when fog of war disappears from given tiles
 	void newObject(const CGObjectInstance * obj);
+	void availableArtifactsChanged(const CGBlackMarket *bm = NULL); //bm may be NULL, then artifacts are changed in the global pool (used by merchants in towns)
 	void yourTurn();
 	void availableCreaturesChanged(const CGDwelling *town);
 	void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain);//if gain hero received bonus, else he lost it

+ 10 - 0
client/GUIBase.cpp

@@ -597,11 +597,21 @@ void CIntObject::printAtMiddleLoc( const std::string & text, int x, int y, EFont
 	CSDL_Ext::printAtMiddle(text, pos.x + x, pos.y + y, font, kolor, dst, refresh);
 }
 
+void CIntObject::printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, SDL_Color kolor, SDL_Surface * dst, bool refresh /*= false*/)
+{
+	printAtMiddleLoc(text, p.x, p.y, font, kolor, dst, refresh);
+}
+
 void CIntObject::blitAtLoc( SDL_Surface * src, int x, int y, SDL_Surface * dst )
 {
 	blitAt(src, pos.x + x, pos.y + y, dst);
 }
 
+void CIntObject::blitAtLoc(SDL_Surface * src, const Point &p, SDL_Surface * dst)
+{
+	blitAtLoc(src, p.x, p.y, dst);
+}
+
 void CIntObject::printAtMiddleWBLoc( const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor, SDL_Surface * dst, bool refrsh /*= false*/ )
 {
 	CSDL_Ext::printAtMiddleWB(text, pos.x + x, pos.y + y, font, charpr, kolor, dst, refrsh);

+ 2 - 0
client/GUIBase.h

@@ -382,8 +382,10 @@ public:
 	void printAtLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst, bool refresh = false);
 	void printToLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst, bool refresh = false);
 	void printAtMiddleLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst, bool refresh = false);
+	void printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, SDL_Color kolor, SDL_Surface * dst, bool refresh = false);
 	void printAtMiddleWBLoc(const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor, SDL_Surface * dst, bool refrsh = false);
 	void blitAtLoc(SDL_Surface * src, int x, int y, SDL_Surface * dst);
+	void blitAtLoc(SDL_Surface * src, const Point &p, SDL_Surface * dst);
 	bool isItInLoc(const SDL_Rect &rect, int x, int y);
 	bool isItInLoc(const SDL_Rect &rect, const Point &p);
 	const Rect & center(const Rect &r); //sets pos so that r will be in the center of screen, returns new position

+ 135 - 28
client/GUIClasses.cpp

@@ -2794,6 +2794,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 	:market(Market), hero(Hero), hLeft(NULL), hRight(NULL), readyToTrade(false)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
 	type = BLOCK_ADV_HOTKEYS;
 	mode = Mode;
 
@@ -2802,6 +2803,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 
 	std::string bgName;
 	std::vector<int> *rIds = NULL, *lIds = NULL;
+	bool sliderNeeded = true;
 
 	switch(Mode)
 	{
@@ -2835,6 +2837,14 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 				lIds->push_back(-1);
 		}
 		break;
+
+	case RESOURCE_ARTIFACT:
+		bgName = "TPMRKABS.bmp";
+		ltype = RESOURCE;
+		rtype = ARTIFACT;
+		sliderNeeded = false;
+
+		rIds = new std::vector<int>(market->availableItemsIds(mode));
 	}
 
 	bg = new CPicture(bgName);
@@ -2865,20 +2875,34 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 	delNull(rIds);
 	delNull(lIds);
 
-	//slider and buttons must be created after bg
-	slider = new CSlider(231,490,137,boost::bind(&CMarketplaceWindow::sliderMoved,this,_1),0,0);
-
 	
 	ok = new AdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN);
 	ok->assignedKeys.insert(SDLK_ESCAPE);
 	deal = new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::makeDeal,this),307,520,"TPMRKB.DEF");
-	max = new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMax,this),229,520,"IRCBTNS.DEF");
+	deal->block(true);
+
+
+	//slider and buttons must be created after bg
+	if(sliderNeeded)
+	{
+		slider = new CSlider(231,490,137,0,0,0);
+		slider->moved = boost::bind(&CMarketplaceWindow::sliderMoved,this,_1);
+		max = new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMax,this),229,520,"IRCBTNS.DEF");
+		max->block(true);
+	}
+	else
+	{
+		slider = NULL;
+		max = NULL;
+		deal->pos.x -= 38;
+	}
 
 	//left side
 	switch(Mode)
 	{
 	case RESOURCE_RESOURCE:
 	case RESOURCE_PLAYER:
+	case RESOURCE_ARTIFACT:
 		printAtMiddle(CGI->generaltexth->allTexts[270],154,148,FONT_SMALL,zwykly,*bg); //kingdom res.
 		break;
 	case CREATURE_RESOURCE: 
@@ -2891,6 +2915,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 	{
 	case RESOURCE_RESOURCE:
 	case CREATURE_RESOURCE:
+	case RESOURCE_ARTIFACT:
 		printAtMiddle(CGI->generaltexth->allTexts[168],445,148,FONT_SMALL,zwykly,*bg); //available for trade
 		break;
 	case RESOURCE_PLAYER:
@@ -2906,8 +2931,6 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan
 		new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, CREATURE_RESOURCE), 516, 450,"TPMRKBU4.DEF");
 
 
-	max->block(true);
-	deal->block(true);
 }
 
 CMarketplaceWindow::~CMarketplaceWindow()
@@ -2944,7 +2967,13 @@ void CMarketplaceWindow::showAll(SDL_Surface * to)
 		if(readyToTrade)
 		{
 			blitAt(hLeft->getSurface(),pos.x+141,pos.y+457,to);
-			printAtMiddle(boost::lexical_cast<std::string>( slider->value * r1 ),pos.x+156,pos.y+505,FONT_SMALL,zwykly,to);
+			int val = -1;
+			if(slider)
+				val = slider->value * r1;
+			else
+				val = (deal->blocked) ? 0 : r1;
+
+			printAtMiddle(boost::lexical_cast<std::string>(val),pos.x+156,pos.y+505,FONT_SMALL,zwykly,to);
 		}
 		break;
 
@@ -2960,6 +2989,11 @@ void CMarketplaceWindow::showAll(SDL_Surface * to)
 		break;
 	}
 
+	Point rightSubOffset;
+	Point selectionPos;
+	Point selectionSubOffset;
+	std::string selectionSub;
+
 	//right side
 	switch(rtype)
 	{
@@ -2974,24 +3008,40 @@ void CMarketplaceWindow::showAll(SDL_Surface * to)
 					printAtMiddle(CGI->generaltexth->allTexts[164],right[i]->pos.x+36,right[i]->pos.y+57,FONT_SMALL,zwykly,to);
 			}
 		}
-		if(readyToTrade)
-		{
-			blitAt(hRight->getSurface(),pos.x+429,pos.y+457,to);
-			printAtMiddle(boost::lexical_cast<std::string>( slider->value * r2 ),pos.x+443,pos.y+505,FONT_SMALL,zwykly,to);
-		}
+		selectionPos = Point(429, 457);
+		selectionSubOffset = Point(14, 47);
+		selectionSub = boost::lexical_cast<std::string>( slider->value * r2 );
+		break;
+
+	case ARTIFACT:
+		if(hLeft) //print prices
+			for(int i=0; i<right.size();i++)
+				if(right[i]->id != hLeft->id || mode != RESOURCE_RESOURCE)
+					printAtMiddle(rSubs[i], right[i]->pos.x+18, right[i]->pos.y+57, FONT_SMALL, zwykly, to);
+		
+		selectionPos = Point(425, 447);
+		selectionSubOffset = Point(18, 57);
+		selectionSub = (deal->blocked) ? "0" : "1";
 		break;
 
 	case PLAYER:
 		BOOST_FOREACH(CTradeableItem *i, right)
 			printAtMiddle(CGI->generaltexth->capColors[i->id], i->pos.x + 31, i->pos.y + 76, FONT_SMALL, zwykly, to);
 
-		if(readyToTrade)
-		{
-			blitAt(hRight->getSurface(),pos.x+417,pos.y+451,to);
-			printAtMiddle(CGI->generaltexth->capColors[hRight->id], pos.x+417 + 31, pos.y+451 + 76, FONT_SMALL, zwykly, to);
-		}
+		selectionPos = Point(417, 451);
+		selectionSubOffset = rightSubOffset = Point(31, 76);
+		selectionSub = hRight ? CGI->generaltexth->capColors[hRight->id] : "";
 		break;
 	}
+
+
+	if(readyToTrade)
+	{
+		assert(hRight);
+		blitAtLoc(hRight->getSurface(), selectionPos, to);
+		printAtMiddleLoc(selectionSub, selectionPos + selectionSubOffset, FONT_SMALL, zwykly, to);
+	}
+
 }
 
 void CMarketplaceWindow::setMax()
@@ -3001,7 +3051,13 @@ void CMarketplaceWindow::setMax()
 
 void CMarketplaceWindow::makeDeal()
 {
-	if(!slider->value)
+	int sliderValue = 0;
+	if(slider)
+		sliderValue = slider->value;
+	else	
+		sliderValue = !deal->blocked; //should always be 1
+
+	if(!sliderValue)
 		return;
 
 	int leftIdToSend = -1;
@@ -3010,8 +3066,16 @@ void CMarketplaceWindow::makeDeal()
 	else
 		leftIdToSend = hLeft->id;
 
-	LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero);
-	slider->moveTo(0);
+	if(mode != RESOURCE_ARTIFACT)
+	{
+		LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero);
+		slider->moveTo(0);
+	}
+	else
+	{
+		LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, r2, hero);
+	}
+
 	hLeft = NULL;
 	hRight = NULL;
 	selectionChanged(true);
@@ -3038,18 +3102,29 @@ void CMarketplaceWindow::selectionChanged(bool side)
 		else
 			assert(0);
 
-		slider->setAmount(newAmount / r1);
-		slider->moveTo(0);
-		max->block(false);
-		deal->block(false);
+		if(slider)
+		{
+			slider->setAmount(newAmount / r1);
+			slider->moveTo(0);
+			max->block(false);
+			deal->block(false);
+		}
+		else
+		{
+			deal->block(LOCPLINT->cb->getResourceAmount(hLeft->id) < r1);
+		}
 	}
 	else
 	{
-		max->block(true);
+		if(slider)
+		{
+			max->block(true);
+			slider->setAmount(0);
+			slider->moveTo(0);
+		}
 		deal->block(true);
-		slider->setAmount(0);
-		slider->moveTo(0);
 	}
+
 	if(side && hLeft) //left selection changed, recalculate offers
 	{
 		rSubs.clear();
@@ -3057,7 +3132,10 @@ void CMarketplaceWindow::selectionChanged(bool side)
 		int h1, h2;
 		for(int i=0;i<right.size();i++)
 		{
-			market->getOffer(hLeft->id, i, h1, h2, mode);
+			if(rtype != ARTIFACT)
+				market->getOffer(hLeft->id, i, h1, h2, mode);
+			else
+				market->getOffer(hLeft->id, right[i]->id, h1, h2, mode);
 
 			std::ostringstream oss;
 			oss << h2;
@@ -3101,6 +3179,14 @@ void CMarketplaceWindow::getPositionsFor(std::vector<Rect> &poss, bool Right, ET
 		dx = 83;
 		dy = 98;
 		assert(!Right);
+
+	case ARTIFACT://45,123
+		x = 342-288;
+		y = 181;
+		w = 44;
+		h = 44;
+		dx = 83;
+		dy = 79;
 	}
 
 
@@ -3144,6 +3230,27 @@ void CMarketplaceWindow::garrisonChanged()
 	}
 }
 
+void CMarketplaceWindow::artifactsChanged(bool left)
+{
+	assert(!left);
+	if(mode != RESOURCE_ARTIFACT)
+		return;
+
+	std::vector<int> available = market->availableItemsIds(mode);
+	std::set<CTradeableItem *> toRemove;
+	BOOST_FOREACH(CTradeableItem *t, right)
+		if(!vstd::contains(available, t->id))
+			toRemove.insert(t);
+
+	BOOST_FOREACH(CTradeableItem *t, toRemove)
+	{
+		if(active)
+			t->deactivate();
+		right -= t;
+		delChild(t);
+	}
+}
+
 CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface * owner)
 {
 	this->pos = pos;

+ 1 - 0
client/GUIClasses.h

@@ -596,6 +596,7 @@ public:
 	void getPositionsFor(std::vector<Rect> &poss, bool Right, EType type) const;
 
 	void garrisonChanged(); //removes creatures with count 0 from the list (apparently whole stack has been sold)
+	void artifactsChanged(bool left);
 };
 
 class CSystemOptionsWindow : public CIntObject

+ 15 - 0
client/NetPacksClient.cpp

@@ -760,6 +760,21 @@ void NewObject::applyCl(CClient *cl)
 	}
 }
 
+void SetAvailableArtifacts::applyCl(CClient *cl)
+{
+	if(id < 0) //artifact merchants globally
+	{
+		for(std::map<ui8, CGameInterface*>::iterator i=cl->playerint.begin();i!=cl->playerint.end();i++)
+			i->second->availableArtifactsChanged(NULL);
+	}
+	else
+	{
+		const CGBlackMarket *bm = dynamic_cast<const CGBlackMarket *>(cl->getObj(id));
+		assert(bm);
+		INTERFACE_CALL_IF_PRESENT(cl->getTile(bm->visitablePos())->visitableObjects.back()->tempOwner, availableArtifactsChanged, bm);
+	}
+}
+
 void TradeComponents::applyCl(CClient *cl)
 {///Shop handler
 	switch (CGI->mh->map->objects[objectid]->ID)

+ 1 - 1
global.h

@@ -109,7 +109,7 @@ const int WEEKLY_GROWTH = 10; //percent
 
 enum EMarketMode
 {
-	RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, ARTIFACT_RESOURCE, RESOURCE_ARTIFACT, ARTIFACT_EXP, CREATURE_EXP,
+	RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, RESOURCE_ARTIFACT, ARTIFACT_RESOURCE, ARTIFACT_EXP, CREATURE_EXP,
 	MARTKET_AFTER_LAST_PLACEHOLDER
 };
 

+ 40 - 5
hch/CObjectHandler.cpp

@@ -1945,6 +1945,22 @@ bool CGTownInstance::allowsTrade(EMarketMode mode) const
 	}
 }
 
+std::vector<int> CGTownInstance::availableItemsIds(EMarketMode mode) const
+{
+	if(mode == RESOURCE_ARTIFACT)
+	{
+		std::vector<int> ret;
+		BOOST_FOREACH(const CArtifact *a, merchantArtifacts)
+			if(a)
+				ret.push_back(a->id);
+			else
+				ret.push_back(-1);
+		return ret;
+	}
+	else
+		return IMarket::availableItemsIds(mode);
+}
+
 void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const
 {
 	if(visitors.find(h->id)==visitors.end())
@@ -6107,6 +6123,21 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
 		val1 = 1;
 		val2 = 1;
 		break;
+	case RESOURCE_ARTIFACT:
+		{
+			float effectiveness = std::min(((float)getMarketEfficiency()+3.0f) / 20.0f, 0.6f);
+			float r = VLC->objh->resVals[id1], //value of offered resource
+				g = VLC->arth->artifacts[id2]->price / effectiveness; //value of bought artifact in gold
+			
+			if(id1 != 6) //non-gold prices are doubled
+				r /= 2; 
+
+			assert(g >= r); //should we allow artifacts cheaper than unit of resource?
+			val1 = (g / r) + 0.5f;
+			val2 = 1;
+		}
+		break;
+
 	default:
 		assert(0);
 		return false;
@@ -6211,6 +6242,9 @@ bool CGMarket::allowsTrade(EMarketMode mode) const
 		}
 	case CREATURE_RESOURCE:
 		return ID == 213; //Freelancer's Guild
+	case ARTIFACT_RESOURCE:
+	case RESOURCE_ARTIFACT:
+		return ID == 7; //Black Market
 	}
 	return false;
 }
@@ -6258,12 +6292,13 @@ std::vector<int> CGBlackMarket::availableItemsIds(EMarketMode mode) const
 	}
 }
 
-void CGBlackMarket::initObj()
-{
-
-}
-
 void CGBlackMarket::newTurn() const
 {
+	if(cb->getDate(2) != 1)
+		return;
 
+	SetAvailableArtifacts saa;
+	saa.id = id;
+	cb->pickAllowedArtsSet(saa.arts);
+	cb->sendAndApply(&saa);
 }

+ 2 - 1
hch/CObjectHandler.h

@@ -513,6 +513,7 @@ public:
 	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
 	int getMarketEfficiency() const; //=market count
 	bool allowsTrade(EMarketMode mode) const;
+	std::vector<int> availableItemsIds(EMarketMode mode) const;
 	void setPropertyDer(ui8 what, ui32 val);
 	void newTurn() const;
 
@@ -1166,13 +1167,13 @@ class DLL_EXPORT CGBlackMarket : public CGMarket
 public:
 	std::vector<const CArtifact *> artifacts; //available artifacts
 
-	void initObj();
 	void newTurn() const; //reset artifacts for black market every month
 	std::vector<int> availableItemsIds(EMarketMode mode) const;
 	
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGMarket&>(*this);
+		h & artifacts;
 	}
 };
 

+ 13 - 1
lib/IGameCallback.cpp

@@ -196,6 +196,18 @@ void IGameCallback::getAllowedArts(std::vector<CArtifact*> &out, std::vector<CAr
 	}
 }
 
+void IGameCallback::pickAllowedArtsSet(std::vector<const CArtifact*> &out)
+{
+	for (int i = 0; i < 2; i++)
+	{
+		for (int j = 0; j < 3 ; j++)
+		{
+			out.push_back(VLC->arth->artifacts[getRandomArt(CArtifact::ART_TREASURE << i)]);
+		}
+	}
+	out.push_back(VLC->arth->artifacts[getRandomArt(CArtifact::ART_MAJOR)]);
+}
+
 void IGameCallback::getAllowed(std::vector<CArtifact*> &out, int flags)
 {
 	if(flags & CArtifact::ART_TREASURE)
@@ -269,4 +281,4 @@ inline TerrainTile * IGameCallback::getTile( int3 pos )
 const PlayerState * IGameCallback::getPlayerState( int color )
 {
 	return gs->getPlayer(color, false);
-}
+}

+ 1 - 0
lib/IGameCallback.h

@@ -61,6 +61,7 @@ public:
 	virtual bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact
 	virtual ui16 getRandomArt (int flags);
 	virtual void getAllowedArts(std::vector<CArtifact*> &out, std::vector<CArtifact*> CArtHandler::*arts, int flag);
+	virtual void pickAllowedArtsSet(std::vector<const CArtifact*> &out); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
 	virtual void getAllowed(std::vector<CArtifact*> &out, int flags); //flags: bitfield uses EartClass
 	virtual void getAllowedSpells(std::vector<ui16> &out, ui16 level);
 	virtual int3 getMapSize(); //returns size of the map

+ 1 - 1
lib/NetPacks.h

@@ -647,7 +647,7 @@ struct NewObject  : public CPackForClient //518
 struct SetAvailableArtifacts  : public CPackForClient //519
 {
 	SetAvailableArtifacts(){type = 519;};
-	//void applyCl(CClient *cl);
+	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 	si32 id; //two variants: id < 0: set artifact pool for Artifact Merchants in towns; id >= 0: set pool for adv. map Black Market (id is the id of Black Market instance then)

+ 2 - 0
lib/RegisterTypes.cpp

@@ -63,6 +63,7 @@ void registerTypes1(Serializer &s)
 	s.template registerType<CGObelisk>();
 	s.template registerType<CGLighthouse>();
 	s.template registerType<CGMarket>();
+	s.template registerType<CGBlackMarket>();
 }
 
 template<typename Serializer> DLL_EXPORT 
@@ -124,6 +125,7 @@ void registerTypes2(Serializer &s)
 	s.template registerType<AdvmapSpellCast>();
 	s.template registerType<OpenWindow>();
 	s.template registerType<NewObject>();
+	s.template registerType<SetAvailableArtifacts>();
 
 	s.template registerType<SaveGame>();
 	s.template registerType<SetSelection>();

+ 5 - 1
lib/map.cpp

@@ -1949,7 +1949,6 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 				break;
 			}
 		//case 2: //Altar of Sacrifice
-		//case 7: //Black Market
 		case 99: //Trading Post
 		case 213: //Freelancer's Guild
 		case 221: //Trading Post (snow)
@@ -1957,6 +1956,11 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 				nobj = new CGMarket();
 				break;
 			}
+		case 7: //Black Market
+			{
+				nobj = new CGBlackMarket();
+				break;
+			}
 
 		default: //any other object
 			{

+ 58 - 0
server/CGameHandler.cpp

@@ -1042,6 +1042,14 @@ void CGameHandler::newTurn()
 		//unhiding what shouldn't be hidden? //that's handled in netpacks client
 	}
 
+	if(getDate(2) == 1)
+	{
+		SetAvailableArtifacts saa;
+		saa.id = -1;
+		pickAllowedArtsSet(saa.arts);
+		sendAndApply(&saa);
+	}
+
 	sendAndApply(&n);
 	tlog5 << "Info about turn " << n.day << "has been sent!" << std::endl;
 	handleTimeEvents();
@@ -3032,6 +3040,56 @@ bool CGameHandler::buyArtifact( ui32 hid, si32 aid )
 	return false;
 }
 
+bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, int rid, int aid)
+{
+	if(!vstd::contains(m->availableItemsIds(RESOURCE_ARTIFACT), aid))
+		COMPLAIN_RET("That artifact is unavailable!");
+
+	int b1, b2;
+	m->getOffer(rid, aid, b1, b2, RESOURCE_ARTIFACT);
+	
+	if(getResource(h->tempOwner, rid) < b1)
+		COMPLAIN_RET("You can't afford to buy this artifact!");
+
+	SetResource sr;
+	sr.player = h->tempOwner;
+	sr.resid = rid;
+	sr.val = getResource(h->tempOwner, rid) - b1;
+	sendAndApply(&sr);
+
+
+	SetAvailableArtifacts saa;
+	if(m->o->ID == TOWNI_TYPE)
+	{
+		saa.id = -1;
+		saa.arts = CGTownInstance::merchantArtifacts;
+	}
+	else if(const CGBlackMarket *bm = dynamic_cast<const CGBlackMarket *>(m->o)) //black market
+	{
+		saa.id = bm->id;
+		saa.arts = bm->artifacts;
+	}
+	else
+		COMPLAIN_RET("Wrong marktet...");
+
+	bool found = false;
+	BOOST_FOREACH(const CArtifact *&art, saa.arts)
+	{
+		if(art && art->id == aid)
+		{
+			art = NULL;
+			found = true;
+			break;
+		}
+	}
+
+	if(!found)
+		COMPLAIN_RET("Cannot find selected artifact on the list");
+
+	sendAndApply(&saa);
+
+	giveHeroArtifact(aid, h->id, -2);
+}
 
 bool CGameHandler::tradeResources(const IMarket *market, ui32 val, ui8 player, ui32 id1, ui32 id2)
 {

+ 2 - 1
server/CGameHandler.h

@@ -172,7 +172,8 @@ public:
 	bool sendResources(ui32 val, ui8 player, ui32 r1, ui32 r2);
 	bool sellCreatures(ui32 count, const IMarket *market, const CGHeroInstance * hero, ui32 slot, ui32 resourceID);
 	bool assembleArtifacts (si32 heroID, ui16 artifactSlot, bool assemble, ui32 assembleTo);
-	bool buyArtifact( ui32 hid, si32 aid );
+	bool buyArtifact( ui32 hid, si32 aid ); //for blacksmith and mage guild only -> buying for gold in common buildings
+	bool buyArtifact( const IMarket *m, const CGHeroInstance *h, int rid, int aid); //for artifact merchant and black market -> buying for any resource in special building / advobject
 	bool swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, ui16 destSlot);
 	bool garrisonSwap(si32 tid);
 	bool upgradeCreature( ui32 objid, ui8 pos, ui32 upgID );

+ 5 - 2
server/NetPacksServer.cpp

@@ -145,9 +145,12 @@ bool TradeOnMarketplace::applyGh( CGameHandler *gh )
 		if(!hero)
 			COMPLAIN_AND_RETURN("Only hero can sell creatures!");
 		return gh->sellCreatures(val, m, hero, r1, r2);
+	case RESOURCE_ARTIFACT:
+		if(!hero)
+			COMPLAIN_AND_RETURN("Only hero can buy artifacts!");
+		return gh->buyArtifact(m, hero, r1, r2);
 	default:
-		gh->complain("Unknown exchange mode!");
-		ERROR_AND_RETURN;
+		COMPLAIN_AND_RETURN("Unknown exchange mode!");
 	}
 }