Browse Source

* server sends confirmation (given later to player interface) after applying request (will be needed for AI)
* created new package for injuring multiple units - needed for area spells (not tested)
* proper screen updating on garrison change
* spell effects will be removed when they time out
* Corpse (Skeleton) will be accessible from all directions
* new objects supported:
- Corpse
- Lean To
- Wagon
- Warrior's Tomb

* several minor improvements

Michał W. Urbańczyk 16 năm trước cách đây
mục cha
commit
d80afb1902

+ 2 - 0
CGameInterface.h

@@ -35,6 +35,7 @@ struct BattleStackAttacked;
 struct SpellCasted;
 struct SetStackEffect;
 struct HeroBonus;
+struct PackageApplied;
 class CLoadFile;
 class CSaveFile;
 template <typename Serializer> class CISer;
@@ -88,6 +89,7 @@ public:
 	virtual void yourTurn(){};
 	virtual void availableCreaturesChanged(const CGTownInstance *town){};
 	virtual void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
+	virtual void requestRealized(PackageApplied *pa){}
 	virtual void serialize(COSer<CSaveFile> &h, const int version){}; //saving
 	virtual void serialize(CISer<CLoadFile> &h, const int version){}; //loading
 

+ 6 - 6
CPlayerInterface.cpp

@@ -2112,8 +2112,8 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
 			wasGarrison = true;
 		}
 	}
-	if(wasGarrison)
-		LOCPLINT->totalRedraw();
+
+	LOCPLINT->totalRedraw();
 }
 
 void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID, int what) //what: 1 - built, 2 - demolished
@@ -4761,14 +4761,14 @@ void CGarrisonWindow::deactivate()
 
 void CGarrisonWindow::show(SDL_Surface * to)
 {
-	blitAt(graphics->flags->ourImages[garr->odown->getOwner()].bitmap,pos.x+29,pos.y+125,to);
-	blitAt(graphics->portraitLarge[static_cast<const CGHeroInstance*>(garr->odown)->portrait],pos.x+29,pos.y+222,to);
-	printAtMiddle(CGI->generaltexth->allTexts[709],pos.x+275,pos.y+30,GEOR16,tytulowy,to);
-
 	blitAt(bg,pos,to);
 	split->show(to);
 	quit->show(to);
 	garr->show(to);
+
+	blitAt(graphics->flags->ourImages[garr->odown->getOwner()].bitmap,pos.x+29,pos.y+125,to);
+	blitAt(graphics->portraitLarge[static_cast<const CGHeroInstance*>(garr->odown)->portrait],pos.x+29,pos.y+222,to);
+	printAtMiddle(CGI->generaltexth->allTexts[709],pos.x+275,pos.y+30,GEOR16,tytulowy,to);
 }
 
 CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance *down )

+ 11 - 5
client/Client.cpp

@@ -124,10 +124,16 @@ void CClient::run()
 		CPack *pack;
 		while(1)
 		{
-			tlog5 << "Listening... ";
-			*serv >> pack;
-			tlog5 << "\treceived server message of type " << typeid(*pack).name() << std::endl;
-			CBaseForCLApply *apply = applier->apps[typeList.getTypeID(pack)];
+
+			//get the package from the server
+			{
+				boost::unique_lock<boost::mutex> lock(*serv->rmx);
+				tlog5 << "Listening... ";
+				*serv >> pack;
+				tlog5 << "\treceived server message of type " << typeid(*pack).name() << std::endl;
+			}
+
+			CBaseForCLApply *apply = applier->apps[typeList.getTypeID(pack)]; //find the applier
 			if(apply)
 			{
 				apply->applyOnClBefore(this,pack);
@@ -139,7 +145,7 @@ void CClient::run()
 			}
 			else
 			{
-				tlog5 << "Message cannot be applied, cannot find applier!\n";
+				tlog1 << "Message cannot be applied, cannot find applier!\n";
 			}
 			delete pack;
 			pack = NULL;

+ 2 - 2
client/Client.h

@@ -79,7 +79,7 @@ public:
 
 	//not working yet, will be implement somewhen later with support for local-sim-based gameplay
 	void changeSpells(int hid, bool give, const std::set<ui32> &spells){};
-	void removeObject(int objid){};
+	bool removeObject(int objid){return false;};
 	void setBlockVis(int objid, bool bv){};
 	void setOwner(int objid, ui8 owner){};
 	void setHoverName(int objid, MetaString * name){};
@@ -98,7 +98,7 @@ public:
 	void startBattleI(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb){}; //use hero=NULL for no hero
 	void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb){}; //for hero<=>neutral army
 	void setAmount(int objid, ui32 val){};
-	void moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255){};
+	bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255){return false;};
 	void giveHeroBonus(GiveBonus * bonus){};
 	void setMovePoints(SetMovePoints * smp){};
 	void setManaPoints(int hid, int val){};

+ 11 - 0
client/NetPacksClient.cpp

@@ -404,6 +404,12 @@ void SetStackEffect::applyCl( CClient *cl )
 		cl->playerint[GS(cl)->curB->side2]->battleStacksEffectsSet(*this);
 }
 
+void StacksInjured::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleStacksAttacked,stacks);
+	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStacksAttacked,stacks);
+}
+
 CGameState* CPackForClient::GS( CClient *cl )
 {
 	return cl->gs;
@@ -418,6 +424,11 @@ void EndAction::applyCl( CClient *cl )
 	cl->curbaction = NULL;
 }
 
+void PackageApplied::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(GS(cl)->currentPlayer,requestRealized,this);
+}
+
 void SystemMessage::applyCl( CClient *cl )
 {
 	std::ostringstream str;

+ 1 - 0
global.h

@@ -54,6 +54,7 @@ enum ElossCon {lossCastle, lossHero, timeExpires, lossStandard=255};
 enum EHeroClasses {HERO_KNIGHT, HERO_CLERIC, HERO_RANGER, HERO_DRUID, HERO_ALCHEMIST, HERO_WIZARD,
 	HERO_DEMONIAC, HERO_HERETIC, HERO_DEATHKNIGHT, HERO_NECROMANCER, HERO_WARLOCK, HERO_OVERLORD,
 	HERO_BARBARIAN, HERO_BATTLEMAGE, HERO_BEASTMASTER, HERO_WITCH, HERO_PLANESWALKER, HERO_ELEMENTALIST};
+enum EartClass {ART_SPECIAL=1, ART_TREASURE=2, ART_MINOR=4, ART_MAJOR=8, ART_RELIC=16}; //artifact classes
 enum EAbilities {DOUBLE_WIDE, FLYING, SHOOTER, TWO_HEX_ATTACK, SIEGE_ABILITY, SIEGE_WEAPON, 
 KING1, KING2, KING3, MIND_IMMUNITY, NO_OBSTACLE_PENALTY, NO_CLOSE_COMBAT_PENALTY, 
 JOUSTING, FIRE_IMMUNITY, TWICE_ATTACK, NO_ENEMY_RETALIATION, NO_MORAL_PENALTY, 

+ 5 - 5
hch/CArtHandler.cpp

@@ -43,7 +43,7 @@ void CArtHandler::loadArtifacts(bool onlyTxt)
 	std::vector<ui16> slots;
 	slots += 17, 16, 15,14,13, 18, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0;
 	std::map<char,EartClass> classes = 
-	  map_list_of('S',SartClass)('T',TartClass)('N',NartClass)('J',JartClass)('R',RartClass);
+	  map_list_of('S',ART_SPECIAL)('T',ART_TREASURE)('N',ART_MINOR)('J',ART_MAJOR)('R',ART_RELIC);
 	std::string buf = bitmaph->getTextFile("ARTRAITS.TXT"), dump, pom;
 	int it=0;
 	for(int i=0; i<2; ++i)
@@ -112,16 +112,16 @@ void CArtHandler::sortArts()
 	{
 		switch (artifacts[i].aClass)
 		{
-		case TartClass:
+		case ART_TREASURE:
 			treasures.push_back(&(artifacts[i]));
 			break;
-		case NartClass:
+		case ART_MINOR:
 			minors.push_back(&(artifacts[i]));
 			break;
-		case JartClass:
+		case ART_MAJOR:
 			majors.push_back(&(artifacts[i]));
 			break;
-		case RartClass:
+		case ART_RELIC:
 			relics.push_back(&(artifacts[i]));
 			break;
 		}

+ 8 - 10
hch/CArtHandler.h

@@ -6,17 +6,15 @@
 #include <string>
 #include <vector>
 
-/*
- * CArtHandler.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
+/*
+ * CArtHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
  */
-
-enum EartClass {SartClass=0, TartClass, NartClass, JartClass, RartClass}; //artifact class (relict, treasure, strong, weak etc.)
 class CDefHandler;
 
 class DLL_EXPORT CArtifact //container for artifacts

+ 1 - 1
hch/CDefObjInfoHandler.cpp

@@ -89,7 +89,7 @@ void CDefObjInfoHandler::load()
 		}
 		else 
 		{
-			static int visitableFromTop[] = {111,33,81,12,9,212,215}; //whirlpool, garrison, scholar, campfire, borderguard, bordergate, questguard
+			static int visitableFromTop[] = {111,33,81,12,9,212,215,22}; //whirlpool, garrison, scholar, campfire, borderguard, bordergate, questguard, corpse
 			for(int i=0; i < ARRAY_COUNT(visitableFromTop); i++)
 			{
 				if(visitableFromTop[i] == nobj->id)

+ 204 - 4
hch/CObjectHandler.cpp

@@ -778,7 +778,8 @@ double CGHeroInstance::getHeroStrength() const
 
 int CGHeroInstance::getTotalStrength() const
 {
-	return getHeroStrength() * getArmyStrength();
+	double ret = getHeroStrength() * getArmyStrength();
+	return (int) ret;
 }
 
 ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell) const
@@ -1444,14 +1445,14 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 
 	//TODO: it's provisional formula, should be replaced with original one (or something closer to it)
 	//TODO: should be deterministic (will be needed for Vision spell)
-	int hlp2 = (hlp - 2)*1000;
+	int hlp2 = (int) (hlp - 2)*1000;
 	if(!neverFlees   
 		&& hlp2 >= 0 
 		&& rand()%2000 < hlp2
 	)
-		return -1;
+		return -1; //flee
 	else
-		return -2;
+		return -2; //fight
 
 }
 
@@ -2425,3 +2426,202 @@ void CGScholar::initObj()
 		}
 	}
 }
+
+void CGOnceVisitable::onHeroVisit( const CGHeroInstance * h ) const
+{
+	int txtid = -1;
+	switch(ID)
+	{
+	case 22: //Corpse
+		txtid = 37; 
+		break;
+	case 39: //Lean To
+		txtid = 64;
+		break;
+	case 105://Wagon
+		txtid = 154;
+		break;
+	case 108:
+		break;
+	default:
+		tlog1 << "Error: Unknown object (" << ID <<") treated as CGOnceVisitable!\n";
+		return;
+	}
+
+	if(ID == 108)//Warrior's Tomb
+	{
+		//ask if player wants to search the Tomb
+		BlockingDialog bd(true, false);
+		bd.player = h->getOwner();
+		bd.text.addTxt(MetaString::ADVOB_TXT,161);
+		cb->showBlockingDialog(&bd,boost::bind(&CGOnceVisitable::searchTomb,this,h,_1));
+		return;
+	}
+
+	InfoWindow iw;
+	iw.player = h->getOwner();
+
+	if(players.size()) //we have been already visited...
+	{
+		txtid++;
+		if(ID == 105) //wagon has extra text (for finding art) we need to ommit
+			txtid++;
+
+		iw.text.addTxt(MetaString::ADVOB_TXT, txtid);
+	}
+	else //first visit - give bonus!
+	{
+		if(ID == 105  &&  artOrRes == 1) 
+		{
+			txtid++;
+			iw.text.replacements.push_back(VLC->arth->artifacts[bonusType].Name());
+		}
+
+
+		switch(artOrRes)
+		{
+		case 0:
+			txtid++;
+			break;
+		case 1: //art
+			iw.components.push_back(Component(Component::ARTIFACT,bonusType,0,0));
+			cb->giveHeroArtifact(bonusType,h->id,-2);
+			break;
+		case 2: //res
+			iw.components.push_back(Component(Component::RESOURCE,bonusType,bonusVal,0));
+			cb->giveResource(h->getOwner(),bonusType,bonusVal);
+			break;
+		}
+
+		iw.text.addTxt(MetaString::ADVOB_TXT, txtid);
+	}
+
+	cb->showInfoDialog(&iw);
+	cb->setObjProperty(id,10,h->getOwner());
+}
+
+const std::string & CGOnceVisitable::getHoverText() const
+{
+	hoverName = VLC->generaltexth->names[ID] + " ";
+
+	hoverName += (hasVisited(cb->getCurrentPlayer())
+		? (VLC->generaltexth->allTexts[352])  //visited
+		: ( VLC->generaltexth->allTexts[353])); //not visited
+
+	return hoverName;
+}
+
+void CGOnceVisitable::initObj()
+{
+	switch(ID)
+	{
+	case 22: //Corpse
+		{
+			blockVisit = true;
+			int hlp = ran()%100;
+			if(hlp < 20)
+			{
+				artOrRes = 1;
+				std::vector<CArtifact*> arts; 
+				cb->getAllowed(arts, ART_TREASURE | ART_MINOR | ART_MAJOR);
+				bonusType = arts[ran() % arts.size()]->id;
+			}
+			else
+			{
+				artOrRes = 0;
+			}
+		}
+		break;
+
+	case 39: //Lean To
+		{
+			artOrRes = 2;
+			bonusType = ran()%6; //any basic resource without gold
+			bonusVal = ran()%4 + 1;
+			break;
+		}
+
+	case 108://Warrior's Tomb
+		{
+			artOrRes = 1;
+
+			std::vector<CArtifact*> arts; 
+
+			int hlp = ran()%100;
+			if(hlp < 30)
+				cb->getAllowed(arts,ART_TREASURE);
+			else if(hlp < 80)
+				cb->getAllowed(arts,ART_MINOR);
+			else if(hlp < 95)
+				cb->getAllowed(arts,ART_MAJOR);
+			else
+				cb->getAllowed(arts,ART_RELIC);
+
+			bonusType = arts[ran() % arts.size()]->id;
+		}
+		break;
+
+	case 105://Wagon
+		{
+			int hlp = ran()%100;
+
+			if(hlp < 10)
+			{
+				artOrRes = 0; // nothing... :(
+			}
+			else if(hlp < 50) //minor or treasure art
+			{
+				artOrRes = 1;
+				std::vector<CArtifact*> arts; 
+				cb->getAllowed(arts, ART_TREASURE | ART_MINOR);
+				bonusType = arts[ran() % arts.size()]->id;
+			}
+			else //2 - 5 of non-gold resource
+			{
+				artOrRes = 2;
+				bonusType = ran()%6;
+				bonusVal = ran()%4 + 2;
+			}
+
+			break;
+		}
+	}
+}
+
+void CGOnceVisitable::searchTomb(const CGHeroInstance *h, ui32 accept) const
+{
+	if(accept)
+	{
+		InfoWindow iw;
+		iw.player = h->getOwner();
+		iw.components.push_back(Component(Component::MORALE,0,-3,0));
+
+		if(players.size()) //we've been already visited, player found nothing
+		{
+			iw.text.addTxt(MetaString::ADVOB_TXT,163);
+		}
+		else //first visit - give artifact
+		{
+			iw.text.addTxt(MetaString::ADVOB_TXT,162);
+			iw.components.push_back(Component(Component::ARTIFACT,bonusType,0,0));
+			iw.text.replacements.push_back(VLC->arth->artifacts[bonusType].Name());
+
+			cb->giveHeroArtifact(bonusType,h->id,-2);
+		}
+
+		if(!h->getBonus(HeroBonus::OBJECT,ID)) //we don't have modifier from this object yet
+		{
+			//ruin morale 
+			GiveBonus gb;
+			gb.hid = h->id;
+			gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::MORALE,HeroBonus::OBJECT,-3,id,"");
+			gb.bdescr.addTxt(MetaString::ARRAY_TXT,104); //Warrior Tomb Visited -3
+			cb->giveHeroBonus(&gb);
+		}
+	
+		cb->showInfoDialog(&iw);
+
+		//add player to the visitors (for visited tooltop)
+		cb->setObjProperty(id,10,h->getOwner());
+	}
+}

+ 19 - 0
hch/CObjectHandler.h

@@ -705,6 +705,25 @@ public:
 	}
 };
 
+class DLL_EXPORT CGOnceVisitable : public CPlayersVisited //wagon, corpse, lean to, warriors tomb
+{
+public:
+	ui8 artOrRes; //0 - nothing; 1 - artifact; 2 - resource
+	ui32 bonusType, //id of res or artifact
+		bonusVal; //resource amount (or not used)
+
+	void onHeroVisit(const CGHeroInstance * h) const;
+	const std::string & getHoverText() const;
+	void initObj();	
+	void searchTomb(const CGHeroInstance *h, ui32 accept) const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this) & static_cast<CPlayersVisited&>(*this);;
+		h & bonusType & bonusVal;
+	}
+};
+
 
 
 class DLL_EXPORT CObjectHandler

+ 28 - 0
lib/IGameCallback.cpp

@@ -4,6 +4,8 @@
 #include "../map.h"
 #include "../hch/CObjectHandler.h"
 #include "../StartInfo.h"
+#include "../hch/CArtHandler.h"
+#include "../lib/VCMI_Lib.h"
 
 /*
  * IGameCallback.cpp, part of VCMI engine
@@ -112,8 +114,34 @@ bool IGameCallback::isAllowed( int type, int id )
 	{
 	case 0:
 		return gs->map->allowedSpell[id];
+	case 1:
+		return gs->map->allowedArtifact[id];
 	default:
 		tlog1 << "Wrong call to IGameCallback::isAllowed!\n";
 		return false;
 	}
+}
+
+void IGameCallback::getAllowedArts(std::vector<CArtifact*> &out, std::vector<CArtifact*> CArtHandler::*arts)
+{
+	for(int i = 0; i < (VLC->arth->*arts).size(); i++)
+	{
+		CArtifact *art = (VLC->arth->*arts)[i];
+		if(isAllowed(1,art->id))
+		{
+			out.push_back(art);
+		}
+	}
+}
+
+void IGameCallback::getAllowed(std::vector<CArtifact*> &out, int flags)
+{
+	if(flags & ART_TREASURE)
+		getAllowedArts(out,&CArtHandler::treasures);
+	if(flags & ART_MINOR)
+		getAllowedArts(out,&CArtHandler::minors);
+	if(flags & ART_MAJOR)
+		getAllowedArts(out,&CArtHandler::majors);
+	if(flags & ART_RELIC)
+		getAllowedArts(out,&CArtHandler::relics);
 }

+ 7 - 3
lib/IGameCallback.h

@@ -29,6 +29,8 @@ struct BattleResult;
 class CGameState;
 struct PlayerSettings;
 struct CPackForClient;
+class CArtHandler;
+class CArtifact;
 
 class DLL_EXPORT IGameCallback
 {
@@ -49,11 +51,13 @@ public:
 	virtual const PlayerSettings * getPlayerSettings(int color);
 	virtual int getHeroCount(int player, bool includeGarrisoned);
 	virtual void getTilesInRange(std::set<int3> &tiles, int3 pos, int radious, int player=-1, int mode=0); //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 -  only unrevealed
-	virtual bool isAllowed(int type, int id); //type: 0 - spell
+	virtual bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact
+	virtual void getAllowedArts(std::vector<CArtifact*> &out, std::vector<CArtifact*> CArtHandler::*arts);
+	virtual void getAllowed(std::vector<CArtifact*> &out, int flags); //flags: bitfield uses EartClass
 
 	//do sth
 	virtual void changeSpells(int hid, bool give, const std::set<ui32> &spells)=0;
-	virtual void removeObject(int objid)=0;
+	virtual bool removeObject(int objid)=0;
 	virtual void setBlockVis(int objid, bool bv)=0;
 	virtual void setOwner(int objid, ui8 owner)=0;
 	virtual void setHoverName(int objid, MetaString * name)=0;
@@ -72,7 +76,7 @@ public:
 	virtual void startBattleI(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb)=0; //use hero=NULL for no hero
 	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 void moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255)=0;
+	virtual bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255)=0;
 	virtual void giveHeroBonus(GiveBonus * bonus)=0;
 	virtual void setMovePoints(SetMovePoints * smp)=0;
 	virtual void setManaPoints(int hid, int val)=0;

+ 51 - 23
lib/NetPacks.h

@@ -57,7 +57,7 @@ struct CPackForServer : public CPack
 		c = NULL;
 	};
 
-	void applyGh(CGameHandler *gh)//called after applying to gs
+	bool applyGh(CGameHandler *gh)//called after applying to gs
 	{}; 
 };
 
@@ -108,6 +108,21 @@ struct MetaString : public CPack //2001 helper for object scrips
 
 /***********************************************************************************************************/
 
+struct PackageApplied : public CPackForClient //94
+{
+	PackageApplied() {type = 94;}
+	PackageApplied(ui8 Result) : result(Result) {type = 94;}
+	void applyCl(CClient *cl);
+
+	ui8 result; //0 - something went wrong, request hasn't been realized; 1 - OK
+	ui32 packType; //type id of applied package
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & result;
+	}
+};
+
 struct SystemMessage : public CPackForClient //95
 {
 	SystemMessage(const std::string Text) : text(Text){type = 95;};
@@ -490,7 +505,7 @@ struct NewTurn : public CPackForClient //101
 
 struct Component : public CPack //2002 helper for object scrips informations
 {
-	enum {PRIM_SKILL,SEC_SKILL,RESOURCE,CREATURE,ARTIFACT,EXPERIENCE,SPELL};
+	enum {PRIM_SKILL,SEC_SKILL,RESOURCE,CREATURE,ARTIFACT,EXPERIENCE,SPELL, MORALE=8, LUCK};
 	ui16 id, subtype; //id uses ^^^ enums, when id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels)
 	si32 val; // + give; - take
 	si16 when; // 0 - now; +x - within x days; -x - per x days
@@ -814,6 +829,19 @@ struct SetStackEffect : public CPackForClient //3010
 	}
 };
 
+struct StacksInjured : public CPackForClient //3011
+{
+	StacksInjured(){type = 3011;}
+	DLL_EXPORT void applyGs(CGameState *gs);
+	void applyCl(CClient *cl);
+
+	std::set<BattleStackAttacked> stacks;
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & stacks;
+	}
+};
+
 struct ShowInInfobox : public CPackForClient //107
 {
 	ShowInInfobox(){type = 107;};
@@ -832,14 +860,14 @@ struct ShowInInfobox : public CPackForClient //107
 
 struct CloseServer : public CPackForServer
 {
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{}
 };
 
 struct EndTurn : public CPackForServer
 {
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{}
 };
@@ -850,7 +878,7 @@ struct DismissHero : public CPackForServer
 	DismissHero(si32 HID) : hid(HID) {};
 	si32 hid;
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & hid;
@@ -864,7 +892,7 @@ struct MoveHero : public CPackForServer
 	int3 dest;
 	si32 hid;
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & dest & hid;
@@ -881,7 +909,7 @@ struct ArrangeStacks : public CPackForServer
 	ui8 p1, p2; //positions of first and second stack
 	si32 id1, id2; //ids of objects with garrison
 	si32 val;
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & what & p1 & p2 & id1 & id2 & val;
@@ -895,7 +923,7 @@ struct DisbandCreature : public CPackForServer
 	ui8 pos; //stack pos
 	si32 id; //object id
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & pos & id;
@@ -908,7 +936,7 @@ struct BuildStructure : public CPackForServer
 	BuildStructure(si32 TID, si32 BID):bid(BID),tid(TID){};
 	si32 bid, tid; //structure and town ids
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & tid & bid;
@@ -922,7 +950,7 @@ struct RecruitCreatures : public CPackForServer
 	si32 tid; //town id
 	ui32 crid, amount;//creature ID and amount
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & tid & crid & amount;
@@ -937,7 +965,7 @@ struct UpgradeCreature : public CPackForServer
 	si32 id; //object id
 	si32 cid; //id of type to which we want make upgrade
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & pos & id & cid;
@@ -950,7 +978,7 @@ struct GarrisonHeroSwap : public CPackForServer
 	GarrisonHeroSwap(si32 TID):tid(TID){};
 	si32 tid; 
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & tid;
@@ -965,7 +993,7 @@ struct ExchangeArtifacts : public CPackForServer
 	si32 hid1, hid2;
 	ui16 slot1, slot2;
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & hid1 & hid2 & slot1 & slot2;
@@ -978,7 +1006,7 @@ struct BuyArtifact : public CPackForServer
 	BuyArtifact(si32 HID, si32 AID):hid(HID),aid(AID){};
 	si32 hid, aid; //hero and artifact id
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & hid & aid;
@@ -996,7 +1024,7 @@ struct TradeOnMarketplace : public CPackForServer
 	ui32 r1, r2; //mode 0: r1 - sold resource, r2 - bought res
 	ui32 val; //units of sold resource
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & player & mode & /*id & */r1 & r2 & val;
@@ -1010,7 +1038,7 @@ struct SetFormation : public CPackForServer
 	si32 hid;
 	ui8 formation;
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & hid & formation;
@@ -1023,7 +1051,7 @@ struct HireHero : public CPackForServer
 	HireHero(si32 HID, si32 TID):hid(HID),tid(TID){};
 	si32 hid, tid; //available hero serial and town id
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & hid & tid;
@@ -1036,7 +1064,7 @@ struct QueryReply : public CPackForServer
 	QueryReply(ui32 QID, ui32 Answer):qid(QID),answer(Answer){};
 	ui32 qid, answer; //hero and artifact id
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & qid & answer;
@@ -1049,7 +1077,7 @@ struct MakeAction : public CPackForServer
 	MakeAction(const BattleAction &BA):ba(BA){};
 	BattleAction ba;
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & ba;
@@ -1062,7 +1090,7 @@ struct MakeCustomAction : public CPackForServer
 	MakeCustomAction(const BattleAction &BA):ba(BA){};
 	BattleAction ba;
 
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & ba;
@@ -1079,7 +1107,7 @@ struct SaveGame : public CPackForClient, public CPackForServer
 
 	void applyCl(CClient *cl);
 	void applyGs(CGameState *gs){};
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & fname;
@@ -1094,7 +1122,7 @@ struct PlayerMessage : public CPackForClient, public CPackForServer //513
 	{CPackForClient::type = 513;};
 	void applyCl(CClient *cl);
 	void applyGs(CGameState *gs){};
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 
 	ui8 player;
 	std::string text;
@@ -1110,7 +1138,7 @@ struct SetSelection : public CPackForClient, public CPackForServer //514
 {
 	SetSelection(){CPackForClient::type = 514;};
 	DLL_EXPORT void applyGs(CGameState *gs);
-	void applyGh(CGameHandler *gh);
+	bool applyGh(CGameHandler *gh);
 
 	ui8 player;
 	ui32 id;

+ 16 - 0
lib/NetPacksLib.cpp

@@ -448,6 +448,16 @@ DLL_EXPORT void BattleNextRound::applyGs( CGameState *gs )
 		s->state -= MOVED;
 		s->state -= HAD_MORALE;
 		s->counterAttacks = 1;
+
+		//remove effects and restore only those with remaining turns in duration
+		std::vector<CStack::StackEffect> tmpEffects = s->effects;
+		s->effects.clear();
+		for(int i=0; i < tmpEffects.size(); i++)
+		{
+			tmpEffects[i].turnsRemain--;
+			if(tmpEffects[i].turnsRemain > 0)
+				s->effects.push_back(tmpEffects[i]);
+		}
 	}
 }
 
@@ -545,6 +555,12 @@ DLL_EXPORT void SetStackEffect::applyGs( CGameState *gs )
 	}
 }
 
+DLL_EXPORT void StacksInjured::applyGs( CGameState *gs )
+{
+	BOOST_FOREACH(BattleStackAttacked stackAttacked, stacks)
+		stackAttacked.applyGs(gs);
+}
+
 DLL_EXPORT void YourTurn::applyGs( CGameState *gs )
 {
 	gs->currentPlayer = player;

+ 3 - 0
lib/RegisterTypes.cpp

@@ -42,12 +42,14 @@ void registerTypes1(Serializer &s)
 	s.template registerType<CGBonusingObject>();
 	s.template registerType<CGMagicWell>();
 	s.template registerType<CGObservatory>();
+	s.template registerType<CGOnceVisitable>();
 	s.template registerType<CGObjectInstance>();
 }
 
 template<typename Serializer> DLL_EXPORT 
 void registerTypes2(Serializer &s)
 {
+	s.template registerType<PackageApplied>();
 	s.template registerType<SystemMessage>();
 	s.template registerType<YourTurn>();
 	s.template registerType<SetResource>();
@@ -89,6 +91,7 @@ void registerTypes2(Serializer &s)
 	s.template registerType<EndAction>();
 	s.template registerType<SpellCasted>();
 	s.template registerType<SetStackEffect>();
+	s.template registerType<StacksInjured>();
 	s.template registerType<ShowInInfobox>();
 
 	s.template registerType<SaveGame>();

+ 8 - 0
map.cpp

@@ -1802,6 +1802,14 @@ void Mapa::readObjects( unsigned char * bufor, int &i)
 				nobj = new CGObservatory();
 				break;
 			}
+		case 22: //Corpse
+		case 39: //Lean To
+		case 105://Wagon
+		case 108://Warrior's Tomb
+			{
+				nobj = new CGOnceVisitable();
+				break;
+			}
 		case 214: //hero placeholder
 			{
 				i+=3; //TODO: handle it more properly

+ 129 - 65
server/CGameHandler.cpp

@@ -55,16 +55,16 @@ CondSh<BattleResult *> battleResult(NULL);
 class CBaseForGHApply
 {
 public:
-	virtual void applyOnGH(CGameHandler *gh, CConnection *c, void *pack) const =0; 
+	virtual bool applyOnGH(CGameHandler *gh, CConnection *c, void *pack) const =0; 
 };
 template <typename T> class CApplyOnGH : public CBaseForGHApply
 {
 public:
-	void applyOnGH(CGameHandler *gh, CConnection *c, void *pack) const
+	bool applyOnGH(CGameHandler *gh, CConnection *c, void *pack) const
 	{
 		T *ptr = static_cast<T*>(pack);
 		ptr->c = c;
-		ptr->applyGh(gh);
+		return ptr->applyGh(gh);
 	}
 };
 
@@ -484,17 +484,32 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 	{
 		while(!end2)
 		{
-			c >> pack;
-			tlog5 << "Received client message of type " << typeid(*pack).name() << std::endl;
-			CBaseForGHApply *apply = applier->apps[typeList.getTypeID(pack)];
+			{
+				boost::unique_lock<boost::mutex> lock(*c.rmx);
+				c >> pack; //get the package
+				tlog5 << "Received client message of type " << typeid(*pack).name() << std::endl;
+			}
+
+			int packType = typeList.getTypeID(pack); //get the id of type
+			CBaseForGHApply *apply = applier->apps[packType]; //and appropriae applier object
+
 			if(apply)
 			{
-				apply->applyOnGH(this,&c,pack);
-				tlog5 << "Message successfully applied!\n";
+				bool result = apply->applyOnGH(this,&c,pack);
+				tlog5 << "Message successfully applied (result=" << result << ")!\n";
+
+				//send confirmation that we've applied the package
+				PackageApplied applied;
+				applied.result = result;
+				applied.packType = packType;
+				{
+					boost::unique_lock<boost::mutex> lock(*c.wmx);
+					c << &applied;
+				}
 			}
 			else
 			{
-				tlog5 << "Message cannot be applied, cannot find applier!\n";
+				tlog1 << "Message cannot be applied, cannot find applier (unregistered type)!\n";
 			}
 			delete pack;
 			pack = NULL;
@@ -759,6 +774,7 @@ void CGameHandler::run(bool resume)
 			{
 				YourTurn yt;
 				yt.player = i->first;
+				boost::unique_lock<boost::mutex> lock(*connections[i->first]->wmx);
 				*connections[i->first] << &yt;    
 			}
 
@@ -1063,11 +1079,19 @@ void CGameHandler::setBlockVis(int objid, bool bv)
 	SetObjectProperty sop(objid,2,bv);
 	sendAndApply(&sop);
 }
-void CGameHandler::removeObject(int objid)
+
+bool CGameHandler::removeObject( int objid )
 {
+	if(!getObj(objid))
+	{
+		tlog1 << "Something wrong, that object already has been removed or hasn't existed!\n";
+		return false;
+	}
+
 	RemoveObject ro;
 	ro.id = objid;
 	sendAndApply(&ro);
+	return true;
 }
 
 void CGameHandler::setAmount(int objid, ui32 val)
@@ -1076,7 +1100,7 @@ void CGameHandler::setAmount(int objid, ui32 val)
 	sendAndApply(&sop);
 }
 
-void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
+bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*/ )
 {
 	bool blockvis = false;
 	const CGHeroInstance *h = getHero(hid);
@@ -1085,7 +1109,7 @@ void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
 	  )
 	{
 		tlog1 << "Illegal call to move hero!\n";
-		return;
+		return false;
 	}
 
 	tlog5 << "Player " <<int(asker) << " wants to move hero "<< hid << " from "<< h->pos << " to " << dst << std::endl;
@@ -1109,7 +1133,7 @@ void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
 	{
 		tlog2 << "Cannot move hero, destination tile is blocked!\n";
 		sendAndApply(&tmh);
-		return;
+		return false;
 	}
 
 	//checks for standard movement
@@ -1121,7 +1145,7 @@ void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
 		{
 			tlog2 << "Cannot move hero, not enough move points or tiles are not neighbouring!\n";
 			sendAndApply(&tmh);
-			return;
+			return false;
 		}
 
 		//check if there is blocking visitable object
@@ -1148,7 +1172,7 @@ void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
 				}
 			}
 			tlog5 << "Blocking visit at " << hmpos << std::endl;
-			return;
+			return true;
 		}
 		else //normal move
 		{
@@ -1169,6 +1193,7 @@ void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
 			}
 		}
 		tlog5 << "Movement end!\n";
+		return true;
 	}
 	else //instant move - teleportation
 	{
@@ -1177,16 +1202,17 @@ void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
 			if(obj->ID==HEROI_TYPE)
 			{
 				if(obj->tempOwner==h->tempOwner) 
-					return;//TODO: exchange
+					return true;//TODO: exchange
 				//TODO: check for ally
 				CGHeroInstance *dh = static_cast<CGHeroInstance *>(obj);
 				startBattleI(&h->army,&dh->army,dst,h,dh,0);
-				return;
+				return true;
 			}
 		}
 		tmh.result = instant+1;
 		getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1);
 		sendAndApply(&tmh);
+		return true;
 	}
 }
 void CGameHandler::setOwner(int objid, ui8 owner)
@@ -1452,7 +1478,7 @@ void CGameHandler::close()
 	//exit(0);
 }
 
-void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, si32 val)
+bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, si32 val )
 {
 	CArmedInstance *s1 = static_cast<CArmedInstance*>(gs->map->objects[id1]),
 		*s2 = static_cast<CArmedInstance*>(gs->map->objects[id2]);
@@ -1462,7 +1488,7 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
 	if(!isAllowedExchange(id1,id2))
 	{
 		complain("Cannot exchange stacks between these two objects!\n");
-		return;
+		return false;
 	}
 
 	if(what==1) //swap
@@ -1480,7 +1506,7 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
 		if(S1.slots[p1].first != S2.slots[p2].first) //not same creature
 		{
 			complain("Cannot merge different creatures stacks!");
-			return; 
+			return false; 
 		}
 
 		S2.slots[p2].second += S1.slots[p1].second;
@@ -1492,7 +1518,7 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
 		if((!vstd::contains(S1.slots,p1) && complain("no creatures to split"))
 			|| (val<1  && complain("no creatures to split"))  )
 		{
-			return;
+			return false;
 		}
 
 
@@ -1503,7 +1529,7 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
 				|| (S2.slots[p2].first != S1.slots[p1].first && complain("Cannot rebalance different creatures stacks!"))
 			)
 			{
-				return; 
+				return false; 
 			}
 			
 			S2.slots[p2].second = val;
@@ -1514,7 +1540,7 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
 			if(S1.slots[p1].second < val)//not enough creatures
 			{
 				complain("Cannot split that stack, not enough creatures!");
-				return; 
+				return false; 
 			}
 			S2.slots[p2].first = S1.slots[p1].first;
 			S2.slots[p2].second = val;
@@ -1529,7 +1555,7 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
 	)
 	{
 		complain("Cannot take the last stack!");
-		return; //leave without applying changes to garrison
+		return false; //leave without applying changes to garrison
 	}
 
 	//apply changes
@@ -1538,6 +1564,7 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
 	if(s1 != s2)
 		sg.garrs[id2] = S2;
 	sendAndApply(&sg);
+	return true;
 }
 
 int CGameHandler::getPlayerAt( CConnection *c ) const
@@ -1564,21 +1591,22 @@ int CGameHandler::getPlayerAt( CConnection *c ) const
 	}
 }
 
-void CGameHandler::disbandCreature(si32 id, ui8 pos)
+bool CGameHandler::disbandCreature( si32 id, ui8 pos )
 {
 	CArmedInstance *s1 = static_cast<CArmedInstance*>(gs->map->objects[id]);
 	if(!vstd::contains(s1->army.slots,pos))
 	{
 		complain("Illegal call to disbandCreature - no such stack in army!");
-		return;
+		return false;
 	}
 	s1->army.slots.erase(pos);
 	SetGarrisons sg;
 	sg.garrs[id] = s1->army;
 	sendAndApply(&sg);
+	return true;
 }
 
-void CGameHandler::buildStructure(si32 tid, si32 bid)
+bool CGameHandler::buildStructure( si32 tid, si32 bid )
 {
 	CGTownInstance * t = static_cast<CGTownInstance*>(gs->map->objects[tid]);
 	CBuilding * b = VLC->buildh->buildings[t->subID][bid];
@@ -1586,7 +1614,7 @@ void CGameHandler::buildStructure(si32 tid, si32 bid)
 	if(gs->canBuildStructure(t,bid) != 7)
 	{
 		complain("Cannot build that building!");
-		return;
+		return false;
 	}
 
 	NewStructures ns;
@@ -1625,6 +1653,8 @@ void CGameHandler::buildStructure(si32 tid, si32 bid)
 		if(t->garrisonHero)
 			giveSpells(t,t->garrisonHero);
 	}
+
+	return true;
 }
 
 void CGameHandler::sendMessageToAll( const std::string &message )
@@ -1634,7 +1664,7 @@ void CGameHandler::sendMessageToAll( const std::string &message )
 	sendToAllClients(&sm);
 }
 
-void CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram )
+bool CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram )
 {
 	si32 ser = -1;
 	CGTownInstance * t = static_cast<CGTownInstance*>(gs->map->objects[objid]);
@@ -1654,11 +1684,13 @@ void CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram )
 	}
 	int slot = t->army.getSlotFor(crid);
 
-	if(!found ||	//no such creature
-		cram > VLC->creh->creatures[crid].maxAmount(gs->getPlayer(t->tempOwner)->resources) ||  //lack of resources
-		cram<=0	||
-		slot<0	) 
-		return;
+	if(!found && complain("Cannot recruit: no such creatures!")
+		|| cram > VLC->creh->creatures[crid].maxAmount(gs->getPlayer(t->tempOwner)->resources) && complain("Cannot recruit: lack of resources!")
+		|| cram<=0	&& complain("Cannot recruit: cram <= 0!")
+		|| slot<0  && complain("Cannot recruit: no available slot!")) 
+	{
+		return false;
+	}
 
 	//recruit
 	SetResources sr;
@@ -1684,9 +1716,10 @@ void CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram )
 	sendAndApply(&sr); 
 	sendAndApply(&sac);
 	sendAndApply(&sg);
+	return true;
 }
 
-void CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
+bool CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 {
 	CArmedInstance *obj = static_cast<CArmedInstance*>(gs->map->objects[objid]);
 	UpgradeInfo ui = gs->getUpgradeInfo(obj,pos);
@@ -1694,8 +1727,10 @@ void CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 	int crQuantity = obj->army.slots[pos].second;
 
 	//check if upgrade is possible
-	if(ui.oldID<0 || !vstd::contains(ui.newID,upgID)) 
-		return;
+	if((ui.oldID<0 || !vstd::contains(ui.newID,upgID)) && complain("That upgrade is not possible!")) 
+	{
+		return false;
+	}
 
 	//check if player has enough resources
 	for(int i=0;i<ui.cost.size();i++)
@@ -1703,7 +1738,10 @@ void CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 		for (std::set<std::pair<int,int> >::iterator j=ui.cost[i].begin(); j!=ui.cost[i].end(); j++)
 		{
 			if(gs->getPlayer(player)->resources[j->first] < j->second*crQuantity)
-				return;
+			{
+				complain("Cannot upgrade, not enough resources!");
+				return false;
+			}
 		}
 	}
 
@@ -1725,9 +1763,10 @@ void CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 	sg.garrs[objid] = obj->army;
 	sg.garrs[objid].slots[pos].first = upgID;
 	sendAndApply(&sg);	
+	return true;
 }
 
-void CGameHandler::garrisonSwap(si32 tid)
+bool CGameHandler::garrisonSwap( si32 tid )
 {
 	CGTownInstance *town = gs->getTown(tid);
 	if(!town->garrisonHero && town->visitingHero) //visiting => garrison, merge armies
@@ -1737,7 +1776,10 @@ void CGameHandler::garrisonSwap(si32 tid)
 		{
 			int pos = csn.getSlotFor(cso.slots.begin()->second.first);
 			if(pos<0)
-				return;
+			{
+				complain("Cannot make garrison swap, not enough free slots!");
+				return false;
+			}
 			if(csn.slots.find(pos)!=csn.slots.end()) //add creatures to the existing stack
 			{
 				csn.slots[pos].second += cso.slots.begin()->second.second;
@@ -1759,6 +1801,7 @@ void CGameHandler::garrisonSwap(si32 tid)
 		intown.visiting = -1;
 		intown.garrison = town->visitingHero->id;
 		sendAndApply(&intown);
+		return true;
 	}					
 	else if (town->garrisonHero && !town->visitingHero) //move hero out of the garrison
 	{
@@ -1766,7 +1809,7 @@ void CGameHandler::garrisonSwap(si32 tid)
 		if(getHeroCount(town->garrisonHero->tempOwner,true) >= 8)
 		{
 			complain("Cannot move hero out of the garrison, there are already 8 wandering heroes!");
-			return;
+			return false;
 		}
 
 		SetHeroesInTown intown;
@@ -1779,6 +1822,7 @@ void CGameHandler::garrisonSwap(si32 tid)
 		SetGarrisons sg;
 		sg.garrs[tid] = CCreatureSet();
 		sendAndApply(&sg);
+		return true;
 	}
 	else if (town->garrisonHero && town->visitingHero) //swap visiting and garrison hero
 	{
@@ -1792,18 +1836,21 @@ void CGameHandler::garrisonSwap(si32 tid)
 		intown.visiting =  town->garrisonHero->id;
 		sendAndApply(&intown);
 		sendAndApply(&sg);
+		return true;
 	}
 	else
 	{
 		complain("Cannot swap garrison hero!");
+		return false;
 	}
 }
 
-void CGameHandler::swapArtifacts( si32 hid1, si32 hid2, ui16 slot1, ui16 slot2 )
+bool CGameHandler::swapArtifacts( si32 hid1, si32 hid2, ui16 slot1, ui16 slot2 )
 {
 	CGHeroInstance *h1 = gs->getHero(hid1), *h2 = gs->getHero(hid2);
 	if((distance(h1->pos,h2->pos) > 1.0)   ||   (h1->tempOwner != h2->tempOwner))
-		return;
+		return false;
+
 	const CArtifact *a1 = h1->getArt(slot1), 
 		*a2=h2->getArt(slot2);
 
@@ -1813,7 +1860,7 @@ void CGameHandler::swapArtifacts( si32 hid1, si32 hid2, ui16 slot1, ui16 slot2 )
 	{
 		//artifact doesn't fit dst slot
 		complain("Cannot swap artifacts!");
-		return;
+		return false;
 	}
 
 
@@ -1832,9 +1879,11 @@ void CGameHandler::swapArtifacts( si32 hid1, si32 hid2, ui16 slot1, ui16 slot2 )
 		sha.setArtAtPos(slot2,h1->getArtAtPos(slot1));
 		sendAndApply(&sha);
 	}
+
+	return true;
 }
 
-void CGameHandler::buyArtifact( ui32 hid, si32 aid )
+bool CGameHandler::buyArtifact( ui32 hid, si32 aid )
 {
 	CGHeroInstance *hero = gs->getHero(hid);
 	CGTownInstance *town = hero->visitedTown;
@@ -1844,29 +1893,32 @@ void CGameHandler::buyArtifact( ui32 hid, si32 aid )
 			|| getResource(hero->getOwner(),6)<500 && complain("Cannot buy a spellbook, not enough gold!") 
 			|| hero->getArt(17) && complain("Cannot buy a spellbook, hero already has a one!")
 			)
-			return;
+			return false;
 
 		giveResource(hero->getOwner(),6,-500);
 		giveHeroArtifact(0,hid,17);
 		giveSpells(town,hero);
+		return true;
 	}
 	else if(aid < 7  &&  aid > 3) //war machine
 	{
 		int price = VLC->arth->artifacts[aid].price;
-		if(vstd::contains(hero->artifWorn,ui16(9+aid))  //hero already has this machine
-			|| !vstd::contains(town->builtBuildings,si32(16)) //no blackismith
-			|| gs->getPlayer(hero->getOwner())->resources[6] < price //no gold
-			|| town->town->warMachine!= aid ) //this machine is not available here (//TODO: support ballista yard in stronghold)
+		if(vstd::contains(hero->artifWorn,ui16(9+aid)) && complain("Hero already has this machine!")
+			|| !vstd::contains(town->builtBuildings,si32(16)) && complain("No blackismith!")
+			|| gs->getPlayer(hero->getOwner())->resources[6] < price  && complain("Not enough gold!")  //no gold
+			|| town->town->warMachine!= aid  &&  complain("This machine is unavailale here!") ) //TODO: ballista yard in Stronghold
 		{
-			return;
+			return false;
 		}
 
 		giveResource(hero->getOwner(),6,-price);
 		giveHeroArtifact(aid,hid,9+aid);
+		return true;
 	}
+	return false;
 }
 
-void CGameHandler::tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 )
+bool CGameHandler::tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 )
 {
 	val = std::min(si32(val),gs->getPlayer(player)->resources[id1]);
 	double uzysk = (double)gs->resVals[id1] * val * gs->getMarketEfficiency(player);
@@ -1880,22 +1932,25 @@ void CGameHandler::tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 )
 	sr.resid = id2;
 	sr.val = gs->getPlayer(player)->resources[id2] + (int)uzysk;
 	sendAndApply(&sr);
+
+	return true;
 }
 
-void CGameHandler::setFormation( si32 hid, ui8 formation )
+bool CGameHandler::setFormation( si32 hid, ui8 formation )
 {
 	gs->getHero(hid)->army.formation = formation;
+	return true;
 }
 
-void CGameHandler::hireHero( ui32 tid, ui8 hid )
+bool CGameHandler::hireHero( ui32 tid, ui8 hid )
 {
 	CGTownInstance *t = gs->getTown(tid);
-	if(!vstd::contains(t->builtBuildings,5) //no tavern in the town
-		|| gs->getPlayer(t->tempOwner)->resources[6]<2500 //not enough gold
-		|| t->visitingHero //there is visiting hero - no place
+	if(!vstd::contains(t->builtBuildings,5)  && complain("No tavern!")
+		|| gs->getPlayer(t->tempOwner)->resources[6]<2500  && complain("Not enough gold for buying hero!")
+		|| t->visitingHero  && complain("There is visiting hero - no place!")
 		|| getHeroCount(t->tempOwner,false) >= 8 && complain("Cannot hire hero, only 8 wandering heroes are allowed!")
 		)
-		return;
+		return false;
 	CGHeroInstance *nh = gs->getPlayer(t->tempOwner)->availableHeroes[hid];
 
 	HeroRecruited hr;
@@ -1919,9 +1974,10 @@ void CGameHandler::hireHero( ui32 tid, ui8 hid )
 	sendAndApply(&sr);
 
 	giveSpells(t,nh);
+	return true;
 }
 
-void CGameHandler::queryReply( ui32 qid, ui32 answer )
+bool CGameHandler::queryReply( ui32 qid, ui32 answer )
 {
 	boost::unique_lock<boost::recursive_mutex> lock(gsm);
 	if(vstd::contains(callbacks,qid))
@@ -1941,11 +1997,14 @@ void CGameHandler::queryReply( ui32 qid, ui32 answer )
 	else
 	{
 		tlog1 << "Unknown query reply...\n";
+		return false;
 	}
+	return true;
 }
 
-void CGameHandler::makeBattleAction( BattleAction &ba )
+bool CGameHandler::makeBattleAction( BattleAction &ba )
 {
+	bool ok = true;
 	switch(ba.actionType)
 	{
 	case 2: //walk
@@ -1985,11 +2044,13 @@ void CGameHandler::makeBattleAction( BattleAction &ba )
 			if(curStack->position != ba.destinationTile) //we wasn't able to reach destination tile
 			{
 				tlog3<<"We cannot move this stack to its destination "<<curStack->creature->namePl<<std::endl;
+				ok = false;
 			}
 
 			if(!stackAtEnd)
 			{
 				tlog3 << "There is no stack on " << ba.additionalInfo << " tile (no attack)!";
+				ok = false;
 				break;
 			}
 
@@ -2010,7 +2071,7 @@ void CGameHandler::makeBattleAction( BattleAction &ba )
 			{
 				tlog3 << "Attack cannot be performed!";
 				sendAndApply(&EndAction());
-				break;
+				ok = false;
 			}
 
 			//attack
@@ -2080,6 +2141,7 @@ void CGameHandler::makeBattleAction( BattleAction &ba )
 		}
 	}
 	battleMadeAction.setn(true);
+	return ok;
 }
 
 void CGameHandler::playerMessage( ui8 player, const std::string &message )
@@ -2182,7 +2244,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	}
 }
 
-void CGameHandler::makeCustomAction( BattleAction &ba )
+bool CGameHandler::makeCustomAction( BattleAction &ba )
 {
 	switch(ba.actionType)
 	{
@@ -2192,12 +2254,12 @@ void CGameHandler::makeCustomAction( BattleAction &ba )
 			if(!h)
 			{
 				tlog2 << "Wrong caster!\n";
-				return;
+				return false;
 			}
 			if(ba.additionalInfo >= VLC->spellh->spells.size())
 			{
 				tlog2 << "Wrong spell id (" << ba.additionalInfo << ")!\n";
-				return;
+				return false;
 			}
 
 			CSpell *s = &VLC->spellh->spells[ba.additionalInfo];
@@ -2210,7 +2272,7 @@ void CGameHandler::makeCustomAction( BattleAction &ba )
 				)
 			{
 				tlog2 << "Spell cannot be casted!\n";
-				return;
+				return false;
 			}
 
 			sendAndApply(&StartAction(ba)); //start spell casting
@@ -2343,8 +2405,10 @@ void CGameHandler::makeCustomAction( BattleAction &ba )
 				}
 			}
 			sendAndApply(&EndAction());
+			return true;
 		}
 	}
+	return false;
 }
 
 void CGameHandler::handleTimeEvents()

+ 16 - 16
server/CGameHandler.h

@@ -103,7 +103,7 @@ public:
 
 	//do sth
 	void changeSpells(int hid, bool give, const std::set<ui32> &spells);
-	void removeObject(int objid);
+	bool removeObject(int objid);
 	void setBlockVis(int objid, bool bv);
 	void setOwner(int objid, ui8 owner);
 	void setHoverName(int objid, MetaString * name);
@@ -124,7 +124,7 @@ public:
 	void startBattleI(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
 	void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army
 	void setAmount(int objid, ui32 val);
-	void moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255);
+	bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255);
 	void giveHeroBonus(GiveBonus * bonus);
 	void setMovePoints(SetMovePoints * smp);
 	void setManaPoints(int hid, int val);
@@ -137,20 +137,20 @@ public:
 	int getPlayerAt(CConnection *c) const;
 
 	void playerMessage( ui8 player, const std::string &message);
-	void makeBattleAction(BattleAction &ba);
-	void makeCustomAction(BattleAction &ba);
-	void queryReply( ui32 qid, ui32 answer );
-	void hireHero( ui32 tid, ui8 hid );
-	void setFormation( si32 hid, ui8 formation );
-	void tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 );
-	void buyArtifact( ui32 hid, si32 aid );
-	void swapArtifacts( si32 hid1, si32 hid2, ui16 slot1, ui16 slot2 );
-	void garrisonSwap(si32 tid);
-	void upgradeCreature( ui32 objid, ui8 pos, ui32 upgID );
-	void recruitCreatures(si32 objid, ui32 crid, ui32 cram);
-	void buildStructure(si32 tid, si32 bid);
-	void disbandCreature( si32 id, ui8 pos );
-	void arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, si32 val );
+	bool makeBattleAction(BattleAction &ba);
+	bool makeCustomAction(BattleAction &ba);
+	bool queryReply( ui32 qid, ui32 answer );
+	bool hireHero( ui32 tid, ui8 hid );
+	bool setFormation( si32 hid, ui8 formation );
+	bool tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 );
+	bool buyArtifact( ui32 hid, si32 aid );
+	bool swapArtifacts( si32 hid1, si32 hid2, ui16 slot1, ui16 slot2 );
+	bool garrisonSwap(si32 tid);
+	bool upgradeCreature( ui32 objid, ui8 pos, ui32 upgID );
+	bool recruitCreatures(si32 objid, ui32 crid, ui32 cram);
+	bool buildStructure(si32 tid, si32 bid);
+	bool disbandCreature( si32 id, ui8 pos );
+	bool arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, si32 val );
 	void save(const std::string &fname);
 	void close();
 	void handleTimeEvents();

+ 48 - 40
server/NetPacksServer.cpp

@@ -5,7 +5,7 @@
 #define PLAYER_OWNS(id) (gh->getPlayerAt(c)==gh->getOwner(id))
 #define ERROR_AND_RETURN	{if(c) *c << &SystemMessage("You are not allowed to perform this action!");	\
 							tlog1<<"Player is not allowed to perform this action!\n";	\
-							return;}
+							return false;}
 #define ERROR_IF_NOT_OWNS(id)	if(!PLAYER_OWNS(id)) ERROR_AND_RETURN
 
 /*
@@ -23,128 +23,136 @@ CGameState* CPackForServer::GS(CGameHandler *gh)
 	return gh->gs;
 }
 
-void SaveGame::applyGh( CGameHandler *gh )
+bool SaveGame::applyGh( CGameHandler *gh )
 {
 	gh->sendMessageTo(*c,"Saving...");
 	gh->save(fname);
 	gh->sendMessageTo(*c,"Game has been succesfully saved!");
+	return true;
 }
 
-void CloseServer::applyGh( CGameHandler *gh )
+bool CloseServer::applyGh( CGameHandler *gh )
 {
 	gh->close();
+	return true;
 }
 
-void EndTurn::applyGh( CGameHandler *gh )
+bool EndTurn::applyGh( CGameHandler *gh )
 {
 	gh->states.setFlag(GS(gh)->currentPlayer,&PlayerStatus::makingTurn,false);
+	return true;
 }
 
-void DismissHero::applyGh( CGameHandler *gh )
+bool DismissHero::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(hid);
-	gh->removeObject(hid);
+	return gh->removeObject(hid);
 }
 
-void MoveHero::applyGh( CGameHandler *gh )
+bool MoveHero::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(hid);
-	gh->moveHero(hid,dest,0,gh->getPlayerAt(c));
+	return gh->moveHero(hid,dest,0,gh->getPlayerAt(c));
 }
 
-void ArrangeStacks::applyGh( CGameHandler *gh )
+bool ArrangeStacks::applyGh( CGameHandler *gh )
 {
-	//ERROR_IF_NOT_OWNS(id1);
-	//ERROR_IF_NOT_OWNS(id2);
-	gh->arrangeStacks(id1,id2,what,p1,p2,val);
+	//checks for owning in the gh func
+	return gh->arrangeStacks(id1,id2,what,p1,p2,val);
 }
 
-void DisbandCreature::applyGh( CGameHandler *gh )
+bool DisbandCreature::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(id);
-	gh->disbandCreature(id,pos);
+	return gh->disbandCreature(id,pos);
 }
 
-void BuildStructure::applyGh( CGameHandler *gh )
+bool BuildStructure::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(tid);
-	gh->buildStructure(tid,bid);
+	return gh->buildStructure(tid,bid);
 }
 
-void RecruitCreatures::applyGh( CGameHandler *gh )
+bool RecruitCreatures::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(tid);
-	gh->recruitCreatures(tid,crid,amount);
+	return gh->recruitCreatures(tid,crid,amount);
 }
 
-void UpgradeCreature::applyGh( CGameHandler *gh )
+bool UpgradeCreature::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(id);
-	gh->upgradeCreature(id,pos,cid);
+	return gh->upgradeCreature(id,pos,cid);
 }
 
-void GarrisonHeroSwap::applyGh( CGameHandler *gh )
+bool GarrisonHeroSwap::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(tid);
-	gh->garrisonSwap(tid);
+	return gh->garrisonSwap(tid);
 }
 
-void ExchangeArtifacts::applyGh( CGameHandler *gh )
+bool ExchangeArtifacts::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(hid1);
 	ERROR_IF_NOT_OWNS(hid2);
-	gh->swapArtifacts(hid1,hid2,slot1,slot2);
+	return gh->swapArtifacts(hid1,hid2,slot1,slot2);
 }
 
-void BuyArtifact::applyGh( CGameHandler *gh )
+bool BuyArtifact::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(hid);
-	gh->buyArtifact(hid,aid);
+	return gh->buyArtifact(hid,aid);
 }
 
-void TradeOnMarketplace::applyGh( CGameHandler *gh )
+bool TradeOnMarketplace::applyGh( CGameHandler *gh )
 {
 	if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
-	gh->tradeResources(val,player,r1,r2);
+	return gh->tradeResources(val,player,r1,r2);
 }
 
-void SetFormation::applyGh( CGameHandler *gh )
+bool SetFormation::applyGh( CGameHandler *gh )
 {	
 	ERROR_IF_NOT_OWNS(hid);
-	gh->setFormation(hid,formation);
+	return gh->setFormation(hid,formation);
 }
 
-void HireHero::applyGh( CGameHandler *gh )
+bool HireHero::applyGh( CGameHandler *gh )
 {
 	ERROR_IF_NOT_OWNS(tid);
-	gh->hireHero(tid,hid);
+	return gh->hireHero(tid,hid);
 }
 
-void QueryReply::applyGh( CGameHandler *gh )
+bool QueryReply::applyGh( CGameHandler *gh )
 {
-	gh->queryReply(qid,answer);
+	//TODO - check if player matches the query
+	return gh->queryReply(qid,answer);
 }
 
-void MakeAction::applyGh( CGameHandler *gh )
+bool MakeAction::applyGh( CGameHandler *gh )
 {
+	if(!GS(gh)->curB) ERROR_AND_RETURN;
 	if(gh->connections[GS(gh)->curB->getStack(GS(gh)->curB->activeStack)->owner] != c) ERROR_AND_RETURN;
-	gh->makeBattleAction(ba);
+	return gh->makeBattleAction(ba);
 }
 
-void MakeCustomAction::applyGh( CGameHandler *gh )
+bool MakeCustomAction::applyGh( CGameHandler *gh )
 {
+	if(!GS(gh)->curB) ERROR_AND_RETURN;
 	if(gh->connections[GS(gh)->curB->getStack(GS(gh)->curB->activeStack)->owner] != c) ERROR_AND_RETURN;
-	gh->makeCustomAction(ba);
+	return gh->makeCustomAction(ba);
 }
 
-void PlayerMessage::applyGh( CGameHandler *gh )
+bool PlayerMessage::applyGh( CGameHandler *gh )
 {
 	if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
 	gh->playerMessage(player,text);
+	return true;
 }
 
-void SetSelection::applyGh( CGameHandler *gh )
+bool SetSelection::applyGh( CGameHandler *gh )
 {
 	if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
+	if(!gh->getObj(id)) ERROR_AND_RETURN;
 	gh->sendAndApply(this);
+	return true;
 }