瀏覽代碼

* mostly done Events objects handling
* some moving hero code improvements
* fixed Blacksmith
* minor changes

Michał W. Urbańczyk 16 年之前
父節點
當前提交
c8b6858716
共有 14 個文件被更改,包括 452 次插入162 次删除
  1. 56 61
      CAdvmapInterface.cpp
  2. 0 27
      CCallback.cpp
  3. 2 2
      CCastleInterface.cpp
  4. 0 3
      CGameState.cpp
  5. 15 10
      CMessage.cpp
  6. 90 28
      CPlayerInterface.cpp
  7. 2 2
      CPlayerInterface.h
  8. 0 11
      client/NetPacksClient.cpp
  9. 247 8
      hch/CObjectHandler.cpp
  10. 10 5
      hch/CObjectHandler.h
  11. 1 0
      lib/NetPacks.h
  12. 18 1
      lib/NetPacksLib.cpp
  13. 8 2
      map.cpp
  14. 3 2
      server/CGameHandler.cpp

+ 56 - 61
CAdvmapInterface.cpp

@@ -414,100 +414,95 @@ void CTerrainRect::clickLeft(tribool down)
 	int3 mp = whichTileIsIt();
 	if ((mp.x<0) || (mp.y<0))
 		return;
-	std::vector < const CGObjectInstance * > objs;
-	if (LOCPLINT->adventureInt->selection->ID != HEROI_TYPE)
+
+	std::vector < const CGObjectInstance * > bobjs = LOCPLINT->cb->getBlockingObjs(mp),  //blocking objects at tile
+		vobjs = LOCPLINT->cb->getVisitableObjs(mp); //visitable objects
+
+	if (LOCPLINT->adventureInt->selection->ID != HEROI_TYPE) //hero is not selected (presumably town)
 	{
-		if (currentPath)
+		if(currentPath)
 		{
 			tlog2<<"Warning: Lost path?" << std::endl;
 			delete currentPath;
 			currentPath = NULL;
 		}
-		objs = LOCPLINT->cb->getBlockingObjs(mp);
-		for(size_t i=0; i < objs.size(); ++i)
+
+		for(size_t i=0; i < bobjs.size(); ++i)
 		{
-			if(objs[i]->ID == TOWNI_TYPE && objs[i]->tempOwner == LOCPLINT->playerID) //town
+			if(bobjs[i]->ID == TOWNI_TYPE && bobjs[i]->getOwner() == LOCPLINT->playerID) //our town clicked
 			{
-				if(LOCPLINT->adventureInt->selection == (objs[i]))
-				{
-					LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(objs[i]));
-				}
+				if(LOCPLINT->adventureInt->selection == (bobjs[i])) //selected town clicked
+					LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(bobjs[i]));
 				else
-				{
-					LOCPLINT->adventureInt->select(static_cast<const CArmedInstance*>(objs[i]));
-					return;
-				}
+					LOCPLINT->adventureInt->select(static_cast<const CArmedInstance*>(bobjs[i]));
+
+				return;
 			}
-			else if(objs[i]->ID == HEROI_TYPE && objs[i]->tempOwner == LOCPLINT->playerID)
+			else if(bobjs[i]->ID == HEROI_TYPE && bobjs[i]->tempOwner == LOCPLINT->playerID) //hero clicked - select him
 			{
-				LOCPLINT->adventureInt->select(static_cast<const CArmedInstance*>(objs[i]));
+				LOCPLINT->adventureInt->select(static_cast<const CArmedInstance*>(bobjs[i]));
 				return;
 			}
 		}
-		return;
 	}
-	else
+
+	else //hero is selected
 	{
-		objs = LOCPLINT->cb->getVisitableObjs(mp);
-		for(size_t i=0; i < objs.size(); ++i)
+		bool townEntrance = false; //town entrance tile has been clicked?
+		const CGHeroInstance * currentHero = static_cast<const CGHeroInstance*>(LOCPLINT->adventureInt->selection);
+
+		for(size_t i=0; i < vobjs.size(); ++i)
 		{
-			if(objs[i]->ID == TOWNI_TYPE)
-				goto endchkpt;
+			if(vobjs[i]->ID == TOWNI_TYPE)
+				townEntrance = true;
 		}
-		objs = LOCPLINT->cb->getBlockingObjs(mp);
-		for(size_t i=0; i < objs.size(); ++i)
+
+		if(!townEntrance) //not entrance - select town or open hero window
 		{
-			if(objs[i]->ID == TOWNI_TYPE && objs[i]->tempOwner == LOCPLINT->playerID) //town
+			for(size_t i=0; i < bobjs.size(); ++i)
 			{
-				LOCPLINT->adventureInt->select(static_cast<const CArmedInstance*>(objs[i]));
-				return;
+				if(bobjs[i]->ID == TOWNI_TYPE && bobjs[i]->tempOwner == LOCPLINT->playerID) //town - switch selection to it
+				{
+					LOCPLINT->adventureInt->select(static_cast<const CArmedInstance*>(bobjs[i]));
+					return;
+				}
+				else if(bobjs[i]->ID == HEROI_TYPE //it's a hero
+					&& bobjs[i]->tempOwner == LOCPLINT->playerID  //our hero (is this condition needed?)
+					&& currentHero == (bobjs[i]) ) //and selected one 
+				{
+					LOCPLINT->openHeroWindow(currentHero);
+					return;
+				}
 			}
-			else if(objs[i]->ID == HEROI_TYPE && objs[i]->tempOwner == LOCPLINT->playerID && LOCPLINT->adventureInt->selection == (objs[i]))
+		}
+
+		//still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
+		if (currentPath)
+		{
+			if (currentPath->endPos() == mp) //we'll be moving
 			{
-				LOCPLINT->openHeroWindow(static_cast<const CGHeroInstance*>(objs[i]));
-				return;
+				LOCPLINT->pim->unlock();
+				LOCPLINT->moveHero(currentHero,*currentPath);
+				LOCPLINT->pim->lock();
 			}
-		}
-	}
-endchkpt:
-	bool mres =true;
-	if (currentPath)
-	{
-		if ( (currentPath->endPos()) == mp)
-		{ //move
-			CPath sended(*currentPath); //temporary path - engine will operate on it
-			LOCPLINT->pim->unlock();
-			mres = LOCPLINT->moveHero(static_cast<const CGHeroInstance*>(LOCPLINT->adventureInt->selection),&sended);
-			LOCPLINT->pim->lock();
-			if(mres)
+			else //remove an old path
 			{
 				delete currentPath;
-				currentPath = NULL;
+				currentPath=NULL;
 				LOCPLINT->adventureInt->heroList.items[LOCPLINT->adventureInt->heroList.getPosOfHero(LOCPLINT->adventureInt->selection)].second = NULL;
 			}
 		}
-		else
-		{
-			delete currentPath;
-			currentPath=NULL;
-			LOCPLINT->adventureInt->heroList.items[LOCPLINT->adventureInt->heroList.getPosOfHero(LOCPLINT->adventureInt->selection)].second = NULL;
-		}
-	}
-	const CGHeroInstance * currentHero = (LOCPLINT->adventureInt->heroList.items.size())?(LOCPLINT->adventureInt->heroList.items[LOCPLINT->adventureInt->heroList.selected].first):(NULL);
-	if(currentHero)
-	{
-		int3 bufpos = currentHero->getPosition(false);
-		if (mres)
+		else //remove old path and find a new one
 		{
+			int3 bufpos = currentHero->getPosition(false);
+
 			CPath *& pathForCurhero = LOCPLINT->adventureInt->heroList.items[LOCPLINT->adventureInt->heroList.selected].second;
 			if(pathForCurhero)
 				delete pathForCurhero;
 
 			currentPath = pathForCurhero = LOCPLINT->cb->getPath(bufpos, mp, currentHero);;
 		}
-		return;
-	}
-	
+	} //end of hero is selected "case"
 }
 void CTerrainRect::clickRight(tribool down)
 {
@@ -1318,9 +1313,9 @@ void CAdvMapInt::fmoveHero()
 		return;
 	if (!terrain.currentPath)
 		return;
-	CPath sended(*(terrain.currentPath)); //temporary path - engine will operate on it
+
 	LOCPLINT->pim->unlock();
-	LOCPLINT->moveHero(static_cast<const CGHeroInstance*>(LOCPLINT->adventureInt->selection),&sended);
+	LOCPLINT->moveHero(static_cast<const CGHeroInstance*>(LOCPLINT->adventureInt->selection),*terrain.currentPath);
 	LOCPLINT->pim->lock();
 }
 void CAdvMapInt::fshowSpellbok()

+ 0 - 27
CCallback.cpp

@@ -25,7 +25,6 @@
 #ifdef max
 #undef max
 #endif
-extern CSharedCond<std::set<CPack*> > mess;
 
 /*
  * CCallback.cpp, part of VCMI engine
@@ -67,21 +66,6 @@ bool CCallback::moveHero(const CGHeroInstance *h, int3 dst) const
 {
 	MoveHero pack(dst,h->id);
 	*cl->serv << &pack;
-
-	{//wait till there is server answer
-		boost::unique_lock<boost::mutex> lock(*mess.mx);
-		while(std::find_if(mess.res->begin(),mess.res->end(),&isType<501>) == mess.res->end())
-			mess.cv->wait(lock);
-		std::set<CPack*>::iterator itr = std::find_if(mess.res->begin(),mess.res->end(),&isType<501>);
-		TryMoveHero *tmh = dynamic_cast<TryMoveHero*>(*itr);
-		mess.res->erase(itr);
-		if(!tmh->result)
-		{
-			delete tmh;
-			return false;
-		}
-		delete tmh;
-	}
 	return true;
 }
 void CCallback::selectionMade(int selection, int asker)
@@ -343,9 +327,6 @@ const CCreatureSet* CCallback::getGarrison(const CGObjectInstance *obj) const
 
 int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
 {
-	if(s1->tempOwner != player   ||   s2->tempOwner != player)
-		return -1;
-
 	ArrangeStacks pack(1,p1,p2,s1->id,s2->id,0);
 	*cl->serv << &pack;
 	return 0;
@@ -353,20 +334,12 @@ int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2,
 
 int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
 {
-	if ((s1->tempOwner!= player  ||  s2->tempOwner!=player))
-	{
-		return -1;
-	}
 	ArrangeStacks pack(2,p1,p2,s1->id,s2->id,0);
 	*cl->serv << &pack;
 	return 0;
 }
 int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val)
 {
-	if (s1->tempOwner!= player  ||  s2->tempOwner!=player || (!val))
-	{
-		return -1;
-	}
 	ArrangeStacks pack(3,p1,p2,s1->id,s2->id,val);
 	*cl->serv << &pack;
 	return 0;

+ 2 - 2
CCastleInterface.cpp

@@ -581,8 +581,8 @@ void CCastleInterface::buildingClicked(int building)
 				bool possible = (LOCPLINT->cb->getResourceAmount(6) >= price);
 				if(vstd::contains(hero->artifWorn,ui16(aid+9))) //hero already has machine
 					possible = false;
-				deactivate();
-				(new CBlacksmithDialog(possible,CArtHandler::convertMachineID(aid,false),aid,hero->id))->activate();
+
+				LOCPLINT->pushInt(new CBlacksmithDialog(possible,CArtHandler::convertMachineID(aid,false),aid,hero->id));
 				break;
 			}
 		default:

+ 0 - 3
CGameState.cpp

@@ -898,9 +898,6 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
 		if(map->objects[no]->ID==26)
 		{
 			map->objects[no]->defInfo->handler=NULL;
-			map->removeBlockVisTiles(map->objects[no]);
-			map->objects[no]->defInfo->blockMap[5] = 255;
-			map->addBlockVisTiles(map->objects[no]);
 		}
 		map->objects[no]->hoverName = VLC->generaltexth->names[map->objects[no]->ID];
 	}

+ 15 - 10
CMessage.cpp

@@ -508,8 +508,11 @@ ComponentResolved::ComponentResolved( SComponent *Comp )
 	std::vector<std::string> * brtext = CMessage::breakText(comp->subtitle,11,true,true); //text 
 	txt = CMessage::drawText(brtext,GEOR13);
 	delete brtext;
-	comp->pos.w = img->w;
-	comp->pos.h = img->h + COMPONENT_TO_SUBTITLE + CMessage::getMaxSizes(txt).second;
+
+	//calculate dimensions
+	std::pair<int,int> textSize = CMessage::getMaxSizes(txt);
+	comp->pos.w = std::max(textSize.first, img->w); //bigger of: subtitle width and image width
+	comp->pos.h = img->h + COMPONENT_TO_SUBTITLE + textSize.second;
 }
 
 ComponentResolved::~ComponentResolved()
@@ -537,16 +540,18 @@ ComponentsToBlit::ComponentsToBlit(std::vector<SComponent*> & SComps, int maxw,
 
 	comps.resize(1);
 	int curw = 0;
-	int curr = 0;
+	int curr = 0; //current row
 
 	for(size_t i=0;i<SComps.size();i++)
 	{
-		int toadd = (SComps[i]->getImg()->w + 12 + (_or ? _or->w : 0));
+		ComponentResolved *cur = new ComponentResolved(SComps[i]);
+
+		int toadd = (cur->comp->pos.w + 12 + (_or ? _or->w : 0));
 		if (curw + toadd > maxw)
 		{
 			curr++;
 			amax(w,curw);
-			curw = SComps[i]->getImg()->w;
+			curw = cur->comp->pos.w;
 			comps.resize(curr+1);
 		}
 		else
@@ -554,13 +559,13 @@ ComponentsToBlit::ComponentsToBlit(std::vector<SComponent*> & SComps, int maxw,
 			curw += toadd;
 		}
 
-		comps[curr].push_back(new ComponentResolved(SComps[i]));
+		comps[curr].push_back(cur);
 	}
 
 	for(size_t i=0;i<comps.size();i++)
 	{
 		int maxh = 0;
-		for(size_t j=0;j<comps.size();j++)
+		for(size_t j=0;j<comps[i].size();j++)
 			amax(maxh,comps[i][j]->comp->pos.h);
 		h += maxh + BETWEEN_COMPS_ROWS;
 	}
@@ -575,7 +580,7 @@ void ComponentsToBlit::blitCompsOnSur( SDL_Surface * _or, int inter, int &curh,
 		{
 			ComponentResolved *cur = (comps)[i][j];
 			totalw += cur->comp->pos.w;
-			amax(maxh,cur->comp->pos.h);
+			amax(maxh,cur->comp->pos.h+BETWEEN_COMPS_ROWS);
 		}
 		if(_or)
 		{
@@ -594,7 +599,7 @@ void ComponentsToBlit::blitCompsOnSur( SDL_Surface * _or, int inter, int &curh,
 
 			//blit img
 			int hlp = curh-(cur->comp->pos.h)/2;
-			blitAt(cur->img,curw,hlp,ret);
+			blitAt(cur->img, curw + (cur->comp->pos.w - cur->comp->getImg()->w)/2, hlp, ret);
 			cur->comp->pos.x = curw;
 			cur->comp->pos.y = hlp;
 
@@ -603,7 +608,7 @@ void ComponentsToBlit::blitCompsOnSur( SDL_Surface * _or, int inter, int &curh,
 			CMessage::blitTextOnSur(cur->txt, hlp, ret, cur->comp->pos.x + cur->comp->pos.w/2 );
 
 			//if there is subsequent component blit "or"
-			curw += cur->img->w;
+			curw += cur->comp->pos.w;
 			if(j<((comps)[i].size()-1))
 			{
 				if(_or)

+ 90 - 28
CPlayerInterface.cpp

@@ -59,12 +59,15 @@
 using namespace boost::assign;
 using namespace CSDL_Ext;
 
+void processCommand(const std::string &message, CClient *&client);
+
 extern TTF_Font * GEOR16;
-CPlayerInterface * LOCPLINT;
 extern std::queue<SDL_Event*> events;
 extern boost::mutex eventsM;
-void processCommand(const std::string &message, CClient *&client);
 
+CPlayerInterface * LOCPLINT;
+enum  EMoveState {STOP_MOVE, WAITING_MOVE, CONTINUE_MOVE, DURING_MOVE};
+CondSh<EMoveState> stillMoveHero; //used during hero movement
 
 struct OCM_HLP_CGIN
 {
@@ -465,7 +468,7 @@ void CGarrisonInt::createSlots()
 				new CGarrisonSlot(this, pos.x + (i->first*(58+interx)), pos.y + 64 + intery,i->first,1, 
 									&CGI->creh->creatures[i->second.first],i->second.second);
 		}
-		for(int i=0; i<sup->size(); i++)
+		for(int i=0; i<sdown->size(); i++)
 			if((*sdown)[i] == NULL)
 				(*sdown)[i] = new CGarrisonSlot(this, pos.x + (i*(58+interx)), pos.y + 64 + intery,i,1, NULL, 0);
 	}
@@ -680,8 +683,21 @@ void SComponent::init(Etype Type, int Subtype, int Val)
 		subtitle = CGI->arth->artifacts[Subtype].Name();
 		break;
 	case primskill:
-		description = CGI->generaltexth->arraytxt[2+Subtype];
-		oss << ((Val>0)?("+"):("-")) << Val << " " << CGI->generaltexth->primarySkillNames[Subtype];
+		oss << ((Val>0)?("+"):("-")) << Val << " ";
+		if(Subtype < 4)
+		{
+			description = CGI->generaltexth->arraytxt[2+Subtype];
+			oss << CGI->generaltexth->primarySkillNames[Subtype];
+		}
+		else if(Subtype == 5) //spell points
+		{
+			description = CGI->generaltexth->allTexts[149];
+			oss <<  CGI->generaltexth->allTexts[387];
+		}
+		else
+		{
+			tlog1 << "Wrong subtype=" << Subtype << std::endl;
+		}
 		subtitle = oss.str();
 		break;
 	case secskill44: case secskill:
@@ -697,6 +713,9 @@ void SComponent::init(Etype Type, int Subtype, int Val)
 		description = CGI->spellh->spells[Subtype].descriptions[Val];
 		subtitle = CGI->spellh->spells[Subtype].name;
 		break;
+	case creature:
+		subtitle = boost::lexical_cast<std::string>(Val) + " " + CGI->creh->creatures[Subtype].*(val != 1 ? &CCreature::namePl : &CCreature::nameSing);
+		break;
 	case experience:
 		description = CGI->generaltexth->allTexts[241];
 		oss << Val ;
@@ -774,12 +793,15 @@ SDL_Surface * SComponent::getImg()
 	case spell:
 		return graphics->spellscr->ourImages[subtype].bitmap;
 		break;
+	case creature:
+		return graphics->bigImgs[subtype];
 	}
 	return NULL;
 }
 void SComponent::clickRight (tribool down)
 {
-	LOCPLINT->adventureInt->handleRightClick(description,down,this);
+	if(description.size())
+		LOCPLINT->adventureInt->handleRightClick(description,down,this);
 }
 void SComponent::activate()
 {
@@ -1291,6 +1313,7 @@ void CPlayerInterface::heroMoved(const HeroMoveDetails & details)
 
 	adventureInt->centerOn(details.ho->pos); //actualizing screen pos
 	adventureInt->minimap.draw(screen2);
+	adventureInt->heroList.draw(screen2);
 
 	if(details.style>0  ||  details.src == details.dst)
 		return;
@@ -1303,19 +1326,27 @@ void CPlayerInterface::heroMoved(const HeroMoveDetails & details)
 		if(ho->movement > 50)
 			ho->moveDir = getDir(details.src,details.dst);
 		ho->isStanding = true;
-		//adventureInt->heroList.draw();
-		if (adventureInt->terrain.currentPath && ho->movement>145) //TODO: better condition on movement - check cost of endtile
+
+		if(ho->movement)
 		{
 			delete adventureInt->terrain.currentPath;
 			adventureInt->terrain.currentPath = NULL;
 			adventureInt->heroList.items[adventureInt->heroList.getPosOfHero(ho)].second = NULL;
 		}
+		stillMoveHero.setn(STOP_MOVE);
 		return;
 	}
 
 	if (adventureInt->terrain.currentPath) //&& hero is moving
 	{
 		adventureInt->terrain.currentPath->nodes.erase(adventureInt->terrain.currentPath->nodes.end()-1);
+		if(!adventureInt->terrain.currentPath->nodes.size())
+		{
+
+			delete adventureInt->terrain.currentPath;
+			adventureInt->terrain.currentPath = NULL;
+			adventureInt->heroList.items[adventureInt->heroList.getPosOfHero(ho)].second = NULL;
+		}
 	}
 
 
@@ -1767,16 +1798,20 @@ void CPlayerInterface::heroMoved(const HeroMoveDetails & details)
 			switch(ev->type)
 			{
 			case SDL_MOUSEBUTTONDOWN:
-				stillMoveHero = false;
+				stillMoveHero.setn(STOP_MOVE);
 				break;
 			case SDL_KEYDOWN:
 				if(ev->key.keysym.sym < SDLK_F1)
-					stillMoveHero = false;
+					stillMoveHero.setn(STOP_MOVE);
 				break;
 			}
 			delete ev;
 		}
 	}
+
+	if(stillMoveHero.get() == 1)
+		stillMoveHero.setn(DURING_MOVE);
+
 }
 void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 {
@@ -2145,11 +2180,10 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID,
 
 void CPlayerInterface::battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side) //called by engine when battle starts; side=0 - left, side=1 - right
 {
-	boost::unique_lock<boost::recursive_mutex> un(*pim);
-
 	while(showingDialog->get())
 		SDL_Delay(20);
 
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	battleInt = new CBattleInterface(army1, army2, hero1, hero2, genRect(600, 800, (conf.cc.resx - 800)/2, (conf.cc.resy - 600)/2));
 	pushInt(battleInt);
 }
@@ -2370,7 +2404,16 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
 
 void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<SComponent*> & components)
 {
+	{
+		boost::unique_lock<boost::mutex> un(showingDialog->mx);
+		while(showingDialog->data)
+			showingDialog->cond.wait(un);
+	}
+
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	
+	if(stillMoveHero.get() == DURING_MOVE)//if we are in the middle of hero movement
+		stillMoveHero.setn(STOP_MOVE); //after showing dialog movement will be stopped
 
 	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
 	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
@@ -2480,9 +2523,9 @@ void CPlayerInterface::availableCreaturesChanged( const CGTownInstance *town )
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	if(castleInt)
 	{
-		//CFortScreen *fs = dynamic_cast<CFortScreen*>();
-		//if(fs)
-		//	fs->draw(castleInt,false);
+		CFortScreen *fs = dynamic_cast<CFortScreen*>(listInt.front());
+		if(fs)
+			fs->draw(castleInt,false);
 	}
 }
 
@@ -2523,26 +2566,26 @@ void CPlayerInterface::redrawHeroWin(const CGHeroInstance * hero)
 		adventureInt->infoBar.draw(screen);
 }
 
-bool CPlayerInterface::moveHero( const CGHeroInstance *h, CPath * path )
+bool CPlayerInterface::moveHero( const CGHeroInstance *h, CPath path )
 {
-	if (!h || !path)
+	if (!h)
 		return false; //can't find hero
 
 	bool result = false;
-	path->convert(0);
-	stillMoveHero = true;
+	path.convert(0);
+	boost::unique_lock<boost::mutex> un(stillMoveHero.mx);
+	stillMoveHero.data = CONTINUE_MOVE;
 
-	for(int i=path->nodes.size()-1; i>0; i--)
+	for(int i=path.nodes.size()-1; i>0 && stillMoveHero.data == CONTINUE_MOVE; i--)
 	{
-		{
-			boost::unique_lock<boost::recursive_mutex> un(*pim);
-			if(!stillMoveHero)
-				return result;
-		}
-		int3 endpos(path->nodes[i-1].coord.x, path->nodes[i-1].coord.y, h->pos.z);
-		result = cb->moveHero(h,endpos);
+		stillMoveHero.data = WAITING_MOVE;
+
+		int3 endpos(path.nodes[i-1].coord.x, path.nodes[i-1].coord.y, h->pos.z);
+		cb->moveHero(h,endpos);
+		while(stillMoveHero.data != STOP_MOVE  &&  stillMoveHero.data != CONTINUE_MOVE)
+			stillMoveHero.cond.wait(un);
 	}
-	stillMoveHero = false;
+	//stillMoveHero = false;
 	return result;
 }
 
@@ -2553,6 +2596,19 @@ bool CPlayerInterface::shiftPressed() const
 
 void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd )
 {
+	{
+		boost::unique_lock<boost::mutex> un(showingDialog->mx);
+		while(showingDialog->data)
+			showingDialog->cond.wait(un);
+	}
+
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	while(dialogs.size())
+	{
+		pim->unlock();
+		SDL_Delay(20);
+		pim->lock();
+	}
 	CGarrisonWindow *cgw = new CGarrisonWindow(up,down);
 	cgw->quit->callback += onEnd;
 	pushInt(cgw);
@@ -2621,6 +2677,12 @@ void CPlayerInterface::totalRedraw()
 		objsToBlit.back()->showAll(screen);
 }
 
+void CPlayerInterface::requestRealized( PackageApplied *pa )
+{
+	if(stillMoveHero.get() == DURING_MOVE)
+		stillMoveHero.setn(CONTINUE_MOVE);
+}
+
 CStatusBar::CStatusBar(int x, int y, std::string name, int maxw)
 {
 	bg=BitmapHandler::loadBitmap(name);

+ 2 - 2
CPlayerInterface.h

@@ -559,7 +559,6 @@ public:
 	
 	CCallback * cb; //to communicate with engine
 	const BattleAction *curAction; //during the battle - action currently performed by active stack (or NULL)
-	bool stillMoveHero; //during hero movement - setting this flag to false will stop movement
 
 	std::list<CInfoWindow *> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
 	std::list<IShowActivable *> listInt; //list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on)
@@ -603,6 +602,7 @@ public:
 	void yourTurn();
 	void availableCreaturesChanged(const CGTownInstance *town);
 	void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain);//if gain hero received bonus, else he lost it
+	void requestRealized(PackageApplied *pa);
 	void serialize(COSer<CSaveFile> &h, const int version); //saving
 	void serialize(CISer<CLoadFile> &h, const int version); //loading
 
@@ -638,7 +638,7 @@ public:
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
 	void showInfoDialog(const std::string &text, const std::vector<SComponent*> & components);
 	void showYesNoDialog(const std::string &text, const std::vector<SComponent*> & components, CFunctionList<void()> onYes, CFunctionList<void()> onNo, bool DelComps); //deactivateCur - whether current main interface should be deactivated; delComps - if components will be deleted on window close
-	bool moveHero(const CGHeroInstance *h, CPath * path);
+	bool moveHero(const CGHeroInstance *h, CPath path);
 
 	CPlayerInterface(int Player, int serial);//c-tor
 	~CPlayerInterface();//d-tor

+ 0 - 11
client/NetPacksClient.cpp

@@ -32,8 +32,6 @@
  *
  */
 
-CSharedCond<std::set<CPack*> > mess(new std::set<CPack*>);
-
 void SetResources::applyCl( CClient *cl )
 {
 	cl->playerint[player]->receivedResource(-1,-1);
@@ -168,15 +166,6 @@ void TryMoveHero::applyCl( CClient *cl )
 			i->second->heroMoved(hmd);
 		}
 	}
-
-	//add info for callback
-	if(result<2)
-	{
-		mess.mx->lock();
-		mess.res->insert(new TryMoveHero(*this));
-		mess.mx->unlock();
-		mess.cv->notify_all();
-	}
 }
 
 void SetGarrisons::applyCl( CClient *cl )

+ 247 - 8
hch/CObjectHandler.cpp

@@ -670,8 +670,12 @@ std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentMoraleModifie
 
 	//various morale bonuses (from buildings, artifacts, etc)
 	for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
+	{
 		if(i->type == HeroBonus::MORALE   ||   i->type == HeroBonus::MORALE_AND_LUCK)
+		{
 			ret.push_back(std::make_pair(i->val, i->description));
+		}
+	}
 
 	//leadership
 	if(getSecSkillLevel(6)) 
@@ -2223,22 +2227,257 @@ void CGEvent::onHeroVisit( const CGHeroInstance * h ) const
 		activated(h);
 }
 
-void CGEvent::endBattle( BattleResult *result ) const
+void CGEvent::endBattle( const CGHeroInstance *h, BattleResult *result ) const
 {
 	if(result->winner)
 		return;
-	//give
+
+	giveContents(h,true);
 }
 
 void CGEvent::activated( const CGHeroInstance * h ) const
+{
+	if(army)
+	{
+		InfoWindow iw;
+		iw.player = h->tempOwner;
+		iw.text << message;
+		cb->showInfoDialog(&iw);
+		cb->startBattleI(h->id,army,pos,boost::bind(&CGEvent::endBattle,this,h,_1));
+	}
+	else
+	{
+		giveContents(h,false);
+	}
+}
+
+void CGEvent::giveContents( const CGHeroInstance *h, bool afterBattle ) const
 {
 	InfoWindow iw;
-	iw.player = h->tempOwner;
-	iw.text << message;
-	cb->showInfoDialog(&iw);
-	if(guarders)
-		cb->startBattleI(h->id,guarders,pos,boost::bind(&CGEvent::endBattle,this,_1));
-	cb->removeObject(id);
+	iw.player = h->getOwner();
+
+	bool changesPrimSkill = false;
+	for (int i = 0; i < primskills.size(); i++)
+	{
+		if(primskills[i])
+		{
+			changesPrimSkill = true;
+			break;
+		}
+	}
+
+	if(gainedExp || changesPrimSkill || abilities.size())
+	{
+		getText(iw,afterBattle,175,h);
+
+		if(gainedExp)
+			iw.components.push_back(Component(Component::EXPERIENCE,0,gainedExp,0));
+		for(int i=0; i<primskills.size(); i++)
+			if(primskills[i])
+				iw.components.push_back(Component(Component::PRIM_SKILL,i,primskills[i],0));
+
+		for(int i=0; i<abilities.size(); i++)
+			iw.components.push_back(Component(Component::SEC_SKILL,abilities[i],abilityLevels[i],0));
+
+		cb->showInfoDialog(&iw);
+
+		//give exp
+		if(gainedExp)
+			cb->changePrimSkill(h->id,5,gainedExp,false);
+		//give prim skills
+		for(int i=0; i<primskills.size(); i++)
+			if(primskills[i])
+				cb->changePrimSkill(h->id,i,primskills[i],false);
+
+		//give sec skills
+		for(int i=0; i<abilities.size(); i++)
+		{
+			int curLev = h->getSecSkillLevel(abilities[i]);
+
+			if(curLev  &&  curLev < abilityLevels[i]
+				|| h->secSkills.size() < SKILL_PER_HERO )
+			{
+				cb->changeSecSkill(h->id,abilities[i],abilityLevels[i],true);
+			}
+		}
+
+	}
+
+	if(manaDiff)
+	{
+		getText(iw,afterBattle,luckDiff,176,177,h);
+		iw.components.push_back(Component(Component::PRIM_SKILL,5,manaDiff,0));
+		cb->showInfoDialog(&iw);
+		cb->setManaPoints(h->id, h->mana + manaDiff);
+	}
+
+	if(moraleDiff)
+	{
+		getText(iw,afterBattle,luckDiff,178,179,h);
+		iw.components.push_back(Component(Component::MORALE,0,moraleDiff,0));
+		cb->showInfoDialog(&iw);
+		GiveBonus gb;
+		gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::MORALE,HeroBonus::OBJECT,moraleDiff,id,"");
+		gb.hid = h->id;
+		cb->giveHeroBonus(&gb);
+	}
+
+	if(luckDiff)
+	{
+		getText(iw,afterBattle,luckDiff,180,181,h);
+		iw.components.push_back(Component(Component::LUCK,0,luckDiff,0));
+		cb->showInfoDialog(&iw);
+		GiveBonus gb;
+		gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::LUCK,HeroBonus::OBJECT,luckDiff,id,"");
+		gb.hid = h->id;
+		cb->giveHeroBonus(&gb);
+	}
+
+	iw.components.clear();
+	iw.text.clear();
+	for(int i=0; i<resources.size(); i++)
+	{
+		if(resources[i] < 0)
+			iw.components.push_back(Component(Component::RESOURCE,i,resources[i],0));
+	}
+	if(iw.components.size())
+	{
+		getText(iw,afterBattle,182,h);
+		cb->showInfoDialog(&iw);
+	}
+
+	iw.components.clear();
+	iw.text.clear();
+	for(int i=0; i<resources.size(); i++)
+	{
+		if(resources[i] > 0)
+			iw.components.push_back(Component(Component::RESOURCE,i,resources[i],0));
+	}
+	if(iw.components.size())
+	{
+		getText(iw,afterBattle,183,h);
+		cb->showInfoDialog(&iw);
+	}
+
+	iw.components.clear();
+	for(int i=0; i<artifacts.size(); i++)
+	{
+		iw.components.push_back(Component(Component::ARTIFACT,artifacts[i],0,0));
+	}
+	if(iw.components.size())
+	{
+		cb->showInfoDialog(&iw);
+	}
+
+	for(int i=0; i<resources.size(); i++)
+		if(resources[i])
+			cb->giveResource(h->getOwner(),i,resources[i]);
+
+	for(int i=0; i<artifacts.size(); i++)
+		cb->giveHeroArtifact(artifacts[i],h->id,-2);
+
+	//show dialog with given creatures
+	iw.components.clear();
+	iw.text.clear();
+	for(std::map<si32,std::pair<ui32,si32> >::const_iterator i = creatures.slots.begin(); i != creatures.slots.end(); i++)
+	{
+		iw.components.push_back(Component(Component::CREATURE,i->second.first,i->second.second,0));
+	}
+	if(iw.components.size())
+	{
+		if(afterBattle)
+		{
+			if(iw.components.front().val == 1)
+			{
+				iw.text.addTxt(MetaString::ADVOB_TXT,185);//A %s joins %s's army.
+				iw.text.replacements.push_back(VLC->creh->creatures[iw.components.front().subtype].nameSing);
+			}
+			else
+			{
+				iw.text.addTxt(MetaString::ADVOB_TXT,186);//%s join %s's army.
+				iw.text.replacements.push_back(VLC->creh->creatures[iw.components.front().subtype].namePl);
+			}
+			iw.text.replacements.push_back(h->name);
+		}
+		else
+		{
+			iw.text << message;
+			afterBattle = true;
+		}
+		cb->showInfoDialog(&iw);
+	}
+
+	//check if creatures can be moved to hero army
+	CCreatureSet heroArmy = h->army;
+	CCreatureSet ourArmy = creatures;
+	while(ourArmy)
+	{
+		int slot = heroArmy.getSlotFor(ourArmy.slots.begin()->second.first);
+		if(slot < 0)
+			break;
+
+		heroArmy.slots[slot].first = ourArmy.slots.begin()->second.first;
+		heroArmy.slots[slot].second += ourArmy.slots.begin()->second.second;
+		ourArmy.slots.erase(ourArmy.slots.begin());
+	}
+
+	if(!ourArmy) //all creatures can be moved to hero army - do that
+	{
+		SetGarrisons sg;
+		sg.garrs[h->id] = heroArmy;
+		cb->sendAndApply(&sg);
+	}
+	else //show garrison window and let player pick creatures
+	{
+		SetGarrisons sg;
+		sg.garrs[id] = creatures;
+		cb->sendAndApply(&sg);
+
+		if(removeAfterVisit)
+			cb->showGarrisonDialog(id,h->id,boost::bind(&IGameCallback::removeObject,cb,id));
+		else
+			cb->showGarrisonDialog(id,h->id,0);
+		return;
+	}
+
+	if(!afterBattle)
+	{
+		iw.text << message;
+		cb->showInfoDialog(&iw);
+	}
+
+	if(removeAfterVisit)
+		cb->removeObject(id);
+}
+
+void CGEvent::getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const
+{
+	if(afterBattle)
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT,text);//%s has lost treasure.
+		iw.text.replacements.push_back(h->name);
+	}
+	else
+	{
+		iw.text << message;
+		afterBattle = true;
+	}
+}
+
+void CGEvent::getText( InfoWindow &iw, bool &afterBattle, int val, int positive, int negative, const CGHeroInstance * h ) const
+{
+	iw.components.clear();
+	iw.text.clear();
+	if(afterBattle)
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT,val < 0 ? negative : positive); //%s's luck takes a turn for the worse / %s's luck increases
+		iw.text.replacements.push_back(h->name);
+	}
+	else
+	{
+		iw.text << message;
+		afterBattle = true;
+	}
 }
 
 void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const

+ 10 - 5
hch/CObjectHandler.h

@@ -41,6 +41,7 @@ class CArtifact;
 class CGDefInfo;
 class CSpecObjInfo;
 struct TerrainTile;
+struct InfoWindow;
 
 class DLL_EXPORT CCastleEvent
 {
@@ -375,10 +376,9 @@ public:
 	}
 };
 
-class DLL_EXPORT CGEvent : public CGObjectInstance //event objects
+class DLL_EXPORT CGEvent : public CArmedInstance //event objects
 {
 public:
-	CCreatureSet guarders;
 	std::string message;
 	ui32 gainedExp;
 	si32 manaDiff; //amount of gained / lost mana
@@ -394,18 +394,23 @@ public:
 	ui8 availableFor; //players whom this event is available for
 	ui8 computerActivate; //true if computre player can activate this event
 	ui8 humanActivate; //true if human player can activate this event
+	ui8 removeAfterVisit; //true if event is removed after occurring
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & guarders & message & gainedExp & manaDiff & moraleDiff & luckDiff & resources & primskills
+		h & static_cast<CArmedInstance&>(*this);
+		h & message & gainedExp & manaDiff & moraleDiff & luckDiff & resources & primskills
 			& abilities & abilityLevels & artifacts & spells & creatures & availableFor 
 			& computerActivate & humanActivate;
 	}
 
 	void activated(const CGHeroInstance * h) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
-	void endBattle(BattleResult *result) const;
+	void endBattle(const CGHeroInstance *h, BattleResult *result) const;
+	void giveContents(const CGHeroInstance *h, bool afterBattle) const;
+
+	void getText( InfoWindow &iw, bool &afterBattle, int val, int positive, int negative, const CGHeroInstance * h ) const;
+	void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const;
 };
 
 class DLL_EXPORT CGCreature : public CArmedInstance //creatures on map

+ 1 - 0
lib/NetPacks.h

@@ -101,6 +101,7 @@ struct MetaString : public CPack //2001 helper for object scrips
 		strings.clear();
 		texts.clear();
 		message.clear();
+		replacements.clear();
 	}
 
 	MetaString(){type = 2001;};

+ 18 - 1
lib/NetPacksLib.cpp

@@ -10,6 +10,8 @@
 #include "../hch/CSpellHandler.h"
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string/replace.hpp>
 #include <boost/thread.hpp>
 #include <boost/thread/shared_mutex.hpp>
 
@@ -166,7 +168,22 @@ DLL_EXPORT void GiveBonus::applyGs( CGameState *gs )
 {
 	CGHeroInstance *h = gs->getHero(hid);
 	h->bonuses.push_back(bonus);
-	h->bonuses.back().description = toString(bdescr);
+
+
+	std::string &descr = h->bonuses.back().description;
+
+	if(!bdescr.texts.size() 
+		&& bonus.source == HeroBonus::OBJECT 
+		&& (bonus.type == HeroBonus::LUCK || bonus.type == HeroBonus::MORALE || bonus.type == HeroBonus::MORALE_AND_LUCK)
+		&& gs->map->objects[bonus.id]->ID == 26) //it's morale/luck bonus from an event without description
+	{
+		descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
+		boost::replace_first(descr,"%d",boost::lexical_cast<std::string>(std::abs(bonus.val)));
+	}
+	else
+	{
+		descr = toString(bdescr);
+	}
 }
 
 DLL_EXPORT void ChangeObjPos::applyGs( CGameState *gs )

+ 8 - 2
map.cpp

@@ -1325,6 +1325,10 @@ void Mapa::readDefInfo( unsigned char * bufor, int &i)
 		}
 		else
 			vinya->visitDir = 0xff;
+
+		if(vinya->id == 26)
+			std::memset(vinya->blockMap,255,6);
+
 		defy.push_back(vinya); // add this def to the vector
 	}
 }
@@ -1366,7 +1370,7 @@ void Mapa::readObjects( unsigned char * bufor, int &i)
 					}
 					if(bufor[i++])
 					{
-						evnt->guarders = readCreatureSet(bufor,i,7,(version>RoE)); 
+						evnt->army = readCreatureSet(bufor,i,7,(version>RoE)); 
 					}
 					i+=4;
 				}
@@ -1415,7 +1419,9 @@ void Mapa::readObjects( unsigned char * bufor, int &i)
 				i+=8;
 				evnt->availableFor = readNormalNr(bufor,i, 1); ++i;
 				evnt->computerActivate = readNormalNr(bufor,i, 1); ++i;
-				evnt->humanActivate = readNormalNr(bufor,i, 1); ++i;
+				evnt->removeAfterVisit = readNormalNr(bufor,i, 1); ++i;
+				evnt->humanActivate = true;
+
 				i+=4;
 				break;
 			}

+ 3 - 2
server/CGameHandler.cpp

@@ -409,8 +409,6 @@ askInterfaceForMove:
 	//end battle, remove all info, free memory
 	giveExp(*battleResult.data);
 	sendAndApply(battleResult.data);
-	if(cb)
-		cb(battleResult.data);
 
 	//if one hero has lost we will erase him
 	if(battleResult.data->winner!=0 && hero1)
@@ -430,6 +428,9 @@ askInterfaceForMove:
 	if(battleResult.data->exp[1] && hero2)
 		changePrimSkill(hero2->id,4,battleResult.data->exp[1]);
 
+	if(cb)
+		cb(battleResult.data);
+
 	delete battleResult.data;
 
 }