Răsfoiți Sursa

- primaryResource and warMachine now use string ID
- fixed long delays on moving units in garrisons
- fixed bug on loading available spells from map
- removed unused bigImgs from Graphics

Ivan Savenko 13 ani în urmă
părinte
comite
e3231db1c9

+ 13 - 48
client/CCastleInterface.cpp

@@ -109,9 +109,8 @@ void CBuildingRect::clickRight(tribool down, bool previousState)
 		const CBuilding *bld = town->town->buildings[bid];
 		if (bid < EBuilding::DWELL_FIRST)
 		{
-			std::vector<CComponent*> comps(1, new CComponent(CComponent::building, bld->tid, bld->bid));
-
-			CRClickPopup::createAndPush(bld->Description(), comps);
+			CRClickPopup::createAndPush(CInfoWindow::genText(bld->Name(), bld->Description()),
+			                            new CComponent(CComponent::building, bld->tid, bld->bid));
 		}
 		else
 		{
@@ -325,8 +324,8 @@ void CHeroGSlot::clickLeft(tribool down, bool previousState)
 	CHeroGSlot *other = upg  ?  owner->garrisonedHero :  owner->visitingHero;
 	if(!down)
 	{
-		owner->garr->splitting = false;
-		owner->garr->highlighted = NULL;
+		owner->garr->setSplittingMode(false);
+		owner->garr->selectSlot(nullptr);
 
 		if(hero && selection)
 		{
@@ -361,7 +360,7 @@ void CHeroGSlot::clickLeft(tribool down, bool previousState)
 		else if(hero)
 		{
 			setHighlight(true);
-			owner->garr->highlighted = NULL;
+			owner->garr->selectSlot(nullptr);
 			showAll(screen2);
 		}
 		hover(false);hover(true); //refresh statusbar
@@ -1067,16 +1066,7 @@ void CCreaInfo::clickRight(tribool down, bool previousState)
 		if (showAvailable)
 			GH.pushInt(new CDwellingInfoBox(screen->w/2, screen->h/2, town, level));
 		else
-		{
-			std::string descr = genGrowthText();
-			CInfoPopup *mess = new CInfoPopup();//creating popup
-			mess->free = true;
-			mess->bitmap = CMessage::drawBoxTextBitmapSub
-			(LOCPLINT->playerID, descr,graphics->bigImgs[creature->iconIndex],"");
-			mess->pos.x = screen->w/2 - mess->bitmap->w/2;
-			mess->pos.y = screen->h/2 - mess->bitmap->h/2;
-			GH.pushInt(mess);
-		}
+			CRClickPopup::createAndPush(genGrowthText(), new CComponent(CComponent::creature, creature->idNumber));
 	}
 }
 
@@ -1118,21 +1108,11 @@ void CTownInfo::hover(bool on)
 }
 
 void CTownInfo::clickRight(tribool down, bool previousState)
-{//FIXME: castleInt may be NULL
-	if(down && building && LOCPLINT->castleInt)
-	{
-		CInfoPopup *mess = new CInfoPopup();
-		mess->free = true;
-
-		mess->bitmap = CMessage::drawBoxTextBitmapSub
-			(LOCPLINT->playerID,
-			 building->Description(),
-			 LOCPLINT->castleInt->bicons->ourImages[building->bid].bitmap,
-			 building->Name());
-		mess->pos.x = screen->w/2 - mess->bitmap->w/2;
-		mess->pos.y = screen->h/2 - mess->bitmap->h/2;
-		GH.pushInt(mess);
-	}
+{
+	if(down && building)
+		CRClickPopup::createAndPush(CInfoWindow::genText(building->Name(), building->Description()),
+		                            new CComponent(CComponent::building, building->tid, building->bid));
+
 }
 
 void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
@@ -1646,28 +1626,13 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
 void CMageGuildScreen::Scroll::clickLeft(tribool down, bool previousState)
 {
 	if(down)
-	{
-		std::vector<CComponent*> comps(1,
-			new CComponent(CComponent::spell,spell->id,0)
-		);
-		LOCPLINT->showInfoDialog(spell->descriptions[0],comps, soundBase::sound_todo);
-	}
+		LOCPLINT->showInfoDialog(spell->descriptions[0], new CComponent(CComponent::spell,spell->id));
 }
 
 void CMageGuildScreen::Scroll::clickRight(tribool down, bool previousState)
 {
 	if(down)
-	{
-		CInfoPopup *vinya = new CInfoPopup();
-		vinya->free = true;
-		vinya->bitmap = CMessage::drawBoxTextBitmapSub
-			(LOCPLINT->playerID,
-			spell->descriptions[0],graphics->spellscr->ourImages[spell->id].bitmap,
-			spell->name,30,30);
-		vinya->pos.x = screen->w/2 - vinya->bitmap->w/2;
-		vinya->pos.y = screen->h/2 - vinya->bitmap->h/2;
-		GH.pushInt(vinya);
-	}
+		CRClickPopup::createAndPush(spell->descriptions[0], new CComponent(CComponent::spell, spell->id));
 }
 
 void CMageGuildScreen::Scroll::hover(bool on)

+ 1 - 1
client/CKingdomInterface.cpp

@@ -813,7 +813,7 @@ CTownItem::CTownItem(const CGTownInstance* Town):
 
 void CTownItem::updateGarrisons()
 {
-	garr->highlighted = NULL;
+	garr->selectSlot(nullptr);
 	garr->setArmy(town->getUpperArmy(), 0);
 	garr->setArmy(town->visitingHero, 1);
 	garr->recreateSlots();

+ 30 - 10
client/CPlayerInterface.cpp

@@ -509,7 +509,7 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 
 	if(CCastleInterface *c = castleInt)
 	{
-		c->garr->highlighted = NULL;
+		c->garr->selectSlot(nullptr);
 		c->garr->setArmy(town->getUpperArmy(), 0);
 		c->garr->setArmy(town->visitingHero, 1);
 		c->garr->recreateSlots();
@@ -535,10 +535,11 @@ void CPlayerInterface::heroVisitsTown(const CGHeroInstance* hero, const CGTownIn
 	waitWhileDialog();
 	openTownWindow(town);
 }
-void CPlayerInterface::garrisonChanged( const CGObjectInstance * obj)
+void CPlayerInterface::garrisonsChanged(std::vector<const CGObjectInstance *> objs)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	updateInfo(obj);
+	BOOST_FOREACH(auto object, objs)
+		updateInfo(object);
 
 	for(std::list<IShowActivatable*>::iterator i = GH.listInt.begin(); i != GH.listInt.end(); i++)
 	{
@@ -548,7 +549,7 @@ void CPlayerInterface::garrisonChanged( const CGObjectInstance * obj)
 
 		if(CTradeWindow *cmw = dynamic_cast<CTradeWindow*>(*i))
 		{
-			if(obj == cmw->hero)
+			if(vstd::contains(objs, cmw->hero))
 				cmw->garrisonChanged();
 		}
 	}
@@ -556,6 +557,11 @@ void CPlayerInterface::garrisonChanged( const CGObjectInstance * obj)
 	GH.totalRedraw();
 }
 
+void CPlayerInterface::garrisonChanged( const CGObjectInstance * obj)
+{
+	garrisonsChanged(std::vector<const CGObjectInstance *>(1, obj));
+}
+
 void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID, int what) //what: 1 - built, 2 - demolished
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
@@ -953,6 +959,14 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
 
 }
 
+void CPlayerInterface::showInfoDialog(const std::string &text, CComponent * component)
+{
+	std::vector<CComponent*> intComps;
+	intComps.push_back(component);
+
+	showInfoDialog(text, intComps, soundBase::sound_todo, true);
+}
+
 void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<CComponent*> & components, int soundID, bool delComps)
 {
 	waitWhileDialog();
@@ -2327,9 +2341,13 @@ void CPlayerInterface::stacksErased(const StackLocation &location)
 void CPlayerInterface::stacksSwapped(const StackLocation &loc1, const StackLocation &loc2)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	garrisonChanged(loc1.army);
+
+	std::vector<const CGObjectInstance *> objects;
+	objects.push_back(loc1.army);
 	if(loc2.army != loc1.army)
-		garrisonChanged(loc2.army);
+		objects.push_back(loc2.army);
+
+	garrisonsChanged(objects);
 }
 
 void CPlayerInterface::newStackInserted(const StackLocation &location, const CStackInstance &stack)
@@ -2342,11 +2360,13 @@ void CPlayerInterface::stacksRebalanced(const StackLocation &src, const StackLoc
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 
-	garrisonChanged(src.army);
-	if(dst.army != src.army)
-		garrisonChanged(dst.army);
+	std::vector<const CGObjectInstance *> objects;
+	objects.push_back(src.army);
+	if(src.army != dst.army)
+		objects.push_back(dst.army);
+
+	garrisonsChanged(objects);
 }
-#undef UPDATE_IF
 
 void CPlayerInterface::artifactPut(const ArtifactLocation &al)
 {

+ 2 - 0
client/CPlayerInterface.h

@@ -210,6 +210,7 @@ public:
 
 	//-------------//
 	void showArtifactAssemblyDialog(ui32 artifactID, ui32 assembleTo, bool assemble, CFunctionList<void()> onYes, CFunctionList<void()> onNo);
+	void garrisonsChanged(std::vector<const CGObjectInstance *> objs);
 	void garrisonChanged(const CGObjectInstance * obj);
 	void heroKilled(const CGHeroInstance* hero);
 	void waitWhileDialog(bool unlockPim = true);
@@ -223,6 +224,7 @@ public:
 	void updateInfo(const CGObjectInstance * specific);
 	void init(CCallback * CB);
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
+	void showInfoDialog(const std::string &text, CComponent * component);
 	void showInfoDialog(const std::string &text, const std::vector<CComponent*> & components = std::vector<CComponent*>(), int soundID = 0, bool delComps = false);
 	void showYesNoDialog(const std::string &text, CFunctionList<void()> onYes, CFunctionList<void()> onNo, bool DelComps = false, const std::vector<CComponent*> & components = std::vector<CComponent*>()); //deactivateCur - whether current main interface should be deactivated; delComps - if components will be deleted on window close
 	void stopMovement();

+ 154 - 101
client/GUIClasses.cpp

@@ -200,6 +200,14 @@ CTownTooltip::CTownTooltip(Point pos, const CGTownInstance * town):
 	init(InfoAboutTown(town, true));
 }
 
+void CGarrisonSlot::setHighlight(bool on)
+{
+	if (on)
+		selectionImage->recActions |= UPDATE | SHOWALL; //show
+	else
+		selectionImage->recActions &= ~(UPDATE | SHOWALL); //hide
+}
+
 void CGarrisonSlot::hover (bool on)
 {
 	////Hoverable::hover(on);
@@ -208,29 +216,29 @@ void CGarrisonSlot::hover (bool on)
 		std::string temp;
 		if(creature)
 		{
-			if(owner->highlighted)
+			if(owner->getSelection())
 			{
-				if(owner->highlighted == this)
+				if(owner->getSelection() == this)
 				{
 					temp = CGI->generaltexth->tcommands[4]; //View %s
 					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
 				}
-				else if (owner->highlighted->creature == creature)
+				else if (owner->getSelection()->creature == creature)
 				{
 					temp = CGI->generaltexth->tcommands[2]; //Combine %s armies
 					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
 				}
-				else if (owner->highlighted->creature)
+				else if (owner->getSelection()->creature)
 				{
 					temp = CGI->generaltexth->tcommands[7]; //Exchange %s with %s
-					boost::algorithm::replace_first(temp,"%s",owner->highlighted->creature->nameSing);
+					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
 					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
 				}
 				else
 				{
-					tlog2 << "Warning - shouldn't be - highlighted void slot "<<owner->highlighted<<std::endl;
+					tlog2 << "Warning - shouldn't be - highlighted void slot "<<owner->getSelection()<<std::endl;
 					tlog2 << "Highlighted set to NULL"<<std::endl;
-					owner->highlighted = NULL;
+					owner->selectSlot(nullptr);
 				}
 			}
 			else
@@ -252,12 +260,12 @@ void CGarrisonSlot::hover (bool on)
 		}
 		else
 		{
-			if(owner->highlighted)
+			if(owner->getSelection())
 			{
-				const CArmedInstance *highl = owner->highlighted->getObj();
+				const CArmedInstance *highl = owner->getSelection()->getObj();
 				if(  highl->needsLastStack()		//we are moving stack from hero's
 				  && highl->stacksCount() == 1	//it's only stack
-				  && owner->highlighted->upg != upg	//we're moving it to the other garrison
+				  && owner->getSelection()->upg != upg	//we're moving it to the other garrison
 				  )
 				{
 					temp = CGI->generaltexth->tcommands[5]; //Cannot move last army to garrison
@@ -265,7 +273,7 @@ void CGarrisonSlot::hover (bool on)
 				else
 				{
 					temp = CGI->generaltexth->tcommands[6]; //Move %s
-					boost::algorithm::replace_first(temp,"%s",owner->highlighted->creature->nameSing);
+					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
 				}
 			}
 			else
@@ -281,12 +289,12 @@ void CGarrisonSlot::hover (bool on)
 	}
 }
 
-const CArmedInstance * CGarrisonSlot::getObj()
+const CArmedInstance * CGarrisonSlot::getObj() const
 {
 	return 	(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]);
 }
 
-bool CGarrisonSlot::our()
+bool CGarrisonSlot::our() const
 {
 	return 	upg?(owner->owned[1]):(owner->owned[0]);
 }
@@ -303,9 +311,9 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 	if(down)
 	{
 		bool refr = false;
-		if(owner->highlighted)
+		if(owner->getSelection())
 		{
-			if(owner->highlighted == this) //view info
+			if(owner->getSelection() == this) //view info
 			{
 				UpgradeInfo pom;
 				LOCPLINT->cb->getUpgradeInfo(getObj(), ID, pom);
@@ -317,8 +325,8 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 				if (canUpgrade) upgr = boost::bind(&CCallback::upgradeCreature, LOCPLINT->cb, getObj(), ID, pom.newID[0]);
 				if (canDismiss) dism = boost::bind(&CCallback::dismissCreature, LOCPLINT->cb, getObj(), ID);
 
-				owner->highlighted = NULL;
-				owner->splitting = false;
+				owner->selectSlot(nullptr);
+				owner->setSplittingMode(false);
 
 				for(size_t i = 0; i<owner->splitButtons.size(); i++)
 					owner->splitButtons[i]->block(true);
@@ -331,61 +339,61 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 			else
 			{
 				// Only allow certain moves if troops aren't removable or not ours.
-				if (  ( owner->highlighted->our()//our creature is selected
-				     || owner->highlighted->creature == creature )//or we are rebalancing army
+				if (  ( owner->getSelection()->our()//our creature is selected
+				     || owner->getSelection()->creature == creature )//or we are rebalancing army
 				   && ( owner->removableUnits
-				     || (upg == 0 &&  ( owner->highlighted->upg == 1 && !creature ) )
-					 || (upg == 1 &&    owner->highlighted->upg == 1 ) ) )
+				     || (upg == 0 &&  ( owner->getSelection()->upg == 1 && !creature ) )
+					 || (upg == 1 &&    owner->getSelection()->upg == 1 ) ) )
 				{
 					//we want to split
-					if((owner->splitting || LOCPLINT->shiftPressed())
+					if((owner->getSplittingMode() || LOCPLINT->shiftPressed())
 						&& (!creature
-							|| (creature == owner->highlighted->creature)))
+							|| (creature == owner->getSelection()->creature)))
 					{
 						owner->p2 = ID; //store the second stack pos
 						owner->pb = upg;//store the second stack owner (up or down army)
-						owner->splitting = false;
+						owner->setSplittingMode(false);
 
 						int minLeft=0, minRight=0;
 
-						if(upg != owner->highlighted->upg) //not splitting within same army
+						if(upg != owner->getSelection()->upg) //not splitting within same army
 						{
-							if(owner->highlighted->getObj()->stacksCount() == 1 //we're splitting away the last stack
-								&& owner->highlighted->getObj()->needsLastStack() )
+							if(owner->getSelection()->getObj()->stacksCount() == 1 //we're splitting away the last stack
+								&& owner->getSelection()->getObj()->needsLastStack() )
 							{
 								minLeft = 1;
 							}
 							if(getObj()->stacksCount() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
-								&& owner->highlighted->creature == creature
+								&& owner->getSelection()->creature == creature
 								&& getObj()->needsLastStack() )
 							{
 								minRight = 1;
 							}
 						}
 
-						GH.pushInt(new CSplitWindow(owner->highlighted->creature, boost::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
-						                            minLeft, minRight, owner->highlighted->count, count));
+						GH.pushInt(new CSplitWindow(owner->getSelection()->creature, boost::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
+						                            minLeft, minRight, owner->getSelection()->myStack->count, myStack->count));
 						refr = true;
 					}
-					else if(creature != owner->highlighted->creature) //swap
+					else if(creature != owner->getSelection()->creature) //swap
 					{
 						LOCPLINT->cb->swapCreatures(
 							(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
-							(!owner->highlighted->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
-							ID,owner->highlighted->ID);
+							(!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
+							ID,owner->getSelection()->ID);
 					}
 					else //merge
 					{
 						LOCPLINT->cb->mergeStacks(
-							(!owner->highlighted->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
+							(!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
 							(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
-							owner->highlighted->ID,ID);
+							owner->getSelection()->ID,ID);
 					}
 				}
 				else // Highlight
 				{
 					if(creature)
-						owner->highlighted = this;
+						owner->selectSlot(this);
 					redraw();
 					refr = true;
 				}
@@ -417,7 +425,7 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 			}
 			if (!artSelected && creature)
 			{
-				owner->highlighted = this;
+				owner->selectSlot(this);
 				if(creature)
 				{
 					for(size_t i = 0; i<owner->splitButtons.size(); i++)
@@ -431,6 +439,26 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 	}
 }
 
+void CGarrisonSlot::update()
+{
+	myStack = getObj()->getStackPtr(ID);
+	creature = myStack ? myStack->type : NULL;
+
+	if (creature)
+	{
+		creatureImage->recActions |= (UPDATE | SHOWALL);
+		creatureImage->setFrame(creature->iconIndex);
+
+		stackCount->recActions |= (UPDATE | SHOWALL);
+		stackCount->setTxt(boost::lexical_cast<std::string>(myStack->count));
+	}
+	else
+	{
+		creatureImage->recActions &= ~(UPDATE | SHOWALL);
+		stackCount->recActions &= ~(UPDATE | SHOWALL);
+	}
+}
+
 CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg, const CStackInstance * Creature)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
@@ -444,15 +472,15 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg
 	ID = IID;
 	myStack = Creature;
 	creature = Creature ? Creature->type : NULL;
-	if (creature)
-	{
-		std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT";
-		creatureImage = new CAnimImage(imgName, creature->iconIndex);
-	}
-	else
-		creatureImage = nullptr;
 
-	count = Creature ? Creature->count : 0;
+	std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT";
+
+	creatureImage = new CAnimImage(imgName, creature ? creature->iconIndex : 0);
+	if (!creature)
+		creatureImage->recActions &= ~(UPDATE | SHOWALL);
+
+	selectionImage = new CAnimImage(imgName, 1);
+	selectionImage->recActions &= ~(UPDATE | SHOWALL); //hide it
 
 	if(Owner->smallIcons)
 	{
@@ -464,29 +492,12 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg
 		pos.w = 58;
 		pos.h = 64;
 	}
-}
-
-void CGarrisonSlot::showAll(SDL_Surface * to)
-{
-	std::map<int,SDL_Surface*> &imgs = (owner->smallIcons ? graphics->smallImgs : graphics->bigImgs);
-	if(creature)
-	{
-		creatureImage->showAll(to);
-		char buf[15];
-		SDL_itoa(count,buf,10);
-		printTo(buf, pos.x+pos.w, pos.y+pos.h+1, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, Colors::WHITE, to);
 
-		if((owner->highlighted==this)
-			|| (owner->splitting && owner->highlighted->creature == creature))
-		{
-			blitAt(imgs[-1],pos,to);
-		}
-	}
-	else//empty slot
-	{
-		if(owner->splitting && owner->highlighted->our())
-			blitAt(imgs[-1],pos,to);
-	}
+	stackCount = new CLabel(pos.w, pos.h, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, BOTTOMRIGHT, Colors::WHITE);
+	if (!creature)
+		stackCount->recActions &= ~(UPDATE | SHOWALL);
+	else
+		stackCount->setTxt(boost::lexical_cast<std::string>(myStack->count));
 }
 
 void CGarrisonInt::addSplitBtn(CAdventureMapButton * button)
@@ -530,46 +541,45 @@ void CGarrisonInt::createSlots()
 		createSet (slotsDown, armedObjs[1], garOffset.x, garOffset.y, width+interx, 1);
 }
 
-void CGarrisonInt::deleteSlots()
-{
-	for (int i=0; i<slotsUp.size(); i++)
-		vstd::clear_pointer(slotsUp[i]);
-
-	for (int i=0; i<slotsDown.size(); i++)
-		vstd::clear_pointer(slotsDown[i]);
-}
-
 void CGarrisonInt::recreateSlots()
 {
-
-	splitting = false;
-	highlighted = NULL;
+	setSplittingMode(false);
+	selectSlot(nullptr);
 
 	for(size_t i = 0; i<splitButtons.size(); i++)
 		splitButtons[i]->block(true);
 
-	deleteSlots();
-	createSlots();
+
+	BOOST_FOREACH(CGarrisonSlot * slot, slotsUp)
+		slot->update();
+
+	BOOST_FOREACH(CGarrisonSlot * slot, slotsDown)
+		slot->update();
 }
 
 void CGarrisonInt::splitClick()
 {
-	if(!highlighted)
+	if(!getSelection())
 		return;
-	splitting = !splitting;
+	setSplittingMode(!getSplittingMode());
 	redraw();
 }
 void CGarrisonInt::splitStacks(int, int amountRight)
 {
-	LOCPLINT->cb->splitStack(armedObjs[highlighted->upg], armedObjs[pb], highlighted->ID, p2, amountRight);
+	LOCPLINT->cb->splitStack(armedObjs[getSelection()->upg], armedObjs[pb], getSelection()->ID, p2, amountRight);
 }
 
 CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point &garsOffset,
-                            SDL_Surface *pomsur, const Point& SurOffset,
-                            const CArmedInstance *s1, const CArmedInstance *s2,
-                            bool _removableUnits, bool smallImgs, bool _twoRows )
-	: interx(inx), garOffset(garsOffset), highlighted(NULL), splitting(false),
-	smallIcons(smallImgs), removableUnits (_removableUnits), twoRows(_twoRows)
+                           SDL_Surface *pomsur, const Point& SurOffset,
+                           const CArmedInstance *s1, const CArmedInstance *s2,
+                           bool _removableUnits, bool smallImgs, bool _twoRows ) :
+    highlighted(nullptr),
+    inSplittingMode(false),
+    interx(inx),
+    garOffset(garsOffset),
+    smallIcons(smallImgs),
+    removableUnits(_removableUnits),
+    twoRows(_twoRows)
 {
 	setArmy(s1, false);
 	setArmy(s2, true);
@@ -581,12 +591,50 @@ CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point &garsOffset,
 void CGarrisonInt::activate()
 {
 	for(size_t i = 0; i<splitButtons.size(); i++)
-		if( (splitButtons[i]->isBlocked()) != !highlighted)
-			splitButtons[i]->block(!highlighted);
+		splitButtons[i]->block(getSelection() != nullptr);
 
 	CIntObject::activate();
 }
 
+const CGarrisonSlot * CGarrisonInt::getSelection()
+{
+	return highlighted;
+}
+
+void CGarrisonInt::selectSlot(CGarrisonSlot *slot)
+{
+	if (slot != highlighted)
+	{
+		if (highlighted)
+			highlighted->setHighlight(false);
+
+		highlighted = slot;
+
+		if (highlighted)
+			highlighted->setHighlight(true);
+	}
+}
+
+void CGarrisonInt::setSplittingMode(bool on)
+{
+	assert(on == false || highlighted != nullptr); //can't be in splitting mode without selection
+
+	if (inSplittingMode || on)
+	{
+		BOOST_FOREACH(CGarrisonSlot * slot, slotsUp)
+			slot->setHighlight(slot->creature == nullptr || slot->creature == getSelection()->creature);
+
+		BOOST_FOREACH(CGarrisonSlot * slot, slotsDown)
+			slot->setHighlight(slot->creature == nullptr || slot->creature == getSelection()->creature);
+		inSplittingMode = on;
+	}
+}
+
+bool CGarrisonInt::getSplittingMode()
+{
+	return inSplittingMode;
+}
+
 void CGarrisonInt::setArmy(const CArmedInstance *army, bool bottomGarrison)
 {
 	owned[bottomGarrison] =  army ? (army->tempOwner == LOCPLINT->playerID || army->tempOwner == 254) : false; //254 - neutral objects (pandora, banks)
@@ -689,6 +737,11 @@ CInfoWindow * CInfoWindow::create(const std::string &text, int playerID /*= 1*/,
 	return ret;
 }
 
+std::string CInfoWindow::genText(std::string title, std::string description)
+{
+	return std::string("{") + title + "}" + "\n\n" + description;
+}
+
 void CInfoWindow::setDelComps(bool DelComps)
 {
 	delComps = DelComps;
@@ -5193,16 +5246,8 @@ void CUniversityWindow::CItem::clickRight(tribool down, bool previousState)
 {
 	if(down)
 	{
-		CInfoPopup *message = new CInfoPopup();
-		message->free = true;
-		message->bitmap = CMessage::drawBoxTextBitmapSub
-		                            (LOCPLINT->playerID,
-		                             CGI->generaltexth->skillInfoTexts[ID][0],
-		                             graphics->abils82->ourImages[ID*3+3].bitmap,
-		                             CGI->generaltexth->skillName[ID]);
-		message->pos.x = screen->w/2 - message->bitmap->w/2;
-		message->pos.y = screen->h/2 - message->bitmap->h/2;
-		GH.pushInt(message);
+		CRClickPopup::createAndPush(CGI->generaltexth->skillInfoTexts[ID][0],
+		        new CComponent(CComponent::secskill, ID, 0));
 	}
 }
 
@@ -5813,6 +5858,14 @@ void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCom
 	GH.pushInt(rcpi);
 }
 
+void CRClickPopup::createAndPush(const std::string &txt, CComponent * component)
+{
+	CInfoWindow::TCompsInfo intComps;
+	intComps.push_back(component);
+
+	createAndPush(txt, intComps);
+}
+
 Point CInfoBoxPopup::toScreen(Point p)
 {
 	vstd::abetween(p.x, adventureInt->terrain.pos.x + 100, adventureInt->terrain.pos.x + adventureInt->terrain.pos.w - 100);

+ 22 - 9
client/GUIClasses.h

@@ -105,6 +105,9 @@ public:
 
 	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, int player = 1); //use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
 	static CInfoWindow *create(const std::string &text, int playerID = 1, const std::vector<CComponent*> *components = NULL, bool DelComps = false);
+
+	/// create text from title and description: {title}\n\n description
+	static std::string genText(std::string title, std::string description);
 };
 
 /// component selection window
@@ -130,6 +133,7 @@ public:
 
 	static CIntObject* createInfoWin(Point position, const CGObjectInstance * specific);
 	static void createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo());
+	static void createAndPush(const std::string &txt, CComponent * component);
 	static void createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment = BOTTOMRIGHT);
 };
 
@@ -210,7 +214,7 @@ public:
 	std::string getDescription();
 	std::string getSubtitle();
 
-	CComponent(Etype Type, int Subtype, int Val, ESize imageSize=large);//c-tor
+	CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large);//c-tor
 	CComponent(const Component &c); //c-tor
 
 	void clickRight(tribool down, bool previousState); //call-in
@@ -311,18 +315,20 @@ class CGarrisonSlot : public CIntObject
 	CGarrisonInt *owner;
 	const CStackInstance *myStack; //NULL if slot is empty
 	const CCreature *creature;
-	int count; //number of creatures
 	int upg; //0 - up garrison, 1 - down garrison
-	bool highlight;
 
 	CAnimImage * creatureImage;
+	CAnimImage * selectionImage; // image for selection, not always visible
+	CLabel * stackCount;
+
+	void setHighlight(bool on);
 public:
 	virtual void hover (bool on); //call-in
-	const CArmedInstance * getObj();
-	bool our();
+	const CArmedInstance * getObj() const;
+	bool our() const;
 	void clickRight(tribool down, bool previousState);
 	void clickLeft(tribool down, bool previousState);
-	void showAll(SDL_Surface * to);
+	void update();
 	CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg=0, const CStackInstance * Creature=NULL);
 
 	friend class CGarrisonInt;
@@ -331,15 +337,23 @@ public:
 /// Class which manages slots of upper and lower garrison, splitting of units
 class CGarrisonInt :public CIntObject
 {
+	CGarrisonSlot *highlighted; //chosen slot. Should be changed only via selectSlot
+	bool inSplittingMode;
+
 public:
+	void selectSlot(CGarrisonSlot * slot); //null = deselect
+	const CGarrisonSlot * getSelection();
+
+	void setSplittingMode(bool on);
+	bool getSplittingMode();
+
 	int interx; //space between slots
 	Point garOffset; //offset between garrisons (not used if only one hero)
-	CGarrisonSlot *highlighted; //chosen slot
 	std::vector<CAdventureMapButton *> splitButtons; //may be empty if no buttons
 
 	int p2, //TODO: comment me
 	    shiftPos;//1st slot of the second row, set shiftPoint for effect
-	bool splitting, pb,
+	bool pb,
 	     smallIcons, //true - 32x32 imgs, false - 58x64
 	     removableUnits,//player can remove units from up
 	     twoRows,//slots will be placed in 2 rows
@@ -358,7 +372,6 @@ public:
 
 	void activate();
 	void createSlots();
-	void deleteSlots();
 	void recreateSlots();
 
 	void splitClick(); //handles click on split button

+ 1 - 9
client/Graphics.cpp

@@ -129,7 +129,7 @@ void Graphics::initializeBattleGraphics()
 }
 Graphics::Graphics()
 {
-	CDefHandler *smi, *smi2;
+	CDefHandler *smi2;
 
 	std::vector<Task> tasks; //preparing list of graphics to load
 	tasks += boost::bind(&Graphics::loadFonts,this);
@@ -140,7 +140,6 @@ Graphics::Graphics()
 	tasks += boost::bind(&Graphics::loadErmuToPicture,this);
 	tasks += GET_DEF_ESS(artDefs,"ARTIFACT.DEF");
 	tasks += GET_DEF_ESS(resources32,"RESOURCE.DEF");
-	tasks += GET_DEF(smi,"CPRSMALL.DEF");
 	tasks += GET_DEF(smi2,"TWCRPORT.DEF");
 	tasks += GET_DEF_ESS(flags,"CREST58.DEF");
 	tasks += GET_DEF_ESS(abils82,"SECSK82.DEF");
@@ -155,13 +154,6 @@ Graphics::Graphics()
 		CSDL_Ext::alphaTransform(heroMoveArrows->ourImages[y].bitmap);
 	}
 
-	//handling 32x32px imgs
-	smi->notFreeImgs = true;
-	for (size_t i=0; i<smi->ourImages.size(); ++i)
-	{
-		smallImgs[i-2] = smi->ourImages[i].bitmap;
-	}
-	delete smi;
 	smi2->notFreeImgs = true;
 	for (size_t i=0; i<smi2->ourImages.size(); ++i)
 	{

+ 0 - 1
client/Graphics.h

@@ -58,7 +58,6 @@ public:
 	CDefEssential * getDef(const CGObjectInstance * obj);
 	CDefEssential * getDef(const CGDefInfo * info);
 	//creatures
-	std::map<int,SDL_Surface*> smallImgs; //creature ID -> small 32x32 img of creature; //ID=-2 is for blank (black) img; -1 for the border
 	std::map<int,SDL_Surface*> bigImgs; //creature ID -> big 58x64 img of creature; //ID=-2 is for blank (black) img; -1 for the border
 	//towns
 	std::map<int, std::string> ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type

+ 14 - 18
config/buildings.json

@@ -145,9 +145,8 @@
 				["Angel", "Archangel"]
 			],
 			"horde" : [ 2, -1 ],
-			"primaryResource" : 127,
 			"mageGuild" : 4,
-			"warMachine" : 4,
+			"warMachine" : "Ballista",
 
 			"buildings" :
 			[
@@ -396,8 +395,8 @@
 			],
 			"horde" : [ 1, 4 ],
 			"mageGuild" : 5,
-			"primaryResource" : 4,
-			"warMachine" : 6,
+			"primaryResource" : "crystal",
+			"warMachine" : "FirstAidTent",
 
 			"buildings" :
 			[
@@ -641,9 +640,9 @@
 				["LesserTitan", "GreaterTitan"]
 			],
 			"horde" : [ 1, -1 ],
-			"primaryResource" : 5,
+			"primaryResource" : "gems",
 			"mageGuild" : 5,
-			"warMachine" : 5,
+			"warMachine" : "AmmoCart",
 
 			"buildings" :
 			[
@@ -886,8 +885,8 @@
 			],
 			"horde" : [ 0, 2 ],
 			"mageGuild" : 5,
-			"primaryResource" : 1,
-			"warMachine" : 5,
+			"primaryResource" : "mercury",
+			"warMachine" : "Ballista",
 
 			"buildings" :
 			[
@@ -1136,8 +1135,7 @@
 			],
 			"horde" : [ 0, -1 ],
 			"mageGuild" : 5,
-			"primaryResource" : 127,
-			"warMachine" : 6,
+			"warMachine" : "FirstAidTent",
 
 			"buildings" :
 			[
@@ -1380,8 +1378,8 @@
 			],
 			"horde" : [ 0, -1 ],
 			"mageGuild" : 5,
-			"primaryResource" : 3,
-			"warMachine" : 4,
+			"primaryResource" : "sulfur",
+			"warMachine" : "Ballista",
 
 			"buildings" :
 			[
@@ -1622,8 +1620,7 @@
 			],
 			"horde" : [ 0, -1 ],
 			"mageGuild" : 3,
-			"primaryResource" : 127,
-			"warMachine" : 5,
+			"warMachine" : "AmmoCart",
 
 			"buildings" :
 			[
@@ -1866,8 +1863,7 @@
 			],
 			"horde" : [ 0, -1 ],
 			"mageGuild" : 3,
-			"primaryResource" : 127,
-			"warMachine" : 6,
+			"warMachine" : "FirstAidTent",
 
 			"buildings" :
 			[
@@ -2115,8 +2111,8 @@
 			],
 			"horde" : [ 0, -1 ],
 			"mageGuild" : 5,
-			"primaryResource" : 1,
-			"warMachine" : 4,
+			"primaryResource" : "mercury",
+			"warMachine" : "Ballista",
 
 			"buildings" :
 			[

+ 12 - 2
lib/CTownHandler.cpp

@@ -396,9 +396,19 @@ void CTownHandler::loadClientData(CTown &town, const JsonNode & source)
 
 void CTownHandler::loadTown(CTown &town, const JsonNode & source)
 {
+	auto resIter = boost::find(GameConstants::RESOURCE_NAMES, source["primaryResource"].String());
+	if (resIter == boost::end(GameConstants::RESOURCE_NAMES))
+		town.primaryRes = 127; //Wood + Ore
+	else
+		town.primaryRes = resIter - boost::begin(GameConstants::RESOURCE_NAMES);
+
+	VLC->modh->identifiers.requestIdentifier(std::string("creature." + source["warMachine"].String()),
+	[&town](si32 creature)
+	{
+		town.warMachine = CArtHandler::convertMachineID(creature, true);
+	});
+
 	town.mageLevel = source["mageGuild"].Float();
-	town.primaryRes  = source["primaryResource"].Float();
-	town.warMachine = source["warMachine"].Float();
 	town.names = source["names"].convertTo<std::vector<std::string> >();
 
 	//  Horde building creature level

+ 2 - 2
lib/Map/CMapService.cpp

@@ -2293,7 +2293,7 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
 			{
 				if((pos - ist) * 8 + yy < GameConstants::SPELLS_QUANTITY)
 				{
-					if(c == (c | static_cast<ui8>(std::pow(2., yy))))
+					if(c != (c | static_cast<ui8>(std::pow(2., yy))))
 					{
 						nt->obligatorySpells.push_back((pos - ist) * 8 + yy);
 					}
@@ -2310,7 +2310,7 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
 		{
 			if((pos - ist) * 8 + yy < GameConstants::SPELLS_QUANTITY)
 			{
-				if(c == (c | static_cast<ui8>(std::pow(2., yy))))
+				if(c != (c | static_cast<ui8>(std::pow(2., yy))))
 				{
 					nt->possibleSpells.push_back((pos - ist) * 8 + yy);
 				}