Przeglądaj źródła

* possibly fixed bug with the mage guild when no spells available
* events won't be shown and won't block movement
* casualties among hero army and neutral creatures are saved
* it's possible to build lighthouse
* increased thread-safety (may prevent some crashes)
* minor fixes

Michał W. Urbańczyk 17 lat temu
rodzic
commit
48cb63f144
17 zmienionych plików z 157 dodań i 69 usunięć
  1. 1 1
      CCastleInterface.cpp
  2. 11 0
      CGameState.cpp
  3. 17 13
      CLua.cpp
  4. 0 1
      CLua.h
  5. 1 1
      CMT.cpp
  6. 13 6
      CPathfinder.cpp
  7. 26 33
      CPlayerInterface.cpp
  8. 2 1
      CPlayerInterface.h
  9. 29 0
      ChangeLog
  10. 1 1
      config/hall.txt
  11. 1 1
      global.h
  12. 1 1
      lib/NetPacks.h
  13. 1 1
      map.cpp
  14. 2 0
      mapHandler.cpp
  15. 44 9
      server/CGameHandler.cpp
  16. 6 0
      server/CScriptCallback.cpp
  17. 1 0
      server/CScriptCallback.h

+ 1 - 1
CCastleInterface.cpp

@@ -1440,7 +1440,7 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner)
 		int sp = owner->town->spellsAtLevel(i+1,false);
 		for(int j=0; j<sp; j++)
 		{
-			if(i < owner->town->mageGuildLevel())
+			if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()<j)
 			{
 				spells.push_back(Scroll(&CGI->spellh->spells[owner->town->spells[i][j]]));
 				spells[spells.size()-1].pos = positions[i][j];

+ 11 - 0
CGameState.cpp

@@ -505,6 +505,12 @@ void CGameState::applyNL(IPack * pack)
 	case 1001://set object property
 		{
 			SetObjectProperty *p = static_cast<SetObjectProperty*>(pack);
+			if(p->what == 3) //set creatures amount
+			{
+				tlog5 << "Setting creatures amount in " << p->id << std::endl;
+				static_cast<CCreatureObjInfo*>(map->objects[p->id]->info)->number = p->val;
+				break;
+			}
 			ui8 CGObjectInstance::*point;
 			switch(p->what)
 			{
@@ -894,7 +900,12 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
 	{
 		randomizeObject(map->objects[no]);
 		if(map->objects[no]->ID==26)
+		{
 			map->objects[no]->defInfo->handler=NULL;
+			map->removeBlockVisTiles(map->objects[no]);
+			map->objects[no]->defInfo->blockMap[5] = 255;
+			map->addBlockVisTiles(map->objects[no]);
+		}
 		map->objects[no]->hoverName = VLC->objh->names[map->objects[no]->ID];
 	}
 	//std::cout<<"\tRandomizing objects: "<<th.getDif()<<std::endl;

+ 17 - 13
CLua.cpp

@@ -791,40 +791,40 @@ void CMonsterS::newObject(int objid)
 	switch(VLC->creh->creatures[os->subID].level)
 	{
 	case 1:
-		amounts[objid] = rand()%31+20;
+		cb->setAmount(objid,rand()%31+20);
 		break;
 	case 2:
-		amounts[objid] = rand()%16+15;
+		cb->setAmount(objid,rand()%16+15);
 		break;
 	case 3:
-		amounts[objid] = rand()%16+10;
+		cb->setAmount(objid,rand()%16+10);
 		break;
 	case 4:
-		amounts[objid] = rand()%11+10;
+		cb->setAmount(objid,rand()%11+10);
 		break;
 	case 5:
-		amounts[objid] = rand()%9+8;
+		cb->setAmount(objid,rand()%9+8);
 		break;
 	case 6:
-		amounts[objid] = rand()%8+5;
+		cb->setAmount(objid,rand()%8+5);
 		break;
 	case 7:
-		amounts[objid] = rand()%7+3;
+		cb->setAmount(objid,rand()%7+3);
 		break;
 	case 8:
-		amounts[objid] = rand()%4+2;
+		cb->setAmount(objid,rand()%4+2);
 		break;
 	case 9:
-		amounts[objid] = rand()%3+2;
+		cb->setAmount(objid,rand()%3+2);
 		break;
 	case 10:
-		amounts[objid] = rand()%3+1;
+		cb->setAmount(objid,rand()%3+1);
 		break;
 
 	}
 
 	MetaString ms;
-	int pom = CCreature::getQuantityID(amounts[objid]);
+	int pom = CCreature::getQuantityID(((CCreatureObjInfo*)(os->info))->number);
 	pom = 174 + 3*pom + 1;
 	ms << std::pair<ui8,ui32>(6,pom) << " " << std::pair<ui8,ui32>(7,os->subID);
 	cb->setHoverName(objid,&ms);
@@ -834,7 +834,7 @@ void CMonsterS::onHeroVisit(int objid, int heroID)
 	DEFOS;
 	CCreatureSet set;
 	//TODO: zrobic secik w sposob wyrafinowany
-	set.slots[0] = std::pair<ui32,si32>(os->subID,amounts[objid]);
+	set.slots[0] = std::pair<ui32,si32>(os->subID,((CCreatureObjInfo*)(os->info))->number);
 	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
@@ -852,7 +852,11 @@ void CMonsterS::endBattleWith(const CGObjectInstance *monster, BattleResult *res
 	}
 	else
 	{
-		//TODO: remove casualties
+		int killedAmount=0;
+		for(std::set<std::pair<ui32,si32> >::iterator i=result->casualties[1].begin(); i!=result->casualties[1].end(); i++)
+			if(i->first == monster->subID)
+				killedAmount += i->second;
+		cb->setAmount(monster->id,((CCreatureObjInfo*)(monster->info))->number - killedAmount);
 	}
 }
 

+ 0 - 1
CLua.h

@@ -171,7 +171,6 @@ public:
 class CMonsterS : public CCPPObjectScript
 {
 public:
-	std::map<int, int> amounts; //monster id -> stack quantity
 	CMonsterS(CScriptCallback * CB):CCPPObjectScript(CB){};
 	void newObject(int objid);
 	void onHeroVisit(int objid, int heroID);

+ 1 - 1
CMT.cpp

@@ -221,7 +221,7 @@ int main(int argc, char** argv)
 		while(1) //main SDL events loop
 		{
 			SDL_WaitEvent(&ev);
-			if(ev.type==SDL_QUIT) 
+			if((ev.type==SDL_QUIT)  ||  (ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
 			{
 				cl.close();
 #ifndef __unix__

+ 13 - 6
CPathfinder.cpp

@@ -172,7 +172,10 @@ void CPathfinder::AddNeighbors(vector<Coordinate>* branch)
 					bool pass = true; //checking for allowed visiting direction
 					for(int b=0; b<CGI->mh->ttiles[i][j][node.z].tileInfo->visitableObjects.size(); ++b) //checking destination tile
 					{
-						CGDefInfo * di = CGI->mh->ttiles[i][j][node.z].tileInfo->visitableObjects[b]->defInfo;
+						const TerrainTile * pom = CGI->mh->ttiles[i][j][node.z].tileInfo;
+						if(!vstd::contains(pom->blockingObjects,pom->visitableObjects[b]))
+							break;
+						CGDefInfo * di = pom->visitableObjects[b]->defInfo;
 						if( (i == node.x-1 && j == node.y-1) && !(di->visitDir & (1<<4)) )
 						{
 							pass = false; break;
@@ -208,7 +211,10 @@ void CPathfinder::AddNeighbors(vector<Coordinate>* branch)
 					}
 					for(int b=0; b<CGI->mh->ttiles[node.x][node.y][node.z].tileInfo->visitableObjects.size(); ++b) //checking source tile
 					{
-						CGDefInfo * di = CGI->mh->ttiles[node.x][node.y][node.z].tileInfo->visitableObjects[b]->defInfo;
+						const TerrainTile * pom = CGI->mh->ttiles[node.x][node.y][node.z].tileInfo;
+						if(!vstd::contains(pom->blockingObjects,pom->visitableObjects[b]))
+							break;
+						CGDefInfo * di = pom->visitableObjects[b]->defInfo;
 						if( (i == node.x-1 && j == node.y-1) && !(di->visitDir & (1<<0)) )
 						{
 							pass = false; break;
@@ -269,11 +275,12 @@ void CPathfinder::CalcH(Coordinate* node)
 	 * If there is fog of war on the node.
 	 *  => Impossible to move there.
 	 */
-	if( (CGI->mh->ttiles[node->x][node->y][node->z].tileInfo->blocked && !(node->x==End.x && node->y==End.y && CGI->mh->ttiles[node->x][node->y][node->z].tileInfo->visitable)) ||
-		(CGI->mh->ttiles[node->x][node->y][node->z].tileInfo->tertype==rock) ||
-		((blockLandSea) && (CGI->mh->ttiles[node->x][node->y][node->z].tileInfo->tertype==water)) ||
+	const TerrainTile *pom = CGI->mh->ttiles[node->x][node->y][node->z].tileInfo;
+	if( (pom->blocked && !(node->x==End.x && node->y==End.y && pom->visitable)) ||
+		(pom->tertype==rock) ||
+		((blockLandSea) && (pom->tertype==water)) ||
 		(!CGI->state->players[Hero->tempOwner].fogOfWarMap[node->x][node->y][node->z]) ||
-		((!blockLandSea) && (CGI->mh->ttiles[node->x][node->y][node->z].tileInfo->tertype!=water)))
+		((!blockLandSea) && (pom->tertype!=water)))
 	{
 		//Impossible.
 

+ 26 - 33
CPlayerInterface.cpp

@@ -957,7 +957,7 @@ CPlayerInterface::CPlayerInterface(int Player, int serial)
 	playerID=Player;
 	serialID=serial;
 	human=true;
-	pim = new boost::mutex;
+	pim = new boost::recursive_mutex;
 	showingDialog = new CondSh<bool>(false);
 }
 CPlayerInterface::~CPlayerInterface()
@@ -1100,7 +1100,7 @@ int getDir(int3 src, int3 dst)
 }
 void CPlayerInterface::heroMoved(const HeroMoveDetails & details)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	//initializing objects and performing first step of move
 	const CGHeroInstance * ho = details.ho; //object representing this hero
 	int3 hp = details.src;
@@ -1536,7 +1536,7 @@ void CPlayerInterface::heroMoved(const HeroMoveDetails & details)
 }
 void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	graphics->heroWins.erase(hero->ID);
 	adventureInt->heroList.updateHList();
 }
@@ -1721,19 +1721,6 @@ void CPlayerInterface::handleKeyDown(SDL_Event *sEvent)
 			LOCPLINT->adventureInt->scrollingDown = true;
 			break;
 		}
-	case (SDLK_F4):
-		{
-			if(sEvent->key.keysym.mod & KMOD_LALT) //Alt+F4
-			{
-				exit(0);
-				break;
-			}
-		}
-	//case (SDLK_q):
-	//	{
-	//		exit(0);
-	//		break;
-	//	}
 	case (SDLK_u):
 		{
 			adventureInt->underground.clickLeft(true);
@@ -1853,7 +1840,7 @@ int3 CPlayerInterface::repairScreenPos(int3 pos)
 }
 void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, int val)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	SDL_FreeSurface(graphics->heroWins[hero->subID]);//TODO: moznaby zmieniac jedynie fragment bitmapy zwiazany z dana umiejetnoscia
 	graphics->heroWins[hero->subID] = infoWin(hero); //a nie przerysowywac calosc. Troche roboty, obecnie chyba nie wartej swieczki.
 	if (adventureInt->selection == hero)
@@ -1863,7 +1850,7 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int
 
 void CPlayerInterface::receivedResource(int type, int val)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	if(!curint->subInt)
 		adventureInt->resdatabar.draw();
 }
@@ -1871,7 +1858,7 @@ void CPlayerInterface::receivedResource(int type, int val)
 void CPlayerInterface::showSelDialog(std::string &text, const std::vector<Component*> &components, ui32 askID)
 //void CPlayerInterface::showSelDialog(std::string text, std::vector<CSelectableComponent*> & components, int askID)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	adventureInt->hide(); //dezaktywacja starego interfejsu
 	std::vector<CSelectableComponent*> intComps;
 	for(int i=0;i<components.size();i++)
@@ -1892,14 +1879,14 @@ void CPlayerInterface::heroGotLevel(const CGHeroInstance *hero, int pskill, std:
 		while(showingDialog->data)
 			showingDialog->cond.wait(un);
 	}
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	CLevelWindow *lw = new CLevelWindow(hero,pskill,skills,callback);
 	curint->deactivate();
 	lw->activate();
 }
 void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	//redraw infowindow
 	SDL_FreeSurface(graphics->townWins[town->id]);
 	graphics->townWins[town->id] = infoWin(town);
@@ -1928,12 +1915,12 @@ void CPlayerInterface::heroVisitsTown(const CGHeroInstance* hero, const CGTownIn
 {
 	if(hero->tempOwner != town->tempOwner)
 		return;
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	openTownWindow(town);
 }
 void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	if(obj->ID == 34) //hero
 	{
 		const CGHeroInstance * hh;
@@ -1977,7 +1964,7 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
 }
 void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID, int what) //what: 1 - built, 2 - demolished
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	switch (buildingID)
 	{
 	case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 15:
@@ -2004,7 +1991,7 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID,
 
 void CPlayerInterface::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
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	curint->deactivate();
 	curint = new CBattleInterface(army1,army2,hero1,hero2);
 	curint->activate();
@@ -2033,7 +2020,7 @@ BattleAction CPlayerInterface::activeStack(int stackID) //called when it's turn
 {
 	CBattleInterface *b = dynamic_cast<CBattleInterface*>(curint);
 	{
-		boost::unique_lock<boost::mutex> un(*pim);
+		boost::unique_lock<boost::recursive_mutex> un(*pim);
 		b->stackActivated(stackID);
 	}
 	//wait till BattleInterface sets its command
@@ -2057,7 +2044,7 @@ void CPlayerInterface::battleEnd(BattleResult *br)
 
 void CPlayerInterface::battleResultQuited()
 {
-	//boost::unique_lock<boost::mutex> un(*pim);
+	//boost::unique_lock<boost::recursive_mutex> un(*pim);
 	((CBattleInterface*)curint)->resWindow->deactivate();
 	objsToBlit -= curint;
 	delete curint;
@@ -2067,12 +2054,12 @@ void CPlayerInterface::battleResultQuited()
 
 void CPlayerInterface::battleStackMoved(int ID, int dest, bool startMoving, bool endMoving)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	dynamic_cast<CBattleInterface*>(curint)->stackMoved(ID, dest, startMoving, endMoving);
 }
 void CPlayerInterface::battleAttack(BattleAttack *ba)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	if(ba->shot())
 		dynamic_cast<CBattleInterface*>(curint)->stackIsShooting(ba->stackAttacking,cb->battleGetPos(ba->bsa.stackAttacked));
 	else
@@ -2094,7 +2081,7 @@ void CPlayerInterface::battleStackIsShooting(int ID, int dest)
 
 void CPlayerInterface::showComp(SComponent comp)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	adventureInt->infoBar.showComp(&comp,4000);
 }
 
@@ -2108,6 +2095,7 @@ void CPlayerInterface::showInfoDialog(std::string &text, const std::vector<Compo
 void CPlayerInterface::showInfoDialog(std::string &text, const std::vector<SComponent*> & components)
 {
 	showingDialog->set(true);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	curint->deactivate(); //dezaktywacja starego interfejsu
 
 	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
@@ -2120,6 +2108,7 @@ void CPlayerInterface::showInfoDialog(std::string &text, const std::vector<SComp
 }
 void CPlayerInterface::showYesNoDialog(std::string &text, const std::vector<SComponent*> & components, CFunctionList<void()> onYes, CFunctionList<void()> onNo, bool deactivateCur, bool DelComps)
 {
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	if(deactivateCur)
 		curint->deactivate(); //dezaktywacja starego interfejsu
 	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
@@ -2142,7 +2131,7 @@ void CPlayerInterface::showYesNoDialog(std::string &text, const std::vector<SCom
 
 void CPlayerInterface::showYesNoDialog( std::string &text, const std::vector<Component*> &components, ui32 askID )
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	curint->deactivate(); //dezaktywacja starego interfejsu
 
 	std::vector<SComponent*> intComps;
@@ -2164,20 +2153,24 @@ void CPlayerInterface::showYesNoDialog( std::string &text, const std::vector<Com
 }
 void CPlayerInterface::removeObjToBlit(IShowable* obj)
 {
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	objsToBlit.erase
 		(std::find(objsToBlit.begin(),objsToBlit.end(),obj));
 	//delete obj;
 }
 void CPlayerInterface::tileRevealed(int3 pos)
 {
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	adventureInt->minimap.showTile(pos);
 }
 void CPlayerInterface::tileHidden(int3 pos)
 {
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	adventureInt->minimap.hideTile(pos);
 }
 void CPlayerInterface::openHeroWindow(const CGHeroInstance *hero)
 {
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	adventureInt->heroWindow->setHero(hero);
 	this->objsToBlit.push_back(adventureInt->heroWindow);
 	curint->deactivate();
@@ -2191,7 +2184,7 @@ void CPlayerInterface::openHeroWindow(const CGHeroInstance *hero)
 
 void CPlayerInterface::heroArtifactSetChanged(const CGHeroInstance*hero)
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	if(curint->subInt == adventureInt->heroWindow)
 	{
 		adventureInt->heroWindow->deactivate();
@@ -2236,7 +2229,7 @@ void CPlayerInterface::updateWater()
 
 void CPlayerInterface::availableCreaturesChanged( const CGTownInstance *town )
 {
-	boost::unique_lock<boost::mutex> un(*pim);
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	if(curint == castleInt)
 	{
 		CFortScreen *fs = dynamic_cast<CFortScreen*>(curint->subInt);

+ 2 - 1
CPlayerInterface.h

@@ -30,6 +30,7 @@ template <typename T> struct CondSh;
 namespace boost
 {
 	class mutex;
+	class recursive_mutex;
 };
 
 class IShowable
@@ -316,7 +317,7 @@ class CPlayerInterface : public CGameInterface
 public:
 	//minor interfaces
 	CondSh<bool> *showingDialog;
-	boost::mutex *pim;
+	boost::recursive_mutex *pim;
 	bool makingTurn;
 	SDL_Event * current;
 	CMainInterface *curint;

+ 29 - 0
ChangeLog

@@ -1,3 +1,32 @@
+0.62 -> 0.63 (Sep 28 2008???)
+* very significant optimization of battles 
+* battle summary window
+* fixed crashbug on exiting battle 
+* mostly done morketplace 
+* confirm window is shown before retreat 
+* it's possible to use other port than 3030 by passing it as an additional argument
+* removed some redundant warnings
+* stack queue in battle (shows when 'c' key is pressed) 
+* partially done spellbook
+* fixed crashbug with battles on swamps and rough terrain
+* counterattacks
+* heroes learn spells in towns
+* it's possible to attack enemy hero
+* setting army formation
+* fixed bug with disappearing head of a hero in adventure map
+* some objects are no longer accessible from the top 
+* no tooltips for objects under FoW
+* working resource silo
+* neutral monster army disappears when defeated
+* Alt+F4 quits the game
+* dead stacks won't be displayed in battle queue 
+* possibly fixed bug with the mage guild when no spells available
+* events won't be shown and won't block movement
+* casualties among hero army and neutral creatures are saved
+* it's possible to build lighthouse
+* increased thread-safety (may prevent some crashes)
+* minor fixes
+
 0.61 -> 0.62 (Sep 01 2008)
 General:
 * restructured to the server-client model

+ 1 - 1
config/hall.txt

@@ -2,7 +2,7 @@
 0
 TPTHBKCS.BMP
 10 11 12 13 | 7 8 9 | 5 22 | 16
-14 15 | 0 1 2 3 | 6
+14 15 | 0 1 2 3 | 6 17
 21 | 18 19
 30 37 | 31 38 | 32 39 | 33 40
 34 41 | 35 42 | 36 43

+ 1 - 1
global.h

@@ -16,7 +16,7 @@ typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 #define THC
 #endif
 
-#define NAME_VER ("VCMI 0.63")
+#define NAME_VER ("VCMI 0.62b")
 #define CONSOLE_LOGGING_LEVEL 5
 #define FILE_LOGGING_LEVEL 6
 

+ 1 - 1
lib/NetPacks.h

@@ -286,7 +286,7 @@ struct InfoWindow : public CPack<InfoWindow> //103  - displays simple info windo
 struct SetObjectProperty : public CPack<SetObjectProperty>//1001
 {
 	ui32 id;
-	ui8 what; //1 - owner; 2 - blockvis
+	ui8 what; //1 - owner; 2 - blockvis; 3 - amount (works with creatures stacks)
 	ui32 val;
 	SetObjectProperty(){type = 1001;};
 	SetObjectProperty(ui32 ID, ui8 What, ui32 Val):id(ID),what(What),val(Val){type = 1001;};

+ 1 - 1
map.cpp

@@ -500,7 +500,7 @@ void Mapa::removeBlockVisTiles(CGObjectInstance * obj)
 				if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1))
 				{
 					curt.blockingObjects -= obj;
-					curt.blocked = curt.visitableObjects.size();
+					curt.blocked = curt.blockingObjects.size();
 				}
 			}
 		}

+ 2 - 0
mapHandler.cpp

@@ -458,6 +458,8 @@ void CMapHandler::initObjectRects()
 }
 void processDef (CGDefInfo* def)
 {
+	if(def->id == 26)
+		return;
 	def->handler=CDefHandler::giveDef(def->name);
 	def->width = def->handler->ourImages[0].bitmap->w/32;
 	def->height = def->handler->ourImages[0].bitmap->h/32;

+ 44 - 9
server/CGameHandler.cpp

@@ -241,6 +241,23 @@ void CGameHandler::changePrimSkill(int ID, int which, int val, bool abs)
 	}
 }
 
+CCreatureSet takeCasualties(int color, const CCreatureSet &set, BattleInfo *bat)
+{
+	CCreatureSet ret(set);
+	for(int i=0; i<bat->stacks.size();i++)
+	{
+		CStack *st = bat->stacks[i];
+		if(st->owner==color && vstd::contains(set.slots,st->slot) && st->amount < set.slots.find(st->slot)->second.second)
+		{
+			if(st->alive())
+				ret.slots[st->slot].second = st->amount;
+			else
+				ret.slots.erase(st->slot);
+		}
+	}
+	return ret;
+}
+
 void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb)
 {
 	BattleInfo *curB = new BattleInfo;
@@ -285,17 +302,35 @@ void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile
 	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);
+		states.setFlag(hero2->tempOwner,&PlayerStatus::engagedIntoBattle,false);	
+
+	//casualties among heroes armies
+	SetGarrisons sg;
+	if(hero1)
+		sg.garrs[hero1->id] = takeCasualties(hero1->tempOwner,hero1->army,gs->curB);
+	if(hero2)
+		sg.garrs[hero2->id] = takeCasualties(hero2->tempOwner,hero2->army,gs->curB);
+	sendAndApply(&sg);
 
 	//end battle, remove all info, free memory
 	sendAndApply(battleResult.data);
 	if(cb)
 		cb(battleResult.data);
+
+	//if one hero has lost we will erase him
+	if(battleResult.data->winner!=0 && hero1)
+	{
+		RemoveObject ro(hero1->id);
+		sendAndApply(&ro);
+	}
+	if(battleResult.data->winner!=1 && hero2)
+	{
+		RemoveObject ro(hero2->id);
+		sendAndApply(&ro);
+	}
+
 	delete battleResult.data;
-	//for(int i=0;i<stacks.size();i++)
-	//	delete stacks[i];
-	//delete curB;
-	//curB = NULL;
+
 }
 void prepareAttack(BattleAttack &bat, CStack *att, CStack *def)
 {
@@ -349,7 +384,7 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 					states.setFlag(gs->currentPlayer,&PlayerStatus::makingTurn,false);
 					break;
 				}
-			case 500:
+			case 500: //dismiss hero
 				{
 					si32 id;
 					c >> id;
@@ -521,7 +556,7 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 					sendAndApply(&sg);
 					break;
 				}
-			case 503:
+			case 503: //disband creature
 				{
 					si32 id;
 					ui8 pos;
@@ -533,7 +568,7 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 					sendAndApply(&sg);
 					break;
 				}
-			case 504:
+			case 504: //build structure
 				{
 					si32 tid, bid;
 					c >> tid >> bid;
@@ -1368,7 +1403,7 @@ void CGameHandler::giveSpells( const CGTownInstance *t, const CGHeroInstance *h
 	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++)
+		for(int j=0; j<t->spellsAtLevel(i+1,true) && j<t->spells[i].size(); j++)
 		{
 			if(!vstd::contains(h->spells,t->spells[i][j]))
 				cs.spells.insert(t->spells[i][j]);

+ 6 - 0
server/CScriptCallback.cpp

@@ -33,6 +33,12 @@ void CScriptCallback::removeObject(int objid)
 	gh->sendAndApply(&ro);
 }
 
+void CScriptCallback::setAmount(int objid, ui32 val)
+{
+	SetObjectProperty sop(objid,3,val);
+	gh->sendAndApply(&sop);
+}
+
 void CScriptCallback::setOwner(int objid, ui8 owner)
 {
 	SetObjectProperty sop(objid,1,owner);

+ 1 - 0
server/CScriptCallback.h

@@ -55,6 +55,7 @@ public:
 	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, 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
+	void setAmount(int objid, ui32 val);
 
 	//friends
 	friend class CGameHandler;