瀏覽代碼

- made main menu configurable (mainmenu.json)
- added credits screen (using NWC credits for now)
- campaigns can be started

Ivan Savenko 14 年之前
父節點
當前提交
064466998a

+ 8 - 2
client/AdventureMapButton.cpp

@@ -48,7 +48,10 @@ void CButtonBase::update()
 			text->moveTo(Point(pos.x+pos.w/2, pos.y+pos.h/2));
 	}
 	
-	size_t newPos = (int)state + bitmapOffset;
+	int newPos = (int)state + bitmapOffset;
+	if (newPos < 0)
+		newPos = 0;
+
 	if (state == HIGHLIGHTED && image->size() < 4)
 		newPos = image->size()-1;
 
@@ -353,7 +356,8 @@ void CHighlightableButtonsGroup::addButton(const std::map<int,std::string> &tool
 	CHighlightableButton *bt = new CHighlightableButton(OnSelect, 0, tooltip, HelpBox, false, defName, 0, x, y, key);
 	if(musicLike)
 	{
-		bt->setOffset(buttons.size() - 3);
+		if (buttons.size() > 3)
+			bt->setOffset(buttons.size()-3);
 	}
 	bt->ID = uid;
 	bt->callback += boost::bind(&CHighlightableButtonsGroup::selectionChanged,this,bt->ID);
@@ -393,6 +397,8 @@ void CHighlightableButtonsGroup::selectionChanged(int to)
 		if(buttons[i]->ID!=to && buttons[i]->isHighlighted())
 			buttons[i]->select(false);
 	onChange(to);
+	if (parent)
+		parent->redraw();
 }
 
 void CHighlightableButtonsGroup::show(SDL_Surface * to )

+ 8 - 5
client/CCastleInterface.cpp

@@ -1376,11 +1376,14 @@ CHallInterface::CHallInterface(const CGTownInstance *Town):
 				int buildingID = boxList[row][col][item];
 				building = CGI->buildh->buildings[town->subID][buildingID];
 
-				if ( (buildingID == 18 && !vstd::contains(town->builtBuildings, town->town->hordeLvl[0]+37))
-				  || (buildingID == 24 && !vstd::contains(town->builtBuildings, town->town->hordeLvl[1]+37)) )
-					break; // horde present, no upgraded dwelling -> select 18 or 24
-				else
-					continue; //upgraded dwelling, no horde -> select 19 or 25
+				if (buildingID == 18 || buildingID == 24)
+				{
+					if ( (buildingID == 18 && !vstd::contains(town->builtBuildings, town->town->hordeLvl[0]+37))
+					  || (buildingID == 24 && !vstd::contains(town->builtBuildings, town->town->hordeLvl[1]+37)) )
+						break; // horde present, no upgraded dwelling -> select 18 or 24
+					else
+						continue; //upgraded dwelling, no horde -> select 19 or 25
+				}
 
 				if(vstd::contains(town->builtBuildings,buildingID))
 					continue;

+ 92 - 339
client/CKingdomInterface.cpp

@@ -466,232 +466,6 @@ bool InfoBoxCustom::prepareMessage(std::string &text, SComponent **comp)
 	return false;
 }
 
-CObjectList::CObjectList(IGuiObjectListManager *Manager):
-	manager(Manager)
-{
-}
-
-CObjectList::~CObjectList()
-{
-	delete manager;
-}
-
-void CObjectList::deleteItem(CIntObject* item)
-{
-	if (!item)
-		return;
-	if (active)
-		item->deactivate();
-	removeChild(item);
-	manager->removeObject(item);
-}
-
-CIntObject* CObjectList::createItem(size_t index)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	CIntObject * item = manager->getObject(index);
-	if (item == NULL)
-		item = new CIntObject();
-
-	item->recActions = defActions;
-
-	//May happen if object was created before call to getObject()
-	if(item->parent != this)
-	{
-		if (item->parent)
-			moveChild(item, item->parent, this);
-		else
-			addChild(item);
-	}
-
-	if (item && active)
-		item->activate();
-	return item;
-}
-
-CTabbedInt::CTabbedInt(IGuiObjectListManager *Manager, Point position, size_t ActiveID):
-	CObjectList(Manager),
-	activeTab(NULL),
-	activeID(ActiveID)
-{
-	pos += position;
-	reset();
-}
-
-void CTabbedInt::setActive(size_t which)
-{
-	if (which != activeID)
-	{
-		activeID = which;
-		reset();
-	}
-}
-
-void CTabbedInt::reset()
-{
-	deleteItem(activeTab);
-	activeTab = createItem(activeID);
-	activeTab->moveTo(pos.topLeft());
-
-	if (active)
-		redraw();
-}
-
-CIntObject * CTabbedInt::getItem()
-{
-	return activeTab;
-}
-
-CListBox::CListBox(IGuiObjectListManager *Manager, Point Pos, Point ItemOffset, size_t VisibleSize,
-                   size_t TotalSize, size_t InitialPos, int Slider, Rect SliderPos):
-	CObjectList(Manager),
-	first(InitialPos),
-	totalSize(TotalSize),
-	itemOffset(ItemOffset)
-{
-	pos += Pos;
-	items.resize(VisibleSize, NULL);
-
-	if (Slider & 1)
-	{
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-		slider = new CSlider(SliderPos.x, SliderPos.y, SliderPos.w, boost::bind(&CListBox::moveToPos, this, _1),
-		                     VisibleSize, TotalSize, InitialPos, Slider & 2, Slider & 4);
-	}
-	reset();
-}
-
-// Used to move active items after changing list position
-void CListBox::updatePositions()
-{
-	Point itemPos = pos.topLeft();
-	for (std::list<CIntObject*>::iterator it = items.begin(); it!=items.end(); it++)
-	{
-		(*it)->moveTo(itemPos);
-		itemPos += itemOffset;
-	}
-	if (active)
-	{
-		redraw();
-		if (slider)
-			slider->moveTo(first);
-	}
-}
-
-void CListBox::reset()
-{
-	size_t current = first;
-	for (std::list<CIntObject*>::iterator it = items.begin(); it!=items.end(); it++)
-	{
-		deleteItem(*it);
-		*it = createItem(current++);
-	}
-	updatePositions();
-}
-
-void CListBox::moveToPos(size_t which)
-{
-	//Calculate new position
-	size_t maxPossible;
-	if (totalSize > items.size())
-		maxPossible = totalSize - items.size();
-	else
-		maxPossible = 0;
-
-	size_t newPos = std::min(which, maxPossible);
-
-	//If move distance is 1 (most of calls from Slider) - use faster shifts instead of resetting all items
-	if (first - newPos == 1)
-		moveToPrev();
-	else if (newPos - first == 1)
-		moveToNext();
-	else if (newPos != first)
-	{
-		first = newPos;
-		reset();
-	}
-}
-
-void CListBox::moveToNext()
-{
-	//Remove front item and insert new one to end
-	if (first + items.size() < totalSize)
-	{
-		first++;
-		deleteItem(items.front());
-		items.pop_front();
-		items.push_back(createItem(first+items.size()));
-		updatePositions();
-	}
-}
-
-void CListBox::moveToPrev()
-{
-	//Remove last item and insert new one at start
-	if (first)
-	{
-		first--;
-		deleteItem(items.back());
-		items.pop_back();
-		items.push_front(createItem(first));
-		updatePositions();
-	}
-}
-
-std::list<CIntObject*> CListBox::getItems()
-{
-	return items;
-}
-
-struct OwnedObjectInfo
-{
-	int imageID;
-	unsigned int count;
-	std::string hoverText;
-};
-
-class OwnedObjectsListManager : public IGuiObjectListManager
-{
-	std::vector<OwnedObjectInfo> objects;
-public:
-	virtual CIntObject * getObject(size_t position)
-	{
-		if (position < objects.size())
-		{
-			OwnedObjectInfo &obj = objects[position];
-			std::string value = boost::lexical_cast<std::string>(obj.count);
-			return new InfoBox(Point(), InfoBox::POS_CORNER, InfoBox::SIZE_SMALL,
-			       new InfoBoxCustom(value,"", "FLAGPORT", obj.imageID, obj.hoverText));
-		}
-		return NULL;
-	}
-
-	OwnedObjectsListManager(std::vector<OwnedObjectInfo> Objects):
-		objects(Objects)
-	{
-	}
-};
-
-class TownHeroListManager : public IGuiObjectListManager
-{
-public:
-	CIntObject *currentItem;
-
-	CIntObject *getObject(size_t position)
-	{
-		size_t size = conf.go()->ac.overviewSize;
-		switch (position)
-		{
-		case 0:
-			return new CKingdHeroList(size);
-		case 1:
-			return new CKingdTownList(size);
-		default:
-			return NULL;
-		}
-	}
-};
-
 CKingdomInterface::CKingdomInterface()
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
@@ -700,7 +474,7 @@ CKingdomInterface::CKingdomInterface()
 	pos = background->center();
 	unsigned int footerPos = conf.go()->ac.overviewSize * 116;
 
-	tabArea = new CTabbedInt(new TownHeroListManager, Point(4,4));
+	tabArea = new CTabbedInt(boost::bind(&CKingdomInterface::createMainTab, this, _1), CTabbedInt::DestroyFunc(), Point(4,4));
 
 	std::vector<const CGObjectInstance * > ownedObjects = LOCPLINT->cb->getMyObjects();
 	generateObjectsList(ownedObjects);
@@ -753,15 +527,38 @@ void CKingdomInterface::generateObjectsList(const std::vector<const CGObjectInst
 			}
 		}
 	}
-	std::vector<OwnedObjectInfo> objectsVector;
-	objectsVector.reserve(visibleObjects.size());
+	objects.reserve(visibleObjects.size());
 
 	std::pair<int, OwnedObjectInfo> element;
 	BOOST_FOREACH(element, visibleObjects)
 	{
-		objectsVector.push_back(element.second);
+		objects.push_back(element.second);
+	}
+	dwellingsList = new CListBox(boost::bind(&CKingdomInterface::createOwnedObject, this, _1), CListBox::DestroyFunc(),
+	                             Point(740,44), Point(0,57), dwellSize, visibleObjects.size());
+}
+
+CIntObject* CKingdomInterface::createOwnedObject(size_t index)
+{
+	if (index < objects.size())
+	{
+		OwnedObjectInfo &obj = objects[index];
+		std::string value = boost::lexical_cast<std::string>(obj.count);
+		return new InfoBox(Point(), InfoBox::POS_CORNER, InfoBox::SIZE_SMALL,
+			   new InfoBoxCustom(value,"", "FLAGPORT", obj.imageID, obj.hoverText));
+	}
+	return NULL;
+}
+
+CIntObject * CKingdomInterface::createMainTab(size_t index)
+{
+	size_t size = conf.go()->ac.overviewSize;
+	switch (index)
+	{
+	case 0: return new CKingdHeroList(size);
+	case 1: return new CKingdTownList(size);
+	default:return NULL;
 	}
-	dwellingsList = new CListBox(new OwnedObjectsListManager(objectsVector), Point(740,44), Point(0,57), dwellSize, visibleObjects.size());
 }
 
 void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstance * > &ownedObjects)
@@ -887,56 +684,6 @@ void CKingdomInterface::artifactRemoved(const ArtifactLocation& artLoc)
 		arts->artifactRemoved(artLoc);
 }
 
-class HeroListManager : public IGuiObjectListManager
-{
-	CWindowWithArtifacts * arts;
-	CArtifactsOfHero::SCommonPart * artsCommonPart;
-public:
-	HeroListManager(CWindowWithArtifacts * parent);
-	~HeroListManager();
-	CIntObject * getObject(size_t position);
-	void removeObject(CIntObject *object);
-};
-
-HeroListManager::HeroListManager(CWindowWithArtifacts * parent)
-{
-	arts = parent;
-	artsCommonPart = new CArtifactsOfHero::SCommonPart;
-}
-
-HeroListManager::~HeroListManager()
-{
-	delete artsCommonPart;
-}
-
-CIntObject * HeroListManager::getObject(size_t position)
-{
-	unsigned int picCount = conf.go()->ac.overviewPics;
-	size_t heroesCount = LOCPLINT->cb->howManyHeroes(false);
-
-	if (position < heroesCount)
-	{
-		CHeroItem * hero = new CHeroItem(LOCPLINT->cb->getHeroBySerial(position, false), artsCommonPart);
-		artsCommonPart->participants.insert(hero->heroArts);
-		arts->artSets.push_back(hero->heroArts);
-		return hero;
-	}
-	else
-	{
-		return new CAnimImage("OVSLOT", (position-2) % picCount );
-	}
-};
-
-void HeroListManager::removeObject(CIntObject *object)
-{
-	if (CHeroItem * hero = dynamic_cast<CHeroItem*>(object))
-	{
-		arts->artSets.erase(std::find(arts->artSets.begin(), arts->artSets.end(), hero->heroArts));
-		artsCommonPart->participants.erase(hero->heroArts);
-	}
-	delete object;
-}
-
 CKingdHeroList::CKingdHeroList(size_t maxSize)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
@@ -947,7 +694,8 @@ CKingdHeroList::CKingdHeroList(size_t maxSize)
 
 	unsigned int townCount = LOCPLINT->cb->howManyHeroes(false);
 	unsigned int size = conf.go()->ac.overviewSize*116 + 19;
-	heroes = new CListBox(new HeroListManager(this), Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size) );
+	heroes = new CListBox(boost::bind(&CKingdHeroList::createHeroItem, this, _1), boost::bind(&CKingdHeroList::destroyHeroItem, this, _1),
+	                      Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size) );
 }
 
 void CKingdHeroList::updateGarrisons()
@@ -960,20 +708,33 @@ void CKingdHeroList::updateGarrisons()
 	}
 }
 
-class TownListManager : public IGuiObjectListManager
+CIntObject* CKingdHeroList::createHeroItem(size_t index)
 {
-public:
-	CIntObject * getObject(size_t position)
+	unsigned int picCount = conf.go()->ac.overviewPics;
+	size_t heroesCount = LOCPLINT->cb->howManyHeroes(false);
+
+	if (index < heroesCount)
+	{
+		CHeroItem * hero = new CHeroItem(LOCPLINT->cb->getHeroBySerial(index, false), &artsCommonPart);
+		artsCommonPart.participants.insert(hero->heroArts);
+		artSets.push_back(hero->heroArts);
+		return hero;
+	}
+	else
 	{
-		unsigned int picCount = conf.go()->ac.overviewPics;
-		size_t townsCount = LOCPLINT->cb->howManyTowns();
+		return new CAnimImage("OVSLOT", (index-2) % picCount );
+	}
+}
 
-		if (position < townsCount)
-			return new CTownItem(LOCPLINT->cb->getTownBySerial(position));
-		else
-			return new CAnimImage("OVSLOT", (position-2) % picCount );
+void CKingdHeroList::destroyHeroItem(CIntObject *object)
+{
+	if (CHeroItem * hero = dynamic_cast<CHeroItem*>(object))
+	{
+		artSets.erase(std::find(artSets.begin(), artSets.end(), hero->heroArts));
+		artsCommonPart.participants.erase(hero->heroArts);
 	}
-};
+	delete object;
+}
 
 CKingdTownList::CKingdTownList(size_t maxSize)
 {
@@ -986,7 +747,8 @@ CKingdTownList::CKingdTownList(size_t maxSize)
 
 	unsigned int townCount = LOCPLINT->cb->howManyTowns();
 	unsigned int size = conf.go()->ac.overviewSize*116 + 19;
-	towns = new CListBox(new TownListManager, Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size) );
+	towns = new CListBox(boost::bind(&CKingdTownList::createTownItem, this, _1), CListBox::DestroyFunc(),
+	                     Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size) );
 }
 
 void CKingdTownList::townChanged(const CGTownInstance *town)
@@ -1010,6 +772,17 @@ void CKingdTownList::updateGarrisons()
 	}
 }
 
+CIntObject* CKingdTownList::createTownItem(size_t index)
+{
+	unsigned int picCount = conf.go()->ac.overviewPics;
+	size_t townsCount = LOCPLINT->cb->howManyTowns();
+
+	if (index < townsCount)
+		return new CTownItem(LOCPLINT->cb->getTownBySerial(index));
+	else
+		return new CAnimImage("OVSLOT", (index-2) % picCount );
+}
+
 CTownItem::CTownItem(const CGTownInstance* Town):
 	town(Town)
 {
@@ -1102,64 +875,33 @@ public:
 	}
 };
 
-class HeroItemManager : public CIntObject, public IGuiObjectListManager
-{
-public:
-	ArtSlotsTab* tab1;
-	ArtSlotsTab* tab2;
-	BackpackTab* tab3;
-
-	HeroItemManager(const CGHeroInstance* Hero);
-	CIntObject * getObject(size_t position);
-	void removeObject(CIntObject * object);
-};
-
-HeroItemManager::HeroItemManager(const CGHeroInstance* Hero)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	recActions = 0;
-	defActions = DISPOSE | SHARE_POS;
-
-	tab1 = new ArtSlotsTab;
-	tab2 = new ArtSlotsTab;
-	tab3 = new BackpackTab;
-}
-
-CIntObject * HeroItemManager::getObject(size_t position)
-{
-	switch (position)
-	{
-	case 0: return tab1;
-	case 1: return tab2;
-	case 2: return tab3;
-	default: assert(0);
-	         return NULL;
-	}
-}
-
-void HeroItemManager::removeObject(CIntObject * object)
-{
-	addChild(object, false);
-}
-
 CHeroItem::CHeroItem(const CGHeroInstance* Hero, CArtifactsOfHero::SCommonPart * artsCommonPart):
 	hero(Hero)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
-	name = new CLabel(75, 7, FONT_SMALL, TOPLEFT, zwykly, hero->name);
+	artTabs.resize(3);
+	ArtSlotsTab* arts1 = new ArtSlotsTab;
+	ArtSlotsTab* arts2 = new ArtSlotsTab;
+	BackpackTab* backpack = new BackpackTab;
+	artTabs[0] = arts1;
+	artTabs[1] = arts2;
+	artTabs[2] = backpack;
+	arts1->recActions = DISPOSE | SHARE_POS;
+	arts2->recActions = DISPOSE | SHARE_POS;
+	backpack->recActions = DISPOSE | SHARE_POS;
 
-	HeroItemManager *manager = new HeroItemManager(hero);
+	name = new CLabel(75, 7, FONT_SMALL, TOPLEFT, zwykly, hero->name);
 
 	std::vector<CArtPlace*> arts;
-	arts.insert(arts.end(), manager->tab1->arts.begin(), manager->tab1->arts.end());
-	arts.insert(arts.end(), manager->tab2->arts.begin(), manager->tab2->arts.end());
+	arts.insert(arts.end(), arts1->arts.begin(), arts1->arts.end());
+	arts.insert(arts.end(), arts2->arts.begin(), arts2->arts.end());
 
-	heroArts = new CArtifactsOfHero(arts, manager->tab3->arts, manager->tab3->btnLeft, manager->tab3->btnRight, false);
+	heroArts = new CArtifactsOfHero(arts, backpack->arts, backpack->btnLeft, backpack->btnRight, false);
 	heroArts->commonInfo = artsCommonPart;
 	heroArts->setHero(hero);
 
-	artsTabs = new CTabbedInt(manager);
+	artsTabs = new CTabbedInt(boost::bind(&CHeroItem::onTabSelected, this, _1), boost::bind(&CHeroItem::onTabDeselected, this, _1));
 
 	artButtons = new CHighlightableButtonsGroup(0);
 	for (size_t it = 0; it<3; it++)
@@ -1211,6 +953,17 @@ CHeroItem::CHeroItem(const CGHeroInstance* Hero, CArtifactsOfHero::SCommonPart *
 	luck->set(hero);
 }
 
+CIntObject * CHeroItem::onTabSelected(size_t index)
+{
+	return artTabs[index];
+}
+
+void CHeroItem::onTabDeselected(CIntObject *object)
+{
+	addChild(object, false);
+	object->recActions = DISPOSE | SHARE_POS;
+}
+
 void CHeroItem::onArtChange(int tabIndex)
 {
 	//redraw item after background change

+ 21 - 89
client/CKingdomInterface.h

@@ -195,99 +195,18 @@ public:
 
 ////////////////////////////////////////////////////////////////////////////////
 
-/// Interface used in CTabbedInt and CListBox
-class IGuiObjectListManager
-{
-public:
-	//Create object for specified position, may return NULL if no object is needed at this position
-	//NOTE: position may be greater then size (empty rows in ListBox)
-	virtual CIntObject * getObject(size_t position)=0;
-
-	//Called when object needs to be removed
-	virtual void removeObject(CIntObject * object)
-	{
-		delete object;
-	};
-	virtual ~IGuiObjectListManager(){};
-};
-
-/// Used as base for Tabs and List classes
-class CObjectList : public CIntObject
-{
-	IGuiObjectListManager *manager;
-
-protected:
-	//Internal methods for safe creation of items (Children capturing and activation/deactivation if needed)
-	void deleteItem(CIntObject* item);
-	CIntObject* createItem(size_t index);
-
-public:
-	CObjectList(IGuiObjectListManager *Manager);
-	~CObjectList();
-};
-
-/// Window element with multiple tabs
-class CTabbedInt : public CObjectList
-{
-private:
-	CIntObject * activeTab;
-	size_t activeID;
-
-public:
-	//Manager - object which implements this interface, will be destroyed by TabbedInt
-	//Pos - position of object, all tabs will be moved here
-	//ActiveID - ID of initially active tab
-	CTabbedInt(IGuiObjectListManager *Manager, Point position=Point(), size_t ActiveID=0);
-
-	void setActive(size_t which);
-	//recreate active tab
-	void reset();
-
-	//return currently active item
-	CIntObject * getItem();
-};
-
-/// List of IntObjects with optional slider
-class CListBox : public CObjectList
-{
-private:
-	std::list< CIntObject* > items;
-	size_t first;
-	size_t totalSize;
-
-	Point itemOffset;
-	CSlider * slider;
-
-	void updatePositions();
-public:
-	//Manager - object which implements this interface, will be destroyed by ListBox
-	//Pos - position of first item
-	//ItemOffset - distance between items in the list
-	//VisibleSize - maximal number of displayable at once items
-	//TotalSize 
-	//Slider - slider style, bit field: 1 = present(disabled), 2=horisontal(vertical), 4=blue(brown)
-	//SliderPos - position of slider, if present
-	CListBox(IGuiObjectListManager *Manager, Point Pos, Point ItemOffset, size_t VisibleSize,
-	         size_t TotalSize, size_t InitialPos=0, int Slider=0, Rect SliderPos=Rect() );
-
-	//recreate all visible items
-	void reset();
-
-	//return currently active items
-	std::list< CIntObject * > getItems();
-
-	//scroll list
-	void moveToPos(size_t which);
-	void moveToNext();
-	void moveToPrev();
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
 /// Class which holds all parts of kingdom overview window
 class CKingdomInterface : public CGarrisonHolder, public CArtifactHolder
 {
 private:
+	struct OwnedObjectInfo
+	{
+		int imageID;
+		unsigned int count;
+		std::string hoverText;
+	};
+	std::vector<OwnedObjectInfo> objects;
+
 	CListBox * dwellingsList;
 	CTabbedInt * tabArea;
 	CPicture * background;
@@ -316,6 +235,9 @@ private:
 	void generateObjectsList(const std::vector<const CGObjectInstance * > &ownedObjects);
 	void generateMinesList(const std::vector<const CGObjectInstance * > &ownedObjects);
 
+	CIntObject* createOwnedObject(size_t index);
+	CIntObject* createMainTab(size_t index);
+
 public:
 	CKingdomInterface();
 
@@ -356,6 +278,8 @@ class CHeroItem : public CWindowWithGarrison
 {
 	const CGHeroInstance * hero;
 
+	std::vector<CIntObject *> artTabs;
+
 	CAnimImage *background;
 	CAnimImage *portrait;
 	CLabel *name;
@@ -370,6 +294,9 @@ class CHeroItem : public CWindowWithGarrison
 
 	void onArtChange(int tabIndex);
 
+	CIntObject * onTabSelected(size_t index);
+	void onTabDeselected(CIntObject *object);
+
 public:
 	CArtifactsOfHero *heroArts;
 
@@ -380,12 +307,16 @@ public:
 class CKingdHeroList : public CGarrisonHolder, public CWindowWithArtifacts
 {
 private:
+	CArtifactsOfHero::SCommonPart artsCommonPart;
+
 	std::vector<CHeroItem*> heroItems;
 	CListBox * heroes;
 	CPicture * title;
 	CLabel * heroLabel;
 	CLabel * skillsLabel;
 
+	CIntObject* createHeroItem(size_t index);
+	void destroyHeroItem(CIntObject *item);
 public:
 	CKingdHeroList(size_t maxSize);
 
@@ -403,6 +334,7 @@ private:
 	CLabel * garrHeroLabel;
 	CLabel * visitHeroLabel;
 	
+	CIntObject* createTownItem(size_t index);
 public:
 	CKingdTownList(size_t maxSize);
 	

+ 318 - 324
client/CPreGame.cpp

@@ -29,6 +29,9 @@
 #include <boost/function.hpp>
 #include <boost/ref.hpp>
 #include <boost/foreach.hpp>
+#include <boost/assign/std/vector.hpp>
+#include <boost/assign/list_of.hpp>
+
 #include <cstdlib>
 #include "../lib/Connection.h"
 #include "../lib/VCMIDirs.h"
@@ -86,7 +89,7 @@ struct EvilHlpStruct
 	CConnection *serv;
 	StartInfo *sInfo;
 
-	void reset(bool strong = true) 
+	void reset(bool strong = true)
 	{
 		if(strong)
 		{
@@ -148,7 +151,7 @@ void setPlayer(PlayerSettings &pset, unsigned player, const std::map<ui32, std::
 void updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader * mapHeader, const std::map<ui32, std::string> &playerNames)
 {
 	sInfo.playerInfos.clear();
-	if(!filename.size()) 
+	if(!filename.size())
 		return;
 
 	/*sInfo.playerInfos.resize(to->playerAmnt);*/
@@ -191,7 +194,7 @@ template <typename T> class CApplyOnPG;
 class CBaseForPGApply
 {
 public:
-	virtual void applyOnPG(CSelectionScreen *selScr, void *pack) const =0; 
+	virtual void applyOnPG(CSelectionScreen *selScr, void *pack) const =0;
 	virtual ~CBaseForPGApply(){};
 	template<typename U> static CBaseForPGApply *getApplier(const U * t=NULL)
 	{
@@ -211,100 +214,212 @@ public:
 
 static CApplier<CBaseForPGApply> *applier = NULL;
 
-CMenuScreen::CMenuScreen( EState which )
+static CPicture* createPicture(const JsonNode& config)
 {
-	OBJ_CONSTRUCTION;
-	bgAd = NULL;
+	return new CPicture(config["name"].String(), config["x"].Float(), config["y"].Float());
+}
 
-	switch(which)
+CMenuScreen::CMenuScreen(const JsonNode& configNode):
+	config(configNode)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	BOOST_FOREACH(const JsonNode& node, config["items"].Vector())
+		menuNameToEntry.push_back(node["name"].String());
+
+	BOOST_FOREACH(const JsonNode& node, config["images"].Vector())
+		images.push_back(createPicture(node));
+
+	//Hardcoded entry
+	menuNameToEntry.push_back("credits");
+
+	tabs = new CTabbedInt(boost::bind(&CMenuScreen::createTab, this, _1), CTabbedInt::DestroyFunc());
+	tabs->type |= REDRAW_PARENT;
+}
+
+CIntObject * CMenuScreen::createTab(size_t index)
+{
+	if (config["items"].Vector().size() == index)
+		return new CreditsScreen();
+
+	return new CMenuEntry(this, config["items"].Vector()[index]);
+}
+
+void CMenuScreen::show(SDL_Surface * to)
+{
+	if (!config["video"].isNull())
+		CCS->videoh->update(config["video"]["x"].Float(), config["video"]["y"].Float(), to, true, false);
+	CIntObject::show(to);
+}
+
+void CMenuScreen::activate()
+{
+	CCS->musich->playMusic(musicBase::mainMenu, -1);
+	if (!config["video"].isNull())
+		CCS->videoh->open(config["video"]["name"].String());
+	CIntObject::activate();
+}
+
+void CMenuScreen::deactivate()
+{
+	if (!config["video"].isNull())
+		CCS->videoh->close();
+
+	CIntObject::deactivate();
+}
+
+void CMenuScreen::switchToTab(size_t index)
+{
+	tabs->setActive(index);
+}
+
+//funciton for std::string -> boost::function conversion for main menu
+static boost::function<void()> genCommand(CMenuScreen* menu, std::vector<std::string> menuType, const std::string &string)
+{
+	static const std::vector<std::string> commandType  = boost::assign::list_of
+		("to")("campaigns")("start")("load")("exit")("highscores");
+
+	static const std::vector<std::string> gameType = boost::assign::list_of
+		("single")("multi")("campaign")("tutorial");
+
+	std::list<std::string> commands;
+	boost::split(commands, string, boost::is_any_of("\t "));
+
+	if (!commands.empty())
 	{
-	case mainMenu:
-		{
-			buttons[0] = new AdventureMapButton("", CGI->generaltexth->zelp[3].second, // New game
-				bind(&CMenuScreen::moveTo, this, ref(CGP->scrs[newGame])), 540, 10, "ZMENUNG.DEF", SDLK_n);
-			buttons[1] = new AdventureMapButton("", CGI->generaltexth->zelp[4].second, // Load game
-				bind(&CMenuScreen::moveTo, this, ref(CGP->scrs[loadGame])), 532, 132, "ZMENULG.DEF", SDLK_l);
-			buttons[2] = new AdventureMapButton("", CGI->generaltexth->zelp[5].second, 0, 524, 251, "ZMENUHS.DEF", SDLK_h); // Highscore
-			buttons[3] = new AdventureMapButton("", CGI->generaltexth->zelp[6].second, 0 /*cb*/, 557, 359, "ZMENUCR.DEF", SDLK_c); // Credits
-			boost::function<void()> confWindow = bind(CInfoWindow::showYesNoDialog, ref(CGI->generaltexth->allTexts[69]), (const std::vector<SComponent*>*)0, do_quit, 0, false, 1);
-			buttons[4] = new AdventureMapButton("", CGI->generaltexth->zelp[7].second, confWindow, 586, 468, "ZMENUQT.DEF", SDLK_ESCAPE); // Exit
-		}
-		break;
-	case newGame:
+		size_t index = std::find(commandType.begin(), commandType.end(), commands.front()) - commandType.begin();
+		commands.pop_front();
+		if (index > 3 || !commands.empty())
 		{
-			bgAd = new CPicture(BitmapHandler::loadBitmap("ZNEWGAM.bmp"), 114, 312, true);
-			buttons[0] = new AdventureMapButton("", CGI->generaltexth->zelp[10].second, // Single player
-				bind(&CGPreGame::openSel, CGP, newGame, SINGLE_PLAYER), 545, 4, "ZTSINGL.DEF", SDLK_s);
-			buttons[1] = new AdventureMapButton("", CGI->generaltexth->zelp[12].second, &pushIntT<CMultiMode>, 568, 120, "ZTMULTI.DEF", SDLK_m);
-			buttons[2] = new AdventureMapButton("", CGI->generaltexth->zelp[11].second, bind(&CMenuScreen::moveTo, this, ref(CGP->scrs[campaignMain])), 541, 233, "ZTCAMPN.DEF", SDLK_c);
-			buttons[3] = new AdventureMapButton("", CGI->generaltexth->zelp[13].second, 0 /*cb*/, 545, 358, "ZTTUTOR.DEF", SDLK_t);
-			buttons[4] = new AdventureMapButton("", CGI->generaltexth->zelp[14].second, bind(&CMenuScreen::moveTo, this, CGP->scrs[mainMenu]), 582, 464, "ZTBACK.DEF", SDLK_ESCAPE);
-		}
-		break;
-	case loadGame:
-		{
-			bgAd = new CPicture(BitmapHandler::loadBitmap("ZLOADGAM.bmp"), 114, 312, true);
-			buttons[0] = new AdventureMapButton("", CGI->generaltexth->zelp[10].second, bind(&CGPreGame::openSel, CGP, loadGame, SINGLE_PLAYER), 545, 4, "ZTSINGL.DEF", SDLK_s);
-			buttons[1] = new AdventureMapButton("", CGI->generaltexth->zelp[12].second, bind(&CGPreGame::openSel, CGP, loadGame, MULTI_HOT_SEAT), 568, 120, "ZTMULTI.DEF", SDLK_m);
-			buttons[2] = new AdventureMapButton("", CGI->generaltexth->zelp[11].second, 0 /*cb*/, 541, 233, "ZTCAMPN.DEF", SDLK_c);
-			buttons[3] = new AdventureMapButton("", CGI->generaltexth->zelp[13].second, 0 /*cb*/, 545, 358, "ZTTUTOR.DEF", SDLK_t);
-			buttons[4] = new AdventureMapButton("", CGI->generaltexth->zelp[14].second, bind(&CMenuScreen::moveTo, this, CGP->scrs[mainMenu]), 582, 464, "ZTBACK.DEF", SDLK_ESCAPE);
-		}
-		break;
-	case campaignMain:
-		{
-			buttons[0] = new AdventureMapButton("", "", bind(&CGPreGame::openCampaignScreen, CGP, CCampaignScreen::WOG), 535, 8, "ZSSSOD.DEF", SDLK_s); // WOG
-			buttons[1] = new AdventureMapButton("", "", bind(&CGPreGame::openCampaignScreen, CGP, CCampaignScreen::ROE) , 494, 117, "ZSSROE.DEF", SDLK_m); // ROE
-			buttons[2] = new AdventureMapButton("", "", bind(&CGPreGame::openCampaignScreen, CGP, CCampaignScreen::AB), 486, 241, "ZSSARM.DEF", SDLK_c); // AB
-			buttons[3] = new AdventureMapButton("", "", bind(&CGPreGame::openSel, CGP, campaignList, SINGLE_PLAYER), 550, 358, "ZSSCUS.DEF", SDLK_t); // Custom
-			buttons[4] = new AdventureMapButton("", "", bind(&CMenuScreen::moveTo, this, CGP->scrs[newGame]), 582, 464, "ZTBACK.DEF", SDLK_ESCAPE); // Back
-
+			switch (index)
+			{
+				break; case 0://to - switch to another tab, if such tab exists
+				{
+					size_t index2 = std::find(menuType.begin(), menuType.end(), commands.front()) - menuType.begin();
+					if ( index2 != menuType.size())
+						return boost::bind(&CMenuScreen::switchToTab, menu, index2);
+				}
+				break; case 1://open campaign selection window
+				{
+					return boost::bind(&CGPreGame::openCampaignScreen, CGP, commands.front());
+				}
+				break; case 2://start
+				{
+					switch (std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())
+					{
+						case 0: return bind(&CGPreGame::openSel, CGP, CMenuScreen::newGame, CMenuScreen::SINGLE_PLAYER);
+						case 1: return &pushIntT<CMultiMode>;
+						case 2: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::campaignList, CMenuScreen::SINGLE_PLAYER);
+						case 3: return boost::function<void()>();//TODO: start tutorial
+					}
+				}
+				break; case 3://load
+				{
+					switch (std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())
+					{
+						case 0: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::loadGame, CMenuScreen::SINGLE_PLAYER);
+						case 1: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::loadGame, CMenuScreen::MULTI_HOT_SEAT);
+						case 2: return boost::function<void()>();//TODO: load campaign
+						case 3: return boost::function<void()>();//TODO: load tutorial
+					}
+				}
+				break; case 4://exit
+				{
+					return boost::bind(CInfoWindow::showYesNoDialog, boost::ref(CGI->generaltexth->allTexts[69]), (const std::vector<SComponent*>*)0, do_quit, 0, false, 1);
+				}
+				break; case 5://highscores
+				{
+					return boost::function<void()>(); //TODO: high scores &pushIntT<CHighScores>;
+				}
+			}
 		}
-		break;
 	}
+	tlog0<<"Failed to parse command: "<<string<<"\n";
+	return boost::function<void()>();
+}
+
+AdventureMapButton* CMenuEntry::createButton(CMenuScreen* parent, const JsonNode& button)
+{
+	boost::function<void()> command = genCommand(parent, parent->menuNameToEntry, button["command"].String());
 
-	for(int i = 0; i < ARRAY_COUNT(buttons); i++)
-		buttons[i]->hoverable = true;
+	std::pair<std::string, std::string> help;
+	if (!button["help"].isNull() && button["help"].Float() > 0)
+		help = CGI->generaltexth->zelp[button["help"].Float()];
+
+	return new AdventureMapButton(help, command, button["x"].Float(), button["y"].Float(), button["name"].String(), button["hotkey"].Float());
 }
 
-CMenuScreen::~CMenuScreen()
+CMenuEntry::CMenuEntry(CMenuScreen* parent, const JsonNode &config)
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	type |= REDRAW_PARENT;
+
+	BOOST_FOREACH(const JsonNode& node, config["images"].Vector())
+		images.push_back(createPicture(node));
+
+	BOOST_FOREACH(const JsonNode& node, config["buttons"].Vector())
+	{
+		buttons.push_back(createButton(parent, node));
+		buttons.back()->hoverable = true;
+		buttons.back()->type |= REDRAW_PARENT;
+	}
 }
 
-void CMenuScreen::showAll( SDL_Surface * to )
+CreditsScreen::CreditsScreen()
 {
-	blitAt(CGP->mainbg, 0, 0, to);
-	CIntObject::showAll(to);
+	used |= LCLICK | RCLICK;
+	type |= REDRAW_PARENT;
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	pos.w = 800;
+	pos.h = 600;
+	std::string text = bitmaph->getTextFile("CREDITS");
+	size_t firstQuote = text.find('\"')+1;
+	text = text.substr(firstQuote, text.find('\"', firstQuote) - firstQuote );
+	credits = new CTextBox(text, Rect(450, 600, 350, 32000), 0, FONT_CREDITS, CENTER, zwykly);
+	credits->pos.h = credits->maxH;
 }
 
-void CMenuScreen::show( SDL_Surface * to )
+void CreditsScreen::show(SDL_Surface *to)
 {
-	CCS->videoh->update(pos.x + 8, pos.y + 105, to, true, false);
-	CIntObject::show(to);
+	static int count = 0;
+	count++;
+	if (count == 2)
+	{
+		credits->pos.y--;
+		count = 0;
+	}
+	SDL_SetClipRect(screen, &credits->pos);
+	redraw();
+	SDL_SetClipRect(screen, NULL);
+
+	//end of credits, close this screen
+	if (credits->pos.y + credits->pos.h < 0)
+		clickRight(false, false);
+}
+
+void CreditsScreen::clickLeft(tribool down, bool previousState)
+{
+	clickRight(down, previousState);
 }
 
-void CMenuScreen::moveTo( CMenuScreen *next )
+void CreditsScreen::clickRight(tribool down, bool previousState)
 {
-	GH.popInt(this);
-	GH.pushInt(next);
+	CTabbedInt* menu = dynamic_cast<CTabbedInt*>(parent);
+	assert(menu);
+	menu->setActive(0);
 }
 
-CGPreGame::CGPreGame()
+CGPreGame::CGPreGame():
+	pregameConfig(new JsonNode(DATA_DIR "/config/mainmenu.json"))
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	GH.defActionsDef = 63;
 	CGP = this;
-	mainbg = BitmapHandler::loadBitmap("ZPIC1005.bmp");
-
-	for(int i = 0; i < ARRAY_COUNT(scrs); i++)
-		scrs[i] = new CMenuScreen((CMenuScreen::EState)i);
+	menu = new CMenuScreen((*pregameConfig)["window"]);
 }
 
 CGPreGame::~CGPreGame()
 {
-	SDL_FreeSurface(mainbg);
-
-	for(int i = 0; i < ARRAY_COUNT(scrs); i++)
-		delete scrs[i];
 }
 
 void CGPreGame::openSel(CMenuScreen::EState screenType, CMenuScreen::EMultiMode multi /*= CMenuScreen::SINGLE_PLAYER*/)
@@ -312,12 +427,6 @@ void CGPreGame::openSel(CMenuScreen::EState screenType, CMenuScreen::EMultiMode
 	GH.pushInt(new CSelectionScreen(screenType, multi));
 }
 
-void CGPreGame::openCampaignScreen(CCampaignScreen::CampaignSet campaigns)
-{
-	std::map<std::string, CCampaignScreen::CampaignStatus> defaultCamp;
-	GH.pushInt(new CCampaignScreen(campaigns, defaultCamp));
-}
-
 void CGPreGame::loadGraphics()
 {
 	victory = CDefHandler::giveDef("SCNRVICT.DEF");
@@ -341,11 +450,10 @@ void CGPreGame::disposeGraphics()
 
 void CGPreGame::update()
 {
-	if (GH.listInt.size() == 0)
+	if (GH.listInt.empty())
 	{
-		CCS->musich->playMusic(musicBase::mainMenu, -1);
-		CCS->videoh->open("ACREDIT.SMK");
-		GH.pushInt(scrs[CMenuScreen::mainMenu]);
+		GH.pushInt(menu);
+		menu->switchToTab(0);
 	}
 
 	if(SEL)
@@ -355,7 +463,7 @@ void CGPreGame::update()
 	GH.updateTime();
 	GH.handleEvents();
 
-	if (GH.curInt == 0) // no redraw, when a new game was created
+	if (GH.curInt == NULL) // no redraw, when a new game was created
 		return;
 
 	GH.topInt()->show(screen);
@@ -369,6 +477,19 @@ void CGPreGame::update()
 	CCS->curh->draw2();
 }
 
+void CGPreGame::openCampaignScreen(std::string name)
+{
+	BOOST_FOREACH(const JsonNode& node, (*pregameConfig)["campaignsset"].Vector())
+	{
+		if (node["name"].String() == name)
+		{
+			GH.pushInt(new CCampaignScreen(node));
+			return;
+		}
+	}
+	tlog1<<"Unknown campaign set: "<<name<<"\n";
+}
+
 CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer /*= CMenuScreen::SINGLE_PLAYER*/, const std::map<ui32, std::string> *Names /*= NULL*/)
 	: ISelectionScreenInfo(Names), serverHandlingThread(NULL), mx(new boost::recursive_mutex),
 	  serv(NULL), ongoingClosing(false), myNameID(255)
@@ -420,7 +541,7 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 	if (screenType == CMenuScreen::campaignList)
 	{
 		opt = NULL;
-	} 
+	}
 	else
 	{
 		opt = new OptionsTab(); //scenario options tab
@@ -476,7 +597,7 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		start  = new AdventureMapButton(std::pair<std::string, std::string>(), bind(&CSelectionScreen::startCampaign, this), 411, 529, "SCNRLOD.DEF", SDLK_b);
 		break;
 	}
-	
+
 
 	start->assignedKeys.insert(SDLK_RETURN);
 
@@ -697,7 +818,7 @@ void CSelectionScreen::difficultyChange( int to )
 	assert(screenType == CMenuScreen::newGame);
 	sInfo.difficulty = to;
 	propagateOptions();
-	GH.totalRedraw();
+	redraw();
 }
 
 void CSelectionScreen::handleConnection()
@@ -818,12 +939,12 @@ void SelectionTab::filter( int size, bool selectFirst )
 
 	if(tabType == CMenuScreen::campaignList)
 	{
-		for (size_t i=0; i<allItems.size(); i++) 
+		for (size_t i=0; i<allItems.size(); i++)
 			curItems.push_back(&allItems[i]);
 	}
 	else
 	{
-		for (size_t i=0; i<allItems.size(); i++) 
+		for (size_t i=0; i<allItems.size(); i++)
 			if( allItems[i].mapHeader && allItems[i].mapHeader->version  &&  (!size || allItems[i].mapHeader->width == size))
 				curItems.push_back(&allItems[i]);
 	}
@@ -866,7 +987,7 @@ void SelectionTab::parseMaps(std::vector<FileInfo> &files, int start, int thread
 		gzclose(tempf);
 		if(read < 50  ||  !mapBuffer[4])
 		{
-			tlog3 << "\t\tWarning: corrupted map file: " << files[start].name << std::endl; 
+			tlog3 << "\t\tWarning: corrupted map file: " << files[start].name << std::endl;
 		}
 		else //valid map
 		{
@@ -885,7 +1006,7 @@ void SelectionTab::parseGames(std::vector<FileInfo> &files, bool multi)
 		if(!lf.sfile)
 			continue;
 
-		ui8 sign[8]; 
+		ui8 sign[8];
 		lf >> sign;
 		if(std::memcmp(sign,"VCMISVG",7))
 		{
@@ -977,7 +1098,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 			else
 			{
 				positions = 16;
-			}	
+			}
 			if(tabType == CMenuScreen::saveGame)
 				txt = new CTextInput(Rect(32, 539, 350, 20), Point(-32, -25), "GSSTRIP.bmp", 0);
 			break;
@@ -1011,7 +1132,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 	}
 
 
-	
+
 	if (tabType != CMenuScreen::campaignList)
 	{
 		//size filter buttons
@@ -1161,7 +1282,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
 
 		if (elemIdx == selectionPos)
 			itemColor=tytulowy;
-		else 
+		else
 			itemColor=zwykly;
 
 		if(tabType != CMenuScreen::campaignList)
@@ -1215,14 +1336,14 @@ void SelectionTab::printMaps(SDL_Surface *to)
 			//victory conditions
 			if (currentItem->mapHeader->victoryCondition.condition == winStandard)
 				temp = 11;
-			else 
+			else
 				temp = currentItem->mapHeader->victoryCondition.condition;
 			blitAt(CGP->victory->ourImages[temp].bitmap, POS(306, 117), to);
 
 			//loss conditions
 			if (currentItem->mapHeader->lossCondition.typeOfLossCon == lossStandard)
 				temp=3;
-			else 
+			else
 				temp=currentItem->mapHeader->lossCondition.typeOfLossCon;
 			blitAt(CGP->loss->ourImages[temp].bitmap, POS(339, 117), to);
 		}
@@ -1252,7 +1373,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
 
 		//print name
 		CSDL_Ext::printAtMiddle(name, POS(213, 128), FONT_SMALL, itemColor, to);
-	
+
 	}
 #undef POS
 }
@@ -1311,7 +1432,7 @@ void SelectionTab::keyPressed( const SDL_KeyboardEvent & key )
 		moveBy = -positions+1;
 		break;
 	case SDLK_PAGEDOWN:
-		moveBy = +positions-1; 
+		moveBy = +positions-1;
 		break;
 	case SDLK_HOME:
 		select(-slider->value);
@@ -1322,7 +1443,7 @@ void SelectionTab::keyPressed( const SDL_KeyboardEvent & key )
 	default:
 		return;
 	}
-	select(selectionPos - slider->value + moveBy); 
+	select(selectionPos - slider->value + moveBy);
 }
 
 void SelectionTab::onDoubleClick()
@@ -1403,7 +1524,7 @@ InfoCard::InfoCard( bool Network )
   : bg(NULL), network(Network), chatOn(false), chat(NULL), playerListBg(NULL),
 	difficulty(NULL), sizes(NULL), sFlags(NULL)
 {
-	OBJ_CONSTRUCTION;
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	pos.x += 393;
 	used = RCLICK;
 	mapDescription = NULL;
@@ -1419,7 +1540,9 @@ InfoCard::InfoCard( bool Network )
 	else
 	{
 		bg = new CPicture(BitmapHandler::loadBitmap("GSELPOP1.bmp"), 0, 0, true);
-		std::swap(children.front(), children.back());
+		moveChild(bg, this, parent);
+		parent->children.insert(parent->children.begin()+1, bg);
+		parent->children.pop_back();
 		pos.w = bg->pos.w;
 		pos.h = bg->pos.h;
 		sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");
@@ -1504,19 +1627,6 @@ void InfoCard::showAll( SDL_Surface * to )
 	{
 		if(SEL->screenType != CMenuScreen::campaignList)
 		{
-			if(SEL->screenType != CMenuScreen::newGame)
-			{
-				for (int i = 0; i < difficulty->buttons.size(); i++)
-				{
-					//if(i == SEL->current->difficulty)
-					//	difficulty->buttons[i]->state = 3;
-					//else
-					//	difficulty->buttons[i]->state = 2;
-
-					difficulty->buttons[i]->showAll(to);
-				}
-			}
-
 			int temp = -1;
 
 			if(!chatOn)
@@ -1540,7 +1650,7 @@ void InfoCard::showAll( SDL_Surface * to )
 
 				temp=SEL->current->mapHeader->lossCondition.typeOfLossCon;
 				if (temp>12) temp=3;
-				blitAtLoc(CGP->loss->ourImages[temp].bitmap, 24, 359, to); //loss cond 
+				blitAtLoc(CGP->loss->ourImages[temp].bitmap, 24, 359, to); //loss cond
 			}
 
 			//difficulty
@@ -1576,12 +1686,12 @@ void InfoCard::showAll( SDL_Surface * to )
 			//print flags
 			int fx = 34  + graphics->fonts[FONT_SMALL]->getWidth(CGI->generaltexth->allTexts[390].c_str());
 			int ex = 200 + graphics->fonts[FONT_SMALL]->getWidth(CGI->generaltexth->allTexts[391].c_str());
-			
+
 			int myT;
 
 			if(playerColor >= 0)
 				myT = SEL->current->mapHeader->players[playerColor].team;
-			else 
+			else
 				myT = -1;
 
 			for (std::map<int, PlayerSettings>::const_iterator i = SEL->sInfo.playerInfos.begin(); i != SEL->sInfo.playerInfos.end(); i++)
@@ -1619,7 +1729,7 @@ void InfoCard::showAll( SDL_Surface * to )
 		if (SEL->screenType == CMenuScreen::campaignList)
 		{
 			name = SEL->current->campaignHeader->name;
-		} 
+		}
 		else
 		{
 			name = SEL->current->mapHeader->name;
@@ -1628,10 +1738,10 @@ void InfoCard::showAll( SDL_Surface * to )
 		//name
 		if (name.length())
 			printAtLoc(name, 26, 39, FONT_BIG, tytulowy, to);
-		else 
+		else
 			printAtLoc("Unnamed", 26, 39, FONT_BIG, tytulowy, to);
 
-		
+
 	}
 }
 
@@ -1681,7 +1791,7 @@ void InfoCard::showTeamsPopup()
 		{
 			blitAt(sFlags->ourImages[flags[j]].bitmap, curx, 75 + 50*i, bmp);
 			curx += 18;
-		}	
+		}
 	}
 
 	GH.pushInt(new CInfoPopup(bmp, true));
@@ -1917,7 +2027,7 @@ void OptionsTab::recreate()
 	usedHeroes.clear();
 
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	for(std::map<int, PlayerSettings>::iterator it = SEL->sInfo.playerInfos.begin(); 
+	for(std::map<int, PlayerSettings>::iterator it = SEL->sInfo.playerInfos.begin();
 		it != SEL->sInfo.playerInfos.end(); ++it)
 	{
 		entries.insert(std::make_pair(it->first, new PlayerOptionsEntry(this, it->second)));
@@ -2032,9 +2142,9 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry( OptionsTab *owner, PlayerSet
 
 	pos = parent->pos + Point(54, 122 + serial*50);
 
-	static const char *flags[] = {"AOFLGBR.DEF", "AOFLGBB.DEF", "AOFLGBY.DEF", "AOFLGBG.DEF", 
+	static const char *flags[] = {"AOFLGBR.DEF", "AOFLGBB.DEF", "AOFLGBY.DEF", "AOFLGBG.DEF",
 		"AOFLGBO.DEF", "AOFLGBP.DEF", "AOFLGBT.DEF", "AOFLGBS.DEF"};
-	static const char *bgs[] = {"ADOPRPNL.bmp", "ADOPBPNL.bmp", "ADOPYPNL.bmp", "ADOPGPNL.bmp", 
+	static const char *bgs[] = {"ADOPRPNL.bmp", "ADOPBPNL.bmp", "ADOPYPNL.bmp", "ADOPGPNL.bmp",
 		"ADOPOPNL.bmp", "ADOPPPNL.bmp", "ADOPTPNL.bmp", "ADOPSPNL.bmp"};
 
 	bg = new CPicture(BitmapHandler::loadBitmap(bgs[s.color]), 0, 0, true);
@@ -2063,7 +2173,7 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry( OptionsTab *owner, PlayerSet
 	else
 		whoCanPlay = HUMAN;
 
-	if(SEL->screenType != CMenuScreen::scenarioInfo  
+	if(SEL->screenType != CMenuScreen::scenarioInfo
 		&&  SEL->current->mapHeader->players[s.color].canHumanPlay
 		&&  SEL->multiPlayer != CMenuScreen::MULTI_NETWORK_GUEST)
 	{
@@ -2194,7 +2304,7 @@ SDL_Surface * OptionsTab::SelectedBox::getImg() const
 			case 2:
 				pom=CGI->townh->towns[s.castle].bonus;
 				break;
-			default: 
+			default:
 				assert(0);
 			}
 			return CGP->bonuses->ourImages[pom].bitmap;
@@ -2206,7 +2316,7 @@ SDL_Surface * OptionsTab::SelectedBox::getImg() const
 
 const std::string * OptionsTab::SelectedBox::getText() const
 {
-	const PlayerSettings &s = SEL->sInfo.playerInfos[player];	
+	const PlayerSettings &s = SEL->sInfo.playerInfos[player];
 	switch(which)
 	{
 	case TOWN:
@@ -2263,11 +2373,11 @@ void OptionsTab::SelectedBox::clickRight( tribool down, bool previousState )
 	int val=-1;
 	switch(which)
 	{
-	case TOWN: 
-		val = s.castle; 
+	case TOWN:
+		val = s.castle;
 		break;
-	case HERO: 
-		val = s.hero; 
+	case HERO:
+		val = s.hero;
 		if(val == -2) //none => we may have some preset info
 		{
 			int p9 = SEL->current->mapHeader->players[s.color].p9;
@@ -2275,8 +2385,8 @@ void OptionsTab::SelectedBox::clickRight( tribool down, bool previousState )
 				val = p9;
 		}
 		break;
-	case BONUS: 
-		val = s.bonus; 
+	case BONUS:
+		val = s.bonus;
 		break;
 	}
 
@@ -2358,7 +2468,7 @@ void OptionsTab::SelectedBox::clickRight( tribool down, bool previousState )
 		title = &CGI->generaltexth->allTexts[80];
 
 		CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[79], 135, 137, FONT_MEDIUM, tytulowy, bmp);
-		
+
 		const CTown &t = CGI->townh->towns[val];
 		//print creatures
 		int x = 60, y = 159;
@@ -2414,7 +2524,7 @@ CScenarioInfo::CScenarioInfo(const CMapHeader *mapHeader, const StartInfo *start
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
-	for(std::map<int, PlayerSettings>::const_iterator it = startInfo->playerInfos.begin(); 
+	for(std::map<int, PlayerSettings>::const_iterator it = startInfo->playerInfos.begin();
 		it != startInfo->playerInfos.end(); ++it)
 	{
 		if(it->second.human)
@@ -2496,7 +2606,7 @@ bool mapSorter::operator()(const CMapInfo *aaa, const CMapInfo *bbb)
 		switch(sortBy)
 		{
 			case _numOfMaps: //by number of maps in campaign
-				return CGI->generaltexth->campaignRegionNames[ aaa->campaignHeader->mapVersion ].size() < 
+				return CGI->generaltexth->campaignRegionNames[ aaa->campaignHeader->mapVersion ].size() <
 					CGI->generaltexth->campaignRegionNames[ bbb->campaignHeader->mapVersion ].size();
 				break;
 			case _name: //by name
@@ -2511,7 +2621,7 @@ bool mapSorter::operator()(const CMapInfo *aaa, const CMapInfo *bbb)
 
 CMultiMode::CMultiMode()
 {
-	OBJ_CONSTRUCTION;
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	bg = new CPicture("MUPOPUP.bmp");
 	bg->convertToScreenBPP(); //so we could draw without problems
 	blitAt(CPicture("MUMAP.bmp"), 16, 77, *bg); //blit img
@@ -2548,7 +2658,7 @@ void CMultiMode::joinTCP()
 
 CHotSeatPlayers::CHotSeatPlayers(const std::string &firstPlayer)
 {
-	OBJ_CONSTRUCTION;
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	bg = new CPicture("MUHOTSEA.bmp");
 	bg->convertToScreenBPP(); //so we could draw without problems
 	bg->printAtMiddleWBLoc(CGI->generaltexth->allTexts[446], 185, 55, FONT_BIG, 50, zwykly, *bg); //HOTSEAT	Please enter names
@@ -2587,7 +2697,7 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 	static const std::string bgNames [] = {"E1_BG.BMP", "G2_BG.BMP", "E2_BG.BMP", "G1_BG.BMP", "G3_BG.BMP", "N1_BG.BMP",
 		"S1_BG.BMP", "BR_BG.BMP", "IS_BG.BMP", "KR_BG.BMP", "NI_BG.BMP", "TA_BG.BMP", "AR_BG.BMP", "HS_BG.BMP",
 		"BB_BG.BMP", "NB_BG.BMP", "EL_BG.BMP", "RN_BG.BMP", "UA_BG.BMP", "SP_BG.BMP"};
-	
+
 	loadPositionsOfGraphics();
 
 	background = BitmapHandler::loadBitmap(bgNames[ourCampaign->camp->header.mapVersion]);
@@ -2603,7 +2713,7 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 	//campaign name
 	if (ourCampaign->camp->header.name.length())
 		printAtLoc(ourCampaign->camp->header.name, 481, 28, FONT_BIG, tytulowy, background);
-	else 
+	else
 		printAtLoc("Unnamed", 481, 28, FONT_BIG, tytulowy, background);
 
 	//map size icon
@@ -2612,7 +2722,7 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 
 	//campaign description
 	printAtLoc(CGI->generaltexth->allTexts[38], 481, 63, FONT_SMALL, tytulowy, background);
- 
+
 	cmpgDesc = new CTextBox(ourCampaign->camp->header.description, Rect(480, 86, 286, 117), 1);
 	cmpgDesc->showAll(background);
 
@@ -2659,7 +2769,7 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 	std::vector<std::string> difficulty;
 	boost::split(difficulty, CGI->generaltexth->allTexts[492], boost::is_any_of(" "));
 	printAtLoc(difficulty.back(), 689, 432, FONT_MEDIUM, zwykly, background); //Difficulty
-	
+
 	//difficulty pics
 	for (int b=0; b<ARRAY_COUNT(diffPics); ++b)
 	{
@@ -2670,7 +2780,7 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 
 		delete cde;
 	}
-	
+
 	//difficulty selection buttons
 	if (ourCampaign->camp->header.difficultyChoosenByPlayer)
 	{
@@ -2680,7 +2790,7 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 
 	//load miniflags
 	sFlags = CDefHandler::giveDef("ITGFLAGS.DEF");
-	
+
 }
 
 CBonusSelection::~CBonusSelection()
@@ -2749,7 +2859,7 @@ void CBonusSelection::selectMap( int whichOne )
 	delete ourHeader;
 	ourHeader = new CMapHeader();
 	ourHeader->initFromMemory((const unsigned char*)ourCampaign->camp->mapPieces.find(whichOne)->second.c_str(), i);
-	
+
 	std::map<ui32, std::string> names;
 	names[1] = GDefaultOptions.playerName;
 	updateStartInfo(ourCampaign->camp->header.filename, sInfo, ourHeader, names);
@@ -2773,7 +2883,7 @@ void CBonusSelection::show( SDL_Surface * to )
 
 	if (mapName.length())
 		printAtLoc(mapName, 481, 219, FONT_BIG, tytulowy, to);
-	else 
+	else
 		printAtLoc("Unnamed", 481, 219, FONT_BIG, tytulowy, to);
 
 	//map description
@@ -2869,7 +2979,7 @@ void CBonusSelection::updateBonusSelection()
 			case 2: //building
 				{
 					int faction = -1;
-					for(std::map<int, PlayerSettings>::iterator it = sInfo.playerInfos.begin(); 
+					for(std::map<int, PlayerSettings>::iterator it = sInfo.playerInfos.begin();
 						it != sInfo.playerInfos.end(); ++it)
 					{
 						if (it->second.human)
@@ -2877,7 +2987,7 @@ void CBonusSelection::updateBonusSelection()
 							faction = it->second.castle;
 							break;
 						}
-						
+
 					}
 					assert(faction != -1);
 
@@ -3023,7 +3133,7 @@ void CBonusSelection::startMap()
 		// one of the main campaign selection screens + Bonus selection screen and
 		// deactivates the main menu
 		GH.popInts(2);
-		GH.popInt(GH.topInt()); 
+		GH.popInt(GH.topInt());
 	}
 	::startGame(si);
 }
@@ -3040,7 +3150,7 @@ void CBonusSelection::selectBonus( int id )
 		if (startB->getState() == CButtonBase::BLOCKED)
 			startB->setState(CButtonBase::NORMAL);
 	}
-	
+
 
 	const CCampaignScenario &scenario = ourCampaign->camp->scenarios[sInfo.whichMapInCampaign];
 	const std::vector<CScenarioTravel::STravelBonus> & bonDescs = scenario.travelOptions.bonusesToChoose;
@@ -3227,7 +3337,7 @@ void PlayerJoined::apply(CSelectionScreen *selScreen)
 {
 	//assert(SEL->playerNames.size() == connectionID); //temporary, TODO when player exits
 	SEL->playerNames[connectionID] = playerName;
-	
+
 	//put new player in first slot with AI
 	for(std::map<int, PlayerSettings>::iterator i = SEL->sInfo.playerInfos.begin(); i != SEL->sInfo.playerInfos.end(); i++)
 	{
@@ -3314,7 +3424,7 @@ void PlayerLeft::apply(CSelectionScreen *selScreen)
 		selScreen->setPlayer(*s, 0);
 		selScreen->opt->entries[s->color]->selectButtons();
 	}
-	
+
 	selScreen->propagateNames();
 	selScreen->propagateOptions();
 	GH.totalRedraw();
@@ -3349,228 +3459,112 @@ void StartWithCurrentSettings::apply(CSelectionScreen *selScreen)
 	throw 666; //EVIL, EVIL, EVIL workaround to kill thread (does "goto catch" outside listening loop)
 }
 
-CCampaignScreen::CCampaignScreen(CampaignSet campaigns, std::map<std::string, CampaignStatus>& camps)
+CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode &config )
 {
-	OBJ_CONSTRUCTION; // sets this as parent
-	std::string bgImage;
-	if (campaigns == ROE || campaigns == AB)
-		bgImage = "CAMPBACK.BMP";
-	else if (campaigns == SOD)
-		bgImage = "CAMPBKX2.BMP";
-	else if (campaigns == WOG)
-		bgImage = "CAMPZALL.BMP";
-
-	// Load background image
-	bg = BitmapHandler::loadBitmap(bgImage);
-	pos.x = 0;
-	pos.y = 0;
-	pos.w = bg->w;
-	pos.h = bg->h;
-	
-	// Create back button
-	back = new AdventureMapButton("", "", bind(&CGuiHandler::popIntTotally, &GH, this), 658, 482, "CMPSCAN.DEF", SDLK_ESCAPE);
-	back->hoverable = true;
+	pos.x += config["x"].Float();
+	pos.y += config["y"].Float();
+	pos.w = 200;
+	pos.h = 116;
 	
-	// Load all campaign buttons
-	// 1.index: 0 => ROE, 1 => AB, 2 => WOG
-	static const int buttonCords[7][2]  = { {90, 72} , {539, 72} , {43, 245} , {313, 244}, {586, 246}, {34, 417}, {404, 414}};
-
-	static const std::string campFiles[3][7] = { {"GOOD1", "EVIL1", "GOOD2", "NEUTRAL1", "EVIL2", "GOOD3", "SECRET"}, 
-												 { "AB", "BLOOD", "SLAYER", "FESTIVAL", "FIRE", "FOOL" },
-												 { "ZC1", "ZC2", "ZC3", "ZC4" } };
+	campFile = config["file"].String();
+	video = config["video"].String();
 
-	static const std::string campImages[3][7] = { { "CAMPGD1S.BMP", "CAMPEV1S.BMP", "CAMPGD2S.BMP", "CAMPNEUS.BMP", "CAMPEV2S.BMP", "CAMPGD3S.BMP", "CAMPSCTS.BMP" }, 
-												  { "CAMP1AB7.BMP", "CAMP1DB2.BMP", "CAMP1DS1.BMP", "CAMP1FL3.BMP", "CAMP1PF2.BMP", "CAMP1FW1.BMP" },
-												  { "CAMPZ01.BMP", "CAMPZ02.BMP", "CAMPZ03.BMP", "CAMPZ04.BMP" } };
+//On linux we can only play *.mjpg videos from LOKI release
 #ifdef _WIN32
-	static const std::string campVideos[3][7] = { { "CGOOD1.BIK", "CEVIL1.BIK", "CGOOD2.BIK", "CNEUTRAL.BIK", "CEVIL2.BIK", "CGOOD3.BIK", "CSECRET.BIK" },
-												  { "C1ab7.BIK", "C1db2.BIK", "C1ds1.BIK", "C1fl3.BIK", "C1pf2.BIK", "C1fw1.BIK" } };
+	std::transform(video.begin(), video.end(), video.begin(), toupper);
+	video += ".BIK";
 #else
-	static const std::string campVideos[3][7] = { { "cgood1.mjpg", "cevil1.mjpg", "cgood2.mjpg", "cneutral.mjpg", "cevil2.mjpg", "cgood3.mjpg", "csecret.mjpg" } };
+	std::transform(video.begin(), video.end(), video.begin(), tolower);
+	video += ".mjpg";
 #endif
-	static const std::string campTexts[3][7] = { { getMapText(0), getMapText(3), getMapText(1), getMapText(5), getMapText(4), getMapText(2), getMapText(6) },
-												 { "Armageddon's Blade", "Dragon's Blood", "Dragon Slayer", "Festival of Life", "Playing With Fire", "Foolhardy Waywardness" },
-												 { "In the Wake of Gods", "The Samaritan", "A Life of A-d-v-e-n-t-u-r-e", "Evil Way Home" } };
 
-	static const CampaignStatus campDefaults[3][7] = { { CCampaignScreen::ENABLED, CCampaignScreen::ENABLED, CCampaignScreen::DISABLED, CCampaignScreen::ENABLED, CCampaignScreen::DISABLED,
-														 CCampaignScreen::DISABLED, CCampaignScreen::DISABLED }, 
-						{ CCampaignScreen::ENABLED, CCampaignScreen::ENABLED, CCampaignScreen::ENABLED, CCampaignScreen::ENABLED, CCampaignScreen::ENABLED, CCampaignScreen::DISABLED},
-						{ CCampaignScreen::ENABLED, CCampaignScreen::ENABLED, CCampaignScreen::ENABLED, CCampaignScreen::ENABLED } };
-
-	if (campaigns == ROE)
-	{
-		createButtons(buttonCords, campFiles[0], campImages[0], campVideos[0], campTexts[0], camps, campDefaults[0]);
-		
-		if (camps[campFiles[0][6]] == 0)
-			drawCampaignPlaceholder();
-	}
-	if (campaigns == AB)
-	{
-		// Foolhardy Waywardness -- draw deactivated image
-		SDL_Surface *ab5Dis = BitmapHandler::loadBitmap("CAMP1FWX");
-		Rect ab5DisRect(buttonCords[5][0] - 2, buttonCords[5][1] - 2, ab5Dis->w, ab5Dis->h);
-		blitAt(ab5Dis, ab5DisRect, bg);
-
-		createButtons(buttonCords, campFiles[1], campImages[1], campVideos[1], campTexts[1], camps, campDefaults[1]);
-		drawCampaignPlaceholder();
-	}
-	if (campaigns == WOG)
-	{
-		createButtons(buttonCords, campFiles[2], campImages[2], campVideos[2], campTexts[2], camps, campDefaults[2]);
-		campButtons[3]->pos.x -= 2; // special rule for the 4.th campaign
-		campButtons[3]->pos.y -= 2;
-		drawCampaignPlaceholder();
-	}
-}
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
-CCampaignScreen::~CCampaignScreen()
-{
-	// Free background image and re-start WOG main menu animation
-	SDL_FreeSurface(bg);
-	if (noCamp != 0)
-		SDL_FreeSurface(noCamp);
+	status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED;
 
-	//campButtons.clear();
-	CCS->videoh->open("ACREDIT.SMK");
-}
+	CCampaignHeader header = CCampaignHandler::getHeader(campFile, true);
+	hoverText = header.name;
 
-void CCampaignScreen::createButtons(const int buttonCords[7][2], const std::string campFiles[], 
-	const std::string campImages[], const std::string campVideos[], const std::string campTexts[], std::map<std::string, CampaignStatus>& camps, const CampaignStatus campDefaults[])
-{
-	for (int i = 0; i < 7; i++)
+	if (status != CCampaignScreen::DISABLED)
 	{
-		if (campFiles[i] != "") // if it's setted in the array
-		{
-			std::string file = campFiles[i];
-			
-			CCampaignButton *button = new CCampaignButton(bg, campImages[i], buttonCords[i][0], buttonCords[i][1], camps[file] != 0 ? camps[file] : campDefaults[i]);
-			button->campFile = file;
-			button->hoverText = campTexts[i];
-			button->video = campVideos[i];
-			campButtons.push_back(button);
-		}
-	}
-}
+		used |= LCLICK | HOVER;
+		image = new CPicture(config["image"].String());
 
-std::string CCampaignScreen::getMapText(int index)
-{
-	return CGI->generaltexth->campaignMapNames[index];
-}
-
-void CCampaignScreen::drawCampaignPlaceholder()
-{
-	noCamp = BitmapHandler::loadBitmap("CAMPNOSC.BMP");
-	noCamp->w -= 2;
-	Rect noCampRect(385, 401, 238, 143);
-	blitAt(noCamp, noCampRect, bg);
-}
-
-void CCampaignScreen::show(SDL_Surface *to)
-{
-	// Draw background image and all interactive objects like buttons
-	blitAt(bg, pos.x, pos.y, to);
-	
-	for (int i = 0; i < this->campButtons.size(); i++)
-	{
-		campButtons[i]->show(to);
+		hoverLabel = new CLabel(pos.w / 2, pos.h + 20, FONT_MEDIUM, CENTER, tytulowy, "");
+		moveChild(hoverLabel, this, parent);
 	}
 
-	back->showAll(to);
+	if (status == CCampaignScreen::COMPLETED)
+		checkMark = new CPicture("CAMPCHK");
 }
 
-CCampaignScreen::CCampaignButton::CCampaignButton(SDL_Surface *bg, const std::string image, const int x, const int y, CampaignStatus status)
+void CCampaignScreen::CCampaignButton::clickLeft(tribool down, bool previousState)
 {
-	OBJ_CONSTRUCTION; // sets this as parent
-
-	// Initialize CCampaignButton members
-	this->bg = bg;
-	this->image = image;
-	this->status = status;
-
-	// Initialize pos and size
-	pos.x = x;
-	pos.y = y;
-	pos.w = 200;
-	pos.h = 116;
-
-	// Creates the button image
-	button = BitmapHandler::loadBitmap(image);
-	if (status != CCampaignScreen::DISABLED && button != 0)
+	if (down)
 	{
-		blitAt(button, pos, bg);
-		used = LCLICK | HOVER; // set these flags to activate left click and hover event functions
+		// Close running video and open the selected campaign
+		CCS->videoh->close();
+		CCampaign * ourCampaign = CCampaignHandler::getCampaign(campFile, true);
+		CCampaignState * campState = new CCampaignState();
+		campState->camp = ourCampaign;
+		GH.pushInt( new CBonusSelection(campState) );
 	}
-
-	// Create the checked image
-	if (status == CCampaignScreen::COMPLETED)
-		checked = BitmapHandler::loadBitmap("CAMPCHK.BMP");
-
-	// Create the button hover effect
-	hoverLabel = new CLabel(pos.w / 2., pos.h + 20, FONT_MEDIUM, CENTER, tytulowy, "");
-	hoverLabel->ignoreLeadingWhitespace = false;
-}
-
-CCampaignScreen::CCampaignButton::~CCampaignButton()
-{
-	if (button != 0)
-		SDL_FreeSurface(button);
-
-	if (status == CCampaignScreen::COMPLETED)
-		SDL_FreeSurface(checked);
 }
 
 void CCampaignScreen::CCampaignButton::hover(bool on)
-{	
+{
 	if (on)
 		hoverLabel->setTxt(hoverText); // Shows the name of the campaign when you get into the bounds of the button
-
 	else
-	{
-		// Deletes the text from the screen when you hover out of the bounds of the button
-		hoverLabel->setTxt(" "); 
-	}
-
+		hoverLabel->setTxt(" ");
 }
 
 void CCampaignScreen::CCampaignButton::show(SDL_Surface *to)
 {
-	hoverLabel->showAll(to);
-
-	if (status == CCampaignScreen::DISABLED || video == "" || button == 0)
+	if (status == CCampaignScreen::DISABLED)
 		return;
 
+	CIntObject::show(to);
+
 	// Play the campaign button video when the mouse cursor is placed over the button
-	if (CCS->curh->xpos >= pos.x && CCS->curh->ypos >= pos.y && CCS->curh->xpos < pos.x + pos.w && CCS->curh->ypos < pos.y + pos.h)
+	if (hovered)
 	{
 		if (CCS->videoh->fname != video)
 			CCS->videoh->open(video);
-		
+
 		CCS->videoh->update(pos.x, pos.y, to, true, false); // plays sequentially frame by frame, starts at the beginning when the video is over
 	}
 	else if (CCS->videoh->fname == video) // When you got out of the bounds of the button then close the video
 	{
 		CCS->videoh->close();
-		blitAt(button, pos, to);
+		redraw();
 	}
+}
 
+AdventureMapButton* CCampaignScreen::createExitButton(const JsonNode& button)
+{
+	std::pair<std::string, std::string> help;
+	if (!button["help"].isNull() && button["help"].Float() > 0)
+		help = CGI->generaltexth->zelp[button["help"].Float()];
 
-	if (status == CCampaignScreen::COMPLETED) // Draw a checked symbol when you completed the mission
-	{
-		Rect chkRect(pos.x + 5, pos.y + 74, 42, 40);
-		blitAt(checked, chkRect, to); // this is needed because the video gets drawn on top of the screen
-		blitAt(checked, chkRect, bg); // this is needed because of the totalRedraw at the hover method
-	}
+	boost::function<void()> close = boost::bind(&CGuiHandler::popIntTotally, &GH, this);
+	return new AdventureMapButton(help, close, button["x"].Float(), button["y"].Float(), button["name"].String(), button["hotkey"].Float());
 }
 
-void CCampaignScreen::CCampaignButton::clickLeft(tribool down, bool previousState)
+
+CCampaignScreen::CCampaignScreen(const JsonNode &config)
 {
-	if (down)
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	BOOST_FOREACH(const JsonNode& node, config["images"].Vector())
+		images.push_back(createPicture(node));
+
+	if (!config["exitbutton"].isNull())
 	{
-		// Close running video and open the selected campaign
-		CCS->videoh->close();
-		CCampaign * ourCampaign = CCampaignHandler::getCampaign(campFile, true);
-		CCampaignState * campState = new CCampaignState();
-		campState->camp = ourCampaign;
-		GH.pushInt( new CBonusSelection(campState) );
+		back = createExitButton(config["exitbutton"]);
+		back->hoverable = true;
 	}
+	
+	BOOST_FOREACH(const JsonNode& node, config["items"].Vector())
+		campButtons.push_back(new CCampaignButton(node));
 }

+ 75 - 54
client/CPreGame.h

@@ -28,6 +28,7 @@ class CGStatusBar;
 class CTextBox;
 class CCampaignState;
 class CConnection;
+class JsonNode;
 struct CPackForSelectionScreen;
 struct PlayerInfo;
 struct FileInfo;
@@ -48,7 +49,16 @@ public:
 /// The main menu screens listed in the EState enum
 class CMenuScreen : public CIntObject
 {
+	const JsonNode& config;
+
+	CTabbedInt *tabs;
+
+	std::vector<CPicture*> images;
+
+	CIntObject *createTab(size_t index);
 public:
+	std::vector<std::string> menuNameToEntry;
+
 	enum EState { //where are we?
 		mainMenu, newGame, loadGame, campaignMain, saveGame, scenarioInfo, campaignList
 	};
@@ -56,17 +66,36 @@ public:
 	enum EMultiMode {
 		SINGLE_PLAYER = 0, MULTI_HOT_SEAT, MULTI_NETWORK_HOST, MULTI_NETWORK_GUEST
 	};
+	CMenuScreen(const JsonNode& configNode);
 
-	CPicture *bgAd;
-	AdventureMapButton *buttons[5];
-
-	CMenuScreen(EState which);
-	~CMenuScreen();
-	void showAll(SDL_Surface * to);
 	void show(SDL_Surface * to);
-	void moveTo(CMenuScreen *next);
+	void activate();
+	void deactivate();
+
+	void switchToTab(size_t index);
+};
+
+class CMenuEntry : public CIntObject
+{
+	std::vector<CPicture*> images;
+	std::vector<AdventureMapButton*> buttons;
+
+	AdventureMapButton* createButton(CMenuScreen* parent, const JsonNode& button);
+public:
+	CMenuEntry(CMenuScreen* parent, const JsonNode &config);
 };
 
+class CreditsScreen : public CIntObject
+{
+	CTextBox* credits;
+public:
+	CreditsScreen();
+
+	void show(SDL_Surface *to);
+
+	void clickLeft(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState);
+};
 
 /// Implementation of the chat box
 class CChatBox : public CIntObject
@@ -84,8 +113,8 @@ public:
 
 class InfoCard : public CIntObject
 {
-	CPicture *bg; 
 public:
+	CPicture *bg;
 	CMenuScreen::EState type;
 
 	bool network;
@@ -117,7 +146,7 @@ private:
 	void parseGames(std::vector<FileInfo> &files, bool multi);
 	void parseCampaigns( std::vector<FileInfo> & files );
 	void getFiles(std::vector<FileInfo> &out, const std::string &dirname, const std::string &ext);
-	CMenuScreen::EState tabType; 
+	CMenuScreen::EState tabType;
 public:
 	int positions; //how many entries (games/maps) can be shown
 	CPicture *bg; //general bg image
@@ -132,7 +161,7 @@ public:
 
 	CTextInput *txt;
 
-	
+
 	void filter(int size, bool selectFirst = false); //0 - all
 	void select(int position); //position: <0 - positions>  position on the screen
 	void selectAbs(int position); //position: absolute position in curItems vector
@@ -182,7 +211,7 @@ public:
 		SelectedBox *hero;
 		SelectedBox *bonus;
 		enum {HUMAN_OR_CPU, HUMAN, CPU} whoCanPlay;
-		
+
 		PlayerOptionsEntry(OptionsTab *owner, PlayerSettings &S);
 		void selectButtons(); //hides unavailable buttons
 		void showAll(SDL_Surface * to);
@@ -194,7 +223,7 @@ public:
 
 	struct PlayerToRestore
 	{
-		int color, id; 
+		int color, id;
 		void reset() { color = id = -1; }
 		PlayerToRestore(){ reset(); }
 	} playerToRestore;
@@ -287,7 +316,7 @@ public:
 class CSavingScreen : public CSelectionScreen
 {
 public:
-	const CMapInfo *ourGame; 
+	const CMapInfo *ourGame;
 
 
 	CSavingScreen(bool hotseat = false);
@@ -414,58 +443,49 @@ public:
 /// Campaign selection screen
 class CCampaignScreen : public CIntObject
 {
-	public:
-		enum CampaignStatus {DEFAULT = 0, ENABLED, DISABLED, COMPLETED}; // the status of the campaign
-	
+public:
+	enum CampaignStatus {DEFAULT = 0, ENABLED, DISABLED, COMPLETED}; // the status of the campaign
+
+private:
+	/// A button which plays a video when you move the mouse cursor over it
+	class CCampaignButton : public CIntObject
+	{
 	private:
-		SDL_Surface *bg; // background image
-		SDL_Surface *noCamp; // no campaign placeholder
-		AdventureMapButton *back; // back button
+		CPicture *image;
+		CPicture *checkMark;
 
-		/// A button which plays a video when you move the mouse cursor over it
-		class CCampaignButton : public CIntObject
-		{
-		private:
-			std::string image;
-			SDL_Surface *bg;
-			SDL_Surface *button;
-			SDL_Surface *checked;
-			CLabel *hoverLabel; 
-			CampaignStatus status;
-
-			void clickLeft(tribool down, bool previousState);
-			void hover(bool on);
-
-		public:
-			std::string campFile; // the filename/resourcename of the campaign
-			std::string video; // the resource name of the video
-			std::string hoverText; // the text which gets shown when you move the mouse cursor over the button
-
-			CCampaignButton(SDL_Surface *bg, const std::string image, const int x, const int y, CampaignStatus status); // c-tor
-			~CCampaignButton(); // d-tor
-			void show(SDL_Surface *to);
-		};
+		CLabel *hoverLabel;
+		CampaignStatus status;
 
-		std::vector<CCampaignButton*> campButtons; // a container which holds all buttons where you can start a campaign
-		
-		void drawCampaignPlaceholder(); // draws the no campaign placeholder at the lower right position
-		std::string getMapText(int index);
-		void createButtons(const int buttonCords[7][2], const std::string campFiles[], 
-			const std::string campImages[], const std::string campVideos[], const std::string campTexts[], std::map<std::string, CampaignStatus>& camps, const CampaignStatus campDefaults[]);
+		std::string campFile; // the filename/resourcename of the campaign
+		std::string video; // the resource name of the video
+		std::string hoverText;
+
+		void clickLeft(tribool down, bool previousState);
+		void hover(bool on);
+
+	public:
+		CCampaignButton(const JsonNode &config );
+		void show(SDL_Surface *to);
+	};
+
+	AdventureMapButton *back;
+	std::vector<CCampaignButton*> campButtons;
+	std::vector<CPicture*> images;
+
+	AdventureMapButton* createExitButton(const JsonNode& button);
 public:
 	enum CampaignSet {ROE, AB, SOD, WOG};
 
-	CCampaignScreen(CampaignSet campaigns, std::map<std::string, CampaignStatus>& camps);
-	~CCampaignScreen();
-	void show(SDL_Surface *to);
+	CCampaignScreen(const JsonNode &config);
 };
 
 /// Handles background screen, loads graphics for victory/loss condition and random town or hero selection
 class CGPreGame : public CIntObject, public IUpdateable
 {
+	const JsonNode * const pregameConfig;
 public:
-	SDL_Surface *mainbg;
-	CMenuScreen *scrs[4];
+	CMenuScreen* menu;
 
 	SDL_Surface *nHero, *rHero, *nTown, *rTown; // none/random hero/town imgs
 	CDefHandler *bonuses;
@@ -475,10 +495,11 @@ public:
 	~CGPreGame();
 	void update();
 	void openSel(CMenuScreen::EState type, CMenuScreen::EMultiMode multi = CMenuScreen::SINGLE_PLAYER);
-	void openCampaignScreen(CCampaignScreen::CampaignSet campaigns);
 
 	void loadGraphics();
 	void disposeGraphics();
+
+	void openCampaignScreen(std::string name);
 };
 
 extern CGPreGame *CGP;

+ 4 - 2
client/FunctionList.h

@@ -61,7 +61,8 @@ public:
 		std::vector<boost::function<Signature> > funcs2 = funcs; //backup
 		for(size_t i=0;i<funcs2.size(); ++i) 
 		{
-			funcs2[i]();
+			if (funcs2[i])
+				funcs2[i]();
 		}
 	}
 	template <typename Arg> 
@@ -70,7 +71,8 @@ public:
 		std::vector<boost::function<Signature> > funcs2 = funcs; //backup
 		for(int i=0;i<funcs2.size(); i++) 
 		{
-			funcs2[i](a);
+			if (funcs2[i])
+				funcs2[i](a);
 		}
 	}
 };

+ 11 - 4
client/GUIBase.cpp

@@ -980,11 +980,18 @@ SetCaptureState::~SetCaptureState()
 	GH.defActionsDef = prevActions;
 }
 
-void IShowable::redraw()
+void CIntObject::redraw()
 {
-	showAll(screenBuf);
-	if(screenBuf != screen)
-		showAll(screen);
+	if (parent && (type & REDRAW_PARENT))
+	{
+		parent->redraw();
+	}
+	else
+	{
+		showAll(screenBuf);
+		if(screenBuf != screen)
+			showAll(screen);
+	}
 }
 
 SDLKey arrowToNum( SDLKey key )

+ 4 - 2
client/GUIBase.h

@@ -300,7 +300,7 @@ struct Rect : public SDL_Rect
 class IShowable
 {
 public:
-	void redraw();
+	virtual void redraw()=0;
 	virtual void show(SDL_Surface * to)=0;
 	virtual void showAll(SDL_Surface * to)
 	{
@@ -333,7 +333,8 @@ public:
 class IShowActivable : public IShowable, public IActivable
 {
 public:
-	enum {WITH_GARRISON = 1, BLOCK_ADV_HOTKEYS = 2, WITH_ARTIFACTS = 4};
+	//redraw parent flag - this int may be semi-transparent and require redraw of parent window
+	enum {WITH_GARRISON = 1, BLOCK_ADV_HOTKEYS = 2, WITH_ARTIFACTS = 4, REDRAW_PARENT=8};
 	int type; //bin flags using etype
 	IShowActivable();
 	virtual ~IShowActivable(){}; //d-tor
@@ -426,6 +427,7 @@ public:
 	void deactivate(ui16 what);
 	void show(SDL_Surface * to);
 	void showAll(SDL_Surface * to);
+	void redraw();
 
 	void drawBorderLoc(SDL_Surface * sur, const Rect &r, const int3 &color);
 	void printAtLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst);

+ 208 - 213
client/GUIClasses.cpp

@@ -391,22 +391,14 @@ void CGarrisonInt::createSet(std::vector<CGarrisonSlot*> &ret, const CCreatureSe
 void CGarrisonInt::createSlots()
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	int h, w; //height and width of slot
-	if(smallIcons)
-	{
-		h = w = 32;
-	}
-	else
-	{
-		h = 64;
-		w = 58;
-	}
+
+	int width = smallIcons? 32 : 58;
 
 	if(armedObjs[0])
-		createSet(slotsUp, armedObjs[0], 0, 0, w+interx, 0);
+		createSet(slotsUp, armedObjs[0], 0, 0, width+interx, 0);
 
 	if(armedObjs[1])
-		createSet (slotsDown, armedObjs[1], garOffset.x, garOffset.y, w+interx, 1);
+		createSet (slotsDown, armedObjs[1], garOffset.x, garOffset.y, width+interx, 1);
 }
 
 void CGarrisonInt::deleteSlots()
@@ -958,6 +950,186 @@ void CSelectableComponent::show(SDL_Surface * to)
 	printAtMiddleWB(subtitle,pos.x+pos.w/2,pos.y+pos.h+25,FONT_SMALL,12,zwykly,to);
 }
 
+static void intDeleter(CIntObject* object)
+{
+	delete object;
+}
+
+CObjectList::CObjectList(CreateFunc create, DestroyFunc destroy):
+	createObject(create),
+	destroyObject(destroy)
+{
+	if (!destroyObject)
+		destroyObject = intDeleter;
+}
+
+void CObjectList::deleteItem(CIntObject* item)
+{
+	if (!item)
+		return;
+	if (active)
+		item->deactivate();
+	removeChild(item);
+	destroyObject(item);
+}
+
+CIntObject* CObjectList::createItem(size_t index)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	CIntObject * item = createObject(index);
+	if (item == NULL)
+		item = new CIntObject();
+
+	item->recActions = defActions;
+
+	//May happen if object was created before call to getObject()
+	if(item->parent != this)
+	{
+		if (item->parent)
+			moveChild(item, item->parent, this);
+		else
+			addChild(item);
+	}
+
+	if (item && active)
+		item->activate();
+	return item;
+}
+
+CTabbedInt::CTabbedInt(CreateFunc create, DestroyFunc destroy, Point position, size_t ActiveID):
+	CObjectList(create, destroy),
+	activeTab(NULL),
+	activeID(ActiveID)
+{
+	pos += position;
+	reset();
+}
+
+void CTabbedInt::setActive(size_t which)
+{
+	if (which != activeID)
+	{
+		activeID = which;
+		reset();
+	}
+}
+
+void CTabbedInt::reset()
+{
+	deleteItem(activeTab);
+	activeTab = createItem(activeID);
+	activeTab->moveTo(pos.topLeft());
+
+	if (active)
+		redraw();
+}
+
+CIntObject * CTabbedInt::getItem()
+{
+	return activeTab;
+}
+
+CListBox::CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
+                   size_t TotalSize, size_t InitialPos, int Slider, Rect SliderPos):
+	CObjectList(create, destroy),
+	first(InitialPos),
+	totalSize(TotalSize),
+	itemOffset(ItemOffset)
+{
+	pos += Pos;
+	items.resize(VisibleSize, NULL);
+
+	if (Slider & 1)
+	{
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		slider = new CSlider(SliderPos.x, SliderPos.y, SliderPos.w, boost::bind(&CListBox::moveToPos, this, _1),
+		                     VisibleSize, TotalSize, InitialPos, Slider & 2, Slider & 4);
+	}
+	reset();
+}
+
+// Used to move active items after changing list position
+void CListBox::updatePositions()
+{
+	Point itemPos = pos.topLeft();
+	for (std::list<CIntObject*>::iterator it = items.begin(); it!=items.end(); it++)
+	{
+		(*it)->moveTo(itemPos);
+		itemPos += itemOffset;
+	}
+	if (active)
+	{
+		redraw();
+		if (slider)
+			slider->moveTo(first);
+	}
+}
+
+void CListBox::reset()
+{
+	size_t current = first;
+	for (std::list<CIntObject*>::iterator it = items.begin(); it!=items.end(); it++)
+	{
+		deleteItem(*it);
+		*it = createItem(current++);
+	}
+	updatePositions();
+}
+
+void CListBox::moveToPos(size_t which)
+{
+	//Calculate new position
+	size_t maxPossible;
+	if (totalSize > items.size())
+		maxPossible = totalSize - items.size();
+	else
+		maxPossible = 0;
+
+	size_t newPos = std::min(which, maxPossible);
+
+	//If move distance is 1 (most of calls from Slider) - use faster shifts instead of resetting all items
+	if (first - newPos == 1)
+		moveToPrev();
+	else if (newPos - first == 1)
+		moveToNext();
+	else if (newPos != first)
+	{
+		first = newPos;
+		reset();
+	}
+}
+
+void CListBox::moveToNext()
+{
+	//Remove front item and insert new one to end
+	if (first + items.size() < totalSize)
+	{
+		first++;
+		deleteItem(items.front());
+		items.pop_front();
+		items.push_back(createItem(first+items.size()));
+		updatePositions();
+	}
+}
+
+void CListBox::moveToPrev()
+{
+	//Remove last item and insert new one at start
+	if (first)
+	{
+		first--;
+		deleteItem(items.back());
+		items.pop_back();
+		items.push_front(createItem(first));
+		updatePositions();
+	}
+}
+
+std::list<CIntObject*> CListBox::getItems()
+{
+	return items;
+}
+
 void CSimpleWindow::show(SDL_Surface * to)
 {
 	if(bitmap)
@@ -3900,6 +4072,7 @@ void CAltarWindow::moveFromSlotToAltar(int slotID, CTradeableItem* altarSlot, co
 
 CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface * owner)
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	this->pos = pos;
 	SDL_Surface *hhlp = BitmapHandler::loadBitmap("SysOpbck.bmp", true);
 	graphics->blueToPlayersAdv(hhlp,LOCPLINT->playerID);
@@ -3927,44 +4100,44 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface
 	// load = new AdventureMapButton (CGI->generaltexth->zelp[321].first, CGI->generaltexth->zelp[321].second, boost::bind(&CSystemOptionsWindow::loadf, this), pos.x+246, pos.y+298, "SOLOAD.DEF", SDLK_l);
 	// std::swap(save->imgs[0][0], load->imgs[0][1]);
 
-	save = new AdventureMapButton (CGI->generaltexth->zelp[322].first, CGI->generaltexth->zelp[322].second, boost::bind(&CSystemOptionsWindow::bsavef, this), pos.x+357, pos.y+298, "SOSAVE.DEF", SDLK_s);
+	save = new AdventureMapButton (CGI->generaltexth->zelp[322].first, CGI->generaltexth->zelp[322].second, boost::bind(&CSystemOptionsWindow::bsavef, this), 357, 298, "SOSAVE.DEF", SDLK_s);
 	save->swappedImages = true;
 	save->update();
 
 	// restart = new AdventureMapButton (CGI->generaltexth->zelp[323].first, CGI->generaltexth->zelp[323].second, boost::bind(&CSystemOptionsWindow::bmainmenuf, this), pos.x+346, pos.y+357, "SORSTRT", SDLK_r);
 	// std::swap(save->imgs[0][0], restart->imgs[0][1]);
 
-	mainMenu = new AdventureMapButton (CGI->generaltexth->zelp[320].first, CGI->generaltexth->zelp[320].second, boost::bind(&CSystemOptionsWindow::bmainmenuf, this), pos.x+357, pos.y+357, "SOMAIN.DEF", SDLK_m);
+	mainMenu = new AdventureMapButton (CGI->generaltexth->zelp[320].first, CGI->generaltexth->zelp[320].second, boost::bind(&CSystemOptionsWindow::bmainmenuf, this), 357, 357, "SOMAIN.DEF", SDLK_m);
 	mainMenu->swappedImages = true;
 	mainMenu->update();
 
-	quitGame = new AdventureMapButton (CGI->generaltexth->zelp[324].first, CGI->generaltexth->zelp[324].second, boost::bind(&CSystemOptionsWindow::bquitf, this), pos.x+246, pos.y+415, "soquit.def", SDLK_q);
+	quitGame = new AdventureMapButton (CGI->generaltexth->zelp[324].first, CGI->generaltexth->zelp[324].second, boost::bind(&CSystemOptionsWindow::bquitf, this), 246, 415, "soquit.def", SDLK_q);
 	quitGame->swappedImages = true;
 	quitGame->update();
-	backToMap = new AdventureMapButton (CGI->generaltexth->zelp[325].first, CGI->generaltexth->zelp[325].second, boost::bind(&CSystemOptionsWindow::breturnf, this), pos.x+357, pos.y+415, "soretrn.def", SDLK_RETURN);
+	backToMap = new AdventureMapButton (CGI->generaltexth->zelp[325].first, CGI->generaltexth->zelp[325].second, boost::bind(&CSystemOptionsWindow::breturnf, this), 357, 415, "soretrn.def", SDLK_RETURN);
 	backToMap->swappedImages = true;
 	backToMap->update();
 	backToMap->assignedKeys.insert(SDLK_ESCAPE);
 
 	heroMoveSpeed = new CHighlightableButtonsGroup(0);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[349].second),CGI->generaltexth->zelp[349].second, "sysopb1.def", pos.x+28, pos.y+77, 1);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[350].second),CGI->generaltexth->zelp[350].second, "sysopb2.def", pos.x+76, pos.y+77, 2);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[351].second),CGI->generaltexth->zelp[351].second, "sysopb3.def", pos.x+124, pos.y+77, 4);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[352].second),CGI->generaltexth->zelp[352].second, "sysopb4.def", pos.x+172, pos.y+77, 8);
+	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[349].second),CGI->generaltexth->zelp[349].second, "sysopb1.def", 28, 77, 1);
+	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[350].second),CGI->generaltexth->zelp[350].second, "sysopb2.def", 76, 77, 2);
+	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[351].second),CGI->generaltexth->zelp[351].second, "sysopb3.def", 124, 77, 4);
+	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[352].second),CGI->generaltexth->zelp[352].second, "sysopb4.def", 172, 77, 8);
 	heroMoveSpeed->select(owner->sysOpts.heroMoveSpeed, 1);
 	heroMoveSpeed->onChange = boost::bind(&SystemOptions::setHeroMoveSpeed, &owner->sysOpts, _1);
 
 	mapScrollSpeed = new CHighlightableButtonsGroup(0);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[357].second),CGI->generaltexth->zelp[357].second, "sysopb9.def", pos.x+28, pos.y+210, 1);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[358].second),CGI->generaltexth->zelp[358].second, "sysob10.def", pos.x+92, pos.y+210, 2);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[359].second),CGI->generaltexth->zelp[359].second, "sysob11.def", pos.x+156, pos.y+210, 4);
+	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[357].second),CGI->generaltexth->zelp[357].second, "sysopb9.def", 28, 210, 1);
+	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[358].second),CGI->generaltexth->zelp[358].second, "sysob10.def", 92, 210, 2);
+	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[359].second),CGI->generaltexth->zelp[359].second, "sysob11.def", 156, 210, 4);
 	mapScrollSpeed->select(owner->sysOpts.mapScrollingSpeed, 1);
 	mapScrollSpeed->onChange = boost::bind(&SystemOptions::setMapScrollingSpeed, &owner->sysOpts, _1);
 
 	musicVolume = new CHighlightableButtonsGroup(0, true);
 	for(int i=0; i<10; ++i)
 	{
-		musicVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[326+i].second),CGI->generaltexth->zelp[326+i].second, "syslb.def", pos.x+29 + 19*i, pos.y+359, i*11);
+		musicVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[326+i].second),CGI->generaltexth->zelp[326+i].second, "syslb.def", 29 + 19*i, 359, i*11);
 	}
 	musicVolume->select(CCS->musich->getVolume(), 1);
 	musicVolume->onChange = boost::bind(&SystemOptions::setMusicVolume, &owner->sysOpts, _1);
@@ -3972,7 +4145,7 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface
 	effectsVolume = new CHighlightableButtonsGroup(0, true);
 	for(int i=0; i<10; ++i)
 	{
-		effectsVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[336+i].second),CGI->generaltexth->zelp[336+i].second, "syslb.def", pos.x+29 + 19*i, pos.y+425, i*11);
+		effectsVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[336+i].second),CGI->generaltexth->zelp[336+i].second, "syslb.def", 29 + 19*i, 425, i*11);
 	}
 	effectsVolume->select(CCS->soundh->getVolume(), 1);
 	effectsVolume->onChange = boost::bind(&SystemOptions::setSoundVolume, &owner->sysOpts, _1);
@@ -3981,15 +4154,6 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface
 CSystemOptionsWindow::~CSystemOptionsWindow()
 {
 	SDL_FreeSurface(background);
-
-	delete save;
-	delete quitGame;
-	delete backToMap;
-	delete mainMenu;
-	delete heroMoveSpeed;
-	delete mapScrollSpeed;
-	delete musicVolume;
-	delete effectsVolume;
 }
 
 void CSystemOptionsWindow::pushSDLEvent(int type, int usercode)
@@ -4030,42 +4194,11 @@ void CSystemOptionsWindow::bsavef()
 	LOCPLINT->showYesNoDialog("Do you want to save current game as " + fname, std::vector<SComponent*>(), boost::bind(&CCallback::save, LOCPLINT->cb, fname), boost::bind(&CSystemOptionsWindow::activate, this), false);*/
 }
 
-void CSystemOptionsWindow::activate()
-{
-	save->activate();
-	quitGame->activate();
-	backToMap->activate();
-	mainMenu->activate();
-	heroMoveSpeed->activate();
-	mapScrollSpeed->activate();
-	musicVolume->activate();
-	effectsVolume->activate();
-}
-
-void CSystemOptionsWindow::deactivate()
-{
-	save->deactivate();
-	quitGame->deactivate();
-	backToMap->deactivate();
-	mainMenu->deactivate();
-	heroMoveSpeed->deactivate();
-	mapScrollSpeed->deactivate();
-	musicVolume->deactivate();
-	effectsVolume->deactivate();
-}
-
-void CSystemOptionsWindow::show(SDL_Surface *to)
+void CSystemOptionsWindow::showAll(SDL_Surface *to)
 {
 	CSDL_Ext::blitSurface(background, NULL, to, &pos);
 
-	save->showAll(to);
-	quitGame->showAll(to);
-	backToMap->showAll(to);
-	mainMenu->showAll(to);
-	heroMoveSpeed->showAll(to);
-	mapScrollSpeed->showAll(to);
-	musicVolume->showAll(to);
-	effectsVolume->showAll(to);
+	CIntObject::showAll(to);
 }
 
 CTavernWindow::CTavernWindow(const CGObjectInstance *TavernObj)
@@ -5518,113 +5651,15 @@ void CExchangeWindow::close()
 	GH.popIntTotally(this);
 }
 
-void CExchangeWindow::activate()
-{
-	quit->activate();
-	garr->activate();
-
-	//artifs[0]->setHero(heroInst[0]);
-	artifs[0]->activate();
-	//artifs[1]->setHero(heroInst[1]);
-	artifs[1]->activate();
-
-	for(int g=0; g<ARRAY_COUNT(secSkillAreas); g++)
-	{
-		for(int b=0; b<secSkillAreas[g].size(); ++b)
-		{
-			secSkillAreas[g][b]->activate();
-		}
-	}
-
-	for(int b=0; b<primSkillAreas.size(); ++b)
-		primSkillAreas[b]->activate();
-
-	GH.statusbar = ourBar;
-
-	for(int g=0; g<ARRAY_COUNT(questlogButton); g++)
-		questlogButton[g]->activate();
-
-	for(int g=0; g<ARRAY_COUNT(morale); g++)
-		morale[g]->activate();
-
-	for(int g=0; g<ARRAY_COUNT(luck); g++)
-		luck[g]->activate();
-
-	for(int g=0; g<ARRAY_COUNT(portrait); g++)
-		portrait[g]->activate();
-
-	for(int g=0; g<ARRAY_COUNT(spellPoints); g++)
-		spellPoints[g]->activate();
-
-	for(int g=0; g<ARRAY_COUNT(experience); g++)
-		experience[g]->activate();
-
-	for(int g=0; g<ARRAY_COUNT(speciality); g++)
-		speciality[g]->activate();
-}
-
-void CExchangeWindow::deactivate()
-{
-	quit->deactivate();
-	garr->deactivate();
-
-	artifs[0]->deactivate();
-	artifs[1]->deactivate();
-
-	for(int g=0; g<ARRAY_COUNT(secSkillAreas); g++)
-	{
-		for(int b=0; b<secSkillAreas[g].size(); ++b)
-		{
-			secSkillAreas[g][b]->deactivate();
-		}
-	}
-
-	for(int b=0; b<primSkillAreas.size(); ++b)
-		primSkillAreas[b]->deactivate();
-
-	for(int g=0; g<ARRAY_COUNT(questlogButton); g++)
-		questlogButton[g]->deactivate();
-
-	for(int g=0; g<ARRAY_COUNT(morale); g++)
-		morale[g]->deactivate();
-
-	for(int g=0; g<ARRAY_COUNT(luck); g++)
-		luck[g]->deactivate();
-
-	for(int g=0; g<ARRAY_COUNT(portrait); g++)
-		portrait[g]->deactivate();
-
-	for(int g=0; g<ARRAY_COUNT(spellPoints); g++)
-		spellPoints[g]->deactivate();
-
-	for(int g=0; g<ARRAY_COUNT(experience); g++)
-		experience[g]->deactivate();
-
-	for(int g=0; g<ARRAY_COUNT(speciality); g++)
-		speciality[g]->deactivate();
-}
-
-void CExchangeWindow::show(SDL_Surface * to)
+void CExchangeWindow::showAll(SDL_Surface * to)
 {
 	blitAt(bg, pos, to);
 
-	quit->showAll(to);
+	CIntObject::showAll(to);
 
 	//printing border around window
 	if(screen->w != 800 || screen->h !=600)
 		CMessage::drawBorder(LOCPLINT->playerID,to,828,628,pos.x-14,pos.y-15);
-
-	artifs[0]->showAll(to);
-	artifs[1]->showAll(to);
-
-	ourBar->showAll(to);
-
-	for(int g=0; g<ARRAY_COUNT(secSkillAreas); g++)
-	{
-		questlogButton[g]->showAll(to);//FIXME: for array count(secondary skill) show quest log button? WTF?
-	}
-
-	garr->showAll(to);
 }
 
 void CExchangeWindow::questlog(int whichHero)
@@ -5705,6 +5740,7 @@ void CExchangeWindow::prepareBackground()
 
 CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) : bg(NULL)
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	char bufor[400];
 	heroInst[0] = LOCPLINT->cb->getHero(hero1);
 	heroInst[1] = LOCPLINT->cb->getHero(hero2);
@@ -5803,61 +5839,20 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) : bg(NULL)
 	//garrison interface
 	garr = new CGarrisonInt(pos.x + 69, pos.y + 131, 4, Point(418,0), bg, Point(69,131), heroInst[0],heroInst[1], true, true);
 
-	garr->addSplitBtn(new AdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),pos.x+10,pos.y+132,"TSBTNS.DEF"));
-	garr->addSplitBtn(new AdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),pos.x+740,pos.y+132,"TSBTNS.DEF"));
+	{
+		BLOCK_CAPTURING;
+		garr->addSplitBtn(new AdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),pos.x+10,pos.y+132,"TSBTNS.DEF"));
+		garr->addSplitBtn(new AdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),pos.x+740,pos.y+132,"TSBTNS.DEF"));
+	}
 }
 
 CExchangeWindow::~CExchangeWindow() //d-tor
 {
 	SDL_FreeSurface(bg);
-	delete quit;
 
-	//warning: don't experiment with these =NULL lines, they prevent heap corruption!
 	delete artifs[0]->commonInfo;
 	artifs[0]->commonInfo = NULL;
-	delete artifs[0];
 	artifs[1]->commonInfo = NULL;
-	delete artifs[1];
-
-	delete garr;
-	delete ourBar;
-
-	for(int g=0; g<ARRAY_COUNT(secSkillAreas); g++)
-	{
-		for(int b=0; b<secSkillAreas[g].size(); ++b)
-		{
-			delete secSkillAreas[g][b];
-		}
-	}
-
-	for(int b=0; b<primSkillAreas.size(); ++b)
-	{
-		delete primSkillAreas[b];
-	}
-
-	
-	for(int g=0; g<ARRAY_COUNT(questlogButton); g++)
-	{
-		delete questlogButton[g];
-	}
-
-	for(int g=0; g<ARRAY_COUNT(morale); g++)
-		delete morale[g];
-
-	for(int g=0; g<ARRAY_COUNT(luck); g++)
-		delete luck[g];
-
-	for(int g=0; g<ARRAY_COUNT(portrait); g++)
-		delete portrait[g];
-
-	for(int g=0; g<ARRAY_COUNT(spellPoints); g++)
-		delete spellPoints[g];
-
-	for(int g=0; g<ARRAY_COUNT(experience); g++)
-		delete experience[g];
-
-	for(int g=0; g<ARRAY_COUNT(speciality); g++)
-		delete speciality[g];
 }
 
 CShipyardWindow::CShipyardWindow(const std::vector<si32> &cost, int state, int boatType, const boost::function<void()> &onBuy)

+ 83 - 6
client/GUIClasses.h

@@ -209,6 +209,87 @@ public:
 	void deactivate();
 	void select(bool on);
 };
+
+////////////////////////////////////////////////////////////////////////////////
+
+/// Used as base for Tabs and List classes
+class CObjectList : public CIntObject
+{
+public:
+	typedef boost::function<CIntObject* (size_t)> CreateFunc;
+	typedef boost::function<void(CIntObject *)> DestroyFunc;
+
+private:
+	CreateFunc createObject;
+	DestroyFunc destroyObject;
+
+protected:
+	//Internal methods for safe creation of items (Children capturing and activation/deactivation if needed)
+	void deleteItem(CIntObject* item);
+	CIntObject* createItem(size_t index);
+	
+	CObjectList(CreateFunc create, DestroyFunc destroy = DestroyFunc());//Protected constructor
+};
+
+/// Window element with multiple tabs
+class CTabbedInt : public CObjectList
+{
+private:
+	CIntObject * activeTab;
+	size_t activeID;
+
+public:
+	//CreateFunc, DestroyFunc - see CObjectList
+	//Pos - position of object, all tabs will be moved to this position
+	//ActiveID - ID of initially active tab
+	CTabbedInt(CreateFunc create, DestroyFunc destroy = DestroyFunc(), Point position=Point(), size_t ActiveID=0);
+
+	void setActive(size_t which);
+	//recreate active tab
+	void reset();
+
+	//return currently active item
+	CIntObject * getItem();
+};
+
+/// List of IntObjects with optional slider
+class CListBox : public CObjectList
+{
+private:
+	std::list< CIntObject* > items;
+	size_t first;
+	size_t totalSize;
+
+	Point itemOffset;
+	CSlider * slider;
+
+	void updatePositions();
+public:
+	//CreateFunc, DestroyFunc - see CObjectList
+	//Pos - position of first item
+	//ItemOffset - distance between items in the list
+	//VisibleSize - maximal number of displayable at once items
+	//TotalSize 
+	//Slider - slider style, bit field: 1 = present(disabled), 2=horisontal(vertical), 4=blue(brown)
+	//SliderPos - position of slider, if present
+	CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
+	         size_t TotalSize, size_t InitialPos=0, int Slider=0, Rect SliderPos=Rect() );
+
+	//recreate all visible items
+	void reset();
+
+	//return currently active items
+	std::list< CIntObject * > getItems();
+
+	//scroll list
+	void moveToPos(size_t which);
+	void moveToNext();
+	void moveToPrev();
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+
 class CGarrisonInt;
 
 /// A single garrison slot which holds one creature of a specific amount
@@ -798,9 +879,7 @@ public:
 
 	void pushSDLEvent(int type, int usercode);
 
-	void activate();
-	void deactivate();
-	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
 };
 
 class CTavernWindow : public CIntObject
@@ -1104,9 +1183,7 @@ public:
 	CArtifactsOfHero * artifs[2];
 
 	void close();
-	void activate();
-	void deactivate();
-	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
 
 	void questlog(int whichHero); //questlog button callback; whichHero: 0 - left, 1 - right
 

+ 125 - 0
config/mainmenu.json

@@ -0,0 +1,125 @@
+{
+	//Main menu window, consists of several sub-menus aka items
+	"window":
+	{
+		"video" :    {"x": 8, "y": 105, "name":"ACREDIT.SMK" },//Floating WoG logo
+		"images" : [ {"x": 0, "y": 0, "name":"ZPIC1005"} ],//Background image
+		"items" : 
+		[
+			{
+				"name" : "main",
+				"buttons":
+				[
+					{"x": 540, "y": 10,  "name":"ZMENUNG", "hotkey" : 110, "help": 3, "command": "to new"},
+					{"x": 532, "y": 132, "name":"ZMENULG", "hotkey" : 108, "help": 4, "command": "to load"},
+					{"x": 524, "y": 251, "name":"ZMENUHS", "hotkey" : 104, "help": 5, "command": "highscores"},
+					{"x": 557, "y": 359, "name":"ZMENUCR", "hotkey" : 99,  "help": 6, "command": "to credits"},
+					{"x": 586, "y": 468, "name":"ZMENUQT", "hotkey" : 27,  "help": 7, "command": "exit"}
+				]
+			},
+			{
+				"name" : "new",
+				"buttons":
+				[
+					{"x": 545, "y": 4,   "name":"ZTSINGL", "hotkey" : 115, "help": 10, "command": "start single"},
+					{"x": 568, "y": 120, "name":"ZTMULTI", "hotkey" : 109, "help": 12, "command": "start multi"},
+					{"x": 541, "y": 233, "name":"ZTCAMPN", "hotkey" : 99,  "help": 11, "command": "to campaign"},
+					{"x": 545, "y": 358, "name":"ZTTUTOR", "hotkey" : 116, "help": 13, "command": "start tutorial"},
+					{"x": 582, "y": 464, "name":"ZTBACK",  "hotkey" : 27,  "help": 14, "command": "to main"}
+				],
+				"images": [ {"x": 114, "y": 312, "name":"ZNEWGAM"}	]
+			},
+			{
+				"name" : "load",
+				"buttons":
+				[
+					{"x": 545, "y": 8,   "name":"ZTSINGL", "hotkey" : 115, "help": 10, "command": "load single"},
+					{"x": 568, "y": 120, "name":"ZTMULTI", "hotkey" : 109, "help": 12, "command": "load multi"},
+					{"x": 541, "y": 233, "name":"ZTCAMPN", "hotkey" : 99,  "help": 11, "command": "load campaign"},
+					{"x": 545, "y": 358, "name":"ZTTUTOR", "hotkey" : 116, "help": 13, "command": "load tutorial"},
+					{"x": 582, "y": 464, "name":"ZTBACK",  "hotkey" : 27,  "help": 14, "command": "to main"}
+				],
+				"images": [ {"x": 114, "y": 312, "name":"ZLOADGAM"}	]
+			},
+			{
+				"name" : "campaign",
+				"buttons":
+				[
+					{"x": 535, "y": 4,   "name":"ZSSSOD", "hotkey" : 119, "command": "campaigns sod"},
+					{"x": 494, "y": 117, "name":"ZSSROE", "hotkey" : 114, "command": "campaigns roe"},
+					{"x": 486, "y": 241, "name":"ZSSARM", "hotkey" : 97,  "command": "campaigns ab"},
+					{"x": 550, "y": 358, "name":"ZSSCUS", "hotkey" : 99,  "command": "start campaign"},
+					{"x": 582, "y": 464, "name":"ZTBACK", "hotkey" : 27,  "command": "to new"}
+				],
+			}
+		]
+	},
+	//Campaigns windows, each campaigns set is a separate window activated by "campaigns %name%" command from main menu
+	"campaignsset":
+	[
+		{
+			"name":"roe",
+			"images" : [ {"x": 0, "y": 0, "name":"CAMPBACK"} ],
+			"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
+			"items":
+			[
+				{ "x":90,  "y":72,  "file":"GOOD1.H3C",    "image":"CAMPGD1S", "video":"CGOOD1",   "open": true },
+				{ "x":539, "y":72,  "file":"EVIL1.H3C",    "image":"CAMPEV1S", "video":"CEVIL1",   "open": true },
+				{ "x":43,  "y":245, "file":"GOOD2.H3C",    "image":"CAMPGD2S", "video":"CGOOD2",   "open": true },
+				{ "x":313, "y":244, "file":"NEUTRAL.H3C",  "image":"CAMPNEUS", "video":"CNEUTRAL", "open": true },
+				{ "x":586, "y":246, "file":"EVIL2.H3C",    "image":"CAMPEV2S", "video":"CEVIL2",   "open": true },
+				{ "x":34,  "y":417, "file":"GOOD3.H3C",    "image":"CAMPGD3S", "video":"CGOOD3",   "open": true },
+				{ "x":404, "y":414, "file":"SECRET.H3C",   "image":"CAMPSCTS", "video":"CSECRET",  "open": true }
+			]
+		},
+		{
+			"name":"ab",
+			"images" :
+			[
+				{"x": 0,   "y": 0,   "name":"CAMPBACK"},
+				{"x": 34,  "y": 417, "name":"CAMP1FWX"},//one campaign have special inactive image
+				{"x": 385, "y": 401, "name":"CAMPNOSC"},//and the last one is not present
+			],
+			"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
+			"items":
+			[
+				{ "x":90,  "y":72,  "file":"AB.H3C",       "image":"CAMP1AB7", "video":"C1ab7", "open": true },
+				{ "x":539, "y":72,  "file":"BLOOD.H3C",    "image":"CAMP1DB2", "video":"C1db2", "open": true },
+				{ "x":43,  "y":245, "file":"SLAYER.H3C",   "image":"CAMP1DS1", "video":"C1ds1", "open": true },
+				{ "x":313, "y":244, "file":"FESTIVAL.H3C", "image":"CAMP1FL3", "video":"C1fl3", "open": true },
+				{ "x":586, "y":246, "file":"FIRE.H3C",     "image":"CAMP1PF2", "video":"C1pf2", "open": true },
+				{ "x":34,  "y":417, "file":"FOOL.H3C",     "image":"CAMP1FW1", "video":"C1fw1", "open": true }
+			]
+
+		},
+		{
+			"name":"sod",
+			"images" : [ {"x": 0, "y": 0, "name":"CAMPBKX2"} ],
+			"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
+			"items":
+			[
+				{ "x":90,  "y":72,  "file":"GEM.H3C",      "image":"CAMPNB1", "video":"NEW",     "open": true },
+				{ "x":539, "y":72,  "file":"GELU.H3C",     "image":"CAMPEL1", "video":"ELIXIR",  "open": true },
+				{ "x":43,  "y":245, "file":"CRAG.H3C",     "image":"CAMPHS1", "video":"HACK",    "open": true },
+				{ "x":313, "y":244, "file":"SANDRO.H3C",   "image":"CAMPRN1", "video":"RISE",    "open": true },
+				{ "x":586, "y":246, "file":"YOG.H3C",      "image":"CAMPBB1", "video":"BIRTH",   "open": true },
+				{ "x":34,  "y":417, "file":"FINAL.H3C",    "image":"CAMPUA1", "video":"UNHOLY",  "open": true },
+				{ "x":404, "y":414, "file":"SECRET1.H3C",  "image":"CAMPSP1", "video":"SPECTRE", "open": true }
+			]
+
+		},
+		{
+			"name":"wog",
+			"images" : [ {"x": 0, "y": 0, "name":"CAMPZALL"} ],
+			"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
+			"items":
+			[
+				{ "x":90,  "y":72,  "file":"ZC1.H3C", "image":"CAMPZ01", "open": true},
+				{ "x":539, "y":72,  "file":"ZC2.H3C", "image":"CAMPZ02", "open": true},
+				{ "x":43,  "y":245, "file":"ZC3.H3C", "image":"CAMPZ03", "open": true},
+				{ "x":311, "y":242, "file":"ZC4.H3C", "image":"CAMPZ04", "open": true}
+			]
+
+		}
+	]
+}

+ 8 - 14
lib/CCampaignHandler.cpp

@@ -154,13 +154,11 @@ CCampaignHeader CCampaignHandler::readHeaderFromMemory( const unsigned char *buf
 	ret.mapVersion -= 1; //change range of it from [1, 20] to [0, 19]
 	ret.name = readString(buffer, outIt);
 	ret.description = readString(buffer, outIt);
-	ret.difficultyChoosenByPlayer = readChar(buffer, outIt);
+	if (ret.version > CampaignVersion::RoE)
+		ret.difficultyChoosenByPlayer = readChar(buffer, outIt);
+	else
+		ret.difficultyChoosenByPlayer = 0;
 	ret.music = readChar(buffer, outIt);
-	if(ret.version == 4)	//I saw one campaign with this version, without either difficulty or music - it's  
-	{						//not editable by any editor so I'll just go back by one byte.
-		outIt--;
-	}
-
 	return ret;
 }
 
@@ -213,12 +211,12 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory( const unsigned c
 	memcpy(ret.monstersKeptByHero, buffer+outIt, ARRAY_COUNT(ret.monstersKeptByHero));
 	outIt += ARRAY_COUNT(ret.monstersKeptByHero);
 	int artifBytes;
-	if (version == 5) //AB
+	if (version < CampaignVersion::SoD)
 	{
 		artifBytes = 17;
 		ret.artifsKeptByHero[17] = 0;
 	} 
-	else //SoD+
+	else
 	{
 		artifBytes = 18;
 	}
@@ -433,11 +431,7 @@ unsigned char * CCampaignHandler::getFile( const std::string & name, bool fromLo
 			cmpgn = bitmaph_ab->giveFile(name, FILE_CAMPAIGN, &outSize);
 		else
 			tlog1 << "Cannot find file: " << name << std::endl;
-		FILE * tmp = fopen("tmp_cmpgn", "wb");
-		fwrite(cmpgn, 1, outSize, tmp);
-		fclose(tmp);
-		delete [] cmpgn;
-		cmpgn = CLodHandler::getUnpackedFile("tmp_cmpgn", &outSize);
+		cmpgn = CLodHandler::getUnpackedData(cmpgn, outSize, &outSize);
 	}
 	else
 	{
@@ -532,7 +526,7 @@ void CCampaignScenario::prepareCrossoverHeroes( std::vector<CGHeroInstance *> he
 			bool takeable = travelOptions.artifsKeptByHero[g / 8] & ( 1 << (g%8) ) ;
 			if (!takeable)
 			{
-				BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
+				//BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
 				{
 					tlog1 << "TODO TODO TODO - take artifacts from hero\n";
 					//TODO how was that supposed to work with worn artifacts?

+ 12 - 1
lib/CCampaignHandler.h

@@ -18,10 +18,21 @@
 struct StartInfo;
 class CGHeroInstance;
 
+namespace CampaignVersion
+{
+	enum Version
+	{
+		RoE = 4,
+		AB = 5,
+		SoD = 6,
+		WoG = 6
+	};
+}
+
 class DLL_EXPORT CCampaignHeader
 {
 public:
-	si32 version; //5 - AB, 6 - SoD, WoG - ?!?
+	si32 version; //4 - RoE, 5 - AB, 6 - SoD and WoG
 	ui8 mapVersion; //CampText.txt's format
 	std::string name, description;
 	ui8 difficultyChoosenByPlayer;

+ 1 - 1
lib/CGameState.cpp

@@ -930,7 +930,7 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 
 			std::string &mapContent = campaign->camp->mapPieces[scenarioOps->whichMapInCampaign];
 			map = new Mapa();
-			map->initFromBytes((const unsigned char*)mapContent.c_str());
+			map->initFromBytes((const unsigned char*)mapContent.c_str(), mapContent.size());
 		}
 		break;
 	case StartInfo::DUEL:

+ 16 - 0
lib/CLodHandler.cpp

@@ -13,6 +13,7 @@
 #include <boost/thread.hpp>
 #include <boost/foreach.hpp>
 #include "vcmi_endian.h"
+#include "VCMIDirs.h"
 #ifdef max
 #undef max
 #endif
@@ -347,6 +348,21 @@ CLodHandler::~CLodHandler()
 	delete mutex;
 }
 
+//It is possible to use uncompress function from zlib but we  need to know decompressed size (not present in compressed data)
+unsigned char * CLodHandler::getUnpackedData(unsigned char *data, size_t inputSize, int * outputSize)
+{
+	std::string filename = GVCMIDirs.UserPath + "/tmp_gzip";
+
+	FILE * file = fopen(filename.c_str(), "wb");
+	fwrite(data, 1, inputSize, file);
+	fclose(file);
+
+	unsigned char * ret = getUnpackedFile(filename, outputSize);
+	remove(filename.c_str());
+	delete [] data;
+	return ret;
+}
+
 unsigned char * CLodHandler::getUnpackedFile( const std::string & path, int * sizeOut )
 {
 	const int bufsize = 65536;

+ 4 - 1
lib/CLodHandler.h

@@ -118,7 +118,10 @@ public:
 	std::string getTextFile(std::string name, LodFileType type=FILE_TEXT); //extracts one file
 	void extractFile(const std::string FName, const std::string name, LodFileType type=FILE_ANY); //extracts a specific file
 
-	static unsigned char * getUnpackedFile(const std::string & path, int * sizeOut); //loads given file, decompresses and returns
+	//unpack data from memory, input data will be deleted
+	static unsigned char * getUnpackedData(unsigned char *data, size_t inputSize, int * outputSize);
+	//loads given file, decompresses and returns
+	static unsigned char * getUnpackedFile(const std::string & path, int * sizeOut);
 };
 
 

+ 10 - 9
lib/map.cpp

@@ -397,8 +397,14 @@ CMapHeader::~CMapHeader()
 
 }
 
-void Mapa::initFromBytes(const unsigned char * bufor)
+void Mapa::initFromBytes(const unsigned char * bufor, size_t size)
 {
+	// Compute checksum
+	boost::crc_32_type  result;
+	result.process_bytes(bufor, size);
+	checksum = result.checksum();
+	tlog0 << "\tOur map checksum: "<<result.checksum() << std::endl;
+
 	int i=0;
 	initFromMemory(bufor,i);
 	timeHandler th;
@@ -438,7 +444,8 @@ void Mapa::initFromBytes(const unsigned char * bufor)
 	}
 	tlog0<<"\tCalculating blocked/visitable tiles: "<<th.getDif()<<std::endl;
 	tlog0 << "\tMap initialization done!" << std::endl;
-}	
+}
+
 void Mapa::removeBlockVisTiles(CGObjectInstance * obj, bool total)
 {
 	for(int fx=0; fx<8; ++fx)
@@ -503,13 +510,7 @@ Mapa::Mapa(std::string filename)
 
 	tlog0<<"done."<<std::endl;
 
-	// Compute checksum
-	boost::crc_32_type  result;
-	result.process_bytes(initTable, mapsize);
-	checksum = result.checksum();
-	tlog0 << "\tOur map checksum: "<<result.checksum() << std::endl;
-
-	initFromBytes(initTable);
+	initFromBytes(initTable, mapsize);
 
 	delete [] initTable;
 }

+ 1 - 1
lib/map.h

@@ -314,7 +314,7 @@ struct DLL_EXPORT Mapa : public CMapHeader
 
 	bmap<si32, si32> questIdentifierToId;
 
-	void initFromBytes( const unsigned char * bufor); //creates map from decompressed .h3m data
+	void initFromBytes( const unsigned char * bufor, size_t size); //creates map from decompressed .h3m data
 
 	void readEvents( const unsigned char * bufor, int &i);
 	void readObjects( const unsigned char * bufor, int &i);