Browse Source

Towards duel support.

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

+ 9 - 36
AI/GeniusAI/BattleLogic.cpp

@@ -261,7 +261,7 @@ BattleAction CBattleLogic::MakeDecision(int stackID)
 	const CStack *currentStack = m_cb->battleGetStackByID(stackID);
 	if(currentStack->position < 0 || currentStack->getCreature()->idNumber == 147) //turret or first aid kit
 	{
-		return MakeDefend(stackID);
+		return BattleAction::makeDefend(currentStack);
 	}
 	MakeStatistics(stackID);
 
@@ -285,11 +285,11 @@ BattleAction CBattleLogic::MakeDecision(int stackID)
 	if (additionalInfo == -1 || creatures.empty())
 	{
 		// defend
-		return MakeDefend(stackID);
+		return BattleAction::makeDefend(currentStack);
 	}
 	else if (additionalInfo == -2)
 	{
-		return MakeWait(stackID);
+		return BattleAction::makeWait(currentStack);
 	}
 
 	list<int>::iterator it, eit;
@@ -472,26 +472,6 @@ std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(const CStack *defend
 	return fields;
 }
 
-BattleAction CBattleLogic::MakeDefend(int stackID)
-{
-	BattleAction ba;
-	ba.side = m_side;
-	ba.actionType = action_defend;
-	ba.stackNumber = stackID;
-	ba.additionalInfo = -1;
-	return ba;
-}
-
-BattleAction CBattleLogic::MakeWait(int stackID)
-{
-	BattleAction ba;
-	ba.side = m_side;
-	ba.actionType = action_wait;
-	ba.stackNumber = stackID;
-	ba.additionalInfo = -1;
-	return ba;
-}
-
 BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 {
 	const CStack *attackerStack = m_cb->battleGetStackByID(attackerID),
@@ -501,19 +481,12 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 	//don't attack ourselves
 	if(destinationStack->attackerOwned == !m_side)
 	{
-		return MakeDefend(attackerID);
+		return BattleAction::makeDefend(attackerStack);
 	}
 
-	if (m_cb->battleCanShoot(attackerID, m_cb->battleGetPos(destinationID)))
+	if (m_cb->battleCanShoot(attackerID, m_cb->battleGetPos(destinationID)))	// shoot
 	{
-		// shoot
-		BattleAction ba;
-		ba.side = m_side;
-		ba.additionalInfo = -1;
-		ba.actionType = action_shoot; // shoot
-		ba.stackNumber = attackerID;
-		ba.destinationTile = (ui16)m_cb->battleGetPos(destinationID);
-		return ba;
+		return BattleAction::makeShotAttack(attackerStack, destinationStack);
 	}
 	else
 	{
@@ -522,7 +495,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 		std::vector<int> av_tiles = GetAvailableHexesForAttacker(m_cb->battleGetStackByID(destinationID), m_cb->battleGetStackByID(attackerID));
 		if (av_tiles.size() < 1)
 		{
-			return MakeDefend(attackerID);
+			return BattleAction::makeDefend(attackerStack);
 		}
 
 		// get the best tile - now the nearest
@@ -549,7 +522,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 
 		if(fields.size() == 0)
 		{
-			return MakeDefend(attackerID);
+			return BattleAction::makeDefend(attackerStack);
 		}
 
 		BattleAction ba;
@@ -566,7 +539,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 		else if(BattleInfo::mutualPosition(dest_tile, destStackPos-1) != -1)
 			ba.additionalInfo = destStackPos-1;
 		else
-			MakeDefend(attackerID);
+			return BattleAction::makeDefend(attackerStack);
 
 		int nearest_dist = m_battleHelper.InfiniteDistance;
 		int nearest_pos = -1;

+ 0 - 8
AI/GeniusAI/BattleLogic.h

@@ -106,14 +106,6 @@ private:
 	 * Helper function. It's used for performing an attack action.
 	 */
 	std::vector<int> GetAvailableHexesForAttacker(const CStack *defender, const CStack *attacker = NULL);
-	/**
-	 * Just make defend action.
-	 */
-	BattleAction MakeDefend(int stackID);
-	/**
-	 * Just make wait action.
-	 */
-	BattleAction MakeWait(int stackID);
 	/**
 	 * Make an attack action if it's possible.
 	 * If it's not possible then function returns defend action.

+ 96 - 7
AI/StupidAI/StupidAI.cpp

@@ -3,33 +3,122 @@
 #include "../../lib/CGameState.h"
 
 CStupidAI::CStupidAI(void)
+	: side(-1), cb(NULL)
 {
+	print("created");
 }
 
 
 CStupidAI::~CStupidAI(void)
 {
+	print("destroyed");
 }
 
 void CStupidAI::init( IBattleCallback * CB )
 {
-
+	print("init called, saving ptr to IBattleCallback");
+	cb = CB;
 }
 
 void CStupidAI::actionFinished( const BattleAction *action )
 {
-
+	print("actionFinished called");
 }
 
 void CStupidAI::actionStarted( const BattleAction *action )
 {
-
+	print("actionStarted called");
 }
 
 BattleAction CStupidAI::activeStack( const CStack * stack )
 {
-	BattleAction ba;
-	ba.actionType = BattleAction::DEFEND;
-	ba.stackNumber = stack->ID;
-	return ba;
+	print("activeStack called");
+	return BattleAction::makeDefend(stack);
+	if(stack->position % 17  <  5) //move army little towards enemy
+	{
+		THex dest = stack->position + side*2 - 1;
+		print(stack->nodeName() << "will be moved to " + boost::lexical_cast<std::string>(dest));
+		return BattleAction::makeMove(stack, ); 
+	}
+}
+
+void CStupidAI::battleAttack(const BattleAttack *ba)
+{
+	print("battleAttack called");
+}
+
+void CStupidAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
+{
+	print("battleStacksAttacked called");
+}
+
+void CStupidAI::battleEnd(const BattleResult *br) 
+{
+	print("battleEnd called");
+}
+
+void CStupidAI::battleResultsApplied() 
+{
+	print("battleResultsApplied called");
+}
+
+void CStupidAI::battleNewRoundFirst(int round) 
+{
+	print("battleNewRoundFirst called");
+}
+
+void CStupidAI::battleNewRound(int round) 
+{
+	print("battleNewRound called");
+}
+
+void CStupidAI::battleStackMoved(const CStack * stack, THex dest, int distance, bool end) 
+{
+	print("battleStackMoved called");;
+}
+
+void CStupidAI::battleSpellCast(const BattleSpellCast *sc) 
+{
+	print("battleSpellCast called");
+}
+
+void CStupidAI::battleStacksEffectsSet(const SetStackEffect & sse) 
+{
+	print("battleStacksEffectsSet called");
+}
+
+void CStupidAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side) 
+{
+	print("battleStart called");
+	side = Side;
+}
+
+void CStupidAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom) 
+{
+	print("battleStacksHealedRes called");
+}
+
+void CStupidAI::battleNewStackAppeared(const CStack * stack) 
+{
+	print("battleNewStackAppeared called");
+}
+
+void CStupidAI::battleObstaclesRemoved(const std::set<si32> & removedObstacles) 
+{
+	print("battleObstaclesRemoved called");
+}
+
+void CStupidAI::battleCatapultAttacked(const CatapultAttack & ca) 
+{
+	print("battleCatapultAttacked called");
+}
+
+void CStupidAI::battleStacksRemoved(const BattleStacksRemoved & bsr) 
+{
+	print("battleStacksRemoved called");
+}
+
+void CStupidAI::print(const std::string &text) const
+{
+	tlog0 << "CStupidAI [" << this <<"]: " << text << std::endl;
 }

+ 21 - 0
AI/StupidAI/StupidAI.h

@@ -2,6 +2,10 @@
 
 class CStupidAI : public CBattleGameInterface
 {
+	int side;
+	IBattleCallback *cb;
+
+	void print(const std::string &text) const;
 public:
 	CStupidAI(void);
 	~CStupidAI(void);
@@ -10,5 +14,22 @@ public:
 	void actionFinished(const BattleAction *action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
 	void actionStarted(const BattleAction *action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
 	BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack
+
+	void battleAttack(const BattleAttack *ba) OVERRIDE; //called when stack is performing attack
+	void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) OVERRIDE; //called when stack receives damage (after battleAttack())
+	void battleEnd(const BattleResult *br) OVERRIDE;
+	void battleResultsApplied() OVERRIDE; //called when all effects of last battle are applied
+	void battleNewRoundFirst(int round) OVERRIDE; //called at the beginning of each turn before changes are applied;
+	void battleNewRound(int round) OVERRIDE; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
+	void battleStackMoved(const CStack * stack, THex dest, int distance, bool end) OVERRIDE;
+	void battleSpellCast(const BattleSpellCast *sc) OVERRIDE;
+	void battleStacksEffectsSet(const SetStackEffect & sse) OVERRIDE;//called when a specific effect is set to stacks
+	void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) OVERRIDE; //called by engine when battle starts; side=0 - left, side=1 - right
+	void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom) OVERRIDE; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp
+	void battleNewStackAppeared(const CStack * stack) OVERRIDE; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
+	void battleObstaclesRemoved(const std::set<si32> & removedObstacles) OVERRIDE; //called when a certain set  of obstacles is removed from batlefield; IDs of them are given
+	void battleCatapultAttacked(const CatapultAttack & ca) OVERRIDE; //called when catapult makes an attack
+	void battleStacksRemoved(const BattleStacksRemoved & bsr) OVERRIDE; //called when certain stack is completely removed from battlefield
+
 };
 

+ 1 - 0
AI/StupidAI/stdafx.h

@@ -1,2 +1,3 @@
 #pragma  once
+#include <boost/lexical_cast.hpp>
 #include "../../AI_Base.h"

+ 8 - 1
CCallback.cpp

@@ -476,7 +476,14 @@ bool CCallback::buildBuilding(const CGTownInstance *town, si32 buildingID)
 int CBattleCallback::battleGetBattlefieldType()
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	return gs->battleGetBattlefieldType();
+	//return gs->battleGetBattlefieldType();
+
+	if(!gs->curB)
+	{
+		tlog2<<"battleGetBattlefieldType called when there is no battle!"<<std::endl;
+		return -1;
+	}
+	return gs->curB->battlefieldType;
 }
 
 int CBattleCallback::battleGetObstaclesAtTile(THex tile) //returns bitfield 

+ 1 - 1
client/CBattleInterface.cpp

@@ -2993,7 +2993,7 @@ void CBattleInterface::showAliveStack(const CStack *stack, SDL_Surface * to)
 		}
 	}
 
-	creAnims[ID]->nextFrame(to, creAnims[ID]->pos.x, creAnims[ID]->pos.y, creDir[ID], animCount, incrementFrame, ID==activeStack->ID, ID==mouseHoveredStack); //increment always when moving, never if stack died
+	creAnims[ID]->nextFrame(to, creAnims[ID]->pos.x, creAnims[ID]->pos.y, creDir[ID], animCount, incrementFrame, activeStack && ID==activeStack->ID, ID==mouseHoveredStack); //increment always when moving, never if stack died
 
 	//printing amount
 	if(stack->count > 0 //don't print if stack is not alive

+ 2 - 0
client/CMT.cpp

@@ -279,6 +279,8 @@ int main(int argc, char** argv)
 	{
 		StartInfo *si = new StartInfo();
 		si->mode = StartInfo::DUEL;
+		si->playerInfos[0].color = 0;
+		si->playerInfos[1].color = 1;
 		startGame(si);
 	}
 	mainGUIThread = new boost::thread(&CGuiHandler::run, boost::ref(GH));

+ 18 - 11
client/Client.cpp

@@ -116,7 +116,8 @@ void CClient::waitForMoveAndSend(int color)
 {
 	try
 	{
-		BattleAction ba = playerint[color]->activeStack(gs->curB->getStack(gs->curB->activeStack, false));
+		assert(vstd::contains(battleints, color));
+		BattleAction ba = battleints[color]->activeStack(gs->curB->getStack(gs->curB->activeStack, false));
 		*serv << &MakeAction(ba);
 		return;
 	}HANDLE_EXCEPTION
@@ -402,13 +403,14 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 				playerint[color] = new CPlayerInterface(color);
 				humanPlayers++;
 			}
+			battleints[color] = playerint[color];
 
 			playerint[color]->init(cb);
 		}
 		else
 		{
 			CBattleCallback * cbc = new CBattleCallback(gs, color, this);
-			battleints[color] = CAIHandler::getNewBattleAI(cb,conf.cc.defaultAI);
+			battleints[color] = CAIHandler::getNewBattleAI(cb,"StupidAI");
 			battleints[color]->init(cbc);
 		}
 	}
@@ -417,16 +419,21 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	{
 		CPlayerInterface *p = new CPlayerInterface(-1);
 		p->observerInDuelMode = true;
-		playerint[254] = p;
+		battleints[254] = playerint[254] = p;
+		GH.curInt = p;
 		p->init(new CCallback(gs, -1, this));
 		battleStarted(gs->curB);
 	}
+	else
+	{
+		playerint[255] =  CAIHandler::getNewAI(cb,conf.cc.defaultAI);
+		playerint[255]->init(new CCallback(gs,255,this));
+	}
 
 	serv->addStdVecItems(const_cast<CGameInfo*>(CGI)->state);
 	hotSeat = (humanPlayers > 1);
 
-	playerint[255] =  CAIHandler::getNewAI(cb,conf.cc.defaultAI);
-	playerint[255]->init(new CCallback(gs,255,this));
+
 }
 
 template <typename Handler>
@@ -553,12 +560,12 @@ void CClient::battleStarted(const BattleInfo * info)
 
 	new CBattleInterface(info->belligerents[0], info->belligerents[1], info->heroes[0], info->heroes[1], Rect((conf.cc.resx - 800)/2, (conf.cc.resy - 600)/2, 800, 600), att, def);
 
-	if(vstd::contains(playerint,info->side1))
-		playerint[info->side1]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 0);
-	if(vstd::contains(playerint,info->side2))
-		playerint[info->side2]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
-	if(vstd::contains(playerint,254))
-		playerint[254]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
+	if(vstd::contains(battleints,info->side1))
+		battleints[info->side1]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 0);
+	if(vstd::contains(battleints,info->side2))
+		battleints[info->side2]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
+	if(vstd::contains(battleints,254))
+		battleints[254]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
 }
 
 template void CClient::serialize( CISer<CLoadFile> &h, const int version );

+ 27 - 54
client/NetPacksClient.cpp

@@ -29,6 +29,14 @@
 		if(vstd::contains(cl->playerint,player))		\
 			cl->playerint[player]->function(__VA_ARGS__);
 
+#define BATTLE_INTERFACE_CALL_IF_PRESENT(player,function,...) 	\
+		if(vstd::contains(cl->battleints,player))		\
+			cl->battleints[player]->function(__VA_ARGS__);
+
+#define BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(function,...) 	\
+	BATTLE_INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, function, __VA_ARGS__) \
+	BATTLE_INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, function, __VA_ARGS__) \
+	BATTLE_INTERFACE_CALL_IF_PRESENT(254, function, __VA_ARGS__)
 /*
  * NetPacksClient.cpp, part of VCMI engine
  *
@@ -486,14 +494,12 @@ void BattleStart::applyCl( CClient *cl )
 
 void BattleNextRound::applyFirstCl(CClient *cl)
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleNewRoundFirst,round);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleNewRoundFirst,round);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewRoundFirst,round);
 }
 
 void BattleNextRound::applyCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleNewRound,round);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleNewRound,round);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewRound,round);
 }
 
 void BattleSetActiveStack::applyCl( CClient *cl )
@@ -508,23 +514,19 @@ void BattleSetActiveStack::applyCl( CClient *cl )
 	{
 		playerToCall = activated->owner;
 	}
-	if( vstd::contains(cl->playerint, playerToCall) )
+	if( vstd::contains(cl->battleints, playerToCall) )
 		boost::thread( boost::bind(&CClient::waitForMoveAndSend, cl, playerToCall) );
 }
 
 void BattleResult::applyFirstCl( CClient *cl )
 {
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleEnd(this);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleEnd(this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleEnd,this);
 }
 
 void BattleStackMoved::applyFirstCl( CClient *cl )
 {
 	const CStack * movedStack = GS(cl)->curB->getStack(stack);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleStackMoved,movedStack,tile,distance,ending);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStackMoved,movedStack,tile,distance,ending);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStackMoved,movedStack,tile,distance,ending);
 }
 
 void BattleStackAttacked::applyCl( CClient *cl )
@@ -532,16 +534,12 @@ void BattleStackAttacked::applyCl( CClient *cl )
 	std::vector<BattleStackAttacked> bsa;
 	bsa.push_back(*this);
 
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleStacksAttacked,bsa);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStacksAttacked,bsa);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,bsa);
 }
 
 void BattleAttack::applyFirstCl( CClient *cl )
 {
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleAttack(this);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleAttack(this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleAttack,this);
 	for (int g=0; g<bsa.size(); ++g)
 	{
 		for (int z=0; z<bsa[g].healedStacks.size(); ++z)
@@ -553,59 +551,39 @@ void BattleAttack::applyFirstCl( CClient *cl )
 
 void BattleAttack::applyCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleStacksAttacked,bsa);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStacksAttacked,bsa);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,bsa);
 }
 
 void StartAction::applyFirstCl( CClient *cl )
 {
 	cl->curbaction = new BattleAction(ba);
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->actionStarted(&ba);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->actionStarted(&ba);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionStarted, &ba);
 }
 
 void BattleSpellCast::applyCl( CClient *cl )
 {
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleSpellCast(this);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleSpellCast(this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleSpellCast,this);
 
 	if(id >= 66 && id <= 69) //elemental summoning
 	{
-		if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-			cl->playerint[GS(cl)->curB->side1]->battleNewStackAppeared(GS(cl)->curB->stacks[GS(cl)->curB->stacks.size() - 1]);
-		if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-			cl->playerint[GS(cl)->curB->side2]->battleNewStackAppeared(GS(cl)->curB->stacks[GS(cl)->curB->stacks.size() - 1]);
+		BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewStackAppeared,GS(cl)->curB->stacks.back());
 	}
 }
 
 void SetStackEffect::applyCl( CClient *cl )
 {
-	BattleSpellCast sc;
-	sc.id = effect.id;
-	sc.side = 3; //doesn't matter
-	sc.skill = effect.val;
-
 	//informing about effects
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleStacksEffectsSet(*this);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleStacksEffectsSet(*this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(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);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,stacks);
 }
 
 void BattleResultsApplied::applyCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(player1,battleResultsApplied);
-	INTERFACE_CALL_IF_PRESENT(player2,battleResultsApplied);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleResultsApplied);
 }
 
 void StacksHealedOrResurrected::applyCl( CClient *cl )
@@ -615,29 +593,25 @@ void StacksHealedOrResurrected::applyCl( CClient *cl )
 	{
 		shiftedHealed.push_back(std::make_pair(healedStacks[v].stackID, healedStacks[v].healedHP));
 	}
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleStacksHealedRes, shiftedHealed, lifeDrain, drainedFrom);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleStacksHealedRes, shiftedHealed, lifeDrain, drainedFrom);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksHealedRes, shiftedHealed, lifeDrain, drainedFrom);
 }
 
 void ObstaclesRemoved::applyCl( CClient *cl )
 {
 	//inform interfaces about removed obstacles
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleObstaclesRemoved, obstacles);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleObstaclesRemoved, obstacles);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleObstaclesRemoved, obstacles);
 }
 
 void CatapultAttack::applyCl( CClient *cl )
 {
 	//inform interfaces about catapult attack
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleCatapultAttacked, *this);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleCatapultAttacked, *this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleCatapultAttacked, *this);
 }
 
 void BattleStacksRemoved::applyCl( CClient *cl )
 {
 	//inform interfaces about removed stacks
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleStacksRemoved, *this);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleStacksRemoved, *this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksRemoved, *this);
 }
 
 CGameState* CPackForClient::GS( CClient *cl )
@@ -647,8 +621,7 @@ CGameState* CPackForClient::GS( CClient *cl )
 
 void EndAction::applyCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,actionFinished,cl->curbaction);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,actionFinished,cl->curbaction);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionFinished, cl->curbaction);
 
 	delete cl->curbaction;
 	cl->curbaction = NULL;

+ 59 - 0
lib/BattleAction.cpp

@@ -0,0 +1,59 @@
+#define VCMI_DLL
+#include "BattleAction.h"
+#include "CGameState.h"
+
+BattleAction::BattleAction()
+{
+	side = -1;
+	stackNumber = -1;
+	actionType = INVALID;
+	destinationTile = -1;
+	additionalInfo = -1;
+}
+
+BattleAction BattleAction::makeDefend(const CStack *stack)
+{
+	BattleAction ba;
+	ba.side = !stack->attackerOwned;
+	ba.actionType = DEFEND;
+	ba.stackNumber = stack->ID;
+	return ba;
+}
+
+BattleAction BattleAction::makeMeleeAttack(const CStack *stack) /*WARNING: stacks must be neighbouring! */
+{
+	BattleAction ba;
+	ba.side = !stack->attackerOwned;
+	ba.actionType = WAIT;
+	ba.stackNumber = stack->ID;
+	return ba;
+}
+
+BattleAction BattleAction::makeWait(const CStack *stack)
+{
+	BattleAction ba;
+	ba.side = !stack->attackerOwned;
+	ba.actionType = WAIT;
+	ba.stackNumber = stack->ID;
+	return ba;
+}
+
+BattleAction BattleAction::makeShotAttack(const CStack *shooter, const CStack *target)
+{
+	BattleAction ba;
+	ba.side = !shooter->attackerOwned;
+	ba.actionType = SHOOT;
+	ba.stackNumber = shooter->ID;
+	ba.destinationTile = target->position;
+	return ba;
+}
+
+BattleAction BattleAction::makeMove(const CStack *stack, THex dest)
+{
+	BattleAction ba;
+	ba.side = !stack->attackerOwned;
+	ba.actionType = WALK;
+	ba.stackNumber = stack->ID;
+	ba.destinationTile = dest;
+	return ba;
+}

+ 14 - 2
lib/BattleAction.h

@@ -1,6 +1,8 @@
+#pragma once;
 #ifndef __BATTLEACTION_H__
 #define __BATTLEACTION_H__
 
+#include "../global.h"
 /*
  * BattleAction.h, part of VCMI engine
  *
@@ -11,13 +13,15 @@
  *
  */
 
-struct BattleAction
+class CStack;
+
+struct DLL_EXPORT BattleAction
 {
 	ui8 side; //who made this action: false - left, true - right player
 	ui32 stackNumber;//stack ID, -1 left hero, -2 right hero,
 	enum ActionType
 	{
-		NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE, STACK_HEAL
+		INVALID = -1, NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE, STACK_HEAL
 	};
 	ui8 actionType; //    0 = No action;   1 = Hero cast a spell   2 = Walk   3 = Defend   4 = Retreat from the battle
 		//5 = Surrender   6 = Walk and Attack   7 = Shoot    8 = Wait   9 = Catapult
@@ -28,5 +32,13 @@ struct BattleAction
 	{
 		h & side & stackNumber & actionType & destinationTile & additionalInfo;
 	}
+
+	BattleAction();
+
+	static BattleAction makeDefend(const CStack *stack);
+	static BattleAction makeWait(const CStack *stack);
+	static BattleAction makeMeleeAttack(const CStack *stack); //WARNING: stacks must be neighbouring!;
+	static BattleAction makeShotAttack(const CStack *shooter, const CStack *target);
+	static BattleAction makeMove(const CStack *stack, THex dest);
 };
 #endif // __BATTLEACTION_H__

+ 7 - 1
lib/CArtHandler.h

@@ -60,6 +60,7 @@ public:
 
 class DLL_EXPORT CArtifactInstance : public CBonusSystemNode
 {
+	void init();
 public:
 	ConstTransitivePtr<CArtifact> art; 
 	si32 id; //id of the instance
@@ -67,7 +68,6 @@ public:
 	CArtifactInstance();
 	CArtifactInstance(CArtifact *Art);
 
-	void init();
 	std::string nodeName() const OVERRIDE;
 	void setType(CArtifact *Art);
 
@@ -80,6 +80,12 @@ public:
 	static CArtifactInstance *createScroll(const CSpell *s);
 };
 
+class DLL_EXPORT CCombinedArtifactInstance : public CArtifactInstance
+{
+public:
+
+};
+
 class DLL_EXPORT IModableArt : public CArtifact //artifact which can have different properties, such as scroll or banner
 { //used only for dynamic cast :P
 public:

+ 4 - 1
lib/CGameState.cpp

@@ -1041,7 +1041,7 @@ const CGHeroInstance * CStack::getMyHero() const
 std::string CStack::nodeName() const
 {
 	std::ostringstream oss;
-	oss << "Battle stack of " << count << " creatures of ";
+	oss << "Battle stack [" << ID << "]: " << count << " creatures of ";
 	if(type)
 		oss << type->namePl;
 	else
@@ -1486,6 +1486,7 @@ BattleInfo * setupBattle( int3 tile, int terrain, int terType, const CArmedInsta
 	std::vector<CStack*> & stacks = (curB->stacks);
 
 	curB->tile = tile;
+	curB->battlefieldType = terType;
 	curB->belligerents[0] = const_cast<CArmedInstance*>(armies[0]);
 	curB->belligerents[1] = const_cast<CArmedInstance*>(armies[1]);
 	curB->heroes[0] = const_cast<CGHeroInstance*>(heroes[0]);
@@ -1930,12 +1931,14 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 			const CGHeroInstance *heroes[2];
 			const CGTownInstance *town = NULL;
 			CGHeroInstance *h = new CGHeroInstance();
+			h->setOwner(0);
 			h->subID = 1;
 			h->initHero(1);
 			h->initObj();
 			//h->putStack(0, new CStackInstance(34, 5));
 
 			CGCreature *c = new CGCreature();
+			c->setOwner(1);
 			c->putStack(0, new CStackInstance(70, 6));
 			c->subID = 34;
 			c->initObj();

+ 2 - 1
lib/CGameState.h

@@ -212,11 +212,12 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 	std::vector<CObstacleInstance> obstacles;
 	ui8 castSpells[2]; //[0] - attacker, [1] - defender
 	SiegeInfo si;
+	si32 battlefieldType;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & side1 & side2 & round & activeStack & siege & tid & tile & stacks & belligerents & obstacles
-			& castSpells & si;
+			& castSpells & si & battlefieldType;
 		h & heroes;
 		h & static_cast<CBonusSystemNode&>(*this);
 	}