Browse Source

* counterattacks
* spells learning
* no tooltips for objects under FoW
* working resource silo
* new system for simple unit abilities/states
* neutral monster army disappears when defeated
* synchronization between client and server processes
* fixed battle ending

Michał W. Urbańczyk 17 years ago
parent
commit
e71b40ccc5

+ 7 - 7
CBattleInterface.cpp

@@ -320,13 +320,13 @@ void CBattleInterface::show(SDL_Surface * to)
 	//double loop because dead stacks should be printed first
 	for(std::map<int, CStack>::iterator j=stacks.begin(); j!=stacks.end(); ++j)
 	{
-		if(j->second.alive)
+		if(j->second.alive())
 			stackAliveByHex[j->second.position].push_back(j->second.ID);
 	}
 	std::vector<int> stackDeadByHex[187];
 	for(std::map<int, CStack>::iterator j=stacks.begin(); j!=stacks.end(); ++j)
 	{
-		if(!j->second.alive)
+		if(!j->second.alive())
 			stackDeadByHex[j->second.position].push_back(j->second.ID);
 	}
 
@@ -336,7 +336,7 @@ void CBattleInterface::show(SDL_Surface * to)
 	{
 		for(int v=0; v<stackDeadByHex[b].size(); ++v)
 		{
-			creAnims[stackDeadByHex[b][v]]->nextFrame(to, creAnims[stackDeadByHex[b][v]]->pos.x, creAnims[stackDeadByHex[b][v]]->pos.y, creDir[stackDeadByHex[b][v]], (animCount%4==0 || creAnims[stackDeadByHex[b][v]]->getType()!=2) && stacks[stackDeadByHex[b][v]].alive, stackDeadByHex[b][v]==activeStack); //increment always when moving, never if stack died
+			creAnims[stackDeadByHex[b][v]]->nextFrame(to, creAnims[stackDeadByHex[b][v]]->pos.x, creAnims[stackDeadByHex[b][v]]->pos.y, creDir[stackDeadByHex[b][v]], (animCount%4==0 || creAnims[stackDeadByHex[b][v]]->getType()!=2) && stacks[stackDeadByHex[b][v]].alive(), stackDeadByHex[b][v]==activeStack); //increment always when moving, never if stack died
 			//printing amount
 			if(stacks[stackDeadByHex[b][v]].amount > 0) //don't print if stack is not alive
 			{
@@ -353,7 +353,7 @@ void CBattleInterface::show(SDL_Surface * to)
 	{
 		for(int v=0; v<stackAliveByHex[b].size(); ++v)
 		{
-			creAnims[stackAliveByHex[b][v]]->nextFrame(to, creAnims[stackAliveByHex[b][v]]->pos.x, creAnims[stackAliveByHex[b][v]]->pos.y, creDir[stackAliveByHex[b][v]], (animCount%4==0) && stacks[stackAliveByHex[b][v]].alive, stackAliveByHex[b][v]==activeStack); //increment always when moving, never if stack died
+			creAnims[stackAliveByHex[b][v]]->nextFrame(to, creAnims[stackAliveByHex[b][v]]->pos.x, creAnims[stackAliveByHex[b][v]]->pos.y, creDir[stackAliveByHex[b][v]], (animCount%4==0) && stacks[stackAliveByHex[b][v]].alive(), stackAliveByHex[b][v]==activeStack); //increment always when moving, never if stack died
 			//printing amount
 			if(stacks[stackAliveByHex[b][v]].amount > 0) //don't print if stack is not alive
 			{
@@ -847,7 +847,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 			return; //we are not permit to do anything
 
 		CStack* dest = LOCPLINT->cb->battleGetStackByPos(whichOne); //creature at destination tile; -1 if there is no one
-		if(!dest || !dest->alive) //no creature at that tile
+		if(!dest || !dest->alive()) //no creature at that tile
 		{
 			if(std::find(shadedHexes.begin(),shadedHexes.end(),whichOne)!=shadedHexes.end())// and it's in our range
 				giveCommand(2,whichOne,activeStack);
@@ -1321,7 +1321,7 @@ void CBattleHex::mouseMoved(SDL_MouseMotionEvent &sEvent)
 	{
 		if(myInterface->console->alterTxt.size() == 0 && LOCPLINT->cb->battleGetStack(myNumber) != -1 &&
 			LOCPLINT->cb->battleGetStackByPos(myNumber)->owner != LOCPLINT->playerID &&
-			LOCPLINT->cb->battleGetStackByPos(myNumber)->alive)
+			LOCPLINT->cb->battleGetStackByPos(myNumber)->alive())
 		{
 			char tabh[160];
 			CStack attackedStack = *LOCPLINT->cb->battleGetStackByPos(myNumber);
@@ -1352,7 +1352,7 @@ void CBattleHex::clickRight(boost::logic::tribool down)
 	if(hovered && strictHovered && stID!=-1)
 	{
 		CStack myst = *LOCPLINT->cb->battleGetStackByID(stID); //stack info
-		if(!myst.alive) return;
+		if(!myst.alive()) return;
 		StackState *pom = NULL;
 		if(down)
 		{

+ 3 - 1
CCallback.cpp

@@ -234,6 +234,8 @@ std::vector < std::string > CCallback::getObjDescriptions(int3 pos)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	std::vector<std::string> ret;
+	if(!isVisible(pos,player))
+		return ret;
 	BOOST_FOREACH(const CGObjectInstance * obj, gs->map->terrain[pos.x][pos.y][pos.z].blockingObjects)
 		ret.push_back(obj->hoverName);
 	return ret;
@@ -521,7 +523,7 @@ bool CCallback::battleCanShoot(int ID, int dest) //TODO: finish
 	if(battleGetStackByID(ID)->creature->isShooting() 
 		&& battleGetStack(dest) != -1 
 		&& battleGetStackByPos(dest)->owner != battleGetStackByID(ID)->owner
-		&& battleGetStackByPos(dest)->alive)
+		&& battleGetStackByPos(dest)->alive())
 		return true;
 	return false;
 }

+ 6 - 2
CCastleInterface.cpp

@@ -522,6 +522,11 @@ void CCastleInterface::buildingClicked(int building)
 				cmw->activate();
 				break;
 			}
+		case 15:
+			{
+				LOCPLINT->showInfoDialog(CGI->buildh->buildings[town->subID][15]->description,std::vector<SComponent*>());
+				break;
+			}
 		case 16:
 			{
 				const CGHeroInstance *hero = town->visitingHero;
@@ -1432,8 +1437,7 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner)
 	blitAt(view,332,76,bg);
 	for(int i=0; i<owner->town->town->mageLevel; i++)
 	{
-		int sp = 5 - i; //how many spells are available at this level
-		if(owner->town->subID==2 && vstd::contains(owner->town->builtBuildings,22)) sp++; //magic library in tower
+		int sp = owner->town->spellsAtLevel(i+1,false);
 		for(int j=0; j<sp; j++)
 		{
 			if(i < owner->town->mageGuildLevel())

+ 0 - 1
CCastleInterface.h

@@ -28,7 +28,6 @@ public:
 	void clickRight (tribool down);
 	void mouseMoved (SDL_MouseMotionEvent & sEvent);
 };
-
 class CHeroGSlot : public ClickableL, public ClickableR, public Hoverable
 {
 public:

+ 23 - 25
CGameState.cpp

@@ -112,7 +112,7 @@ CStack * BattleInfo::getStackT(int tileID)
 			|| (stacks[g]->creature->isDoubleWide() && stacks[g]->attackerOwned && stacks[g]->position-1 == tileID)
 			|| (stacks[g]->creature->isDoubleWide() && !stacks[g]->attackerOwned && stacks[g]->position+1 == tileID))
 		{
-			if(stacks[g]->alive)
+			if(stacks[g]->alive())
 			{
 				return stacks[g];
 			}
@@ -125,7 +125,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, int stackToOmmit)
 	memset(accessibility,1,187); //initialize array with trues
 	for(int g=0; g<stacks.size(); ++g)
 	{
-		if(!stacks[g]->alive || stacks[g]->ID==stackToOmmit) //we don't want to lock position of this stack
+		if(!stacks[g]->alive() || stacks[g]->ID==stackToOmmit) //we don't want to lock position of this stack
 			continue;
 
 		accessibility[stacks[g]->position] = false;
@@ -254,8 +254,9 @@ std::vector<int> BattleInfo::getPath(int start, int dest, bool*accessibility)
 }
 
 CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S)
-	:creature(C),amount(A), baseAmount(A), owner(O), alive(true), position(-1), ID(I), attackerOwned(AO), firstHPleft(C->hitPoints), slot(S)
+	:creature(C),amount(A), baseAmount(A), owner(O), position(-1), ID(I), attackerOwned(AO), firstHPleft(C->hitPoints), slot(S), counterAttacks(0)
 {
+	state.insert(ALIVE);
 }
 void CGameState::applyNL(IPack * pack)
 {
@@ -372,6 +373,18 @@ void CGameState::applyNL(IPack * pack)
 			}
 			break;
 		}
+	case 109:
+		{
+			ChangeSpells *rh = static_cast<ChangeSpells*>(pack);
+			CGHeroInstance *hero = getHero(rh->hid);
+			if(rh->learn)
+				BOOST_FOREACH(ui32 sid, rh->spells)
+					hero->spells.insert(sid);
+			else
+				BOOST_FOREACH(ui32 sid, rh->spells)
+					hero->spells.erase(sid);
+			break;
+		}
 	case 500:
 		{
 			RemoveObject *rh = static_cast<RemoveObject*>(pack);
@@ -519,6 +532,8 @@ void CGameState::applyNL(IPack * pack)
 		{
 			BattleNextRound *ns = static_cast<BattleNextRound*>(pack);
 			curB->round = ns->round;
+			for(int i=0; i<curB->stacks.size();i++)
+				curB->stacks[i]->counterAttacks = 0;
 			break;
 		}
 	case 3002:
@@ -551,12 +566,15 @@ void CGameState::applyNL(IPack * pack)
 			CStack * at = curB->getStack(br->stackAttacked);
 			at->amount = br->newAmount;
 			at->firstHPleft = br->newHP;
-			at->alive = !br->killed();
+			if(br->killed())
+				at->state -= ALIVE;
 			break;
 		}
 	case 3006:
 		{
 			BattleAttack *br = static_cast<BattleAttack*>(pack);
+			if(br->counter())
+				curB->getStack(br->stackAttacking)->counterAttacks++;
 			applyNL(&br->bsa);
 			break;
 		}
@@ -1325,7 +1343,7 @@ void BattleInfo::calculateCasualties( std::set<std::pair<ui32,si32> > *casualtie
 {
 	for(int i=0; i<stacks.size();i++)//setting casualties
 	{
-		if(!stacks[i]->alive)
+		if(!stacks[i]->alive())
 		{
 			casualties[!stacks[i]->attackerOwned].insert(std::pair<ui32,si32>(stacks[i]->creature->idNumber,stacks[i]->baseAmount));
 		}
@@ -1334,24 +1352,4 @@ void BattleInfo::calculateCasualties( std::set<std::pair<ui32,si32> > *casualtie
 			casualties[!stacks[i]->attackerOwned].insert(std::pair<ui32,si32>(stacks[i]->creature->idNumber,stacks[i]->baseAmount - stacks[i]->amount));
 		}
 	}
-	//if(br->killedAmount>0) 
-	//{
-	//	bool found = false;
-	//	for(std::set<std::pair<ui32,si32> >::iterator it = curB->cas[1 - at->attackerOwned].begin(); it!=curB->cas[1 - at->attackerOwned].end(); ++it)
-	//	{
-	//		if(it->first == at->creature->idNumber)
-	//		{
-	//			found = true;
-	//			std::pair<ui32,si32>  mod = *it;
-	//			mod.second += br->killedAmount;
-
-	//			curB->cas[1 - at->attackerOwned].insert(it, mod);
-	//			curB->cas[1 - at->attackerOwned].erase(it);
-	//		}
-	//	}
-	//	if(!found)
-	//	{
-	//		curB->cas[1 - at->attackerOwned].insert(std::make_pair(at->creature->idNumber, br->killedAmount));
-	//	}
-	//}
 }

+ 13 - 5
CGameState.h

@@ -78,19 +78,22 @@ struct DLL_EXPORT BattleInfo
 };
 
 class DLL_EXPORT CStack
-{
+{ 
 public:
 	ui32 ID; //unique ID of stack
 	CCreature * creature;
 	ui32 amount, baseAmount;
 	ui32 firstHPleft; //HP of first creature in stack
-	ui8 owner, slot;  //owner - player colour (255 for neutrals), slot - position in garrison (255 for neutrals/called creatures)
+	ui8 owner, slot;  //owner - player colour (255 for neutrals), slot - position in garrison (may be 255 for neutrals/called creatures)
 	ui8 attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
 	ui16 position; //position on battlefield
-	ui8 alive; //true if it is alive
+	ui8 counterAttacks; //how many counter attacks has this stack perfomed in current round
+
+	std::set<EAbilities> abilities;
+	std::set<ECombatInfo> state;
 
 	CStack(CCreature * C, int A, int O, int I, bool AO, int S);
-	CStack() : creature(NULL),amount(-1),owner(255), alive(true), position(-1), ID(-1), attackerOwned(true), firstHPleft(-1), slot(255), baseAmount(-1){};
+	CStack() : creature(NULL),amount(-1),owner(255), position(-1), ID(-1), attackerOwned(true), firstHPleft(-1), slot(255), baseAmount(-1), counterAttacks(0){};
 
 	template <typename Handler> void save(Handler &h, const int version)
 	{
@@ -101,15 +104,20 @@ public:
 		ui32 id;
 		h & id;
 		creature = &VLC->creh->creatures[id];
+		abilities = creature->abilities;
 	}
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & ID & amount & baseAmount & firstHPleft & owner & attackerOwned & position & alive;
+		h & ID & amount & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks;
 		if(h.saving)
 			save(h,version);
 		else
 			load(h,version);
 	}
+	bool alive()
+	{
+		return vstd::contains(state,ALIVE);
+	}
 };
 
 struct UpgradeInfo

+ 15 - 3
CLua.cpp

@@ -720,7 +720,7 @@ std::vector<int> CPickable::yourObjects() //returns IDs of objects which are han
 
 void CTownScript::onHeroVisit(int objid, int heroID)
 {
-
+	DEFOS;
 	if(cb->getOwner(objid)!=cb->getOwner(heroID))
 	{
 		return;
@@ -774,7 +774,8 @@ void CHeroScript::onHeroVisit(int objid, int heroID)
 			&vis->army,
 			my->pos,
 			my,
-			vis);
+			vis,
+			0);
 	}
 }
 std::vector<int> CHeroScript::yourObjects() //returns IDs of objects which are handled by script
@@ -834,7 +835,7 @@ void CMonsterS::onHeroVisit(int objid, int heroID)
 	CCreatureSet set;
 	//TODO: zrobic secik w sposob wyrafinowany
 	set.slots[0] = std::pair<ui32,si32>(os->subID,amounts[objid]);
-	cb->startBattle(heroID,set,os->pos);
+	cb->startBattle(heroID,set,os->pos,boost::bind(&CMonsterS::endBattleWith,this,os,_1));
 }
 std::vector<int> CMonsterS::yourObjects() //returns IDs of objects which are handled by script
 {
@@ -843,6 +844,17 @@ std::vector<int> CMonsterS::yourObjects() //returns IDs of objects which are han
 	return ret;
 }
 
+void CMonsterS::endBattleWith(const CGObjectInstance *monster, BattleResult *result )
+{
+	if(result->winner==0)
+	{
+		cb->removeObject(monster->id);
+	}
+	else
+	{
+		//TODO: remove casualties
+	}
+}
 
 void CCreatureGen::newObject(int objid)
 {

+ 2 - 0
CLua.h

@@ -25,6 +25,7 @@ class CSelectableComponent;
 class CGameState;
 struct Mapa;
 struct lua_State;
+struct BattleResult;
 enum ESLan{UNDEF=-1,CPP,ERM,LUA};
 class CObjectScript
 {
@@ -175,6 +176,7 @@ public:
 	void newObject(int objid);
 	void onHeroVisit(int objid, int heroID);
 	std::vector<int> yourObjects(); //returns IDs of objects which are handled by script
+	void endBattleWith(const CGObjectInstance *monster, BattleResult *result);
 };
 
 class CCreatureGen : public CCPPObjectScript

+ 26 - 4
CMT.cpp

@@ -6,6 +6,8 @@
 #include <vector>
 #include <queue>
 #include <cmath>
+#include <boost/interprocess/mapped_region.hpp>
+#include <boost/interprocess/shared_memory_object.hpp>
 #include <boost/thread.hpp>
 #include "SDL_ttf.h"
 #include "SDL_mixer.h"
@@ -37,6 +39,7 @@
 #include "client/Graphics.h"
 #include "client/Client.h"
 #include "lib/Connection.h"
+#include "lib/Interprocess.h"
 #include "lib/VCMI_Lib.h"
 std::string NAME = NAME_VER + std::string(" (client)");
 DLL_EXPORT void initDLL(CLodHandler *b);
@@ -45,6 +48,7 @@ extern SDL_Surface * CSDL_Ext::std32bppSurface;
 std::queue<SDL_Event> events;
 boost::mutex eventsM;
 TTF_Font * TNRB16, *TNR, *GEOR13, *GEORXX, *GEORM, *GEOR16;
+namespace intpr = boost::interprocess;
 #ifndef __GNUC__
 int _tmain(int argc, _TCHAR* argv[])
 #else
@@ -149,16 +153,23 @@ int main(int argc, char** argv)
 		THC std::cout<<"Initialization CPreGame (together): "<<tmh.getDif()<<std::endl;
 		THC std::cout<<"Initialization of VCMI (togeter): "<<total.getDif()<<std::endl;
 		cpg->mush = mush;
+
 		StartInfo *options = new StartInfo(cpg->runLoop());
-///////////////////////////////////////////////////////////////////////////////////////
+		tmh.getDif();
+	////////////////////////SERVER STARTING/////////////////////////////////////////////////
 		char portc[10]; SDL_itoa(port,portc,10);
+		intpr::shared_memory_object smo(intpr::open_or_create,"vcmi_memory",intpr::read_write);
+		smo.truncate(sizeof(ServerReady));
+		intpr::mapped_region mr(smo,intpr::read_write);
+		ServerReady *sr = new(mr.get_address())ServerReady();
 		std::string comm = std::string(SERVER_NAME) + " " + portc + " > server_log.txt";
-		boost::thread servthr(boost::bind(system,comm.c_str())); //runs server executable; 
-												//TODO: will it work on non-windows platforms?
+		boost::thread servthr(boost::bind(system,comm.c_str())); //runs server executable; 	//TODO: will it work on non-windows platforms?
+		THC std::cout<<"Preparing shared memory and starting server: "<<tmh.getDif()<<std::endl;
+	///////////////////////////////////////////////////////////////////////////////////////
 		THC tmh.getDif();pomtime.getDif();//reset timers
 		cgi->pathf = new CPathfinder();
 		THC std::cout<<"\tPathfinder: "<<pomtime.getDif()<<std::endl;
-		if(argc>1)
+		if(argc>2)
 		{
 			std::cout << "Special mode without support for console!" << std::endl;
 		}
@@ -171,6 +182,17 @@ int main(int argc, char** argv)
 		std::ofstream lll("client_log.txt");
 
 		CConnection *c=NULL;
+		//wait until server is ready
+		std::cout<<"Waiting for server... " << std::flush;
+		{
+			intpr::scoped_lock<intpr::interprocess_mutex> slock(sr->mutex);
+			while(!sr->ready)
+			{
+				sr->cond.wait(slock);
+			}
+		}
+		intpr::shared_memory_object::remove("vcmi_memory");
+		std::cout << tmh.getDif()<<std::endl;
 		while(!c)
 		{
 			try

+ 7 - 7
client/CSpellWindow.cpp

@@ -1,11 +1,11 @@
 #include "CSpellWindow.h"
-#include "client/Graphics.h"
-#include "hch/CDefHandler.h"
-#include "hch/CObjectHandler.h"
-#include "hch/CPreGameTextHandler.h"
-#include "CAdvmapInterface.h"
-#include "CGameInfo.h"
-#include "SDL_Extensions.h"
+#include "Graphics.h"
+#include "../hch/CDefHandler.h"
+#include "../hch/CObjectHandler.h"
+#include "../hch/CPreGameTextHandler.h"
+#include "../CAdvmapInterface.h"
+#include "../CGameInfo.h"
+#include "../SDL_Extensions.h"
 #include <boost/bind.hpp>
 #include <sstream>
 

+ 2 - 2
client/CSpellWindow.h

@@ -1,6 +1,6 @@
 #pragma once
-#include "global.h"
-#include "CPlayerInterface.h"
+#include "../global.h"
+#include "../CPlayerInterface.h"
 
 struct SDL_Surface;
 class CDefHandler;

+ 8 - 0
client/Client.cpp

@@ -255,6 +255,14 @@ void CClient::process(int what)
 			}
 			break;
 		}
+	case 109:
+		{
+			ChangeSpells vc;
+			*serv >> vc;
+			std::cout << "Changing spells of hero "<<vc.hid<<std::endl;
+			gs->apply(&vc);
+			break;
+		}
 	case 500:
 		{
 			RemoveObject rh;

+ 8 - 0
client/VCMI_client.vcproj

@@ -365,6 +365,10 @@
 				RelativePath="..\hch\CSndHandler.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\CSpellWindow.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\CThreadHelper.cpp"
 				>
@@ -511,6 +515,10 @@
 				RelativePath="..\hch\CSndHandler.h"
 				>
 			</File>
+			<File
+				RelativePath=".\CSpellWindow.h"
+				>
+			</File>
 			<File
 				RelativePath="..\CThreadHelper.h"
 				>

+ 8 - 2
global.h

@@ -40,6 +40,12 @@ 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 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, 
+UNDEAD, MULTI_HEAD_ATTACK, EXTENDED_RADIOUS_SHOOTER, GHOST, RAISES_MORALE,
+LOWERS_MORALE, DRAGON, STRIKE_AND_RETURN, FEARLESS, REBIRTH}; //some flags are used only for battles
+enum ECombatInfo{ALIVE = REBIRTH+1, SUMMONED, CLONED, HAD_MORALE, WAITING, MOVED, DEFENDING};
 class CGameInfo;
 extern CGameInfo* CGI;
 
@@ -111,8 +117,8 @@ namespace vstd
 	{
 		return std::find(c.begin(),c.end(),i) != c.end();
 	}
-	template <typename V, typename Item>
-	bool contains(const std::map<Item,V> & c, const Item &i)
+	template <typename V, typename Item, typename Item2>
+	bool contains(const std::map<Item,V> & c, const Item2 &i)
 	{
 		return c.find(i)!=c.end();
 	}

+ 13 - 5
hch/CCreatureHandler.cpp

@@ -40,16 +40,16 @@ int CCreature::getQuantityID(int quantity)
 
 bool CCreature::isDoubleWide()
 {
-	return boost::algorithm::find_first(abilityRefs, "DOUBLE_WIDE");
+	return vstd::contains(abilities,DOUBLE_WIDE);
 }
 
 bool CCreature::isFlying()
 {
-	return boost::algorithm::find_first(abilityRefs, "FLYING_ARMY");
+	return vstd::contains(abilities,FLYING);
 }
 bool CCreature::isShooting()
 {
-	return boost::algorithm::find_first(abilityRefs, "SHOOTING_ARMY");
+	return vstd::contains(abilities,SHOOTER);
 }
 si32 CCreature::maxAmount(const std::vector<si32> &res) const //how many creatures can be bought
 {
@@ -306,6 +306,12 @@ void CCreatureHandler::loadCreatures()
 		}
 		ncre.abilityRefs = buf.substr(befi, i-befi);
 		i+=2;
+		if(boost::algorithm::find_first(ncre.abilityRefs, "DOUBLE_WIDE"))
+			ncre.abilities.insert(DOUBLE_WIDE);
+		if(boost::algorithm::find_first(ncre.abilityRefs, "FLYING_ARMY"))
+			ncre.abilities.insert(FLYING);
+		if(boost::algorithm::find_first(ncre.abilityRefs, "SHOOTING_ARMY"))
+			ncre.abilities.insert(SHOOTER);
 		if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string(""))
 		{
 			ncre.idNumber = creatures.size();
@@ -443,8 +449,10 @@ void CCreatureHandler::loadCreatures()
 	}
 	inp2.close();
 
-	creatures[122].abilityRefs += "DOUBLE_WIDE"; //water elemental should be treated as double-wide
-	creatures[123].abilityRefs += "DOUBLE_WIDE"; //ice elemental should be treated as double-wide
+
+
+	creatures[122].abilities.insert(DOUBLE_WIDE);//water elemental should be treated as double-wide
+	creatures[123].abilities.insert(DOUBLE_WIDE);//ice elemental should be treated as double-wide
 }
 
 void CCreatureHandler::loadAnimationInfo()

+ 1 - 0
hch/CCreatureHandler.h

@@ -22,6 +22,7 @@ public:
 	std::string abilityRefs; //references to abilities, in textformat
 	std::string animDefName;
 	ui32 idNumber;
+	std::set<EAbilities> abilities;
 	int faction; //-1 = neutral
 
 	///animation info

+ 10 - 0
hch/CObjectHandler.cpp

@@ -412,6 +412,16 @@ CGHeroInstance::~CGHeroInstance()
 
 CGTownInstance::~CGTownInstance()
 {}
+
+int CGTownInstance::spellsAtLevel(int level, bool checkGuild) const
+{
+	if(checkGuild && mageGuildLevel() < level)
+		return 0;
+	int ret = 6 - level; //how many spells are available at this level
+	if(subID == 2   &&   vstd::contains(builtBuildings,22)) //magic library in Tower
+		ret++; 
+	return ret;
+}
 CGObjectInstance::CGObjectInstance(const CGObjectInstance & right)
 {
 	pos = right.pos;

+ 1 - 0
hch/CObjectHandler.h

@@ -168,6 +168,7 @@ public:
 	bool hasFort() const;
 	bool hasCapitol() const;
 	int dailyIncome() const;
+	int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5)
 
 	CGTownInstance();
 	virtual ~CGTownInstance();

+ 22 - 0
lib/Interprocess.h

@@ -0,0 +1,22 @@
+#include <boost/interprocess/sync/interprocess_mutex.hpp>
+#include <boost/interprocess/sync/interprocess_condition.hpp>
+
+struct ServerReady
+{
+	bool ready;
+	boost::interprocess::interprocess_mutex  mutex;
+	boost::interprocess::interprocess_condition  cond;
+
+	ServerReady()
+	{
+		ready = false;
+	}
+
+	void setToTrueAndNotify()
+	{
+		mutex.lock();
+		ready = true;
+		mutex.unlock();
+		cond.notify_all();
+	}
+};

+ 16 - 0
lib/NetPacks.h

@@ -96,6 +96,18 @@ struct HeroVisitCastle : public CPack<HeroVisitCastle> //108
 		h & flags & tid & hid;
 	}
 }; 
+struct ChangeSpells : public CPack<ChangeSpells> //109
+{
+	ChangeSpells(){type = 109;};
+	ui8 learn; //1 - gives spell, 0 - takes
+	ui32 hid;
+	std::set<ui32> spells;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & learn & hid & spells;
+	}
+}; 
 struct RemoveObject : public CPack<RemoveObject> //500
 {
 	RemoveObject(){type = 500;};
@@ -442,6 +454,10 @@ struct BattleAttack : public CPack<BattleAttack>//3006
 	{
 		return flags & 1;
 	}
+	bool counter()//is it counterattack?
+	{
+		return flags & 2;
+	}
 	bool killed() //if target stack was killed
 	{
 		return bsa.killed();

+ 4 - 0
lib/VCMI_lib.vcproj

@@ -372,6 +372,10 @@
 				RelativePath="..\hch\CTownHandler.h"
 				>
 			</File>
+			<File
+				RelativePath=".\Interprocess.h"
+				>
+			</File>
 			<File
 				RelativePath="..\map.h"
 				>

+ 61 - 9
server/CGameHandler.cpp

@@ -240,11 +240,12 @@ void CGameHandler::changePrimSkill(int ID, int which, int val, bool abs)
 	}
 }
 
-void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2)
+void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb)
 {
 	BattleInfo *curB = new BattleInfo;
-	setupBattle(curB, tile, army1, army2, hero1, hero2); //battle start
+	setupBattle(curB, tile, army1, army2, hero1, hero2); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
 	NEW_ROUND;
+	//TODO: pre-tactic stuff, call scripts etc.
 
 
 	//tactic round
@@ -267,15 +268,15 @@ void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile
 		//stack loop
 		for(unsigned i=0;i<stacks.size() && !battleResult.get();i++)
 		{
-			if(!stacks[i]->alive) continue;//indicates imposiibility of making action for this dead unit
+			if(!stacks[i]->alive()) continue;//indicates imposiibility of making action for this dead unit
 			BattleSetActiveStack sas;
 			sas.stack = stacks[i]->ID;
 			sendAndApply(&sas);
 			boost::unique_lock<boost::mutex> lock(battleMadeAction.mx);
-			while(!battleMadeAction.data)
+			while(!battleMadeAction.data  &&  !battleResult.get()) //active stack hasn't made its action and battle is still going
 				battleMadeAction.cond.wait(lock);
 			battleMadeAction.data = false;
-			checkForBattleEnd(stacks);
+			checkForBattleEnd(stacks); //check if this action ended the battle
 		}
 	}
 
@@ -287,6 +288,8 @@ void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile
 
 	//end battle, remove all info, free memory
 	sendAndApply(battleResult.data);
+	if(cb)
+		cb(battleResult.data);
 	delete battleResult.data;
 	//for(int i=0;i<stacks.size();i++)
 	//	delete stacks[i];
@@ -565,6 +568,14 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 						sr.res[i]-=b->resources[i];
 					sendAndApply(&sr);
 
+					if(bid<5) //it's mage guild
+					{
+						if(t->visitingHero)
+							giveSpells(t,t->visitingHero);
+						if(t->garrisonHero)
+							giveSpells(t,t->garrisonHero);
+					}
+
 					break;
 				}
 			case 506: //recruit creature
@@ -770,7 +781,7 @@ upgend:
 					c >> hid >> aid;
 					CGHeroInstance *hero = gs->getHero(hid);
 					CGTownInstance *town = hero->visitedTown;
-					if(aid==0)
+					if(aid==0) //spellbok
 					{
 						if(!vstd::contains(town->builtBuildings,si32(0)))
 							break;
@@ -786,6 +797,8 @@ upgend:
 						sha.artifWorn = hero->artifWorn;
 						sha.artifWorn[17] = 0;
 						sendAndApply(&sha);
+
+						giveSpells(town,hero);
 					}
 					else if(aid < 7  &&  aid > 3) //war machine
 					{
@@ -883,6 +896,14 @@ upgend:
 							BattleAttack bat;
 							prepareAttack(bat,curStack,stackAtEnd);
 							sendAndApply(&bat);
+							//counterattack
+							if(!vstd::contains(curStack->abilities,NO_ENEMY_RETALIATION)
+								&& !stackAtEnd->counterAttacks	) //TODO: support for multiple retaliatons per turn
+							{
+								prepareAttack(bat,stackAtEnd,curStack);
+								bat.flags |= 2;
+								sendAndApply(&bat);
+							}
 							break;
 						}
 					case 7: //shoot
@@ -942,7 +963,7 @@ void CGameHandler::moveStack(int stack, int dest)
 	else 
 		gs->curB->getAccessibilityMap(accessibility,curStack->ID);
 
-	if((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive) || !accessibility[dest])
+	if((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive()) || !accessibility[dest])
 		return;
 
 	//if(dists[dest] > curStack->creature->speed && !(stackAtEnd && dists[dest] == curStack->creature->speed+1)) //we can attack a stack if we can go to adjacent hex
@@ -1050,6 +1071,18 @@ void CGameHandler::newTurn()
 		}
 		for(std::vector<CGTownInstance *>::iterator j=i->second.towns.begin();j!=i->second.towns.end();j++)//handle towns
 		{
+			if(vstd::contains((**j).builtBuildings,15)) //there is resource silo
+			{
+				if((**j).town->primaryRes == 127) //we'll give wood and ore
+				{
+					r.res[0] += 1;
+					r.res[2] += 1;
+				}
+				else
+				{
+					r.res[(**j).town->primaryRes] += 1;
+				}
+			}
 			if(gs->getDate(1)==7) //first day of week
 			{
 				SetAvailableCreatures sac;
@@ -1318,17 +1351,36 @@ void CGameHandler::checkForBattleEnd( std::vector<CStack*> &stacks )
 	hasStack[0] = hasStack[1] = false;
 	for(int b = 0; b<stacks.size(); ++b)
 	{
-		if(stacks[b]->alive)
+		if(stacks[b]->alive())
 		{
 			hasStack[1-stacks[b]->attackerOwned] = true;
 		}
 	}
 	if(!hasStack[0] || !hasStack[1]) //somebody has won
 	{
-		BattleResult *br = new BattleResult;
+		BattleResult *br = new BattleResult; //will be deleted at the end of startBattle(...)
 		br->result = 0;
 		br->winner = hasStack[1]; //fleeing side loses
 		gs->curB->calculateCasualties(br->casualties);
 		battleResult.set(br);
 	}
+}
+
+void CGameHandler::giveSpells( const CGTownInstance *t, const CGHeroInstance *h )
+{
+	if(!vstd::contains(h->artifWorn,17))
+		return; //hero hasn't spellbok
+	ChangeSpells cs;
+	cs.hid = h->id;
+	cs.learn = true;
+	for(int i=0; i<std::min(t->mageGuildLevel(),h->getSecSkillLevel(7)+4);i++)
+	{
+		for(int j=0; j<t->spellsAtLevel(i+1,true); j++)
+		{
+			if(!vstd::contains(h->spells,t->spells[i][j]))
+				cs.spells.insert(t->spells[i][j]);
+		}
+	}
+	if(cs.spells.size())
+		sendAndApply(&cs);
 }

+ 3 - 1
server/CGameHandler.h

@@ -11,6 +11,7 @@ class CGameState;
 struct StartInfo;
 class CCPPObjectScript;
 class CScriptCallback;
+struct BattleResult;
 template <typename T> struct CPack;
 template <typename T> struct Query;
 class CGHeroInstance;
@@ -52,8 +53,9 @@ class CGameHandler
 	void handleCPPObjS(std::map<int,CCPPObjectScript*> * mapa, CCPPObjectScript * script);
 	void changePrimSkill(int ID, int which, int val, bool abs=false);
 	void changeSecSkill(int ID, ui16 which, int val, bool abs=false);
+	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 	void moveStack(int stack, int dest);
-	void startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2); //use hero=NULL for no hero
+	void startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
 
 	void checkForBattleEnd( std::vector<CStack*> &stacks );
 	void setupBattle( BattleInfo * curB, int3 tile, CCreatureSet &army1, CCreatureSet &army2, CGHeroInstance * hero1, CGHeroInstance * hero2 );

+ 14 - 4
server/CScriptCallback.cpp

@@ -117,6 +117,7 @@ void CScriptCallback::heroVisitCastle(int obj, int heroID)
 	vc.tid = obj;
 	vc.flags |= 1;
 	gh->sendAndApply(&vc);
+	gh->giveSpells(getTown(obj),getHero(heroID));
 }
 
 void CScriptCallback::stopHeroVisitCastle(int obj, int heroID)
@@ -144,16 +145,25 @@ void CScriptCallback::giveHeroArtifact(int artid, int hid, int position) //pos==
 	gh->sendAndApply(&sha);
 }
 
-void CScriptCallback::startBattle(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) //use hero=NULL for no hero
+void CScriptCallback::startBattle(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
 {
-	boost::thread(boost::bind(&CGameHandler::startBattle,gh,*(CCreatureSet *)army1,*(CCreatureSet *)army2,tile,(CGHeroInstance *)hero1,(CGHeroInstance *)hero2));
+	boost::thread(boost::bind(&CGameHandler::startBattle,gh,*(CCreatureSet *)army1,*(CCreatureSet *)army2,tile,(CGHeroInstance *)hero1,(CGHeroInstance *)hero2,cb));
 }
-void CScriptCallback::startBattle(int heroID, CCreatureSet army, int3 tile) //for hero<=>neutral army
+void CScriptCallback::startBattle(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb) //for hero<=>neutral army
 {
 	CGHeroInstance* h = const_cast<CGHeroInstance*>(getHero(heroID));
-	startBattle(&h->army,&army,tile,h,NULL);
+	startBattle(&h->army,&army,tile,h,NULL,cb);
 	//gh->gs->battle(&h->army,army,tile,h,NULL);
 }
+
+void CScriptCallback::changeSpells( int hid, bool give, const std::set<ui32> &spells )
+{
+	ChangeSpells cs;
+	cs.hid = hid;
+	cs.spells = spells;
+	cs.learn = give;
+	gh->sendAndApply(&cs);
+}
 void CLuaCallback::registerFuncs(lua_State * L)
 {
 //	lua_newtable(L);

+ 5 - 2
server/CScriptCallback.h

@@ -1,6 +1,7 @@
 #pragma once
 #include "../global.h"
 #include <vector>
+#include <set>
 #include "../client/FunctionList.h"
 class CVCMIServer;
 class CGameHandler;
@@ -19,6 +20,7 @@ struct InfoWindow;
 struct ShowInInfobox;
 struct SelectionDialog;
 struct YesNoDialog;
+struct BattleResult;
 class CScriptCallback
 {
 	CScriptCallback(void);
@@ -37,6 +39,7 @@ public:
 	const CGTownInstance* getTown(int objid);
 
 	//do sth
+	void changeSpells(int hid, bool give, const std::set<ui32> &spells);
 	void removeObject(int objid);
 	void setBlockVis(int objid, bool bv);
 	void setOwner(int objid, ui8 owner);
@@ -50,8 +53,8 @@ public:
 	void heroVisitCastle(int obj, int heroID);
 	void stopHeroVisitCastle(int obj, int heroID);
 	void giveHeroArtifact(int artid, int hid, int position); //pos==-1 - first free slot in backpack
-	void startBattle(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //use hero=NULL for no hero
-	void startBattle(int heroID, CCreatureSet army, int3 tile); //for hero<=>neutral army
+	void startBattle(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 startBattle(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army
 
 	//friends
 	friend class CGameHandler;

+ 31 - 3
server/CVCMIServer.cpp

@@ -9,21 +9,28 @@
 #endif
 #include "CVCMIServer.h"
 #include <boost/crc.hpp>
-#include <boost/serialization/split_member.hpp>
+#include <boost/interprocess/mapped_region.hpp>
+#include <boost/interprocess/shared_memory_object.hpp>
 #include "../StartInfo.h"
 #include "../map.h"
 #include "../hch/CLodHandler.h" 
+#include "../lib/Interprocess.h"
 #include "../lib/VCMI_Lib.h"
 #include "CGameHandler.h"
 std::string NAME = NAME_VER + std::string(" (server)");
-using boost::asio::ip::tcp;
 using namespace boost;
 using namespace boost::asio;
 using namespace boost::asio::ip;
+namespace intpr = boost::interprocess;
 
 bool end2 = false;
 int port = 3030;
 
+void vaccept(tcp::acceptor *ac, tcp::socket *s, boost::system::error_code *error)
+{
+	ac->accept(*s,*error);
+}
+
 CVCMIServer::CVCMIServer()
 : io(new io_service()), acceptor(new tcp::acceptor(*io, tcp::endpoint(tcp::v4(), port)))
 {
@@ -90,10 +97,29 @@ void CVCMIServer::newGame(CConnection *c)
 }
 void CVCMIServer::start()
 {
+	ServerReady *sr = NULL;
+	intpr::mapped_region *mr;
+	try
+	{
+		intpr::shared_memory_object smo(intpr::open_only,"vcmi_memory",intpr::read_write);
+		mr = new intpr::mapped_region(smo,intpr::read_write);
+		sr = (ServerReady*)mr->get_address();
+	}
+	catch(...)
+	{
+		intpr::shared_memory_object smo(intpr::create_only,"vcmi_memory",intpr::read_write);
+		smo.truncate(sizeof(ServerReady));
+		mr = new intpr::mapped_region(smo,intpr::read_write);
+		sr = new(mr->get_address())ServerReady();
+	}
+
 	boost::system::error_code error;
 	std::cout<<"Listening for connections at port " << acceptor->local_endpoint().port() << std::endl;
 	tcp::socket * s = new tcp::socket(acceptor->io_service());
-	acceptor->accept(*s,error);
+	boost::thread acc(boost::bind(vaccept,acceptor,s,&error));
+	sr->setToTrueAndNotify();
+	delete mr;
+	acc.join();
 	if (error)
 	{
 		std::cout<<"Got connection but there is an error " << std::endl << error;
@@ -147,7 +173,9 @@ int main(int argc, char** argv)
 		io_service io_service;
 		CVCMIServer server;
 		while(!end2)
+		{
 			server.start();
+		}
 		io_service.run();
 	} HANDLE_EXCEPTION
   return 0;