浏览代码

* started making morale/luck system
* basic calculation of hero/stack morale
* displaying morale in hero window
* redone checking if attack is possible (should fix some problems with attacking with 2-hex creatures, at least on server side)
* std::list serialization
* version set to 0.7b

Michał W. Urbańczyk 16 年之前
父节点
当前提交
680993459a
共有 14 个文件被更改,包括 324 次插入83 次删除
  1. 7 0
      CGameState.cpp
  2. 24 8
      CHeroWindow.cpp
  3. 2 0
      CHeroWindow.h
  4. 12 2
      CPlayerInterface.cpp
  5. 1 1
      CPlayerInterface.h
  6. 10 8
      CPreGame.cpp
  7. 2 2
      CPreGame.h
  8. 8 0
      client/Client.cpp
  9. 1 1
      global.h
  10. 107 14
      hch/CObjectHandler.cpp
  11. 52 8
      hch/CObjectHandler.h
  12. 22 0
      lib/Connection.h
  13. 52 33
      lib/NetPacks.h
  14. 24 6
      server/CGameHandler.cpp

+ 7 - 0
CGameState.cpp

@@ -583,6 +583,13 @@ void CGameState::applyNL(IPack * pack)
 			}
 			break;
 		}
+	case 115:
+		{
+			GiveBonus *rh = static_cast<GiveBonus*>(pack);
+			CGHeroInstance *h = getHero(rh->hid);
+			h->bonuses.push_back(CGHeroInstance::Bonus(rh->bduration,rh->btype,rh->bval,rh->bid,toString(rh->bdescr)));
+			break;
+		}
 	case 500:
 		{
 			RemoveObject *rh = static_cast<RemoveObject*>(pack);

+ 24 - 8
CHeroWindow.cpp

@@ -101,6 +101,12 @@ CHeroWindow::CHeroWindow(int playerColor):
 	expArea->pos.h = 42;
 	expArea->hoverText = CGI->generaltexth->heroscrn[9];
 
+	morale = new LRClickableAreaWTextComp();
+	morale->pos = genRect(45,53,pos.x+240,pos.y+187);
+
+	luck = new LRClickableAreaWTextComp();
+	luck->pos = genRect(45,53,pos.x+298,pos.y+187);
+
 	spellPointsArea = new LRClickableAreaWText();
 	spellPointsArea->pos.x = pos.x+227;
 	spellPointsArea->pos.y = pos.y  +  236;
@@ -161,6 +167,8 @@ CHeroWindow::~CHeroWindow()
 
 	delete portraitArea;
 	delete expArea;
+	delete luck;
+	delete morale;
 	delete spellPointsArea;
 	for(size_t v=0; v<primSkillAreas.size(); ++v)
 	{
@@ -339,6 +347,18 @@ void CHeroWindow::setHero(const CGHeroInstance *Hero)
 	formations->select(hero->army.formation,true);
 	formations->onChange = boost::bind(&CCallback::setFormation, LOCPLINT->cb, Hero, _1);
 
+	std::vector<std::pair<int,std::string> > mrl = hero->getCurrentMoraleModifiers();
+	int mrlv = hero->getCurrentMorale();
+	int mrlt = (mrlv>0)-(mrlv<0); //signum: -1 - bad morale, 0 - neutral, 1 - good
+	morale->hoverText = CGI->generaltexth->heroscrn[4 - mrlt];
+	morale->baseType = SComponent::morale;
+	morale->bonus = mrlv;
+	morale->text = CGI->generaltexth->arraytxt[88];
+	boost::algorithm::replace_first(morale->text,"%s",CGI->generaltexth->arraytxt[86-mrlt]);
+	for(int it=0; it < mrl.size(); it++)
+		morale->text += mrl[it].second;
+
+
 	pos.x += 65;
 	pos.y += 8;
 
@@ -385,6 +405,8 @@ void CHeroWindow::activate()
 	portraitArea->activate();
 	expArea->activate();
 	spellPointsArea->activate();
+	morale->activate();
+	luck->activate();
 
 	garInt->activate();
 	LOCPLINT->statusbar = ourBar;
@@ -430,6 +452,8 @@ void CHeroWindow::deactivate()
 	portraitArea->deactivate();
 	expArea->deactivate();
 	spellPointsArea->deactivate();
+	morale->activate();
+	luck->activate();
 
 	garInt->deactivate();
 
@@ -830,10 +854,6 @@ void LClickableArea::clickLeft(boost::logic::tribool down)
 	//{
 	//	LOCPLINT->showInfoDialog("TEST TEST AAA", std::vector<SComponent*>());
 	//}
-
-
-
-
 }
 
 void RClickableArea::activate()
@@ -850,10 +870,6 @@ void RClickableArea::clickRight(boost::logic::tribool down)
 	//{
 	//	LOCPLINT->showInfoDialog("TEST TEST AAA", std::vector<SComponent*>());
 	//}
-
-
-
-
 }
 
 void LRClickableAreaWText::clickLeft(boost::logic::tribool down)

+ 2 - 0
CHeroWindow.h

@@ -105,6 +105,8 @@ class CHeroWindow: public IShowActivable, public virtual CIntObject
 	std::vector<LRClickableAreaWTextComp *> primSkillAreas;
 	LRClickableAreaWText * expArea;
 	LRClickableAreaWText * spellPointsArea;
+	LRClickableAreaWTextComp * luck;
+	LRClickableAreaWTextComp * morale;
 	std::vector<LRClickableAreaWTextComp *> secSkillAreas;
 public:
 	AdventureMapButton * quitButton, * dismissButton, * questlogButton, //general

+ 12 - 2
CPlayerInterface.cpp

@@ -168,7 +168,7 @@ void CGarrisonSlot::clickRight (tribool down)
 			pom->attackBonus = h->getPrimSkillLevel(0);
 			pom->defenseBonus = h->getPrimSkillLevel(1);
 			pom->luck = h->getCurrentLuck();
-			pom->morale = h->getCurrentMorale();
+			pom->morale = h->getCurrentMorale(ID);
 		}
 		(new CCreInfoWindow(creature->idNumber,0,count,pom,boost::function<void()>(),boost::function<void()>(),NULL))
 				->activate();
@@ -723,6 +723,9 @@ SDL_Surface * SComponent::getImg()
 	case experience:
 		return graphics->pskillsb->ourImages[4].bitmap;
 		break;
+	case morale:
+		return graphics->morale82->ourImages[val+3].bitmap;
+		break;
 	}
 	return NULL;
 }
@@ -1197,7 +1200,14 @@ void CPlayerInterface::heroMoved(const HeroMoveDetails & details)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 
-	adventureInt->centerOn(details.ho->pos); //centering screen on hero
+	int3 buff = details.ho->pos;
+	buff.x-=11;
+	buff.y-=9;
+	buff = repairScreenPos(buff);
+	LOCPLINT->adventureInt->position = buff; //actualizing screen pos
+
+	if(adventureInt == curint)
+		adventureInt->minimap.draw();
 
 	if(details.style>0)
 		return;

+ 1 - 1
CPlayerInterface.h

@@ -224,7 +224,7 @@ class SComponent : public ClickableR
 public:
 	enum Etype
 	{
-		primskill, secskill, resource, creature, artifact, experience, secskill44, spell
+		primskill, secskill, resource, creature, artifact, experience, secskill44, spell, morale, luck
 	} type;
 	int subtype;
 	int val;

+ 10 - 8
CPreGame.cpp

@@ -839,6 +839,7 @@ int MapSel::countWL()
 void MapSel::printMaps(int from, int to, int at, bool abs)
 {
 	if (!slid->positionsAmnt) return; //no maps to print
+	slid->capacity = (CPG->fromnewgame == 2  ?  16  : 18);
 	if(slid->positionsAmnt < slid->capacity)
 		from = 0;
 	int help=-1;
@@ -928,7 +929,7 @@ void MapSel::printMaps(int from, int to, int at, bool abs)
 		else
 			tlog2 << "Warning: " << curVector()[(i-at)+from].filename << " has wrong version!\n";
 
-		if(CPG->fromnewgame)
+		if(CPG->fromnewgame == 1)
 		{
 			if (!(curVector()[(i-at)+from].name.length()))
 				curVector()[(i-at)+from].name = "Unnamed";
@@ -998,7 +999,7 @@ void MapSel::show()
 	//blit bg
 	blitAt(bg,3,6);
 	CSDL_Ext::printAt("Map Sizes",55,60,GEOR13);
-	CSDL_Ext::printAt(CGI->generaltexth->arraytxt[CPG->fromnewgame ? 229 : 230],110,25,TNRB16); //Select a Scenario to Play : Load a Saved Game
+	CSDL_Ext::printAt(CGI->generaltexth->arraytxt[CPG->fromnewgame==1 ? 229 : 230],110,25,TNRB16); //Select a Scenario to Play : Load a Saved Game
 	//size buttons
 	small.show();
 	medium.show();
@@ -1318,7 +1319,7 @@ void MapSel::printSelectedInfo()
 
 	SDL_BlitSurface(CPG->ourScenSel->scenInf,&genRect(399,337,17,23),screen,&genRect(399,337,413,29));
 	SDL_BlitSurface(CPG->ourScenSel->scenInf,&genRect(50,91,18,447),screen,&genRect(50,91,414,453));
-	if(CPG->fromnewgame)
+	if(CPG->fromnewgame==1)
 	{
 		SDL_BlitSurface(CPG->ourScenSel->bScens.imgs->ourImages[0].bitmap,NULL,screen,&CPG->ourScenSel->bScens.pos);
 		SDL_BlitSurface(CPG->ourScenSel->bOptions.imgs->ourImages[0].bitmap,NULL,screen,&CPG->ourScenSel->bOptions.pos);
@@ -1471,7 +1472,7 @@ std::string MapSel::gdiff(std::string ss)
 
 CMapInfo & MapSel::selectedMap()
 {
-	if(CPG->fromnewgame)
+	if(CPG->fromnewgame==1)
 		return ourMaps[selected];
 	else
 		return ourGames[selected];
@@ -1480,7 +1481,7 @@ CMapInfo & MapSel::selectedMap()
 std::vector<CMapInfo> & MapSel::curVector()
 {
 
-	if (CPG->fromnewgame) 
+	if (CPG->fromnewgame==1) 
 		return ourMaps;
 	else
 		return ourGames;
@@ -1581,7 +1582,7 @@ void CPreGame::showScenSel()
 	SDL_BlitSurface(ourScenSel->bHard.imgs->ourImages[0].bitmap,NULL,screen,&ourScenSel->bHard.pos);
 	SDL_BlitSurface(ourScenSel->bExpert.imgs->ourImages[0].bitmap,NULL,screen,&ourScenSel->bExpert.pos);
 	SDL_BlitSurface(ourScenSel->bImpossible.imgs->ourImages[0].bitmap,NULL,screen,&ourScenSel->bImpossible.pos);
-	SDL_BlitSurface((fromnewgame ? ourScenSel->bBegin : ourScenSel->bLoad).imgs->ourImages[0].bitmap,NULL,screen,&ourScenSel->bBegin.pos);
+	SDL_BlitSurface((fromnewgame==1 ? ourScenSel->bBegin : ourScenSel->bLoad).imgs->ourImages[0].bitmap,NULL,screen,&ourScenSel->bBegin.pos);
 	SDL_BlitSurface(ourScenSel->bBack.imgs->ourImages[0].bitmap,NULL,screen,&ourScenSel->bBack.pos);
 	//blitAt(ourScenSel->bScens.imgs->ourImages[0].bitmap,ourScenSel->bScens.pos.x,ourScenSel->bScens.pos.y);
 	//blitAt(ourScenSel->bRandom.imgs->ourImages[0].bitmap,414,105);
@@ -1592,7 +1593,7 @@ void CPreGame::showScenSel()
 	//add buttons info
 	if(first)
 	{
-		if(fromnewgame)
+		if(fromnewgame==1)
 		{
 			btns.push_back(&ourScenSel->bEasy);
 			btns.push_back(&ourScenSel->bNormal);
@@ -1605,7 +1606,7 @@ void CPreGame::showScenSel()
 		}
 		else
 			ourScenSel->mapsel.show();
-		btns.push_back(&(fromnewgame ? ourScenSel->bBegin : ourScenSel->bLoad));
+		btns.push_back(&(fromnewgame==1 ? ourScenSel->bBegin : ourScenSel->bLoad));
 		btns.push_back(&ourScenSel->bBack);
 
 		ourScenSel->selectedDiff=1;
@@ -2468,6 +2469,7 @@ ScenSel::ScenSel()
 	else 
 		background = BitmapHandler::loadBitmap("ZPIC1001.bmp");
 
+	savenameStrip = BitmapHandler::loadBitmap("GSSTRIP.bmp");
 	scenInf = BitmapHandler::loadBitmap("GSELPOP1.bmp");
 	randMap = BitmapHandler::loadBitmap("RANMAPBK.bmp");
 	options = BitmapHandler::loadBitmap("ADVOPTBK.bmp");

+ 2 - 2
CPreGame.h

@@ -221,7 +221,7 @@ public:
 	bool listShowed;
 	//RanSel ransel;
 	MapSel mapsel;
-	SDL_Surface * background, *scenInf, *scenList, *randMap, *options ;
+	SDL_Surface * background, *savenameStrip, *scenInf, *scenList, *randMap, *options ;
 	Button bScens, bOptions, bRandom, bBegin, bLoad, bBack;
 	IntSelBut	bEasy, bNormal, bHard, bExpert, bImpossible;
 	Button * pressed;
@@ -244,7 +244,7 @@ public:
 	StartInfo ret;
 	bool run;
 	bool first; //hasn't we showed the scensel
-	bool fromnewgame;
+	int fromnewgame; //1 - new game; 0 - load game; 2 - save game
 	std::vector<Slider *> interested;
 	CMusicHandler * mush;
 	std::vector<HighButton *> btns;

+ 8 - 0
client/Client.cpp

@@ -206,6 +206,14 @@ void CClient::process(int what)
 			gs->apply(&sav);
 			break;
 		}
+	case 115:
+		{
+			GiveBonus gb;
+			*serv >> gb;
+			tlog5 << "Hero receives bonus\n";
+			gs->apply(&gb);
+			break;
+		}
 	case 500:
 		{
 			RemoveObject rh;

+ 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.7")
+#define NAME_VER ("VCMI 0.7b")
 #define CONSOLE_LOGGING_LEVEL 5
 #define FILE_LOGGING_LEVEL 6
 

+ 107 - 14
hch/CObjectHandler.cpp

@@ -330,11 +330,6 @@ int CGHeroInstance::getCurrentLuck() const
 	//TODO: write it
 	return 0;
 }
-int CGHeroInstance::getCurrentMorale() const
-{
-	//TODO: write it
-	return 0;
-}
 int CGHeroInstance::getPrimSkillLevel(int id) const
 {
 	return primSkills[id];
@@ -601,6 +596,60 @@ void CGHeroInstance::initObj()
 	blockVisit = true;
 }
 
+int CGHeroInstance::getCurrentMorale( int stack, bool town ) const
+{
+	int ret = 0;
+	std::vector<std::pair<int,std::string> > mods = getCurrentMoraleModifiers(stack,town);
+	for(int i=0; i < mods.size(); i++)
+		ret += mods[i].first;
+	return ret;
+}
+
+std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentMoraleModifiers( int stack/*=-1*/, bool town/*=false*/ ) const
+{
+	//TODO: check if stack is undead/mechanic/elemental => always neutrl morale
+	std::vector<std::pair<int,std::string> > ret;
+
+	//various morale bonuses (from buildings, artifacts, etc)
+	for(std::list<Bonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
+		if(i->type == 2)
+			ret.push_back(std::make_pair(i->val, i->description));
+
+	//leadership
+	if(getSecSkillLevel(6)) 
+		ret.push_back(std::make_pair(getSecSkillLevel(6),VLC->generaltexth->arraytxt[104+getSecSkillLevel(6)]));
+
+
+	//number of alignments and presence of undead
+	if(stack>=0)
+	{
+		std::set<si8> factions;
+		for(std::map<si32,std::pair<ui32,si32> >::const_iterator i=army.slots.begin(); i!=army.slots.end(); i++)
+			factions.insert(VLC->creh->creatures[i->second.first].faction);
+
+		if(factions.size() == 1)
+			ret.push_back(std::pair<int,std::string>(1,VLC->generaltexth->arraytxt[115])); //All troops of one alignment +1
+		else
+		{
+			if(VLC->generaltexth->arraytxt[114].length() <= 100)
+			{
+				char buf[150];
+				std::sprintf(buf,VLC->generaltexth->arraytxt[114].c_str(),factions.size(),2-factions.size());
+				ret.push_back(std::pair<int,std::string>(2-factions.size(),buf)); //Troops of %d alignments %d
+			}
+			else
+			{
+				ret.push_back(std::pair<int,std::string>(2-factions.size(),"")); //Troops of %d alignments %d
+			}
+		}
+
+		if(vstd::contains(factions,4))
+			ret.push_back(std::pair<int,std::string>(-1,VLC->generaltexth->arraytxt[116])); //Undead in group -1
+	}
+
+	return ret;
+}
+
 int CGTownInstance::getSightDistance() const //returns sight distance
 {
 	return 10;
@@ -1151,10 +1200,8 @@ void CGResource::collectRes( int player ) const
 
 void CGResource::fightForRes(ui32 refusedFight, const CGHeroInstance *h) const
 {
-	if(refusedFight)
-		return;
-
-	cb->startBattleI(h->id,army,pos,boost::bind(&CGResource::endBattle,this,_1,h));
+	if(!refusedFight)
+		cb->startBattleI(h->id,army,pos,boost::bind(&CGResource::endBattle,this,_1,h));
 }
 
 void CGResource::endBattle( BattleResult *result, const CGHeroInstance *h ) const
@@ -1307,16 +1354,52 @@ void CGArtifact::initObj()
 }
 
 void CGArtifact::onHeroVisit( const CGHeroInstance * h ) const
+{
+	if(!army.slots.size())
+	{
+		InfoWindow iw;
+		iw.player = h->tempOwner;
+		iw.components.push_back(Component(4,subID,0,0));
+		if(message.length())
+			iw.text <<  message;
+		else
+			iw.text << std::pair<ui8,ui32>(12,subID);
+		cb->showInfoDialog(&iw);
+		pick(h);
+	}
+	else
+	{
+		if(message.size())
+		{
+			YesNoDialog ynd;
+			ynd.player = h->getOwner();
+			ynd.text << message;
+			cb->showYesNoDialog(&ynd,boost::bind(&CGArtifact::fightForArt,this,_1,h));
+		}
+		else
+		{
+			fightForArt(0,h);
+		}
+	}
+}
+
+void CGArtifact::pick(const CGHeroInstance * h) const
 {
 	cb->giveHeroArtifact(subID,h->id,-2);
 	cb->removeObject(id);
-	InfoWindow iw;
-	iw.player = h->tempOwner;
-	iw.components.push_back(Component(4,subID,0,0));
-	iw.text << std::pair<ui8,ui32>(12,subID);
-	cb->showInfoDialog(&iw);
 }
 
+void CGArtifact::fightForArt( ui32 refusedFight, const CGHeroInstance *h ) const
+{
+	if(!refusedFight)
+		cb->startBattleI(h->id,army,pos,boost::bind(&CGArtifact::endBattle,this,_1,h));
+}
+
+void CGArtifact::endBattle( BattleResult *result, const CGHeroInstance *h ) const
+{
+	if(result->winner == 0) //attacker won
+		pick(h);
+}
 void CGPickable::initObj()
 {
 	blockVisit = true;
@@ -1453,4 +1536,14 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
 	}
 
 	cb->showInfoDialog(&iw);
+}
+
+void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
+{
+
+}
+
+void CGDwelling::initObj()
+{
+
 }

+ 52 - 8
hch/CObjectHandler.h

@@ -5,6 +5,7 @@
 #include <vector>
 #include <set>
 #include <map>
+#include <list>
 #include "CCreatureHandler.h"
 #ifndef _MSC_VER
 #include "CHeroHandler.h"
@@ -165,18 +166,41 @@ public:
 	si32 movement; //remaining movement points
 	si32 identifier; //from the map file
 	ui8 sex;
-	struct DLL_EXPORT Patrol
-	{
-		Patrol(){patrolling=false;patrolRadious=-1;};
-		bool patrolling;
-		int patrolRadious;
-	} patrol;
 	ui8 inTownGarrison; // if hero is in town garrison 
 	CGTownInstance * visitedTown; //set if hero is visiting town or in the town garrison
 	std::vector<ui32> artifacts; //hero's artifacts from bag
 	std::map<ui16,ui32> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
 	std::set<ui32> spells; //known spells (spell IDs)
 
+	struct DLL_EXPORT Patrol
+	{
+		Patrol(){patrolling=false;patrolRadious=-1;};
+		ui8 patrolling;
+		si32 patrolRadious;
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & patrolling & patrolRadious;
+		}
+	} patrol;
+
+	struct DLL_EXPORT Bonus
+	{
+		ui8 duration; //0 - Permanent, 1 - OneBattle, 2 - OneDay, 3 - OneWeek
+		ui8 type; //0 - none, 1 - movement; 2 - morale; 3 - luck
+		si32 val;
+		ui32 id;
+		std::string description;
+
+		Bonus(ui8 Dur, ui8 Type, si32 Val, ui32 ID, std::string Desc)
+			:duration(Dur), type(Type), val(Val), id(ID), description(Desc)
+		{}
+		Bonus(){};
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & duration & type & val & id;
+		}
+	};
+	std::list<Bonus> bonuses;
 	//////////////////////////////////////////////////////////////////////////
 
 
@@ -184,7 +208,7 @@ public:
 	{
 		h & static_cast<CArmedInstance&>(*this);
 		h & exp & level & name & biography & portrait & mana & primSkills & secSkills & movement
-			& identifier & sex & inTownGarrison & artifacts & artifWorn & spells;
+			& identifier & sex & inTownGarrison & artifacts & artifWorn & spells & patrol & bonuses;
 
 		ui8 standardType = (VLC->heroh->heroes[subID] == type);
 		h & standardType;
@@ -207,7 +231,8 @@ public:
 	si32 manaLimit() const; //maximum mana value for this hero (basically 10*knowledge)
 	bool canWalkOnSea() const;
 	int getCurrentLuck() const;
-	int getCurrentMorale() const;
+	int getCurrentMorale(int stack=-1, bool town=false) const; //if stack - position of creature, if -1 then morale for hero is calculated; town - if bonuses from town (tavern) should be considered
+	std::vector<std::pair<int,std::string> > getCurrentMoraleModifiers(int stack=-1, bool town=false) const; //args as above
 	int getPrimSkillLevel(int id) const;
 	ui8 getSecSkillLevel(const int & ID) const; //0 - no skill
 	int maxMovePoints(bool onLand) const;
@@ -217,6 +242,7 @@ public:
 	int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
 	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
 
+
 	//////////////////////////////////////////////////////////////////////////
 
 	void initHero(); 
@@ -452,6 +478,9 @@ public:
 	std::string message;
 	ui32 spell; //if it's spell scroll
 	void onHeroVisit(const CGHeroInstance * h) const;
+	void fightForArt(ui32 refusedFight, const CGHeroInstance *h) const;
+	void endBattle(BattleResult *result, const CGHeroInstance *h) const;
+	void pick( const CGHeroInstance * h ) const;
 	void initObj();	
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -583,6 +612,21 @@ public:
 	}
 };
 
+class DLL_EXPORT CGDwelling : public CGObjectInstance //teleports and subterranean gates
+{
+public:
+	static std::map<int,std::map<int, std::vector<int> > > objs; //map[ID][subID] => vector of ids
+	void onHeroVisit(const CGHeroInstance * h) const;
+	void initObj();	
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this);
+	}
+};
+
+
+
 class DLL_EXPORT CObjectHandler
 {
 public:

+ 22 - 0
lib/Connection.h

@@ -4,6 +4,7 @@
 #include <string>
 #include <vector>
 #include <set>
+#include <list>
 
 #include <boost/type_traits/is_fundamental.hpp>
 #include <boost/type_traits/is_enum.hpp>
@@ -263,6 +264,15 @@ public:
 		for(typename std::set<T>::iterator i=d.begin();i!=d.end();i++)
 			*this << *i;
 	}
+	template <typename T>
+	void saveSerializable(const std::list<T> &data)
+	{
+		std::list<T> &d = const_cast<std::list<T> &>(data);
+		boost::uint32_t length = d.size();
+		*this << length;
+		for(typename std::list<T>::iterator i=d.begin();i!=d.end();i++)
+			*this << *i;
+	}
 	void saveSerializable(const std::string &data)
 	{
 		*this << ui32(data.length());
@@ -383,6 +393,18 @@ public:
 			data.insert(ins);
 		}
 	}
+	template <typename T>
+	void loadSerializable(std::list<T> &data)
+	{
+		boost::uint32_t length;
+		*this >> length;
+		T ins;
+		for(ui32 i=0;i<length;i++)
+		{
+			*this >> ins;
+			data.push_back(ins);
+		}
+	}
 	template <typename T1, typename T2>
 	void loadSerializable(std::pair<T1,T2> &data)
 	{

+ 52 - 33
lib/NetPacks.h

@@ -33,6 +33,41 @@ template <typename T> struct Query
 {
 	ui32 id;
 };
+
+struct MetaString : public CPack<MetaString> //2001 helper for object scrips
+{
+	std::vector<std::string> strings;
+	std::vector<std::pair<ui8,ui32> > texts; //pairs<text handler type, text number>; types: 1 - generaltexthandler->all; 2 - objh->xtrainfo; 3 - objh->names; 4 - objh->restypes; 5 - arth->artifacts[id].name; 6 - generaltexth->arraytxt; 7 - creh->creatures[os->subID].namePl; 8 - objh->creGens; 9 - objh->mines[ID].first; 10 - objh->mines[ID].second; 11 - objh->advobtxt
+	std::vector<si32> message;
+	std::vector<std::string> replacements;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & strings & texts & message & replacements;
+	}
+
+	MetaString& operator<<(const std::pair<ui8,ui32> &txt)
+	{
+		message.push_back(-((si32)texts.size())-1);
+		texts.push_back(txt);
+		return *this;
+	}
+	MetaString& operator<<(const std::string &txt)
+	{
+		message.push_back(strings.size()+1);
+		strings.push_back(txt);
+		return *this;
+	}
+	void clear()
+	{
+		strings.clear();
+		texts.clear();
+		message.clear();
+	}
+
+	MetaString(){type = 2001;};
+}; 
+
 struct SetResources : public CPack<SetResources> //104
 {
 	SetResources(){res.resize(RESOURCE_QUANTITY);type = 104;};
@@ -155,6 +190,23 @@ struct SetAvailableHeroes : public CPack<SetAvailableHeroes> //113
 	}
 };
 
+struct GiveBonus :  public CPack<GiveBonus> //115
+{
+	GiveBonus(){type = 115;};
+
+	ui8 bduration;
+	ui8 btype;
+	si32 bval;
+	ui32 bid;
+	ui32 hid;
+	MetaString bdescr;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & bduration & btype & bval & bid & hid & bdescr;
+	}
+};
+
 struct RemoveObject : public CPack<RemoveObject> //500
 {
 	RemoveObject(){type = 500;};
@@ -295,39 +347,6 @@ struct NewTurn : public CPack<NewTurn> //101
 //		h & sac;
 //	}
 //};  
-struct MetaString : public CPack<MetaString> //2001 helper for object scrips
-{
-	std::vector<std::string> strings;
-	std::vector<std::pair<ui8,ui32> > texts; //pairs<text handler type, text number>; types: 1 - generaltexthandler->all; 2 - objh->xtrainfo; 3 - objh->names; 4 - objh->restypes; 5 - arth->artifacts[id].name; 6 - generaltexth->arraytxt; 7 - creh->creatures[os->subID].namePl; 8 - objh->creGens; 9 - objh->mines[ID].first; 10 - objh->mines[ID].second; 11 - objh->advobtxt
-	std::vector<si32> message;
-	std::vector<std::string> replacements;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & strings & texts & message & replacements;
-	}
-
-	MetaString& operator<<(const std::pair<ui8,ui32> &txt)
-	{
-		message.push_back(-((si32)texts.size())-1);
-		texts.push_back(txt);
-		return *this;
-	}
-	MetaString& operator<<(const std::string &txt)
-	{
-		message.push_back(strings.size()+1);
-		strings.push_back(txt);
-		return *this;
-	}
-	void clear()
-	{
-		strings.clear();
-		texts.clear();
-		message.clear();
-	}
-
-	MetaString(){type = 2001;};
-}; 
 struct Component : public CPack<Component> //2002 helper for object scrips informations
 {
 	ui16 id, subtype; //ids: 0 - primskill; 1 - secskill; 2 - resource; 3 - creature; 4 - artifact; 5 - experience (sub==0 exp points; sub==1 levels)

+ 24 - 6
server/CGameHandler.cpp

@@ -1136,20 +1136,37 @@ upgend:
 								tlog3<<"We cannot move this stack to its destination "<<curStack->creature->namePl<<std::endl;
 							}
 
-							if( (BattleInfo::mutualPosition(ba.destinationTile, ba.additionalInfo) < 0 //destination tile is not neighbouring with enemy stack
-									&& !curStack->creature->isDoubleWide())
-								|| (curStack->creature->isDoubleWide()
-									&& (BattleInfo::mutualPosition(ba.destinationTile, ba.additionalInfo) < 0)
-									&& (BattleInfo::mutualPosition(ba.destinationTile + (curStack->attackerOwned ? -1 : 1), ba.additionalInfo) < 0))
-							  ) 
+							if(!stackAtEnd)
 							{
+								tlog3 << "There is no stack on " << ba.additionalInfo << " tile (no attack)!";
+								break;
+							}
+
+							ui16 curpos = curStack->position, 
+								enemypos = stackAtEnd->position;
+
+
+							if( !(
+								(BattleInfo::mutualPosition(curpos, enemypos) >= 0)						//front <=> front
+								|| (curStack->creature->isDoubleWide()									//back <=> front
+									&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos) >= 0)
+								|| (stackAtEnd->creature->isDoubleWide()									//front <=> back
+									&& BattleInfo::mutualPosition(curpos, enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
+								|| (stackAtEnd->creature->isDoubleWide() && curStack->creature->isDoubleWide()//back <=> back
+									&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
+								)
+							)
+							{
+								tlog3 << "Attack cannot be performed!";
 								sendDataToClients(ui16(3008)); //end movement and attack
 								break;
 							}
 
+							//attack
 							BattleAttack bat;
 							prepareAttack(bat,curStack,stackAtEnd);
 							sendAndApply(&bat);
+
 							//counterattack
 							if(!vstd::contains(curStack->abilities,NO_ENEMY_RETALIATION)
 								&& stackAtEnd->alive()
@@ -1161,6 +1178,7 @@ upgend:
 								sendAndApply(&bat);
 							}
 
+							//second attack
 							if(vstd::contains(curStack->abilities,TWICE_ATTACK)
 								&& curStack->alive()
 								&& stackAtEnd->alive()  )