浏览代码

* fixed crashes with paths when fastly switching heroes
* fixed and improved slider behavior
* fixed possible thread access conflicts
* support for attacking town with visiting hero
* fixed dwellings saving
* minor improvements

Michał W. Urbańczyk 16 年之前
父节点
当前提交
efb1049860

+ 7 - 0
CCallback.cpp

@@ -738,6 +738,13 @@ void CCallback::setSelection(const CArmedInstance * obj)
 	ss.player = player;
 	ss.id = obj->id;
 	sendRequest(&ss);
+
+	if(obj->ID == HEROI_TYPE)
+	{
+		cl->gs->calculatePaths(static_cast<const CGHeroInstance *>(obj), *cl->pathInfo);
+		//nasty workaround. TODO: nice workaround
+		cl->gs->getPlayer(player)->currentSelection = obj->id;
+	}
 }
 
 void CCallback::recruitHero(const CGTownInstance *town, const CGHeroInstance *hero)

+ 7 - 10
client/AdventureMapButton.cpp

@@ -91,6 +91,7 @@ AdventureMapButton::AdventureMapButton ()
 	ourObj=NULL;
 	state=0;
 	blocked = actOnDown = false;
+	used = LCLICK | RCLICK | HOVER | KEYBOARD;
 }
 //AdventureMapButton::AdventureMapButton( std::string Name, std::string HelpBox, boost::function<void()> Callback, int x, int y, std::string defName, bool activ,  std::vector<std::string> * add, bool playerColoredButton)
 //{
@@ -435,10 +436,9 @@ void CHighlightableButtonsGroup::block( ui8 on )
 
 void CSlider::sliderClicked()
 {
-	if(!moving)
+	if(!(active & MOVE))
 	{
 		activateMouseMove();
-		moving = true;
 	}
 }
 
@@ -527,26 +527,24 @@ void CSlider::clickLeft(tribool down, bool previousState)
 		float rw = 0;
 		if(horizontal)
 		{
-			pw = GH.current->motion.x-pos.x-16;
+			pw = GH.current->motion.x-pos.x-25;
 			rw = pw / ((float)(pos.w-48));
 		}
 		else
 		{
-			pw = GH.current->motion.y-pos.y-16;
+			pw = GH.current->motion.y-pos.y-24;
 			rw = pw / ((float)(pos.h-48));
 		}
-		if(pw < 0  ||  pw > (horizontal ? pos.w : pos.h) - 32)
+		if(pw < -8  ||  pw > (horizontal ? pos.w : pos.h) - 40)
 			return;
 // 		if (rw>1) return;
 // 		if (rw<0) return;
+		slider->clickLeft(true, slider->pressedL);
 		moveTo(rw * positions  +  0.5f);
 		return;
 	}
-	if(moving)
-	{
+	if(active & MOVE)
 		deactivateMouseMove();
-		moving = false;
-	}
 }
 
 CSlider::~CSlider()
@@ -561,7 +559,6 @@ CSlider::CSlider(int x, int y, int totalw, boost::function<void(int)> Moved, int
 	setAmount(amount);
 
 	used = LCLICK;
-	moving = false;
 	strongInterest = true;
 
 

+ 1 - 1
client/AdventureMapButton.h

@@ -120,7 +120,7 @@ public:
 		amount, //how many elements
 		positions, //number of highest position (0 if there is only one)
 		value; //first active element
-	bool horizontal, moving;
+	bool horizontal;
 	CDefEssential *imgs ;
 
 	boost::function<void(int)> moved;

+ 4 - 0
client/CAdvmapInterface.cpp

@@ -1896,13 +1896,17 @@ void CAdvMapInt::select(const CArmedInstance *sel )
 	{
 		int pos = vstd::findPos(townList.items,sel);
 		townList.selected = pos;
+		townList.fixPos();
 	}
 	else //hero selected
 	{
 		const CGHeroInstance *h = static_cast<const CGHeroInstance*>(sel);
 
 		if(LOCPLINT->getWHero(heroList.selected) != h)
+		{
 			heroList.selected = heroList.getPosOfHero(h);
+			heroList.fixPos();
+		}
 
 		if(vstd::contains(paths,h)) //hero has assigned path
 		{

+ 2 - 0
client/CPlayerInterface.cpp

@@ -1504,12 +1504,14 @@ void CPlayerInterface::requestRealized( PackageApplied *pa )
 
 void CPlayerInterface::heroExchangeStarted(si32 hero1, si32 hero2)
 {
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	GH.pushInt(new CExchangeWindow(hero2, hero1));
 }
 
 void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 {
 	//redraw minimap if owner changed
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	if(sop->what == 1)
 	{
 		const CGObjectInstance * obj = cb->getObjectInfo(sop->id);

+ 3 - 3
client/Client.h

@@ -104,9 +104,9 @@ public:
 	void heroVisitCastle(int obj, int heroID){};
 	void stopHeroVisitCastle(int obj, int heroID){};
 	void giveHeroArtifact(int artid, int hid, int position){}; //pos==-1 - first free slot in backpack=0; pos==-2 - default if available or backpack
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL){}; //use hero=NULL for no hero
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank, boost::function<void(BattleResult*)> cb = 0){}; //if any of armies is hero, hero will be used
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0){}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL){}; //use hero=NULL for no hero
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false){}; //if any of armies is hero, hero will be used
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false){}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
 	void setAmount(int objid, ui32 val){};
 	bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255){return false;};
 	void giveHeroBonus(GiveBonus * bonus){};

+ 25 - 0
client/GUIClasses.cpp

@@ -1126,6 +1126,20 @@ CList::CList(int Size)
 {
 }
 
+void CList::fixPos()
+{
+	int oldFrom = from;
+	if(selected < 0) //no selection, do nothing
+		return;
+	if(selected < from) //scroll up
+		from = selected;
+	else if(from + SIZE <= selected)
+		from = selected - SIZE + 1; 
+
+	amin(from, size() - SIZE);
+	amax(from, 0);
+}
+
 CHeroList::CHeroList(int Size)
 :CList(Size), heroes(LOCPLINT->wanderingHeroes)
 {
@@ -1422,6 +1436,12 @@ void CHeroList::show( SDL_Surface * to )
 {
 
 }
+
+int CHeroList::size()
+{
+	return heroes.size();
+}
+
 CTownList::~CTownList()
 {
 	delete arrup;
@@ -1659,6 +1679,11 @@ void CTownList::show( SDL_Surface * to )
 
 }
 
+int CTownList::size()
+{
+	return items.size();
+}
+
 CCreaturePic::CCreaturePic(const CCreature *cre, bool Big)
 :c(cre),big(Big)
 {

+ 4 - 0
client/GUIClasses.h

@@ -272,6 +272,8 @@ public:
 	virtual void genList()=0;
 	virtual void select(int which)=0;
 	virtual void draw(SDL_Surface * to)=0;
+	virtual int size() = 0; //how many elements do we have
+	void fixPos(); //scrolls list, so the selection will be visible
 };
 class CHeroList
 	: public CList
@@ -295,6 +297,7 @@ public:
 	void draw(SDL_Surface * to);
 	void show(SDL_Surface * to);
 	void init();
+	int size(); //how many elements do we have
 };
 
 class CTownList
@@ -316,6 +319,7 @@ public:
 	void keyPressed (const SDL_KeyboardEvent & key);  //call-in
 	void draw(SDL_Surface * to);
 	void show(SDL_Surface * to);
+	int size(); //how many elements do we have
 };
 
 class CCreaturePic //draws picture with creature on background, use nextFrame=true to get animation

+ 1 - 1
client/NetPacksClient.cpp

@@ -547,7 +547,7 @@ void SetSelection::applyCl(CClient *cl)
 	if(!h)
 		return;
 
-	CPackForClient::GS(cl)->calculatePaths(h, *cl->pathInfo);
+	//CPackForClient::GS(cl)->calculatePaths(h, *cl->pathInfo);
 }
 
 void ShowInInfobox::applyCl(CClient *cl)

+ 14 - 12
hch/CObjectHandler.cpp

@@ -777,10 +777,12 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
 			//exchange
 			cb->heroExchange(id, h->id);
 		}
-		else
+		else //battle
 		{
-			//battle
-			cb->startBattleI(h,	this, false);
+			if(visitedTown) //we're in town
+				visitedTown->onHeroVisit(h); //town will handle attacking
+			else
+				cb->startBattleI(h,	this);
 		}
 	}
 	else if(ID == 62) //prison
@@ -1350,7 +1352,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h, ui32 answer ) co
 void CGDwelling::wantsFight( const CGHeroInstance *h, ui32 answer ) const
 {
 	if(answer)
-		cb->startBattleI(h, this, false, boost::bind(&CGDwelling::fightOver, this, h, _1));
+		cb->startBattleI(h, this, boost::bind(&CGDwelling::fightOver, this, h, _1));
 }
 
 void CGDwelling::fightOver(const CGHeroInstance *h, BattleResult *result) const
@@ -1503,7 +1505,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
 	if(getOwner() != h->getOwner())
 	{
 		//TODO ally check
-		if(army)
+		if(army || visitingHero)
 		{
 			const CGHeroInstance *defendingHero = NULL;
 			if(visitingHero)
@@ -2226,7 +2228,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
 
 void CGCreature::fight( const CGHeroInstance *h ) const
 {
-	cb->startBattleI(h, this, false, boost::bind(&CGCreature::endBattle,this,_1));
+	cb->startBattleI(h, this, boost::bind(&CGCreature::endBattle,this,_1));
 }
 
 void CGCreature::flee( const CGHeroInstance * h ) const
@@ -2362,7 +2364,7 @@ void CGResource::collectRes( int player ) const
 void CGResource::fightForRes(ui32 agreed, const CGHeroInstance *h) const
 {
 	if(agreed)
-		cb->startBattleI(h, this, false, boost::bind(&CGResource::endBattle,this,_1,h));
+		cb->startBattleI(h, this, boost::bind(&CGResource::endBattle,this,_1,h));
 }
 
 void CGResource::endBattle( BattleResult *result, const CGHeroInstance *h ) const
@@ -2614,7 +2616,7 @@ void CGArtifact::pick(const CGHeroInstance * h) const
 void CGArtifact::fightForArt( ui32 agreed, const CGHeroInstance *h ) const
 {
 	if(agreed)
-		cb->startBattleI(h, this, false, boost::bind(&CGArtifact::endBattle,this,_1,h));
+		cb->startBattleI(h, this, boost::bind(&CGArtifact::endBattle,this,_1,h));
 }
 
 void CGArtifact::endBattle( BattleResult *result, const CGHeroInstance *h ) const
@@ -3118,7 +3120,7 @@ void CGPandoraBox::open( const CGHeroInstance * h, ui32 accept ) const
 			iw.player = h->tempOwner;
 			iw.text.addTxt(MetaString::ADVOB_TXT, 16);
 			cb->showInfoDialog(&iw);
-			cb->startBattleI(h, this, false, boost::bind(&CGPandoraBox::endBattle, this, h, _1)); //grants things after battle
+			cb->startBattleI(h, this, boost::bind(&CGPandoraBox::endBattle, this, h, _1)); //grants things after battle
 		}
 		else if (message.size() == 0 && resources.size() == 0
 				&& primskills.size() == 0 && abilities.size() == 0
@@ -3415,7 +3417,7 @@ void CGEvent::activated( const CGHeroInstance * h ) const
 		else
 			iw.text.addTxt(MetaString::ADVOB_TXT, 16);
 		cb->showInfoDialog(&iw);
-		cb->startBattleI(h, this, false, boost::bind(&CGEvent::endBattle,this,h,_1));
+		cb->startBattleI(h, this, boost::bind(&CGEvent::endBattle,this,h,_1));
 	}
 	else
 	{
@@ -3623,7 +3625,7 @@ void CGGarrison::onHeroVisit (const CGHeroInstance *h) const
 {
 	if (h->tempOwner != tempOwner && army) {
 		//TODO: Find a way to apply magic garrison effects in battle.
-		cb->startBattleI(h, this, false, boost::bind(&CGGarrison::fightOver, this, h, _1));
+		cb->startBattleI(h, this, boost::bind(&CGGarrison::fightOver, this, h, _1));
 		return;
 	}
 
@@ -4026,7 +4028,7 @@ void CBank::fightGuards (const CGHeroInstance * h, ui32 accept) const
 	if (accept)
 	{
 		cb->setObjProperty (id, 17, ran()); //get army
-		cb->startBattleI (h, this, true, boost::bind (&CBank::endBattle, this, h, _1));
+		cb->startBattleI (h, this, boost::bind (&CBank::endBattle, this, h, _1), true);
 	}
 }
 void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) const

+ 13 - 7
lib/CGameState.cpp

@@ -1485,16 +1485,22 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
 		//init visiting and garrisoned heroes
 		for(unsigned int l=0; l<k->second.heroes.size();l++)
 		{ 
+			CGHeroInstance *h = k->second.heroes[l];
 			for(unsigned int m=0; m<k->second.towns.size();m++)
 			{
-				int3 vistile = k->second.towns[m]->pos; vistile.x--; //tile next to the entrance
-				if(vistile == k->second.heroes[l]->pos || k->second.heroes[l]->pos==k->second.towns[m]->pos)
+				CGTownInstance *t = k->second.towns[m];
+				int3 vistile = t->pos; vistile.x--; //tile next to the entrance
+				if(vistile == h->pos || h->pos==t->pos)
 				{
-					k->second.towns[m]->visitingHero = k->second.heroes[l];
-					k->second.heroes[l]->visitedTown = k->second.towns[m];
-					k->second.heroes[l]->inTownGarrison = false;
-					if(k->second.heroes[l]->pos==k->second.towns[m]->pos)
-						k->second.heroes[l]->pos.x -= 1;
+					t->visitingHero = h;
+					h->visitedTown = t;
+					h->inTownGarrison = false;
+					if(h->pos == t->pos) //visiting hero placed in the editor has same pos as the town - we need to correct it
+					{
+						map->removeBlockVisTiles(h);
+						h->pos.x -= 1;
+						map->addBlockVisTiles(h);
+					}
 					break;
 				}
 			}

+ 3 - 3
lib/IGameCallback.h

@@ -80,9 +80,9 @@ public:
 	virtual void heroVisitCastle(int obj, int heroID)=0;
 	virtual void stopHeroVisitCastle(int obj, int heroID)=0;
 	virtual void giveHeroArtifact(int artid, int hid, int position)=0; //pos==-1 - first free slot in backpack=0; pos==-2 - default if available or backpack
-	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL)=0; //use hero=NULL for no hero
-	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank, boost::function<void(BattleResult*)> cb = 0)=0; //if any of armies is hero, hero will be used
-	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
+	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL)=0; //use hero=NULL for no hero
+	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false)=0; //if any of armies is hero, hero will be used
+	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
 	//virtual void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb)=0; //for hero<=>neutral army
 	virtual void setAmount(int objid, ui32 val)=0;
 	virtual bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255)=0;

+ 1 - 0
lib/RegisterTypes.cpp

@@ -25,6 +25,7 @@ void registerTypes1(Serializer &s)
 	s.template registerType<CTownBonus>();
 	s.template registerType<CGPandoraBox>();
 	s.template registerType<CGEvent>();
+	s.template registerType<CGDwelling>();
 	s.template registerType<CGVisitableOPH>();
 	s.template registerType<CGVisitableOPW>();
 	s.template registerType<CGTeleport>();

+ 4 - 4
server/CGameHandler.cpp

@@ -1501,7 +1501,7 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 					return true;
 				}
 				//TODO: check for ally
-				startBattleI(h, dh, false);
+				startBattleI(h, dh);
 				return true;
 			}
 		}
@@ -1670,7 +1670,7 @@ void CGameHandler::startBattleI(const CArmedInstance *army1, const CArmedInstanc
 	boost::thread(boost::bind(&CGameHandler::startBattle, this, army1, army2, tile, hero1, hero2, creatureBank, cb, town));
 }
 
-void CGameHandler::startBattleI( const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank, boost::function<void(BattleResult*)> cb )
+void CGameHandler::startBattleI( const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb, bool creatureBank )
 {
 	startBattleI(army1, army2, tile,
 		army1->ID == HEROI_TYPE ? static_cast<const CGHeroInstance*>(army1) : NULL, 
@@ -1678,9 +1678,9 @@ void CGameHandler::startBattleI( const CArmedInstance *army1, const CArmedInstan
 		creatureBank, cb);
 }
 
-void CGameHandler::startBattleI( const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank, boost::function<void(BattleResult*)> cb)
+void CGameHandler::startBattleI( const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb, bool creatureBank)
 {
-	startBattleI(army1, army2, army2->pos - army2->getVisitableOffset(), creatureBank, cb);
+	startBattleI(army1, army2, army2->pos - army2->getVisitableOffset(), cb, creatureBank);
 }
 
 //void CGameHandler::startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb) //for hero<=>neutral army

+ 3 - 4
server/CGameHandler.h

@@ -122,10 +122,9 @@ public:
 	void giveHeroArtifact(int artid, int hid, int position); //pos==-1 - first free slot in backpack; pos==-2 - default if available or backpack
 	void moveArtifact(int hid, int oldPosition, int destPos);
 	void removeArtifact(int hid, int pos);
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank, boost::function<void(BattleResult*)> cb);
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0); //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
-	//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL); //use hero=NULL for no hero
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false); //if any of armies is hero, hero will be used
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false); //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army
 	void setAmount(int objid, ui32 val);
 	bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255);
 	void giveHeroBonus(GiveBonus * bonus);