Procházet zdrojové kódy

* version set 0.73b
* config entry for 1600x1200 resolution
* several fixes
* http://vcmi.antypika.aplus.pl/forum/viewtopic.php?p=3185#3185

Michał W. Urbańczyk před 16 roky
rodič
revize
de1ed92379

+ 19 - 7
AI/GeniusAI/BattleLogic.cpp

@@ -460,7 +460,7 @@ std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(CStack *defender, CS
 BattleAction CBattleLogic::MakeDefend(int stackID)
 {
 	BattleAction ba;
-	ba.side = 1;
+	ba.side = m_side;
 	ba.actionType = action_defend;
 	ba.stackNumber = stackID;
 	ba.additionalInfo = -1;
@@ -470,7 +470,7 @@ BattleAction CBattleLogic::MakeDefend(int stackID)
 BattleAction CBattleLogic::MakeWait(int stackID)
 {
 	BattleAction ba;
-	ba.side = 1;
+	ba.side = m_side;
 	ba.actionType = action_wait;
 	ba.stackNumber = stackID;
 	ba.additionalInfo = -1;
@@ -479,11 +479,21 @@ BattleAction CBattleLogic::MakeWait(int stackID)
 
 BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 {
+	CStack *attackerStack = m_cb->battleGetStackByID(attackerID),
+		*destinationStack = m_cb->battleGetStackByID(destinationID);
+	assert(attackerStack && destinationStack);
+
+	//don't attack ourselves
+	if(destinationStack->attackerOwned == !m_side)
+	{
+		return MakeDefend(attackerID);
+	}
+
 	if (m_cb->battleCanShoot(attackerID, m_cb->battleGetPos(destinationID)))
 	{
 		// shoot
 		BattleAction ba;
-		ba.side = 1;
+		ba.side = m_side;
 		ba.additionalInfo = -1;
 		ba.actionType = action_shoot; // shoot
 		ba.stackNumber = attackerID;
@@ -528,7 +538,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 		}
 
 		BattleAction ba;
-		ba.side = 1;
+		ba.side = m_side;
 		//ba.actionType = 6; // go and attack
 		ba.stackNumber = attackerID;
 		ba.destinationTile = static_cast<ui16>(dest_tile);
@@ -547,9 +557,6 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 		int nearest_pos = -1;
 
 		// if double wide calculate tail
-		CStack *attackerStack = m_cb->battleGetStackByID(attackerID);
-		assert(attackerStack != NULL);
-
 		int tail_pos = -1;
 		if (attackerStack->creature->isDoubleWide())
 		{
@@ -572,6 +579,8 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 #if defined PRINT_DEBUG
 				PrintBattleAction(ba);
 #endif
+				assert(m_cb->battleGetStackByPos(ba.additionalInfo)); //if action is action_walk_and_attack additional info must point on enemy stack
+				assert(m_cb->battleGetStackByPos(ba.additionalInfo) != attackerStack); //don't attack ourselve
 				return ba;
 			}
 		}
@@ -585,6 +594,8 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 #if defined PRINT_DEBUG
 				PrintBattleAction(ba);
 #endif
+				assert(m_cb->battleGetStackByPos(ba.additionalInfo)); //if action is action_walk_and_attack additional info must point on enemy stack
+				assert(m_cb->battleGetStackByPos(ba.additionalInfo) != attackerStack); //don't attack ourselve
 				return ba;
 			}
 			int d = m_battleHelper.GetDistanceWithObstacles(dest_tile, *it);
@@ -606,6 +617,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 #if defined PRINT_DEBUG
 		PrintBattleAction(ba);
 #endif
+
 		return ba;
 	}
 }

+ 22 - 2
AI/GeniusAI/CGeniusAI.cpp

@@ -25,7 +25,7 @@ void DbgBox(const char *msg, bool messageBox)
 }
 
 CGeniusAI::CGeniusAI()
-	: m_generalAI(),turn(0),firstTurn(true)
+	: m_generalAI(), turn(0), m_state(NO_BATTLE), firstTurn(true)
 {
 }
 
@@ -272,6 +272,20 @@ void GeniusAI::CGeniusAI::showGarrisonDialog( const CArmedInstance *up, const CG
 	onEnd();
 }
 
+void GeniusAI::CGeniusAI::playerBlocked( int reason )
+{
+	if(reason == 0) //battle is coming...
+	{
+		m_state.setn(UPCOMING_BATTLE);
+	}
+}
+
+void GeniusAI::CGeniusAI::battleResultsApplied()
+{
+	assert(m_state.get() == ENDING_BATTLE);
+	m_state.setn(NO_BATTLE);
+}
+
 void CGeniusAI::showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const int soundID, bool selection, bool cancel)
 {
 	m_cb->selectionMade(cancel ? 0 : 1, askID);
@@ -321,7 +335,10 @@ void CGeniusAI::battleStacksAttacked(std::set<BattleStackAttacked> & bsa)
 
 void CGeniusAI::battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side)
 {
-	assert(!m_battleLogic); //************** assert fails when AI starts two battles at same time? ***************
+	assert(!m_battleLogic);
+	assert(playerID > PLAYER_LIMIT  ||  m_state.get() == UPCOMING_BATTLE); //we have been informed that battle will start (or we are neutral AI)
+
+	m_state.setn(ONGOING_BATTLE);
 	m_battleLogic = new BattleAI::CBattleLogic(m_cb, army1, army2, tile, hero1, hero2, side);
 
 	DbgBox("** CGeniusAI::battleStart **");
@@ -341,6 +358,9 @@ void CGeniusAI::battleEnd(BattleResult *br)
 	delete m_battleLogic;
 	m_battleLogic = NULL;
 
+	assert(m_state.get() == ONGOING_BATTLE);
+	m_state.setn(ENDING_BATTLE);
+
 	DbgBox("** CGeniusAI::battleEnd **");
 }
 /**

+ 63 - 51
AI/GeniusAI/CGeniusAI.h

@@ -1,21 +1,30 @@
-#ifndef __CGENIUSAI_H__
-#define __CGENIUSAI_H__
-
-#include "Common.h"
-#include "BattleLogic.h"
-#include "GeneralAI.h"
+#ifndef __CGENIUSAI_H__
+#define __CGENIUSAI_H__
+
+#include "Common.h"
+#include "BattleLogic.h"
+#include "GeneralAI.h"
+#include "..\..\lib\CondSh.h"
 #include "../../lib/VCMI_Lib.h"
 #include <set>
 #include <list>
 #include <queue>
-namespace GeniusAI {
-
-class CGeniusAI : public CGlobalAI
-{
-private:
-	ICallback*							m_cb;
-	GeniusAI::BattleAI::CBattleLogic*	m_battleLogic;
-	GeniusAI::GeneralAI::CGeneralAI		m_generalAI;
+namespace GeniusAI {
+
+enum BattleState
+{
+	NO_BATTLE,
+	UPCOMING_BATTLE,
+	ONGOING_BATTLE,
+	ENDING_BATTLE
+};
+
+class CGeniusAI : public CGlobalAI
+{
+private:
+	ICallback*							m_cb;
+	GeniusAI::BattleAI::CBattleLogic*	m_battleLogic;
+	GeniusAI::GeneralAI::CGeneralAI		m_generalAI;
 	class AIObjectContainer
 	{
 	public:
@@ -29,7 +38,7 @@ private:
 		}
 	};
 	std::set< AIObjectContainer > knownVisitableObjects;
-
+
 	class HypotheticalGameState
 	{
 	public:
@@ -74,9 +83,10 @@ private:
 	void addTownObjectives(HypotheticalGameState::TownModel &h, HypotheticalGameState & hgs);
 	void getObjectives(HypotheticalGameState & hgs);
 	void reportResources();
-	int turn;
+	int turn;
+	CondSh<BattleState> m_state; //are we engaged into battle?
 	bool firstTurn;
-
+
 
 	class AIObjective
 	{
@@ -179,39 +189,41 @@ private:
 	std::set<HeroObjective> currentHeroObjectives;	//can be fulfilled right now
 	std::set<TownObjective> currentTownObjectives;
 
-public:
-	CGeniusAI();
-	virtual ~CGeniusAI();
-
-	virtual void init(ICallback * CB);
-	virtual void yourTurn();
-	virtual void heroKilled(const CGHeroInstance *);
-	virtual void heroCreated(const CGHeroInstance *);
-	virtual void heroMoved(const TryMoveHero &);
-	virtual void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, int val) {};
-	virtual void showSelDialog(std::string text, std::vector<CSelectableComponent*> & components, int askID){};
-	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const int soundID, bool selection, bool cancel); //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
+public:
+	CGeniusAI();
+	virtual ~CGeniusAI();
+
+	virtual void init(ICallback * CB);
+	virtual void yourTurn();
+	virtual void heroKilled(const CGHeroInstance *);
+	virtual void heroCreated(const CGHeroInstance *);
+	virtual void heroMoved(const TryMoveHero &);
+	virtual void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, int val) {};
+	virtual void showSelDialog(std::string text, std::vector<CSelectableComponent*> & components, int askID){};
+	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const int soundID, bool selection, bool cancel); //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	virtual void tileRevealed(int3 pos);
 	virtual void tileHidden(int3 pos);
-	virtual void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback);
-	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd);
-	// battle
-	virtual void actionFinished(const BattleAction *action);//occurs AFTER every action taken by any stack or by the hero
-	virtual void actionStarted(const BattleAction *action);//occurs BEFORE every action taken by any stack or by the hero
-	virtual void battleAttack(BattleAttack *ba); //called when stack is performing attack
-	virtual void battleStacksAttacked(std::set<BattleStackAttacked> & bsa); //called when stack receives damage (after battleAttack())
-	virtual void battleEnd(BattleResult *br);
-	virtual void battleNewRound(int round); //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
-	virtual void battleStackMoved(int ID, int dest, int distance, bool end);
-	virtual void battleSpellCast(SpellCast *sc);
-	virtual void 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
-	virtual void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles); //called when battlefield is prepared, prior the battle beginning
-	//
-	virtual void battleStackMoved(int ID, int dest, bool startMoving, bool endMoving);
-	virtual void battleStackAttacking(int ID, int dest);
-	virtual void battleStackIsAttacked(int ID, int dmg, int killed, int IDby, bool byShooting);
-	virtual BattleAction activeStack(int stackID);
-};
-}
-
-#endif // __CGENIUSAI_H__
+	virtual void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback);
+	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd);
+	virtual void playerBlocked(int reason);
+	// battle
+	virtual void actionFinished(const BattleAction *action);//occurs AFTER every action taken by any stack or by the hero
+	virtual void actionStarted(const BattleAction *action);//occurs BEFORE every action taken by any stack or by the hero
+	virtual void battleAttack(BattleAttack *ba); //called when stack is performing attack
+	virtual void battleStacksAttacked(std::set<BattleStackAttacked> & bsa); //called when stack receives damage (after battleAttack())
+	virtual void battleEnd(BattleResult *br);
+	virtual void battleNewRound(int round); //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
+	virtual void battleStackMoved(int ID, int dest, int distance, bool end);
+	virtual void battleSpellCast(SpellCast *sc);
+	virtual void 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
+	virtual void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles); //called when battlefield is prepared, prior the battle beginning
+	//
+	virtual void battleStackMoved(int ID, int dest, bool startMoving, bool endMoving);
+	virtual void battleStackAttacking(int ID, int dest);
+	virtual void battleStackIsAttacked(int ID, int dmg, int killed, int IDby, bool byShooting);
+	virtual BattleAction activeStack(int stackID);
+	void battleResultsApplied();
+};
+}
+
+#endif // __CGENIUSAI_H__

+ 2 - 0
AI/GeniusAI/GeneralAI.cpp

@@ -1,4 +1,5 @@
 #include "GeneralAI.h"
+#include "../../CCallback.h"
 
 using namespace GeniusAI::GeneralAI;
 
@@ -15,4 +16,5 @@ void CGeneralAI::init(ICallback *CB)
 {
 	assert(CB != NULL);
 	m_cb = CB;
+	CB->waitTillRealize = true;
 }

+ 40 - 21
CCallback.cpp

@@ -58,10 +58,10 @@ template <ui16 N> bool isType(CPack *pack)
 	return pack->getType() == N;
 }
 
-bool CCallback::moveHero(const CGHeroInstance *h, int3 dst) const
+bool CCallback::moveHero(const CGHeroInstance *h, int3 dst)
 {
 	MoveHero pack(dst,h->id);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 	return true;
 }
 void CCallback::selectionMade(int selection, int asker)
@@ -74,7 +74,7 @@ void CCallback::recruitCreatures(const CGObjectInstance *obj, ui32 ID, ui32 amou
 	if(player!=obj->tempOwner) return;
 
 	RecruitCreatures pack(obj->id,ID,amount);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 }
 
 
@@ -84,20 +84,20 @@ bool CCallback::dismissCreature(const CArmedInstance *obj, int stackPos)
 		return false;
 
 	DisbandCreature pack(stackPos,obj->id);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 	return true;
 }
 bool CCallback::upgradeCreature(const CArmedInstance *obj, int stackPos, int newID)
 {
 	UpgradeCreature pack(stackPos,obj->id,newID);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 	return false;
 }
 void CCallback::endTurn()
 {
 	tlog5 << "Player " << (unsigned)player << " end his turn." << std::endl;
 	EndTurn pack;
-	*cl->serv << &pack; //report that we ended turn
+	sendRequest(&pack); //report that we ended turn
 }
 UpgradeInfo CCallback::getUpgradeInfo(const CArmedInstance *obj, int stackPos) const
 {
@@ -349,20 +349,20 @@ const CCreatureSet* CCallback::getGarrison(const CGObjectInstance *obj) const
 int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
 {
 	ArrangeStacks pack(1,p1,p2,s1->id,s2->id,0);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 	return 0;
 }
 
 int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
 {
 	ArrangeStacks pack(2,p1,p2,s1->id,s2->id,0);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 	return 0;
 }
 int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val)
 {
 	ArrangeStacks pack(3,p1,p2,s1->id,s2->id,val);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 	return 0;
 }
 
@@ -371,7 +371,7 @@ bool CCallback::dismissHero(const CGHeroInstance *hero)
 	if(player!=hero->tempOwner) return false;
 
 	DismissHero pack(hero->id);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 	return true;
 }
 
@@ -387,7 +387,7 @@ bool CCallback::swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGH
 		return false;
 
 	ExchangeArtifacts ea(hero1->id, hero2->id, pos1, pos2);
-	*cl->serv << &ea;
+	sendRequest(&ea);
 	return true;
 }
 
@@ -403,7 +403,7 @@ bool CCallback::buildBuilding(const CGTownInstance *town, si32 buildingID)
 			return false; //lack of resources
 
 	BuildStructure pack(town->id,buildingID);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 	return true;
 }
 
@@ -444,7 +444,7 @@ CStack* CCallback::battleGetStackByID(int ID)
 int CCallback::battleMakeAction(BattleAction* action)
 {
 	MakeCustomAction mca(*action);
-	*cl->serv << &mca;
+	sendRequest(&mca);
 	return 0;
 }
 
@@ -582,7 +582,7 @@ void CCallback::swapGarrisonHero( const CGTownInstance *town )
 	if(town->tempOwner != player) return;
 
 	GarrisonHeroSwap pack(town->id);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 }
 
 void CCallback::buyArtifact(const CGHeroInstance *hero, int aid)
@@ -590,7 +590,7 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, int aid)
 	if(hero->tempOwner != player) return;
 
 	BuyArtifact pack(hero->id,aid);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 }
 
 std::vector < const CGObjectInstance * > CCallback::getBlockingObjs( int3 pos ) const
@@ -660,14 +660,14 @@ void CCallback::trade( int mode, int id1, int id2, int val1 )
 	int p1, p2;
 	getMarketOffer(id1,id2,p1,p2,mode);
 	TradeOnMarketplace pack(player,mode,id1,id2,val1);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 }
 
 void CCallback::setFormation(const CGHeroInstance * hero, bool tight)
 {
 	const_cast<CGHeroInstance*>(hero)->army.formation = tight;
 	SetFormation pack(hero->id,tight);
-	*cl->serv << &pack;
+	sendRequest(&pack);
 }
 
 void CCallback::setSelection(const CArmedInstance * obj)
@@ -675,7 +675,7 @@ void CCallback::setSelection(const CArmedInstance * obj)
 	SetSelection ss;
 	ss.player = player;
 	ss.id = obj->id;
-	*cl->serv << &ss;
+	sendRequest(&ss);
 }
 
 void CCallback::recruitHero(const CGTownInstance *town, const CGHeroInstance *hero)
@@ -686,7 +686,7 @@ void CCallback::recruitHero(const CGTownInstance *town, const CGHeroInstance *he
 		if(gs->players[player].availableHeroes[i] == hero)
 		{
 			HireHero pack(i,town->id);
-			*cl->serv << &pack;
+			sendRequest(&pack);
 			return;
 		}
 	}
@@ -731,14 +731,33 @@ void CCallback::save( const std::string &fname )
 void CCallback::sendMessage(const std::string &mess)
 {
 	PlayerMessage pm(player, mess);
-	*cl->serv << &pm;
+	sendRequest(&pm);
 }
 
 void CCallback::buildBoat( const IShipyard *obj )
 {
 	BuildBoat bb;
 	bb.objid = obj->o->id;
-	*cl->serv << &bb;
+	sendRequest(&bb);
+}
+
+template <typename T>
+void CCallback::sendRequest(const T* request)
+{
+	//TODO? should be part of CClient but it would have to be very tricky cause template/serialization issues
+	if(waitTillRealize)
+		cl->waitingRequest.set(true);
+
+	*cl->serv << request;
+
+	if(waitTillRealize)
+		cl->waitingRequest.waitWhileTrue();
+}
+
+CCallback::CCallback( CGameState * GS, int Player, CClient *C ) 
+	:gs(GS), cl(C), player(Player)
+{
+	waitTillRealize = false;
 }
 
 InfoAboutHero::InfoAboutHero()

+ 6 - 3
CCallback.h

@@ -38,6 +38,7 @@ class CClient;
 struct TerrainTile;
 class CHeroClass;
 class IShipyard;
+struct CPackForServer;
 
 struct InfoAboutHero
 {
@@ -84,8 +85,9 @@ struct InfoAboutTown
 class ICallback
 {
 public:
+	bool waitTillRealize; //if true, request functions will return after they are realized by server
 	//hero
-	virtual bool moveHero(const CGHeroInstance *h, int3 dst) const =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
+	virtual bool moveHero(const CGHeroInstance *h, int3 dst) =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
 	virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses given hero; true - successfuly, false - not successfuly
 	
 	//town
@@ -185,18 +187,19 @@ struct HeroMoveDetails
 class CCallback : public ICallback
 {
 private:
-	CCallback(CGameState * GS, int Player, CClient *C):gs(GS), cl(C), player(Player){};
+	CCallback(CGameState * GS, int Player, CClient *C);;
 	CGameState * gs;
 	CClient *cl;
 	bool isVisible(int3 pos, int Player) const;
 	bool isVisible(const CGObjectInstance *obj, int Player) const;
+	template <typename T> void sendRequest(const T*request);
 
 protected:
 	int player;
 
 public:
 //commands
-	bool moveHero(const CGHeroInstance *h, int3 dst) const; //dst must be free, neighbouring tile (this function can move hero only by one tile)
+	bool moveHero(const CGHeroInstance *h, int3 dst); //dst must be free, neighbouring tile (this function can move hero only by one tile)
 	void selectionMade(int selection, int asker);
 	int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2);
 	int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2); //first goes to the second

+ 4 - 3
CGameInterface.h

@@ -76,7 +76,7 @@ public:
 	virtual void heroCreated(const CGHeroInstance*){};
 	virtual void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback)=0; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
 	virtual void heroInGarrisonChange(const CGTownInstance *town){};
-	virtual void heroKilled(const CGHeroInstance*){};
+	//virtual void heroKilled(const CGHeroInstance*){};
 	virtual void heroMoved(const TryMoveHero & details){};
 	virtual void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, int val){};
 	virtual void heroManaPointsChanged(const CGHeroInstance * hero){} //not called at the beginning of turn and after spell casts
@@ -87,8 +87,6 @@ public:
 	virtual void showInfoDialog(const std::string &text, const std::vector<Component*> &components, int soundID){};
 	virtual void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level){}
 	virtual void showShipyardDialog(const IShipyard *obj){} //obj may be town or shipyard; state: 0 - can buid, 1 - lack of resources, 2 - dest tile is blocked, 3 - no water
-	//virtual void showSelDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID){};
-	//virtual void showYesNoDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID){};
 	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const int soundID, bool selection, bool cancel) = 0; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd) = 0; //all stacks operations between these objects become allowed, interface has to call onEnd when done
 	virtual void tileHidden(const std::set<int3> &pos){};
@@ -100,6 +98,8 @@ public:
 	virtual void requestRealized(PackageApplied *pa){};
 	virtual void heroExchangeStarted(si32 hero1, si32 hero2){};
 	virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
+	virtual void objectRemoved(const CGObjectInstance *obj){}; //eg. collected resource, picked artifact, beaten hero
+	virtual void playerBlocked(int reason){}; //reason: 0 - upcoming battle
 	virtual void serialize(COSer<CSaveFile> &h, const int version){}; //saving
 	virtual void serialize(CISer<CLoadFile> &h, const int version){}; //loading
 
@@ -110,6 +110,7 @@ public:
 	virtual void battleAttack(BattleAttack *ba){}; //called when stack is performing attack
 	virtual void battleStacksAttacked(std::set<BattleStackAttacked> & bsa){}; //called when stack receives damage (after battleAttack())
 	virtual void battleEnd(BattleResult *br){};
+	virtual void battleResultsApplied(){}; //called when all effects of last battle are applied
 	virtual void battleNewRound(int round){}; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
 	virtual void battleStackMoved(int ID, int dest, int distance, bool end){};
 	virtual void battleSpellCast(SpellCast *sc){};

+ 9 - 0
client/CPlayerInterface.cpp

@@ -1789,6 +1789,15 @@ void CPlayerInterface::newObject( const CGObjectInstance * obj )
 	}
 }
 
+void CPlayerInterface::objectRemoved( const CGObjectInstance *obj )
+{
+	if(obj->ID == HEROI_TYPE  &&  obj->tempOwner == playerID)
+	{
+		const CGHeroInstance *h = static_cast<const CGHeroInstance*>(obj);
+		heroKilled(h);
+	}
+}
+
 void SystemOptions::setMusicVolume( int newVolume )
 {
 	musicVolume = newVolume;

+ 2 - 1
client/CPlayerInterface.h

@@ -154,7 +154,6 @@ public:
 	void heroCreated(const CGHeroInstance* hero);
 	void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback);
 	void heroInGarrisonChange(const CGTownInstance *town);
-	void heroKilled(const CGHeroInstance* hero);
 	void heroMoved(const TryMoveHero & details);
 	void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, int val);
 	void heroManaPointsChanged(const CGHeroInstance * hero);
@@ -175,6 +174,7 @@ public:
 	void requestRealized(PackageApplied *pa);
 	void heroExchangeStarted(si32 hero1, si32 hero2);
 	void objectPropertyChanged(const SetObjectProperty * sop);
+	void objectRemoved(const CGObjectInstance *obj);
 	void serialize(COSer<CSaveFile> &h, const int version); //saving
 	void serialize(CISer<CLoadFile> &h, const int version); //loading
 
@@ -195,6 +195,7 @@ public:
 
 
 	//-------------//
+	void heroKilled(const CGHeroInstance* hero);
 	void waitWhileDialog();
 	bool shiftPressed() const; //determines if shift key is pressed (left or right or both)
 	void redrawHeroWin(const CGHeroInstance * hero);

+ 13 - 0
client/Client.cpp

@@ -95,10 +95,12 @@ void CClient::init()
 }
 
 CClient::CClient(void)
+:waitingRequest(false)
 {
 	init();
 }
 CClient::CClient(CConnection *con, StartInfo *si)
+:waitingRequest(false)
 {
 	init();
 	newGame(con,si);
@@ -418,5 +420,16 @@ void CClient::serialize( Handler &h, const int version )
 	}
 }
 
+//void CClient::sendRequest( const CPackForServer *request, bool waitForRealization )
+//{
+//	if(waitForRealization)
+//		waitingRequest.set(true);
+//
+//	*serv << request;
+//
+//	if(waitForRealization)
+//		waitingRequest.waitWhileTrue();
+//}
+
 template void CClient::serialize( CISer<CLoadFile> &h, const int version );
 template void CClient::serialize( COSer<CSaveFile> &h, const int version );

+ 4 - 0
client/Client.h

@@ -5,6 +5,7 @@
 #include "../global.h"
 #include <boost/thread.hpp>
 #include "../lib/IGameCallback.h"
+#include "../lib/CondSh.h"
 
 /*
  * Client.h, part of VCMI engine
@@ -62,7 +63,10 @@ public:
 	SharedMem *shared;
 	BattleAction *curbaction;
 
+	CondSh<bool> waitingRequest;
+
 	void waitForMoveAndSend(int color);
+	//void sendRequest(const CPackForServer *request, bool waitForRealization);
 	CClient(void);
 	CClient(CConnection *con, StartInfo *si);
 	~CClient(void);

+ 24 - 5
client/NetPacksClient.cpp

@@ -125,12 +125,18 @@ void ChangeObjPos::applyCl( CClient *cl )
 
 void RemoveObject::applyFirstCl( CClient *cl )
 {
-	CGI->mh->hideObject(cl->getObj(id));
-	CGHeroInstance *h = GS(cl)->getHero(id);
-	if(h)
+	const CGObjectInstance *o = cl->getObj(id);
+	CGI->mh->hideObject(o);
+
+	int3 pos = o->pos - o->getVisitableOffset();
+	//notify interfaces about removal
+	for(std::map<ui8, CGameInterface*>::iterator i=cl->playerint.begin();i!=cl->playerint.end();i++)
 	{
-		if(vstd::contains(cl->playerint,h->tempOwner))
-			cl->playerint[h->tempOwner]->heroKilled(h);
+		if(i->first >= PLAYER_LIMIT) continue;
+		if(GS(cl)->players[i->first].fogOfWarMap[pos.x][pos.y][pos.z])
+		{
+			i->second->objectRemoved(o);
+		}
 	}
 }
 
@@ -422,6 +428,12 @@ void StacksInjured::applyCl( CClient *cl )
 	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStacksAttacked,stacks);
 }
 
+void BattleResultsApplied::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(player1,battleResultsApplied);
+	INTERFACE_CALL_IF_PRESENT(player2,battleResultsApplied);
+}
+
 CGameState* CPackForClient::GS( CClient *cl )
 {
 	return cl->gs;
@@ -439,6 +451,8 @@ void EndAction::applyCl( CClient *cl )
 void PackageApplied::applyCl( CClient *cl )
 {
 	INTERFACE_CALL_IF_PRESENT(GS(cl)->currentPlayer,requestRealized,this);
+	if(cl->waitingRequest.get())
+		cl->waitingRequest.setn(false);
 }
 
 void SystemMessage::applyCl( CClient *cl )
@@ -451,6 +465,11 @@ void SystemMessage::applyCl( CClient *cl )
 		LOCPLINT->cingconsole->print(str.str());
 }
 
+void PlayerBlocked::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(player,playerBlocked,reason);
+}
+
 void YourTurn::applyCl( CClient *cl )
 {
 	boost::thread(boost::bind(&CGameInterface::yourTurn,cl->playerint[player]));

+ 28 - 0
config/settings.txt

@@ -125,4 +125,32 @@ GUISettings
 			ButtonEndTurn: x=1159 y=524 graphic=IAM001.DEF playerColoured=1;
 		};
 	}
+	1600x1200 //setting specific for this resolution
+	{
+		AdventureMap
+		{
+			AdvMap: x=7 y=6 width=1395 height=1147 smoothMove=1;
+			InfoBox: x=1406 y=989;
+			gem0: x=6 y=1108 graphic=agemLL.def;
+			gem1: x=1356 y=1108 graphic=agemLR.def;
+			gem2: x=6 y=6 graphic=agemUL.def;
+			gem3: x=1356 y=6 graphic=agemUR.def;
+			background=ADVMAP4.pcx;
+			HeroList: size=23 x=1409 y=201 movePoints=IMOBIL.DEF manaPoints=IMANA.DEF arrowUp=IAM012.DEF arrowDown=IAM013.DEF;
+			TownList: size=23 x=1547 y=201 arrowUp=IAM014.DEF arrowDown=IAM015.DEF;
+			Minimap: width=144 height=144 x=1430 y=26;
+			Statusbar: x=285 y=1155 graphic=ADROLLVR3.pcx;
+			ResDataBar: x=0 y=1175 graphic=ZRESBAR3.pcx offsetX=65 offsetY=2 resSpace=192 resDateSpace=210;
+			ButtonKingdomOv: x=1479 y=197 graphic=IAM002L.DEF playerColoured=1;
+			ButtonUnderground: x=1479 y=229 graphic=IAM010L.DEF playerColoured=1 additionalDefs=(IAM003L.DEF);
+			ButtonQuestLog: x=1479 y=261 graphic=IAM004L.DEF playerColoured=1;
+			ButtonSleepWake: x=1479 y=294 graphic=IAM005L.DEF playerColoured=1;
+			ButtonMoveHero: x=1479 y=327 graphic=IAM006L.DEF playerColoured=1;
+			ButtonSpellbook: x=1479 y=359 graphic=IAM007L.DEF playerColoured=1;
+			ButtonAdvOptions: x=1479 y=393 graphic=IAM008L.DEF playerColoured=1;
+			ButtonSysOptions: x=1479 y=426 graphic=IAM009L.DEF playerColoured=1;
+			ButtonNextHero: x=1479 y=458 graphic=IAM000.DEF playerColoured=1;
+			ButtonEndTurn: x=1479 y=491 graphic=IAM001.DEF playerColoured=1;
+		};
+	}
 }

+ 1 - 1
global.h

@@ -19,7 +19,7 @@ typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 #define THC
 #endif
 
-#define NAME_VER ("VCMI 0.73")
+#define NAME_VER ("VCMI 0.73b")
 extern std::string NAME; //full name
 extern std::string NAME_AFFIX; //client / server
 #define CONSOLE_LOGGING_LEVEL 5

+ 1 - 1
hch/CObjectHandler.cpp

@@ -1868,7 +1868,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
 			cb->showInfoDialog(&iw);
 
 			//act as if player refused
-			joinDecision(h,cost,true);
+			joinDecision(h,cost,false);
 			return;
 		}
 

+ 3 - 1
lib/CGameState.cpp

@@ -1625,7 +1625,9 @@ int CGameState::canBuildStructure( const CGTownInstance *t, int ID )
 
 void CGameState::apply(CPack *pack)
 {
-	applierGs->apps[typeList.getTypeID(pack)]->applyOnGS(this,pack);
+	ui16 typ = typeList.getTypeID(pack);
+	assert(typ >= 0);
+	applierGs->apps[typ]->applyOnGS(this,pack);
 }
 
 PlayerState * CGameState::getPlayer( ui8 color )

+ 50 - 5
lib/CondSh.h

@@ -17,10 +17,55 @@ template <typename T> struct CondSh
 	T data;
 	boost::condition_variable cond;
 	boost::mutex mx;
-	CondSh(){};
-	CondSh(T t){data = t;};
-	void set(T t){mx.lock();data=t;mx.unlock();}; //set data
-	void setn(T t){mx.lock();data=t;mx.unlock();cond.notify_all();}; //set data and notify
-	T get(){boost::unique_lock<boost::mutex> lock(mx); return data;};
+
+	CondSh()
+	{}
+
+	CondSh(T t)
+	{
+		data = t;
+	}
+
+	void set(T t)
+	{
+		mx.lock();
+		data=t;
+		mx.unlock();
+	} 
+
+	void setn(T t) //set data and notify
+	{
+		mx.lock();
+		data=t;
+		mx.unlock();
+		cond.notify_all();
+	};
+
+	T get() //get stored value
+	{
+		boost::unique_lock<boost::mutex> lock(mx); 
+		return data;
+	}
+
+	void waitWhileTrue() //waits until data is set to false
+	{
+		boost::unique_lock<boost::mutex> un(mx);
+		while(data)
+			cond.wait(un);
+	}
+
+	void waitWhile(const T &t) //waits while data is set to arg
+	{
+		boost::unique_lock<boost::mutex> un(mx);
+		while(data == t)
+			cond.wait(un);
+	}
+
+	void waitUntil(const T &t) //waits until data is set to arg
+	{
+		boost::unique_lock<boost::mutex> un(mx);
+		while(data != t)
+			cond.wait(un);
+	}
 };
 #endif // __CONDSH_H__

+ 31 - 2
lib/NetPacks.h

@@ -170,6 +170,22 @@ struct SystemMessage : public CPackForClient //95
 	}
 };
 
+struct PlayerBlocked : public CPackForClient //96
+{
+	PlayerBlocked(){type = 96;};
+	void applyCl(CClient *cl);
+
+	enum EReason { UPCOMING_BATTLE };
+
+	ui8 reason;
+	ui8 player;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & reason & player;
+	}
+};
+
 struct YourTurn : public CPackForClient //100
 {
 	YourTurn(){type = 100;};
@@ -921,6 +937,19 @@ struct StacksInjured : public CPackForClient //3011
 	}
 };
 
+struct BattleResultsApplied : public CPackForClient //3012
+{
+	BattleResultsApplied(){type = 3012;}
+
+	ui8 player1, player2;
+
+	void applyCl(CClient *cl);
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & player1 & player2;
+	}
+};
+
 struct ShowInInfobox : public CPackForClient //107
 {
 	ShowInInfobox(){type = 107;};
@@ -1152,8 +1181,8 @@ struct BuildBoat : public CPackForServer
 
 struct QueryReply : public CPackForServer
 {
-	QueryReply(){};
-	QueryReply(ui32 QID, ui32 Answer):qid(QID),answer(Answer){};
+	QueryReply(){type = 6000;};
+	QueryReply(ui32 QID, ui32 Answer):qid(QID),answer(Answer){type = 6000;};
 	ui32 qid, answer; //hero and artifact id
 
 	bool applyGh(CGameHandler *gh);

+ 2 - 0
lib/RegisterTypes.cpp

@@ -54,6 +54,7 @@ void registerTypes2(Serializer &s)
 {
 	s.template registerType<PackageApplied>();
 	s.template registerType<SystemMessage>();
+	s.template registerType<PlayerBlocked>();
 	s.template registerType<YourTurn>();
 	s.template registerType<SetResource>();
 	s.template registerType<SetResources>();
@@ -95,6 +96,7 @@ void registerTypes2(Serializer &s)
 	s.template registerType<SpellCast>();
 	s.template registerType<SetStackEffect>();
 	s.template registerType<StacksInjured>();
+	s.template registerType<BattleResultsApplied>();
 	s.template registerType<ShowInInfobox>();
 	s.template registerType<OpenWindow>();
 	s.template registerType<NewObject>();

+ 63 - 24
server/CGameHandler.cpp

@@ -403,11 +403,15 @@ askInterfaceForMove:
 		}
 	}
 
+	BattleResultsApplied resultsApplied;
+	resultsApplied.player1 = army1->tempOwner;
+	resultsApplied.player2 = army2->tempOwner;
+
 	//unblock engaged players
-	if(hero1->tempOwner<PLAYER_LIMIT)
-		states.setFlag(hero1->tempOwner,&PlayerStatus::engagedIntoBattle,false);
-	if(hero2 && hero2->tempOwner<PLAYER_LIMIT)
-		states.setFlag(hero2->tempOwner,&PlayerStatus::engagedIntoBattle,false);	
+	if(army1->tempOwner<PLAYER_LIMIT)
+		states.setFlag(army1->tempOwner,&PlayerStatus::engagedIntoBattle,false);
+	if(army2 && army2->tempOwner<PLAYER_LIMIT)
+		states.setFlag(army2->tempOwner,&PlayerStatus::engagedIntoBattle,false);	
 
 	//casualties among heroes armies
 	SetGarrisons sg;
@@ -441,7 +445,8 @@ askInterfaceForMove:
 		cb(battleResult.data);
 
 	delete battleResult.data;
-
+	
+	sendAndApply(&resultsApplied);
 }
 void CGameHandler::prepareAttacked(BattleStackAttacked &bsa, CStack *def)
 {	
@@ -513,12 +518,15 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 				tlog5 << "Message successfully applied (result=" << result << ")!\n";
 
 				//send confirmation that we've applied the package
-				PackageApplied applied;
-				applied.result = result;
-				applied.packType = packType;
+				if(pack->type != 6000) //WORKAROUND - not confirm query replies TODO: reconsider
 				{
-					boost::unique_lock<boost::mutex> lock(*c.wmx);
-					c << &applied;
+					PackageApplied applied;
+					applied.result = result;
+					applied.packType = packType;
+					{
+						boost::unique_lock<boost::mutex> lock(*c.wmx);
+						c << &applied;
+					}
 				}
 			}
 			else
@@ -1201,11 +1209,6 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 
 	//premies given
 
-	//block engaged players
-	if(hero1->tempOwner<PLAYER_LIMIT)
-		states.setFlag(hero1->tempOwner,&PlayerStatus::engagedIntoBattle,true);
-	if(hero2 && hero2->tempOwner<PLAYER_LIMIT)
-		states.setFlag(hero2->tempOwner,&PlayerStatus::engagedIntoBattle,true);
 
 	//send info about battles
 	BattleStart bs;
@@ -1292,6 +1295,9 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 		return false;
 	}
 
+	if(states.checkFlag(h->tempOwner, &PlayerStatus::engagedIntoBattle) && complain("Cannot move hero during the battle"))
+		return false;
+
 	tlog5 << "Player " <<int(asker) << " wants to move hero "<< hid << " from "<< h->pos << " to " << dst << std::endl;
 	int3 hmpos = dst + int3(-1,0,0);
 
@@ -1356,11 +1362,9 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 	//checks for standard movement
 	if(!instant)
 	{
-		if( (distance(h->pos,dst)>=1.5) //tiles are not neighbouring
-			|| (h->movement < cost  &&  h->movement < 100) //lack of movement points
-		  ) 
+		if( distance(h->pos,dst) >= 1.5  &&  complain("Tiles are not neighbouring!")
+			|| h->movement < cost  &&  h->movement < 100  &&  complain("Not enough move points!")) 
 		{
-			tlog2 << "Cannot move hero, not enough move points or tiles are not neighbouring!\n";
 			sendAndApply(&tmh);
 			return false;
 		}
@@ -1556,6 +1560,12 @@ void CGameHandler::giveHeroArtifact(int artid, int hid, int position) //pos==-1
 
 void CGameHandler::startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb) //use hero=NULL for no hero
 {
+	engageIntoBattle(army1->tempOwner);
+	engageIntoBattle(army2->tempOwner);
+	//block engaged players
+	if(army2->tempOwner < PLAYER_LIMIT)
+		states.setFlag(army2->tempOwner,&PlayerStatus::engagedIntoBattle,true);
+
 	boost::thread(boost::bind(&CGameHandler::startBattle,this,army1,army2,tile,hero1,hero2,cb));
 }
 
@@ -2442,19 +2452,35 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	{
 		SetMana sm;
 		ChangeSpells cs;
+		SetHeroArtifacts sha;
+
+		CGHeroInstance *h = gs->getHero(gs->getPlayer(player)->currentSelection);
+		if(!h && complain("Cannot realize cheat, no hero selected!")) return;
+
+		sm.hid = cs.hid = h->id;
+
+		//give all spells
 		cs.learn = 1;
 		for(int i=0;i<VLC->spellh->spells.size();i++)
 		{
 			if(!VLC->spellh->spells[i].creatureAbility)
 				cs.spells.insert(i);
 		}
-		sm.hid = cs.hid = gs->players[player].currentSelection;
+
+		//give mana
 		sm.val = 999;
-		if(gs->getHero(cs.hid))
+
+		if(!h->getArt(17)) //hero doesn't have spellbook
 		{
-			sendAndApply(&cs);
-			sendAndApply(&sm);
+			//give spellbook
+			sha.artifacts = h->artifacts;
+			sha.artifWorn = h->artifWorn;
+			sha.artifWorn[17] = 0;
+			sendAndApply(&sha);
 		}
+
+		sendAndApply(&cs);
+		sendAndApply(&sm);
 	}
 	else if(message == "vcmiainur") //gives 5 archangels into each slot
 	{
@@ -2467,7 +2493,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 				sg.garrs[hero->id].slots[i] = std::pair<ui32,si32>(13,5);
 		sendAndApply(&sg);
 	}
-	else if(message == "vcmiangband") //gives 10 black knightinto each slot
+	else if(message == "vcmiangband") //gives 10 black knight into each slot
 	{
 		SetGarrisons sg;
 		CGHeroInstance *hero = gs->getHero(gs->getPlayer(player)->currentSelection);
@@ -2513,6 +2539,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	else if(message == "vcmieagles") //reveal FoW
 	{
 		FoWChange fc;
+		fc.mode = 1;
 		fc.player = player;
 		for(int i=0;i<gs->map->width;i++)
 			for(int j=0;j<gs->map->height;j++)
@@ -2978,4 +3005,16 @@ bool CGameHandler::buildBoat( ui32 objid )
 	sendAndApply(&no);
 
 	return true;
+}
+
+void CGameHandler::engageIntoBattle( ui8 player )
+{
+	if(vstd::contains(states.players, player))
+		states.setFlag(player,&PlayerStatus::engagedIntoBattle,true);
+
+	//notify interfaces
+	PlayerBlocked pb;
+	pb.player = player;
+	pb.reason = PlayerBlocked::UPCOMING_BATTLE;
+	sendAndApply(&pb);
 }

+ 1 - 1
server/CGameHandler.h

@@ -160,6 +160,7 @@ public:
 	void handleTimeEvents();
 	bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
 	void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h );
+	void engageIntoBattle( ui8 player );
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -176,7 +177,6 @@ public:
 
 	void run(bool resume);
 	void newTurn();
-
 	friend class CVCMIServer;
 	friend class CScriptCallback;
 };