Browse Source

Trading Post handling.

Michał W. Urbańczyk 15 years ago
parent
commit
1002bd2b4f

+ 8 - 23
CCallback.cpp

@@ -732,25 +732,6 @@ std::vector < const CGObjectInstance * > CCallback::getVisitableObjs( int3 pos )
 	return ret;
 }
 
-void CCallback::getMarketOffer( int t1, int t2, int &give, int &rec, int mode/*=0*/ ) const
-{
-	if(mode) return; //TODO - support
-	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	//if(gs->resVals[t1] >= gs->resVals[t2])
-	float r = gs->resVals[t1], //price of given resource
-		g = gs->resVals[t2] / gs->getMarketEfficiency(player,mode); //price of wanted resource
-	if(r>g) //if given resource is more expensive than wanted
-	{
-		rec = ceil(r / g);
-		give = 1;
-	}
-	else //if wanted resource is more expensive
-	{
-		give = ceil(g / r);
-		rec = 1;
-	}
-}
-
 std::vector < const CGObjectInstance * > CCallback::getFlaggableObjects(int3 pos) const
 {
 	if(!isVisible(pos))
@@ -772,11 +753,15 @@ int3 CCallback::getMapSize() const
 	return CGI->mh->sizes;
 }
 
-void CCallback::trade( int mode, int id1, int id2, int val1 )
+void CCallback::trade(const CGObjectInstance *market, int mode, int id1, int id2, int val1, const CGHeroInstance *hero/* = NULL*/)
 {
-	int p1, p2;
-	getMarketOffer(id1,id2,p1,p2,mode);
-	TradeOnMarketplace pack(player,mode,id1,id2,val1);
+	TradeOnMarketplace pack;
+	pack.market = market;
+	pack.hero = hero;
+	pack.mode = mode;
+	pack.r1 = id1;
+	pack.r2 = id2;
+	pack.val = val1;
 	sendRequest(&pack);
 }
 

+ 2 - 4
CCallback.h

@@ -84,7 +84,7 @@ public:
 	virtual bool upgradeCreature(const CArmedInstance *obj, int stackPos, int newID=-1)=0; //if newID==-1 then best possible upgrade will be made
 	virtual void swapGarrisonHero(const CGTownInstance *town)=0;
 	
-	virtual void trade(int mode, int id1, int id2, int val1)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
+	virtual void trade(const CGObjectInstance *market, int mode, int id1, int id2, int val1, const CGHeroInstance *hero = NULL)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
 	
 	virtual void selectionMade(int selection, int asker) =0;
 	virtual int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)=0;//swaps creatures between two posiibly different garrisons // TODO: AI-unsafe code - fix it!
@@ -146,7 +146,6 @@ public:
 	virtual std::vector<const CGHeroInstance *> getAvailableHeroes(const CGTownInstance * town) const =0; //heroes that can be recruited
 	virtual int canBuildStructure(const CGTownInstance *t, int ID) =0;//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
 	virtual std::set<int> getBuildingRequiments(const CGTownInstance *t, int ID) =0;
-	virtual void getMarketOffer(int t1, int t2, int &give, int &rec, int mode=0)const =0; //t1 - type of given resource, t2 - type of received resource; give is the amount of resource t1 that can be traded for amount rec of resource t2 (one of them is 1)
 	virtual bool getTownInfo(const CGObjectInstance *town, InfoAboutTown &dest) const = 0;
 
 	virtual UpgradeInfo getUpgradeInfo(const CArmedInstance *obj, int stackPos)const =0;
@@ -226,7 +225,7 @@ public:
 	void endTurn();
 	void swapGarrisonHero(const CGTownInstance *town);
 	void buyArtifact(const CGHeroInstance *hero, int aid);
-	void trade(int mode, int id1, int id2, int val1);
+	void trade(const CGObjectInstance *market, int mode, int id1, int id2, int val1, const CGHeroInstance *hero = NULL);
 	void setFormation(const CGHeroInstance * hero, bool tight);
 	void setSelection(const CArmedInstance * obj);
 	void recruitHero(const CGTownInstance *town, const CGHeroInstance *hero);
@@ -265,7 +264,6 @@ public:
 
 	std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos) const;
 	std::vector < const CGObjectInstance * > getVisitableObjs(int3 pos) const;
-	void getMarketOffer(int t1, int t2, int &give, int &rec, int mode=0) const; //t1 - type of given resource, t2 - type of received resource; give is the amount of resource t1 that can be traded for amount rec of resource t2 (one of them is 1)
 	std::vector < const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
 	int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map
 	std::vector<const CGHeroInstance *> getAvailableHeroes(const CGTownInstance * town) const; //heroes that can be recruited

+ 2 - 0
CGameInterface.h

@@ -31,6 +31,7 @@ class CGDwelling;
 class CCreatureSet;
 class CArmedInstance;
 class IShipyard;
+class IMarket;
 struct BattleResult;
 struct BattleAttack;
 struct BattleStackAttacked;
@@ -81,6 +82,7 @@ public:
 	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const int soundID, bool selection, bool cancel) = 0; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, boost::function<void()> &onEnd) = 0; //all stacks operations between these objects become allowed, interface has to call onEnd when done
 	virtual void showPuzzleMap(){};
+	virtual void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor){};
 	virtual void advmapSpellCast(const CGHeroInstance * caster, int spellID){}; //called when a hero casts a spell
 	virtual void tileHidden(const std::set<int3> &pos){};
 	virtual void tileRevealed(const std::set<int3> &pos){};

+ 4 - 0
ChangeLog

@@ -2,6 +2,7 @@
 GENERAL:
 * It's possible to start campaign
 * Support for build grail victory condition
+* New artifacts supported: Angel's Wings and Boots of levitation
 
 ADVENTURE MAP:
 * Creatures now guard surrounding tiles
@@ -9,6 +10,8 @@ ADVENTURE MAP:
 - Summon Boat
 - Scuttle Boat 
 - Dimension Door
+- Fly
+- Water walk
 
 BATTLES:
 * A number of new creature abilities supported
@@ -33,6 +36,7 @@ TOWNS:
 
 OBJECTS:
 New object supported:
+- Trading Post
 - War Machine Factory
 
 0.75 -> 0.8 (Mar 01 2010)

+ 12 - 7
client/CAdvmapInterface.cpp

@@ -28,6 +28,7 @@
 #include "CPreGame.h"
 #include "../lib/VCMI_Lib.h"
 #include "../hch/CSpellHandler.h"
+#include <boost/foreach.hpp>
 
 #ifdef _MSC_VER
 #pragma warning (disable : 4355)
@@ -1521,15 +1522,19 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 			//act on key down if marketplace windows is not already opened
 			if(key.state != SDL_PRESSED  || GH.topInt()->type & BLOCK_ADV_HOTKEYS) return;
 
-			//check if we have aby marketplace
-			std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo();
-			size_t i = 0;
-			for(; i<towns.size(); i++)
-				if(vstd::contains(towns[i]->builtBuildings, 14))
+			//check if we have any marketplace
+			const CGTownInstance *townWithMarket = NULL;
+			BOOST_FOREACH(const CGTownInstance *t, LOCPLINT->cb->getTownsInfo())
+			{
+				if(vstd::contains(t->builtBuildings, 14))
+				{
+					townWithMarket = t;
 					break;
+				}
+			}
 
-			if(i != towns.size()) //if any town has marketplace, open window
-				GH.pushInt(new CMarketplaceWindow); 
+			if(townWithMarket) //if any town has marketplace, open window
+				GH.pushInt(new CMarketplaceWindow(townWithMarket)); 
 			else //if not - complain
 				LOCPLINT->showInfoDialog("No available marketplace!", std::vector<SComponent*>(), soundBase::sound_todo);
 			return;

+ 1 - 1
client/CCastleInterface.cpp

@@ -635,7 +635,7 @@ void CCastleInterface::buildingClicked(int building)
 			break;
 		case 14:  //marketplace
 			{
-				CMarketplaceWindow *cmw = new CMarketplaceWindow();
+				CMarketplaceWindow *cmw = new CMarketplaceWindow(town);
 				GH.pushInt(cmw);
 				break;
 			}

+ 6 - 0
client/CPlayerInterface.cpp

@@ -1973,4 +1973,10 @@ void CPlayerInterface::stopMovement()
 {
 	if(stillMoveHero.get() == DURING_MOVE)//if we are in the middle of hero movement
 		stillMoveHero.setn(STOP_MOVE); //after showing dialog movement will be stopped
+}
+
+void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor)
+{
+	CMarketplaceWindow *cmw = new CMarketplaceWindow(market, market->availableModes().front());
+	GH.pushInt(cmw);
 }

+ 1 - 0
client/CPlayerInterface.h

@@ -161,6 +161,7 @@ public:
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, boost::function<void()> &onEnd);
 	void showArtifactAssemblyDialog(ui32 artifactID, ui32 assembleTo, bool assemble, CFunctionList<void()> onYes, CFunctionList<void()> onNo);
 	void showPuzzleMap();
+	void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor);
 	void advmapSpellCast(const CGHeroInstance * caster, int spellID); //called when a hero casts a spell
 	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

+ 9 - 6
client/GUIClasses.cpp

@@ -2655,7 +2655,8 @@ static void initItems( std::vector<CMarketplaceWindow::CTradeableItem*> &i, std:
 		i[j]->pos = p[j] + i[j]->pos;
 	}
 }
-CMarketplaceWindow::CMarketplaceWindow(int Mode)
+CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode)
+	:market(Market)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	type = BLOCK_ADV_HOTKEYS;
@@ -2808,7 +2809,7 @@ void CMarketplaceWindow::setMax()
 
 void CMarketplaceWindow::makeDeal()
 {
-	LOCPLINT->cb->trade(mode,hLeft->id,hRight->id,slider->value*r1);
+	LOCPLINT->cb->trade(market->o, mode, hLeft->id, hRight->id, slider->value*r1);
 	slider->moveTo(0);
 	hLeft = NULL;
 	selectionChanged(true);
@@ -2824,7 +2825,7 @@ void CMarketplaceWindow::selectionChanged(bool side)
 	{
 		if(mode == RESOURCE_RESOURCE)
 		{
-			LOCPLINT->cb->getMarketOffer(hLeft->id,hRight->id,r1,r2,0);
+			market->getOffer(hLeft->id, hRight->id, r1, r2, mode);
 			slider->setAmount(LOCPLINT->cb->getResourceAmount(hLeft->id) / r1);
 		}
 		else if(mode == RESOURCE_PLAYER)
@@ -2850,8 +2851,9 @@ 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);
+
 			std::ostringstream oss;
-			LOCPLINT->cb->getMarketOffer(hLeft->id,i,h1,h2,0);
 			oss << h2;
 			if(h1!=1)
 				oss << "/" << h1;
@@ -2880,10 +2882,11 @@ void CMarketplaceWindow::getPositionsFor(std::vector<Rect> &poss, bool Right, ET
 	}
 }
 
-void CMarketplaceWindow::setMode(int Mode)
+void CMarketplaceWindow::setMode(EMarketMode Mode)
 {
+	CMarketplaceWindow *nwindow = new CMarketplaceWindow(market, Mode);
 	GH.popIntTotally(this);
-	GH.pushInt(new CMarketplaceWindow(Mode));
+	GH.pushInt(nwindow);
 }
 
 CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface * owner)

+ 6 - 4
client/GUIClasses.h

@@ -65,6 +65,7 @@ class CResDataBar;
 struct SPuzzleInfo;
 class CGGarrison;
 class CStackInstance;
+class IMarket;
 
 extern SDL_Color tytulowy, tlo, zwykly ;
 
@@ -536,14 +537,15 @@ public:
 		CTradeableItem(int Type, int ID, bool Left);
 	};
 
+	const IMarket *market;
 	CPicture *bg; //background
 	std::vector<CTradeableItem*> left, right;
 	std::vector<std::string> rSubs; //offer caption
 	CTradeableItem *hLeft, *hRight; //highlighted items (NULL if no highlight)
 
 	EType ltype, rtype;
-	int mode,//0 - res<->res; 1 - res<->plauer; 2 - buy artifact; 3 - sell artifact
-		r1, r2; //suggested amounts of traded resources
+	EMarketMode mode;//0 - res<->res; 1 - res<->plauer; 2 - buy artifact; 3 - sell artifact
+	int r1, r2; //suggested amounts of traded resources
 	AdventureMapButton *ok, *max, *deal;
 	CSlider *slider; //for choosing amount to be exchanged
 
@@ -552,9 +554,9 @@ public:
 	void sliderMoved(int to);
 	void makeDeal();
 	void selectionChanged(bool side); //true == left
-	CMarketplaceWindow(int Mode = RESOURCE_RESOURCE); //c-tor
+	CMarketplaceWindow(const IMarket *Market, EMarketMode Mode = RESOURCE_RESOURCE); //c-tor
 	~CMarketplaceWindow(); //d-tor
-	void setMode(int Mode); //mode setter
+	void setMode(EMarketMode Mode); //mode setter
 
 	void getPositionsFor(std::vector<Rect> &poss, bool Right, EType type) const;
 };

+ 9 - 0
client/NetPacksClient.cpp

@@ -720,6 +720,15 @@ void OpenWindow::applyCl(CClient *cl)
 			GH.pushInt( new CThievesGuildWindow(obj) );
 		}
 		break;
+	case MARKET_WINDOW:
+		{
+			//displays Thieves' Guild window (when hero enters Den of Thieves)
+			const CGObjectInstance *obj = cl->getObj(id1);
+			const CGHeroInstance *hero = cl->getHero(id2);
+			const IMarket *market = IMarket::castFrom(obj);
+			INTERFACE_CALL_IF_PRESENT(cl->getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, showMarketWindow, market, hero);
+		}
+		break;
 	case PUZZLE_MAP:
 		{
 			INTERFACE_CALL_IF_PRESENT(id1, showPuzzleMap);

+ 2 - 1
global.h

@@ -107,7 +107,8 @@ const int SPELL_LEVELS = 5;
 
 enum EMarketMode
 {
-	RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, ARTIFACT_RESOURCE
+	RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, ARTIFACT_RESOURCE, RESOURCE_ARTIFACT, ARTIFACT_EXP, CREATURE_EXP,
+	MARTKET_AFTER_LAST_PLACEHOLDER
 };
 
 //uncomment to make it work

+ 199 - 1
hch/CObjectHandler.cpp

@@ -144,8 +144,24 @@ void CObjectHandler::loadObjects()
 			cregens[dw]=cr;
 		}
 		tlog5 << "\t\tDone loading objects!\n";
+
+		ifs.close();
+		ifs.clear();
+
+		int k = -1;
+		ifs.open(DATA_DIR "/config/resources.txt");
+		ifs >> k;
+		int pom;
+		for(int i=0;i<k;i++)
+		{
+			ifs >> pom;
+			resVals.push_back(pom);
+		}
+		tlog5 << "\t\tDone loading resource prices!\n";
 	}
 
+
+
 	std::ifstream istr;
 	istr.open(DATA_DIR "/config/bankconfig.txt", std::ios_base::binary);
 	if(!istr.is_open())
@@ -1567,7 +1583,7 @@ bool CGTownInstance::hasCapitol() const
 	return (builtBuildings.find(13))!=builtBuildings.end();
 }
 CGTownInstance::CGTownInstance()
-	:IShipyard(this)
+	:IShipyard(this), IMarket(this)
 {
 	builded=-1;
 	destroyed=-1;
@@ -1802,6 +1818,39 @@ void CGTownInstance::getBonuses(BonusList &out, const CSelector &selector, const
 	}
 }
 
+int CGTownInstance::getMarketEfficiency() const
+{
+	if(!vstd::contains(builtBuildings, 14)) 
+		return 0;
+
+	const PlayerState *p = cb->getPlayerState(tempOwner);
+	assert(p);
+
+	int marketCount = 0;
+	BOOST_FOREACH(const CGTownInstance *t, p->towns)
+		if(vstd::contains(t->builtBuildings, 14))
+			marketCount++;
+
+	return marketCount;
+}
+
+bool CGTownInstance::allowsTrade(EMarketMode mode) const
+{
+	switch(mode)
+	{
+	case RESOURCE_RESOURCE:
+	case RESOURCE_PLAYER:
+		return vstd::contains(builtBuildings, 14); // 	marketplace
+	case ARTIFACT_RESOURCE:
+		return (subID == 2 || subID == 5 || subID == 8) && vstd::contains(builtBuildings, 17);//artifact merchants
+	case CREATURE_RESOURCE:
+		return subID == 6 && vstd::contains(builtBuildings, 21); //Freelancer's guild
+	default:
+		assert(0);
+		return false;
+	}
+}
+
 void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const
 {
 	if(visitors.find(h->id)==visitors.end())
@@ -5859,4 +5908,153 @@ void CArmedInstance::getBonuses(BonusList &out, const CSelector &selector, const
 				out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, id, VLC->generaltexth->arraytxt[116]));//Undead in group -1
 		}
 	}
+}
+
+bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const
+{
+	switch(mode)
+	{
+	case RESOURCE_RESOURCE:
+		{
+			float effectiveness = std::min(((float)getMarketEfficiency()+1.0f) / 20.0f, 0.5f);
+
+			float r = VLC->objh->resVals[id1], //value of given resource
+				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;
+	default:
+		assert(0);
+		return false;
+	}
+
+	return true;
+}
+
+bool IMarket::allowsTrade(EMarketMode mode) const
+{
+	return false;
+}
+
+int IMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
+{
+	if(mode == RESOURCE_RESOURCE || ARTIFACT_RESOURCE || CREATURE_RESOURCE)
+		return -1;
+	else 
+		return 1;
+}
+
+std::vector<int> IMarket::availableItemsIds(EMarketMode mode) const
+{
+	std::vector<int> ret;
+	if(mode == RESOURCE_RESOURCE || ARTIFACT_RESOURCE || CREATURE_RESOURCE)
+		for (int i = 0; i < 7; i++)
+			ret.push_back(i);
+
+	return ret;
+}
+
+const IMarket * IMarket::castFrom(const CGObjectInstance *obj)
+{
+	switch(obj->ID)
+	{
+	case TOWNI_TYPE:
+		return static_cast<const CGTownInstance*>(obj);
+	case 99: //Trading Post
+	case 221: //Trading Post (snow)
+		return static_cast<const CGMarket*>(obj);
+	default:
+		tlog1 << "Cannot cast to IMarket object with ID " << obj->ID << std::endl;
+		return NULL;
+	}
+}
+
+IMarket::IMarket(const CGObjectInstance *O)
+	:o(O)
+{
+
+}
+
+std::vector<EMarketMode> IMarket::availableModes() const
+{
+	std::vector<EMarketMode> ret;
+	for (int i = 0; i < MARTKET_AFTER_LAST_PLACEHOLDER; i++)
+		if(allowsTrade((EMarketMode)i))
+			ret.push_back((EMarketMode)i);
+
+	return ret;
+}
+
+void CGMarket::onHeroVisit(const CGHeroInstance * h) const
+{
+	OpenWindow ow;
+	ow.id1 = id;
+	ow.id2 = h->id;
+	ow.window = OpenWindow::MARKET_WINDOW;
+	cb->sendAndApply(&ow);
+}
+
+void CGMarket::initObj()
+{
+
+}
+
+void CGMarket::newTurn() const
+{
+
+}
+
+int CGMarket::getMarketEfficiency() const
+{
+	return 5;
+}
+
+bool CGMarket::allowsTrade(EMarketMode mode) const
+{
+	switch(mode)
+	{
+	case RESOURCE_RESOURCE:
+	case RESOURCE_PLAYER:
+		switch(ID)
+		{
+		case 99: //Trading Post
+		case 221: //Trading Post (snow)
+			return true;
+		default:
+			return false;
+		}
+	}
+}
+
+int CGMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
+{
+	return -1;
+}
+
+std::vector<int> CGMarket::availableItemsIds(EMarketMode mode) const
+{
+	switch(mode)
+	{
+	case RESOURCE_RESOURCE:
+	case RESOURCE_PLAYER:
+		return IMarket::availableItemsIds(mode);
+	default:
+		return std::vector<int>();
+	}
+}
+
+CGMarket::CGMarket()
+	:IMarket(this)
+{
 }

+ 44 - 16
hch/CObjectHandler.h

@@ -141,18 +141,22 @@ public:
 	static IShipyard *castFrom(CGObjectInstance *obj);
 };
 
- /*class DLL_EXPORT IMarket
- {
- public:
- 	const CGObjectInstance *o;
- 
- 	IMarket(const CGObjectInstance *O);
- 	virtual bool allowsMode(EMarketMode mode);
- 	virtual float getEfficiency(EMarketMode mode);
- 
- 	static const IMarket *castFrom(const CGObjectInstance *obj);
- 	static IMarket castFrom(CGObjectInstance *obj);
- };*/
+class DLL_EXPORT IMarket
+{
+	virtual int getMarketEfficiency() const =0;
+public:
+	const CGObjectInstance *o;
+
+	IMarket(const CGObjectInstance *O);
+	virtual bool allowsTrade(EMarketMode mode) const;
+	virtual int availableUnits(EMarketMode mode, int marketItemSerial) const; //-1 if unlimited
+	virtual std::vector<int> availableItemsIds(EMarketMode mode) const;
+
+	bool getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const; //val1 - how many units of id1 player has to give to receive val2 units 
+	std::vector<EMarketMode> availableModes() const;
+
+	static const IMarket *castFrom(const CGObjectInstance *obj);
+};
 
 class DLL_EXPORT CGObjectInstance : public IObjectInterface
 {
@@ -446,6 +450,7 @@ public:
 		h & visitors;
 	}
 };
+
 class DLL_EXPORT CTownBonus : public CGTownBuilding
 {
 ///used for one-time bonusing structures
@@ -463,7 +468,8 @@ public:
 		h & visitors;
 	}
 };
-class DLL_EXPORT CGTownInstance : public CGDwelling, public IShipyard
+
+class DLL_EXPORT CGTownInstance : public CGDwelling, public IShipyard, public IMarket
 {
 public:
 	CTown * town;
@@ -505,6 +511,8 @@ public:
 	int getSightRadious() const; //returns sight distance
 	int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
 	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;
 	void setPropertyDer(ui8 what, ui32 val);
 	void newTurn() const;
 
@@ -1121,7 +1129,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<CPlayersVisited&>(*this);;
+		h & static_cast<CPlayersVisited&>(*this);
 	}
 };
 
@@ -1134,11 +1142,30 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<CGObjectInstance&>(*this);;
+		h & static_cast<CGObjectInstance&>(*this);
 	}
 	void giveBonusTo( ui8 player ) const;
 };
 
+class DLL_EXPORT CGMarket : public CGObjectInstance, public IMarket
+{
+public:
+	CGMarket();
+	void onHeroVisit(const CGHeroInstance * h) const; //open trading window
+	void initObj();
+	void newTurn() const; //reset artifacts for black market every month
+	
+	int getMarketEfficiency() const;
+	bool allowsTrade(EMarketMode mode) const;
+	int availableUnits(EMarketMode mode, int marketItemSerial) const; //-1 if unlimited
+	std::vector<int> availableItemsIds(EMarketMode mode) const;
+	
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this);
+	}
+};
+
 struct BankConfig
 {
 	BankConfig() {level = chance = upgradeChance = combatValue = value = rewardDifficulty = easiest = 0; };
@@ -1166,12 +1193,13 @@ public:
 	std::vector<si32> cregens; //type 17. dwelling subid -> creature ID
 	std::map <ui32, std::vector <BankConfig*> > banksInfo; //[index][preset]
 	std::map <ui32, std::string> creBanksNames; //[crebank index] -> name of this creature bank
+	std::vector<ui32> resVals; //default values of resources in gold
 
 	void loadObjects();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & cregens & banksInfo & creBanksNames;
+		h & cregens & banksInfo & creBanksNames & resVals;
 	}
 };
 

+ 0 - 20
lib/CGameState.cpp

@@ -1347,14 +1347,6 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 			(*i).second.resources[x] = startres[x];
 	}
 
-	tis.open(DATA_DIR "/config/resources.txt");
-	tis >> k;
-	int pom;
-	for(int i=0;i<k;i++)
-	{
-		tis >> pom;
-		resVals.push_back(pom);
-	}
 
 	/*************************HEROES************************************************/
 	std::set<int> hids;
@@ -1735,18 +1727,6 @@ UpgradeInfo CGameState::getUpgradeInfo(const CArmedInstance *obj, int stackPos)
 	return ret;
 }
 
-float CGameState::getMarketEfficiency( int player, int mode/*=0*/ )
-{
-	boost::shared_lock<boost::shared_mutex> lock(*mx);
-	if(mode) return -1; //todo - support other modes
-	int mcount = 0;
-	for(unsigned int i=0;i<getPlayer(player)->towns.size();i++)
-		if(vstd::contains(getPlayer(player)->towns[i]->builtBuildings,14))
-			mcount++;
-	float ret = std::min(((float)mcount+1.0f)/20.0f,0.5f);
-	return ret;
-}
-
 void CGameState::loadTownDInfos()
 {
 	for(int i=0;i<F_NUMBER;i++)

+ 2 - 3
lib/CGameState.h

@@ -378,7 +378,6 @@ public:
 	Mapa * map;
 	std::map<ui8, PlayerState> players; //ID <-> player state
 	std::map<int, CGDefInfo*> villages, forts, capitols; //def-info for town graphics
-	std::vector<ui32> resVals; //default values of resources in gold
 	CBonusSystemNode globalEffects;
 
 	struct DLL_EXPORT HeroesPool
@@ -417,7 +416,7 @@ public:
 	si8 battleMaxSpellLevel(); //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
 	bool battleCanShoot(int ID, int dest); //determines if stack with given ID shoot at the selected destination
 	UpgradeInfo getUpgradeInfo(const CArmedInstance *obj, int stackPos);
-	float getMarketEfficiency(int player, int mode=0);
+	//float getMarketEfficiency(int player, int mode=0);
 	std::set<int> getBuildingRequiments(const CGTownInstance *t, int ID);
 	int canBuildStructure(const CGTownInstance *t, int ID);// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
 	bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile
@@ -441,7 +440,7 @@ public:
 	int getDate(int mode=0) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & scenarioOps & seed & currentPlayer & day & map & players & resVals & hpool & globalEffects & campaign;
+		h & scenarioOps & seed & currentPlayer & day & map & players & hpool & globalEffects & campaign;
 		if(!h.saving)
 		{
 			loadTownDInfos();

+ 6 - 7
lib/NetPacks.h

@@ -615,7 +615,7 @@ struct OpenWindow : public CPackForClient //517
 	OpenWindow(){type = 517;};
 	void applyCl(CClient *cl);
 
-	enum EWindow {EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL, SHIPYARD_WINDOW, THIEVES_GUILD, PUZZLE_MAP};
+	enum EWindow {EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL, SHIPYARD_WINDOW, THIEVES_GUILD, PUZZLE_MAP, MARKET_WINDOW};
 	ui8 window;
 	ui32 id1, id2;
 
@@ -673,7 +673,7 @@ struct NewTurn : public CPackForClient //101
 
 struct Component : public CPack //2002 helper for object scrips informations
 {
-	enum {PRIM_SKILL, SEC_SKILL, RESOURCE, CREATURE, ARTIFACT, EXPERIENCE, SPELL, MORALE=8, LUCK, BUILDING, HERO, FLAG};
+	enum EComponentType {PRIM_SKILL, SEC_SKILL, RESOURCE, CREATURE, ARTIFACT, EXPERIENCE, SPELL, MORALE=8, LUCK, BUILDING, HERO, FLAG};
 	ui16 id, subtype; //id uses ^^^ enums, when id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels)
 	si32 val; // + give; - take
 	si16 when; // 0 - now; +x - within x days; -x - per x days
@@ -1361,18 +1361,17 @@ struct BuyArtifact : public CPackForServer
 struct TradeOnMarketplace : public CPackForServer
 {
 	TradeOnMarketplace(){};
-	TradeOnMarketplace(ui8 Player, ui8 Mode, /*si32 ID, */ui32 R1, ui32 R2, ui32 Val)
-		:player(Player),mode(Mode),/*id(ID),*/r1(R1),r2(R2),val(Val){};
-	ui8 player;
+
+	const CGObjectInstance *market;
+	const CGHeroInstance *hero; //needed when trading artifacts / creatures
 	ui8 mode;//0 - res<->res; 
-	//si32 id; //object id
 	ui32 r1, r2; //mode 0: r1 - sold resource, r2 - bought res
 	ui32 val; //units of sold resource
 
 	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & player & mode & /*id & */r1 & r2 & val;
+		h & market & hero & mode & r1 & r2 & val;
 	}
 };
 

+ 1 - 0
lib/RegisterTypes.cpp

@@ -62,6 +62,7 @@ void registerTypes1(Serializer &s)
 	s.template registerType<CGDenOfthieves>();
 	s.template registerType<CGObelisk>();
 	s.template registerType<CGLighthouse>();
+	s.template registerType<CGMarket>();
 }
 
 template<typename Serializer> DLL_EXPORT 

+ 10 - 0
lib/map.cpp

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

+ 20 - 6
server/CGameHandler.cpp

@@ -2968,24 +2968,38 @@ bool CGameHandler::buyArtifact( ui32 hid, si32 aid )
 	return false;
 }
 
-bool CGameHandler::tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 )
+
+bool CGameHandler::tradeResources(const IMarket *market, ui32 val, ui8 player, ui32 id1, ui32 id2)
 {
-	val = std::min(si32(val),gs->getPlayer(player)->resources[id1]);
-	double yield = (double)gs->resVals[id1] * val * gs->getMarketEfficiency(player);
-	yield /= gs->resVals[id2];
+	int r1 = gs->getPlayer(player)->resources[id1], 
+		r2 = gs->getPlayer(player)->resources[id2];
+
+	amin(val, r1); //can't trade more resources than have
+
+	int b1, b2; //base quantities for trade
+	market->getOffer(id1, id2, b1, b2, RESOURCE_RESOURCE);
+	int units = val / b1; //how many base quantities we trade
+
+	if(val%b1) //all offered units of resource should be used, if not -> somewhere in calculations must be an error
+	{
+		//TODO: complain?
+		assert(0);
+	}
+
 	SetResource sr;
 	sr.player = player;
 	sr.resid = id1;
-	sr.val = gs->getPlayer(player)->resources[id1] - val;
+	sr.val = r1 - b1 * units;
 	sendAndApply(&sr);
 
 	sr.resid = id2;
-	sr.val = gs->getPlayer(player)->resources[id2] + (int)yield;
+	sr.val = r2 + b2 * units;
 	sendAndApply(&sr);
 
 	return true;
 }
 
+
 bool CGameHandler::sendResources(ui32 val, ui8 player, ui32 r1, ui32 r2)
 {
 	const PlayerState *p2 = gs->getPlayer(r2, false);

+ 3 - 1
server/CGameHandler.h

@@ -36,6 +36,8 @@ struct SetResource;
 struct SetResources;
 struct NewStructures;
 class CGHeroInstance;
+class IMarket;
+
 extern std::map<ui32, CFunctionList<void(ui32)> > callbacks; //question id => callback functions - for selection dialogs
 extern boost::mutex gsm;
 
@@ -163,7 +165,7 @@ public:
 	bool hireHero( ui32 tid, ui8 hid );
 	bool buildBoat( ui32 objid );
 	bool setFormation( si32 hid, ui8 formation );
-	bool tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 );
+	bool tradeResources(const IMarket *market, ui32 val, ui8 player, ui32 id1, ui32 id2);
 	bool sendResources(ui32 val, ui8 player, ui32 r1, ui32 r2);
 	bool assembleArtifacts (si32 heroID, ui16 artifactSlot, bool assemble, ui32 assembleTo);
 	bool buyArtifact( ui32 hid, si32 aid );

+ 25 - 2
server/NetPacksServer.cpp

@@ -1,5 +1,8 @@
 #include "../lib/NetPacks.h"
 #include "CGameHandler.h"
+#include "../hch/CObjectHandler.h"
+#include "../lib/IGameCallback.h"
+#include "../lib/map.h"
 
 
 #define PLAYER_OWNS(id) (gh->getPlayerAt(c)==gh->getOwner(id))
@@ -7,6 +10,7 @@
 							tlog1<<"Player is not allowed to perform this action!\n";	\
 							return false;}
 #define ERROR_IF_NOT_OWNS(id)	if(!PLAYER_OWNS(id)) ERROR_AND_RETURN
+#define COMPLAIN_AND_RETURN(txt)	{ gh->complain(txt); ERROR_AND_RETURN }
 
 /*
  * NetPacksServer.cpp, part of VCMI engine
@@ -111,11 +115,30 @@ bool BuyArtifact::applyGh( CGameHandler *gh )
 
 bool TradeOnMarketplace::applyGh( CGameHandler *gh )
 {
-	if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
+	//market must be owned or visited
+	const IMarket *m = IMarket::castFrom(market);
+
+	if(!m)
+		COMPLAIN_AND_RETURN("market is not-a-market! :/");
+
+	ui8 player = market->tempOwner;
+
+	if(player >= PLAYER_LIMIT)
+		player = gh->getTile(market->visitablePos())->visitableObjects.back()->tempOwner;
+
+	if(player >= PLAYER_LIMIT)
+		COMPLAIN_AND_RETURN("No player can use this market!");
+
+	if(hero && (player != hero->tempOwner || hero->visitablePos() != market->visitablePos()))
+		COMPLAIN_AND_RETURN("This hero can't use this marketplace!");
+
+	if(gh->getPlayerAt(c) != player) 
+		ERROR_AND_RETURN;
+
 	switch(mode)
 	{
 	case RESOURCE_RESOURCE:
-		return gh->tradeResources(val,player,r1,r2);
+		return gh->tradeResources(m, val, player, r1, r2);
 	case RESOURCE_PLAYER:
 		return gh->sendResources(val, player, r1, r2);
 	default: