Browse Source

* Freelancer's Guild support (both town structure and adventure map object)
* fixed crashes on r-click on hero in tavern and adv map
* fixed descync issue
* allow free movement FROM guarded tile

Michał W. Urbańczyk 15 years ago
parent
commit
8bda10b695

+ 2 - 0
ChangeLog

@@ -38,12 +38,14 @@ TOWNS:
 * Support for new town structures:
 - Lighthouse
 - Colossus
+- Freelancer's Guild
 - Guardian Spirit
 - Necromancy Amplifier
 - Soul Prison
 
 OBJECTS:
 New object supported:
+- Freelancer's Guild
 - Trading Post
 - War Machine Factory
 

+ 16 - 10
client/CCastleInterface.cpp

@@ -23,6 +23,7 @@
 #include <boost/lexical_cast.hpp>
 #include <cmath>
 #include <sstream>
+#include <boost/format.hpp>
 using namespace boost::assign;
 using namespace CSDL_Ext;
 
@@ -557,6 +558,8 @@ void CCastleInterface::buildingClicked(int building)
 		building = town->town->hordeLvl[1] + 30;
 	}
 
+	const CBuilding *b = CGI->buildh->buildings[town->subID][building];
+
 	if(building >= 30)
 	{
 		showRecruitmentWindow(building);
@@ -635,7 +638,7 @@ void CCastleInterface::buildingClicked(int building)
 			break;
 		case 14:  //marketplace
 			{
-				CMarketplaceWindow *cmw = new CMarketplaceWindow(town);
+				CMarketplaceWindow *cmw = new CMarketplaceWindow(town, town->visitingHero);
 				GH.pushInt(cmw);
 				break;
 			}
@@ -648,13 +651,13 @@ void CCastleInterface::buildingClicked(int building)
 			{
 				switch(town->subID)
 				{
-	/*Rampart*/		case 1://Mystic Pond
+	/*Rampart*/	case 1://Mystic Pond
 					enterFountain(building);
 					break;
-	/*Tower*/		case 2://Artifact Merchant
-	/*Dungeon*/		case 5://Artifact Merchant
-	/*Conflux*/		case 8://Artifact Merchant
-					tlog4<<"Artifact Merchant not handled\n";
+	/*Tower*/	case 2://Artifact Merchant
+	/*Dungeon*/	case 5://Artifact Merchant
+	/*Conflux*/	case 8://Artifact Merchant
+					GH.pushInt(new CMarketplaceWindow(town, town->visitingHero, RESOURCE_ARTIFACT));
 					break;
 				default:
 					defaultBuildingClicked(building);
@@ -671,13 +674,16 @@ void CCastleInterface::buildingClicked(int building)
 			{
 				switch(town->subID)
 				{
-	/*Rampart*/		case 1: //Fountain of Fortune
+	/*Rampart*/	case 1: //Fountain of Fortune
 					enterFountain(building);
 					break;
-	/*Stronghold*/		case 6: //Freelancer's Guild
-					tlog4<<"Freelancer's Guild not handled\n";
+	/*Stronghold*/case 6: //Freelancer's Guild
+					if(town->visitingHero)
+						GH.pushInt(new CMarketplaceWindow(town, town->visitingHero, CREATURE_RESOURCE));
+					else
+						LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
 					break;
-	/*Conflux*/		case 8: //Magic University
+	/*Conflux*/	case 8: //Magic University
 					tlog4<<"Magic University not handled\n";
 					break;
 				default:

+ 9 - 5
client/CPlayerInterface.cpp

@@ -478,11 +478,15 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
 			wwg->garr->recreateSlots();
 			wasGarrison = true;
 		}
-		else
+		else if(CKingdomInterface *cki = dynamic_cast<CKingdomInterface*>(*i))
 		{//a cheat for Kingdom Overview window (it has CWindowWithGarrison-childrens which are not present in ListInt)
-			CKingdomInterface *cki = dynamic_cast<CKingdomInterface*>(*i);//need to create "Garrison Holder" class thingy
-			if (cki)
-				cki->updateAllGarrisons();
+		 //need to create "Garrison Holder" class thingy
+			cki->updateAllGarrisons();
+		}
+		else if(CMarketplaceWindow *cmw = dynamic_cast<CMarketplaceWindow*>(*i))
+		{
+			if(obj == cmw->hero)
+				cmw->garrisonChanged();
 		}
 	}
 
@@ -1977,6 +1981,6 @@ void CPlayerInterface::stopMovement()
 
 void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor)
 {
-	CMarketplaceWindow *cmw = new CMarketplaceWindow(market, market->availableModes().front());
+	CMarketplaceWindow *cmw = new CMarketplaceWindow(market, visitor, market->availableModes().front());
 	GH.pushInt(cmw);
 }

+ 186 - 60
client/GUIClasses.cpp

@@ -2581,8 +2581,9 @@ CCustomImgComponent::~CCustomImgComponent()
 		SDL_FreeSurface(bmp);
 }
 
-CMarketplaceWindow::CTradeableItem::CTradeableItem( int Type, int ID, bool Left)
+CMarketplaceWindow::CTradeableItem::CTradeableItem( EType Type, int ID, bool Left, int Serial)
 {
+	serial = Serial;
 	left = Left;
 	type = Type;
 	id = ID;
@@ -2639,24 +2640,29 @@ SDL_Surface * CMarketplaceWindow::CTradeableItem::getSurface()
 		return graphics->flags->ourImages[id].bitmap;
 	case ARTIFACT:
 		return graphics->artDefs->ourImages[id].bitmap;
+	case CREATURE:
+		return graphics->bigImgs[id];
 	default:
 		return NULL;
 	}
 }
-static void initItems( std::vector<CMarketplaceWindow::CTradeableItem*> &i, std::vector<Rect> &p, int type, int amount, bool left, std::vector<int> *ids/*=NULL*/ )
+static void initItems( std::vector<CMarketplaceWindow::CTradeableItem*> &i, std::vector<Rect> &p, CMarketplaceWindow::EType type, int amount, bool left, std::vector<int> *ids/*=NULL*/ )
 {
 	if(ids)
 		amin(amount, ids->size());
 
-	i.resize(amount);
 	for(int j=0;j<amount;j++)
 	{
-		i[j] = new CMarketplaceWindow::CTradeableItem(type,(ids && ids->size()>j) ? (*ids)[j] : j, left);
-		i[j]->pos = p[j] + i[j]->pos;
+		int id = (ids && ids->size()>j) ? (*ids)[j] : j;
+		if(id < 0) 
+			continue;
+
+		i.push_back(new CMarketplaceWindow::CTradeableItem(type, id, left, j));
+		i.back()->pos =  p[j] + i.back()->pos;
 	}
 }
-CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode)
-	:market(Market)
+CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode Mode)
+	:market(Market), hero(Hero), hLeft(NULL), hRight(NULL), readyToTrade(false)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	type = BLOCK_ADV_HOTKEYS;
@@ -2675,6 +2681,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode)
 		ltype = RESOURCE;
 		rtype = RESOURCE;
 		break;
+
 	case RESOURCE_PLAYER:
 		bgName = "TPMRKPTS.bmp";
 		ltype = RESOURCE;
@@ -2685,13 +2692,40 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode)
 			if(i != LOCPLINT->playerID && LOCPLINT->cb->getPlayerStatus(i) == PlayerState::INGAME)
 				rIds->push_back(i);
 		break;
+
+	case CREATURE_RESOURCE:
+		bgName = "TPMRKCRS.bmp";
+		ltype = CREATURE;
+		rtype = RESOURCE;
+		lIds = new std::vector<int>;
+		for(int i = 0; i < 7; i++)
+		{
+			if(const CCreature *c = hero->getCreature(i))
+				lIds->push_back(c->idNumber);
+			else
+				lIds->push_back(-1);
+		}
+		break;
 	}
 
 	bg = new CPicture(bgName);
 	bg->colorizeAndConvert(LOCPLINT->playerID);
 
-	printAtMiddle(CGI->generaltexth->allTexts[158],300,27,FONT_BIG,tytulowy,*bg); //title
-	printAtMiddle(CGI->generaltexth->allTexts[270],154,148,FONT_SMALL,zwykly,*bg); //kingdom res.
+	if(market->o->ID == 99 || market->o->ID == 221)
+	{
+		printAtMiddle(CGI->generaltexth->allTexts[159],300,27,FONT_BIG,tytulowy,*bg); //title
+	}
+	else if(mode == CREATURE_RESOURCE)
+	{
+		if(market->o->ID == TOWNI_TYPE)
+			printAtMiddle(CGI->buildh->buildings[6][21]->Name(), 300, 27, FONT_BIG, tytulowy, *bg); //title
+		else
+			printAtMiddle(market->o->getHoverText(), 300, 27, FONT_BIG, tytulowy, *bg); //title
+	}
+	else
+	{
+		printAtMiddle(CGI->generaltexth->allTexts[158],300,27,FONT_BIG,tytulowy,*bg); //trading post
+	}
 
 	std::vector<Rect> lpos, rpos;
 	getPositionsFor(lpos, false, ltype);
@@ -2705,28 +2739,44 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode)
 	//slider and buttons must be created after bg
 	slider = new CSlider(231,490,137,boost::bind(&CMarketplaceWindow::sliderMoved,this,_1),0,0);
 
-	hLeft = hRight = NULL;
+	
 	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");
 
+	//left side
 	switch(Mode)
 	{
 	case RESOURCE_RESOURCE:
-		{
-			new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF");
-			printAtMiddle(CGI->generaltexth->allTexts[168],445,147,FONT_SMALL,zwykly,*bg); //available for trade
-		}
+	case RESOURCE_PLAYER:
+		printAtMiddle(CGI->generaltexth->allTexts[270],154,148,FONT_SMALL,zwykly,*bg); //kingdom res.
+		break;
+	case CREATURE_RESOURCE: 
+		printAtMiddle(boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name), 152, 102, FONT_SMALL, zwykly, *bg); //%s's Creatures
+		break;
+	}
+
+	//right side
+	switch(Mode)
+	{
+	case RESOURCE_RESOURCE:
+	case CREATURE_RESOURCE:
+		printAtMiddle(CGI->generaltexth->allTexts[168],445,148,FONT_SMALL,zwykly,*bg); //available for trade
 		break;
 	case RESOURCE_PLAYER:
-		{
-			new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_RESOURCE), 516, 450,"TPMRKBU5.DEF");
-			printAtMiddle(CGI->generaltexth->allTexts[169],445,55,FONT_SMALL,zwykly,*bg); //players
-		}
+		printAtMiddle(CGI->generaltexth->allTexts[169],445,55,FONT_SMALL,zwykly,*bg); //players
 		break;
 	}
 
+	if(printButtonFor(RESOURCE_PLAYER))
+		new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF");
+	if(printButtonFor(RESOURCE_RESOURCE))
+		new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_RESOURCE), 516, 450,"TPMRKBU5.DEF");
+	if(printButtonFor(CREATURE_RESOURCE))
+		new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, CREATURE_RESOURCE), 516, 450,"TPMRKBU4.DEF");
+
+
 	max->block(true);
 	deal->block(true);
 }
@@ -2745,9 +2795,9 @@ CMarketplaceWindow::~CMarketplaceWindow()
 	bg = NULL;
 }
 
-void CMarketplaceWindow::show(SDL_Surface * to)
+void CMarketplaceWindow::showAll(SDL_Surface * to)
 {
-	CIntObject::show(to);
+	CIntObject::showAll(to);
 
 
 	if(hRight)
@@ -2756,49 +2806,62 @@ void CMarketplaceWindow::show(SDL_Surface * to)
 		CSDL_Ext::drawBorder(to,hLeft->pos.x-1,hLeft->pos.y-1,hLeft->pos.w+2,hLeft->pos.h+2,int3(255,231,148));
 
 	//left side
-	if(mode == RESOURCE_RESOURCE || mode == RESOURCE_PLAYER)
+	switch(ltype)
 	{
+	case RESOURCE:
 		for(int i=0;i<left.size();i++)
-			printAtMiddle(boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i)),
-						  left[i]->pos.x+36,left[i]->pos.y+57,FONT_SMALL,zwykly,to);
-
+			printAtMiddle(boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i)), left[i]->pos.x+36,left[i]->pos.y+57,FONT_SMALL,zwykly,to);
 
-		if(hLeft && hRight && (hLeft->id != hRight->id || mode != RESOURCE_RESOURCE))
+		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);
 		}
+		break;
+
+	case CREATURE:
+		BOOST_FOREACH(CTradeableItem *t, left)
+			printAtMiddle(boost::lexical_cast<std::string>(hero->getAmount(t->serial)), t->pos.x+29, t->pos.y+76, FONT_SMALL, zwykly, to);
+
+		if(readyToTrade)
+		{
+			blitAt(hLeft->getSurface(),pos.x+128,pos.y+450,to);
+			printAtMiddle(boost::lexical_cast<std::string>( slider->value * r1 ),pos.x+160,pos.y+527,FONT_SMALL,zwykly,to);
+		}
+		break;
 	}
 
-	if(mode == RESOURCE_RESOURCE)
+	//right side
+	switch(rtype)
 	{
+	case RESOURCE:
 		if(hLeft) //print prices
 		{
 			for(int i=0; i<right.size();i++)
 			{
-				if(right[i]->id != hLeft->id)
+				if(right[i]->id != hLeft->id || mode != RESOURCE_RESOURCE)
 					printAtMiddle(rSubs[i],right[i]->pos.x+36,right[i]->pos.y+57,FONT_SMALL,zwykly,to);
 				else
 					printAtMiddle(CGI->generaltexth->allTexts[164],right[i]->pos.x+36,right[i]->pos.y+57,FONT_SMALL,zwykly,to);
 			}
 		}
-		if(hLeft && hRight && (hLeft->id != hRight->id))
+		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);
 		}
-	}
-	else if(mode == RESOURCE_PLAYER)
-	{
+		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(hLeft && hRight)
+		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);
 		}
+		break;
 	}
 }
 
@@ -2809,30 +2872,44 @@ void CMarketplaceWindow::setMax()
 
 void CMarketplaceWindow::makeDeal()
 {
-	LOCPLINT->cb->trade(market->o, mode, hLeft->id, hRight->id, slider->value*r1);
+	if(!slider->value)
+		return;
+
+	int leftIdToSend = -1;
+	if(mode == CREATURE_RESOURCE)
+		leftIdToSend = hLeft->serial;
+	else
+		leftIdToSend = hLeft->id;
+
+	LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero);
 	slider->moveTo(0);
 	hLeft = NULL;
+	hRight = NULL;
 	selectionChanged(true);
 }
 
 void CMarketplaceWindow::sliderMoved( int to )
 {
+	redraw();
 }
 
 void CMarketplaceWindow::selectionChanged(bool side)
 {
-	if(hLeft && hRight && (hLeft->id!= hRight->id || mode != RESOURCE_RESOURCE))
+	readyToTrade = (hLeft && hRight && (hLeft->id!= hRight->id || mode != RESOURCE_RESOURCE));
+
+	if(readyToTrade)
 	{
-		if(mode == RESOURCE_RESOURCE)
-		{
-			market->getOffer(hLeft->id, hRight->id, r1, r2, mode);
-			slider->setAmount(LOCPLINT->cb->getResourceAmount(hLeft->id) / r1);
-		}
-		else if(mode == RESOURCE_PLAYER)
-		{
-			r1 = 1;
-			slider->setAmount(LOCPLINT->cb->getResourceAmount(hLeft->id));
-		}
+		int newAmount = -1;
+		market->getOffer(hLeft->id, hRight->id, r1, r2, mode);
+
+		if(ltype == RESOURCE)
+			newAmount = LOCPLINT->cb->getResourceAmount(hLeft->id);
+		else if(ltype ==  CREATURE)
+			newAmount = hero->getAmount(hLeft->serial) - (hero->Slots().size() == 1  &&  hero->needsLastStack());
+		else
+			assert(0);
+
+		slider->setAmount(newAmount / r1);
 		slider->moveTo(0);
 		max->block(false);
 		deal->block(false);
@@ -2851,7 +2928,7 @@ void CMarketplaceWindow::selectionChanged(bool side)
 		int h1, h2;
 		for(int i=0;i<right.size();i++)
 		{
-			market->getOffer(hLeft->id, i, h1, h2, RESOURCE_RESOURCE);
+			market->getOffer(hLeft->id, i, h1, h2, mode);
 
 			std::ostringstream oss;
 			oss << h2;
@@ -2860,35 +2937,84 @@ void CMarketplaceWindow::selectionChanged(bool side)
 			rSubs[i] = oss.str();
 		}
 	}
+	redraw();
 }
 
 void CMarketplaceWindow::getPositionsFor(std::vector<Rect> &poss, bool Right, EType type) const
 {
-	if(type == RESOURCE)
-	{
-		poss += genRect(66,74,39 ,180), genRect(66,74,122,180), genRect(66,74,204,180),
-				genRect(66,74,39,259), genRect(66,74,122,259), genRect(66,74,204,259),
-				genRect(66,74,122,338);
-		if(Right)
-			BOOST_FOREACH(Rect &r, poss)
-				r.x += 288;
-	}
-	else if(type == PLAYER)
+	int h, w, x, y, dx, dy;
+	int leftToRightOffset = 288;
+	
+	switch(type)
 	{
+	case RESOURCE:
+		dx = 82;
+		dy = 79;
+		x = 39;
+		y = 180;
+		h = 66;
+		w = 74;
+		break;
+	case PLAYER:
+		dx = 83;
+		dy = 118;
+		h = 64;
+		w = 58;
+		x = 44;
+		y = 83;
 		assert(Right);
-		poss += genRect(64, 58, 333, 84), genRect(64, 58, 333 + 83, 84), genRect(64, 58, 333 + 2 * 83, 84),
-			genRect(64, 58, 333, 84 + 118), genRect(64, 58, 333 + 83, 84 + 118), genRect(64, 58, 333 + 2 * 83, 84 + 118),
-			genRect(64, 58, 333 + 83, 84 + 2*118);
+		break;
+	case CREATURE://45,123
+		x = 45;
+		y = 123;
+		w = 58;
+		h = 64;
+		dx = 83;
+		dy = 98;
+		assert(!Right);
 	}
+
+
+	poss += genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y),
+		genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy),
+		genRect(h, w, x + dx, y + 2*dy);
+
+	if(Right)
+		BOOST_FOREACH(Rect &r, poss)
+			r.x += leftToRightOffset;
 }
 
 void CMarketplaceWindow::setMode(EMarketMode Mode)
 {
-	CMarketplaceWindow *nwindow = new CMarketplaceWindow(market, Mode);
+	CMarketplaceWindow *nwindow = new CMarketplaceWindow(market, hero, Mode);
 	GH.popIntTotally(this);
 	GH.pushInt(nwindow);
 }
 
+bool CMarketplaceWindow::printButtonFor(EMarketMode M) const
+{
+	return market->allowsTrade(M) && M != mode && (hero || mode != CREATURE_RESOURCE);
+}
+
+void CMarketplaceWindow::garrisonChanged()
+{
+	if(mode != CREATURE_RESOURCE)
+		return;
+
+	std::set<CTradeableItem *> toRemove;
+	BOOST_FOREACH(CTradeableItem *t, left)
+		if(!hero->getAmount(t->serial))
+			toRemove.insert(t);
+
+	BOOST_FOREACH(CTradeableItem *t, toRemove)
+	{
+		if(active)
+			t->deactivate();
+		left -= t;
+		delChild(t);
+	}
+}
+
 CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface * owner)
 {
 	this->pos = pos;

+ 12 - 6
client/GUIClasses.h

@@ -518,26 +518,29 @@ public:
 
 class CMarketplaceWindow : public CIntObject
 {
+	bool printButtonFor(EMarketMode M) const;
 public:
 	enum EType
 	{
-		RESOURCE, PLAYER, ARTIFACT
+		RESOURCE, PLAYER, ARTIFACT, CREATURE
 	};
 	class CTradeableItem : public CIntObject
 	{
 	public:
-		int type; //0 - res, 1 - artif big, 2 - artif small, 3 - player flag
-		int id;
+		EType type;
+		int id; 
+		int serial;
 		bool left;
 		CFunctionList<void()> callback;
 
 		void show(SDL_Surface * to);
 		void clickLeft(tribool down, bool previousState);
 		SDL_Surface *getSurface();
-		CTradeableItem(int Type, int ID, bool Left);
+		CTradeableItem(EType Type, int ID, bool Left, int Serial);
 	};
 
 	const IMarket *market;
+	const CGHeroInstance *hero;
 	CPicture *bg; //background
 	std::vector<CTradeableItem*> left, right;
 	std::vector<std::string> rSubs; //offer caption
@@ -548,17 +551,20 @@ public:
 	int r1, r2; //suggested amounts of traded resources
 	AdventureMapButton *ok, *max, *deal;
 	CSlider *slider; //for choosing amount to be exchanged
+	bool readyToTrade;
 
-	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
 	void setMax();
 	void sliderMoved(int to);
 	void makeDeal();
 	void selectionChanged(bool side); //true == left
-	CMarketplaceWindow(const IMarket *Market, EMarketMode Mode = RESOURCE_RESOURCE); //c-tor
+	CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero = NULL, EMarketMode Mode = RESOURCE_RESOURCE); //c-tor
 	~CMarketplaceWindow(); //d-tor
 	void setMode(EMarketMode Mode); //mode setter
 
 	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)
 };
 
 class CSystemOptionsWindow : public CIntObject

+ 1 - 1
client/Graphics.cpp

@@ -61,7 +61,7 @@ SDL_Surface * Graphics::drawHeroInfoWin(const InfoAboutHero &curh)
 		}
 		else
 		{
-			printAtMiddle(VLC->generaltexth->arraytxt[174 + 3*i->second.count],slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,zwykly,ret);
+			printAtMiddle(VLC->generaltexth->arraytxt[174 + 3*(i->second.count-1)],slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,zwykly,ret);
 		}
 	}
 

+ 50 - 11
hch/CObjectHandler.cpp

@@ -79,7 +79,7 @@ void IObjectInterface::preInit()
 void CPlayersVisited::setPropertyDer( ui8 what, ui32 val )
 {
 	if(what == 10)
-		players.insert(val);
+		players.insert((ui8)val);
 }
 
 bool CPlayersVisited::hasVisited( ui8 player ) const
@@ -1162,6 +1162,7 @@ void CGHeroInstance::pushPrimSkill(int which, int val)
 
 void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
 {
+#define FOREACH_OWNER_TOWN(town) if(const PlayerState *p = cb->getPlayerState(tempOwner)) BOOST_FOREACH(const CGTownInstance *town, p->towns)
 	CArmedInstance::getBonuses(out, selector, root);
 
 	//TODO eliminate by moving secondary skills effects to bonus system
@@ -1172,7 +1173,7 @@ void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const
 			out.push_back(Bonus(Bonus::PERMANENT, Bonus::LUCK, Bonus::SECONDARY_SKILL, luckSkill, 9, VLC->generaltexth->arraytxt[73+luckSkill]));
 
 		//guardian spirit
-		BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns)
+		FOREACH_OWNER_TOWN(t)
 			if(t->subID ==1 && vstd::contains(t->builtBuildings,26)) //rampart with grail
 				out.push_back(Bonus(Bonus::PERMANENT, Bonus::LUCK, Bonus::TOWN_STRUCTURE, +2, 26, VLC->generaltexth->buildings[1][26].first + " +2"));
 	}
@@ -1180,7 +1181,7 @@ void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const
 	if(Selector::matchesType(selector, Bonus::SEA_MOVEMENT))
 	{
 		//lighthouses
-		BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns)
+		FOREACH_OWNER_TOWN(t)
 			if(t->subID == 0 && vstd::contains(t->builtBuildings,17)) //castle
 				out.push_back(Bonus(Bonus::PERMANENT, Bonus::SEA_MOVEMENT, Bonus::TOWN_STRUCTURE, +500, 17, VLC->generaltexth->buildings[0][17].first + " +500"));
 	}
@@ -1192,14 +1193,14 @@ void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const
 			out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::SECONDARY_SKILL, moraleSkill, 6, VLC->generaltexth->arraytxt[104+moraleSkill]));
 
 		//colossus
-		BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns)
+		FOREACH_OWNER_TOWN(t)
 			if(t->subID == 0 && vstd::contains(t->builtBuildings,26)) //castle
 				out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::TOWN_STRUCTURE, +2, 26, VLC->generaltexth->buildings[0][26].first + " +2"));
 	}
 
 	if(Selector::matchesTypeSubtype(selector, Bonus::SECONDARY_SKILL_PREMY, 12)) //necromancy
 	{
-		BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns)
+		FOREACH_OWNER_TOWN(t)
 		{
 			if(t->subID == 4) //necropolis
 			{
@@ -5932,7 +5933,30 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
 				val2 = 1;
 			}
 		}
+		break;
+	case CREATURE_RESOURCE:
+		{
+			const float effectivenessArray[] = {0, 0.3, 0.45, 0.50, 0.65, 0.7, 0.85, 0.9, 1};
+			float effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)];
+
+			float r = VLC->creh->creatures[id1]->cost[6], //value of given creature in gold
+				g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource
 
+			if(r>g) //if given resource is more expensive than wanted
+			{
+				val2 = ceil(r / g);
+				val1 = 1;
+			}
+			else //if wanted resource is more expensive
+			{
+				val1 = (g / r) + 0.5f;
+				val2 = 1;
+			}
+		}
+		break;
+	case RESOURCE_PLAYER:
+		val1 = 1;
+		val2 = 1;
 		break;
 	default:
 		assert(0);
@@ -5949,19 +5973,29 @@ bool IMarket::allowsTrade(EMarketMode mode) const
 
 int IMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
 {
-	if(mode == RESOURCE_RESOURCE || ARTIFACT_RESOURCE || CREATURE_RESOURCE)
-		return -1;
-	else 
-		return 1;
+	switch(mode)
+	{
+	case RESOURCE_RESOURCE:
+	case ARTIFACT_RESOURCE:
+	case CREATURE_RESOURCE:
+			return -1;
+	default:
+			return 1;
+	}
 }
 
 std::vector<int> IMarket::availableItemsIds(EMarketMode mode) const
 {
 	std::vector<int> ret;
-	if(mode == RESOURCE_RESOURCE || ARTIFACT_RESOURCE || CREATURE_RESOURCE)
+	switch(mode)
+	{
+	case RESOURCE_RESOURCE:
+	case ARTIFACT_RESOURCE:
+	case CREATURE_RESOURCE:
 		for (int i = 0; i < 7; i++)
 			ret.push_back(i);
-
+		break;
+	}
 	return ret;
 }
 
@@ -5971,8 +6005,10 @@ const IMarket * IMarket::castFrom(const CGObjectInstance *obj)
 	{
 	case TOWNI_TYPE:
 		return static_cast<const CGTownInstance*>(obj);
+	case 7: //Black Market
 	case 99: //Trading Post
 	case 221: //Trading Post (snow)
+	case 213: //Freelancer's Guild
 		return static_cast<const CGMarket*>(obj);
 	default:
 		tlog1 << "Cannot cast to IMarket object with ID " << obj->ID << std::endl;
@@ -6034,7 +6070,10 @@ bool CGMarket::allowsTrade(EMarketMode mode) const
 		default:
 			return false;
 		}
+	case CREATURE_RESOURCE:
+		return ID == 213; //Freelancer's Guild
 	}
+	return false;
 }
 
 int CGMarket::availableUnits(EMarketMode mode, int marketItemSerial) const

+ 1 - 0
lib/CCreatureSet.cpp

@@ -192,6 +192,7 @@ void CCreatureSet::setFormation(bool tight)
 void CCreatureSet::setStackCount(TSlot slot, TQuantity count)
 {
 	assert(vstd::contains(slots, slot));
+	assert(count > 0);
 	slots[slot].count = count;
 }
 

+ 1 - 1
lib/CCreatureSet.h

@@ -80,7 +80,7 @@ public:
 	virtual bool needsLastStack() const; //true if last stack cannot be taken
 	int getArmyStrength() const; //sum of AI values of creatures
 	ui64 getPower (TSlot slot) const; //value of specific stack
-	std::string getRoughAmount (TSlot slot) const; //rought size of specific stack
+	std::string getRoughAmount (TSlot slot) const; //rough size of specific stack
 	
 	bool contains(const CStackInstance *stack) const;
 

+ 6 - 5
lib/CGameState.cpp

@@ -1752,7 +1752,7 @@ void CGameState::getNeighbours( const TerrainTile &srct, int3 tile, std::vector<
 		const TerrainTile &hlpt = map->getTile(hlp);
 
 		//we cannot visit things from blocked tiles
-		if(srct.blocked && hlpt.visitable)
+		if(srct.blocked && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE)
 		{
 			continue;
 		}
@@ -2216,6 +2216,8 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 		CGPathNode *cp = mq.front();
 		mq.pop();
 
+		const int3 guardPosition = guardingCreaturePosition(cp->coord);
+		const bool guardedPosition = (guardPosition != int3(-1, -1, -1) && cp->coord != src);
 		const TerrainTile &ct = map->getTile(cp->coord);
 		int movement = cp->moveRemains, turn = cp->turns;
 		if(!movement)
@@ -2252,7 +2254,6 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 				remains = moveAtNextTile - cost;
 			}
 
-			const bool guardedPosition = guardingCreaturePosition(cp->coord) != int3(-1, -1, -1);
 			const bool neighborIsGuard = guardingCreaturePosition(cp->coord) == dp.coord;
 
 			if((dp.turns==0xff		//we haven't been here before
@@ -2267,10 +2268,10 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 				dp.theNodeBefore = cp;
 
 				const bool guardedNeighbor = guardingCreaturePosition(dp.coord) != int3(-1, -1, -1);
-				const bool positionIsGuard = guardingCreaturePosition(cp->coord) == cp->coord;
+				//const bool positionIsGuard = guardingCreaturePosition(cp->coord) == cp->coord; //can this be true? hero never passes from monster tile...
 
 				if (dp.accessible == CGPathNode::ACCESSIBLE || dp.accessible == CGPathNode::FLYABLE
-					|| (guardedNeighbor && !positionIsGuard)) // Can step into a hostile tile once.
+					|| (guardedNeighbor && !guardedPosition)) // Can step into a hostile tile once.
 				{
 					mq.push(&dp);
 				}
@@ -3851,7 +3852,7 @@ void InfoAboutHero::initFromHero( const CGHeroInstance *h, bool detailed )
 		//hide info about hero stacks counts using descriptives names ids
 		for(TSlots::const_iterator i = army.Slots().begin(); i != army.Slots().end(); ++i)
 		{
-			army.setStackCount(i->first, i->second.getQuantityID());
+			army.setStackCount(i->first, i->second.getQuantityID()+1);
 		}
 	}
 }

+ 42 - 2
server/CGameHandler.cpp

@@ -1064,7 +1064,7 @@ void CGameHandler::newTurn()
 		}
 	}
 }
-void CGameHandler::run(bool resume)
+void CGameHandler::run(bool resume, const StartInfo *si /*= NULL*/)
 {
 	using namespace boost::posix_time;
 	BOOST_FOREACH(CConnection *cc, conns)
@@ -1072,7 +1072,7 @@ void CGameHandler::run(bool resume)
 		ui8 quantity, pom;
 		//ui32 seed;
 		if(!resume)
-			(*cc) << gs->scenarioOps << gs->map->checksum << gs->seed;
+			(*cc) << si << gs->map->checksum << gs->seed; // gs->scenarioOps
 
 		(*cc) >> quantity; //how many players will be handled at that client
 		for(int i=0;i<quantity;i++)
@@ -3028,6 +3028,46 @@ bool CGameHandler::tradeResources(const IMarket *market, ui32 val, ui8 player, u
 	return true;
 }
 
+bool CGameHandler::sellCreatures(ui32 count, const IMarket *market, const CGHeroInstance * hero, ui32 slot, ui32 resourceID)
+{
+	if(!vstd::contains(hero->Slots(), slot))
+		COMPLAIN_RET("Hero doesn't have any creature in that slot!");
+
+	const CStackInstance &s = hero->getStack(slot);
+
+	if(s.count < count  //can't sell more creatures than have
+		|| hero->Slots().size() == 1  &&  hero->needsLastStack()  &&  s.count == count) //can't sell last stack
+	{
+		COMPLAIN_RET("Not enough creatures in army!");
+	}
+
+	int b1, b2; //base quantities for trade
+ 	market->getOffer(s.type->idNumber, resourceID, b1, b2, CREATURE_RESOURCE);
+ 	int units = count / b1; //how many base quantities we trade
+ 
+ 	if(count%b1) //all offered units of resource should be used, if not -> somewhere in calculations must be an error
+ 	{
+ 		//TODO: complain?
+ 		assert(0);
+ 	}
+ 
+
+	SetGarrisons sg;
+	sg.garrs[hero->id] = hero->getArmy();
+	if(s.count > count)
+		sg.garrs[hero->id].setStackCount(slot, s.count - count);
+	else
+		sg.garrs[hero->id].eraseStack(slot);
+	sendAndApply(&sg);
+
+ 	SetResource sr;
+ 	sr.player = hero->tempOwner;
+ 	sr.resid = resourceID;
+ 	sr.val = getResource(hero->tempOwner, resourceID) + b2 * units;
+ 	sendAndApply(&sr);
+
+	return true;
+}
 
 bool CGameHandler::sendResources(ui32 val, ui8 player, ui32 r1, ui32 r2)
 {

+ 2 - 1
server/CGameHandler.h

@@ -167,6 +167,7 @@ public:
 	bool setFormation( si32 hid, ui8 formation );
 	bool tradeResources(const IMarket *market, ui32 val, ui8 player, ui32 id1, ui32 id2);
 	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 swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, ui16 destSlot);
@@ -203,7 +204,7 @@ public:
 	void sendAndApply(SetResources * info);
 	void sendAndApply(NewStructures * info);
 
-	void run(bool resume);
+	void run(bool resume, const StartInfo *si = NULL);
 	void newTurn();
 	void handleAfterAttackCasting( const BattleAttack & bat );
 	friend class CVCMIServer;

+ 2 - 1
server/CVCMIServer.cpp

@@ -92,6 +92,7 @@ void CVCMIServer::newGame(CConnection *c)
 		*c << ui8(0); //OK!
 	}
 
+	StartInfo startInfoCpy = *si;
 	gh.init(si,rand());
 	c->setGS(gh.gs);
 
@@ -118,7 +119,7 @@ void CVCMIServer::newGame(CConnection *c)
 		gh.conns.insert(cc);
 	}
 
-	gh.run(false);
+	gh.run(false, &startInfoCpy);
 }
 void CVCMIServer::start()
 {

+ 4 - 0
server/NetPacksServer.cpp

@@ -141,6 +141,10 @@ bool TradeOnMarketplace::applyGh( CGameHandler *gh )
 		return gh->tradeResources(m, val, player, r1, r2);
 	case RESOURCE_PLAYER:
 		return gh->sendResources(val, player, r1, r2);
+	case CREATURE_RESOURCE:
+		if(!hero)
+			COMPLAIN_AND_RETURN("Only hero can sell creatures!");
+		return gh->sellCreatures(val, m, hero, r1, r2);
 	default:
 		gh->complain("Unknown exchange mode!");
 		ERROR_AND_RETURN;