Преглед изворни кода

* artifact manipulation in exchange window works (without switching artifacts between different heroes)
* support for 3 new artifacts:
- Ring of Vitality
- Ring of Life
- Vial of Lifeblood
* restructures creature ability preparing (creature abilities are now loaded from cr_abils.txt file). It needs further work - all changes in abilities should be moved from CCreatureHandler.cpp to cr_abils.txt as it's done in this commit (I hope it's clear how it should be done as there is an example)

mateuszb пре 16 година
родитељ
комит
85eb5c7eb9

+ 1 - 0
client/CBattleInterface.cpp

@@ -2594,6 +2594,7 @@ void CBattleHex::clickRight(boost::logic::tribool down)
 			pom->luck = myst.Luck();
 			pom->morale = myst.Morale();
 			pom->speedBonus = myst.Speed() - myst.creature->speed;
+			pom->healthBonus = myst.MaxHealth() - myst.creature->hitPoints;
 
 			pom->shotsLeft = myst.shots;
 			for(int vb=0; vb<myst.effects.size(); ++vb)

+ 14 - 0
client/CPlayerInterface.cpp

@@ -1520,6 +1520,20 @@ void CPlayerInterface::heroArtifactSetChanged(const CGHeroInstance*hero)
 		adventureInt->heroWindow->setHero(adventureInt->heroWindow->curHero);
 		adventureInt->heroWindow->activate();
 	}
+	CExchangeWindow* cew = dynamic_cast<CExchangeWindow*>(listInt.front());
+	if(cew) //exchange window is open
+	{
+		cew->deactivate();
+		for(int g=0; g<ARRAY_COUNT(cew->heroInst); ++g)
+		{
+			if(cew->heroInst[g] == hero)
+			{
+				cew->artifs[g]->setHero(hero);
+			}
+		}
+		cew->prepareBackground();
+		cew->activate();
+	}
 }
 
 void CPlayerInterface::updateWater()

+ 60 - 39
client/GUIClasses.cpp

@@ -2053,7 +2053,7 @@ CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount, StackState
 
 	//health
 	printAt(CGI->generaltexth->allTexts[388],155,124,GEOR13,zwykly,bitmap);
-	SDL_itoa(c->hitPoints,pom,10);
+	SDL_itoa(c->hitPoints + State->healthBonus,pom,10);
 	printToWR(pom,276,137,GEOR13,zwykly,bitmap);
 
 	//remaining health
@@ -3766,17 +3766,10 @@ void CExchangeWindow::questlog(int whichHero)
 {
 }
 
-CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) //c-tor
+void CExchangeWindow::prepareBackground()
 {
-	char bufor[400];
-
-	heroInst[0] = LOCPLINT->cb->getHeroInfo(hero1, 2);
-	heroInst[1] = LOCPLINT->cb->getHeroInfo(hero2, 2);
-
-	artifs[0] = new CArtifactsOfHero(genRect(600, 800, -334, 150));
-	artifs[0]->setHero(heroInst[0]);
-	artifs[1] = new CArtifactsOfHero(genRect(600, 800, 96, 150));
-	artifs[1]->setHero(heroInst[1]);
+	if(bg)
+		SDL_FreeSurface(bg);
 
 	SDL_Surface * bgtemp; //loaded as 8bpp surface
 	bgtemp = BitmapHandler::loadBitmap("TRADE2.BMP");
@@ -3797,14 +3790,6 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) //c-tor
 	{
 		//graphics
 		blitAt(skilldef->ourImages[g].bitmap, genRect(32, 32, 385, 19 + 36 * g), bg);
-
-		//primary skill's clickable areas
-		primSkillAreas.push_back(new LRClickableAreaWTextComp());
-		primSkillAreas[g]->pos = genRect(32, 32, pos.x+385, pos.y + 19 + 36 * g);
-		primSkillAreas[g]->text = CGI->generaltexth->arraytxt[2+g];
-		primSkillAreas[g]->type = g;
-		primSkillAreas[g]->bonus = -1;
-		primSkillAreas[g]->baseType = 0;
 	}
 
 	CDefHandler * un32 = CDefHandler::giveDef("UN32.DEF");
@@ -3825,6 +3810,62 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) //c-tor
 			blitAt(graphics->abils32->ourImages[heroInst[b]->secSkills[m].first * 3 + heroInst[b]->secSkills[m].second + 2].bitmap, genRect(32, 32, pos.x + 32 + 36 * m + 454 * b, pos.y + 88), bg);
 		}
 
+		//hero's specialty
+		blitAt(un32->ourImages[heroInst[b]->subID].bitmap, 67 + 490*b, 45, bg);
+
+		//experience
+		blitAt(skilldef->ourImages[4].bitmap, 103 + 490*b, 45, bg);
+		printAtMiddle( makeNumberShort(heroInst[b]->exp), 119 + 490*b, 71, GEOR13, zwykly, bg );
+
+		//mana points
+		blitAt(skilldef->ourImages[5].bitmap, 139 + 490*b, 45, bg);
+		printAtMiddle( makeNumberShort(heroInst[b]->mana), 155 + 490*b, 71, GEOR13, zwykly, bg );
+
+		//setting morale
+		blitAt(graphics->morale30->ourImages[heroInst[b]->getCurrentMorale()+3].bitmap, 177 + 490*b, 45, bg);
+
+		//setting luck
+		blitAt(graphics->luck30->ourImages[heroInst[b]->getCurrentLuck()+3].bitmap, 213 + 490*b, 45, bg);
+	}
+
+	//printing portraits
+	blitAt(graphics->portraitLarge[heroInst[0]->portrait], 257, 13, bg);
+	blitAt(graphics->portraitLarge[heroInst[1]->portrait], 485, 13, bg);
+
+	delete un32;
+	delete skilldef;
+}
+
+CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) : bg(NULL)
+{
+	char bufor[400];
+
+	heroInst[0] = LOCPLINT->cb->getHeroInfo(hero1, 2);
+	heroInst[1] = LOCPLINT->cb->getHeroInfo(hero2, 2);
+
+	artifs[0] = new CArtifactsOfHero(genRect(600, 800, -334, 150));
+	artifs[0]->setHero(heroInst[0]);
+	artifs[1] = new CArtifactsOfHero(genRect(600, 800, 96, 150));
+	artifs[1]->setHero(heroInst[1]);
+
+	prepareBackground();
+
+
+	//primary skills
+	for(int g=0; g<4; ++g)
+	{
+		//primary skill's clickable areas
+		primSkillAreas.push_back(new LRClickableAreaWTextComp());
+		primSkillAreas[g]->pos = genRect(32, 32, pos.x+385, pos.y + 19 + 36 * g);
+		primSkillAreas[g]->text = CGI->generaltexth->arraytxt[2+g];
+		primSkillAreas[g]->type = g;
+		primSkillAreas[g]->bonus = -1;
+		primSkillAreas[g]->baseType = 0;
+	}
+
+	//heroes related thing
+	for(int b=0; b<ARRAY_COUNT(heroInst); b++)
+	{
 		//secondary skill's clickable areas
 		for(int g=0; g<heroInst[b]->secSkills.size(); ++g)
 		{
@@ -3841,21 +3882,9 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) //c-tor
 			secSkillAreas[b][g]->hoverText = std::string(bufor);
 		}
 
-		//hero's specialty
-		blitAt(un32->ourImages[heroInst[b]->subID].bitmap, 67 + 490*b, 45, bg);
-
-		//experience
-		blitAt(skilldef->ourImages[4].bitmap, 103 + 490*b, 45, bg);
-		printAtMiddle( makeNumberShort(heroInst[b]->exp), 119 + 490*b, 71, GEOR13, zwykly, bg );
-
-		//mana points
-		blitAt(skilldef->ourImages[5].bitmap, 139 + 490*b, 45, bg);
-		printAtMiddle( makeNumberShort(heroInst[b]->mana), 155 + 490*b, 71, GEOR13, zwykly, bg );
-
 		//setting morale
 		morale[b] = new LRClickableAreaWTextComp();
 		morale[b]->pos = genRect(32, 32, pos.x + 177 + 490*b, pos.y + 45);
-		blitAt(graphics->morale30->ourImages[heroInst[b]->getCurrentMorale()+3].bitmap, 177 + 490*b, 45, bg);
 
 		std::vector<std::pair<int,std::string> > mrl = heroInst[b]->getCurrentMoraleModifiers();
 		int mrlv = heroInst[b]->getCurrentMorale();
@@ -3871,7 +3900,6 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) //c-tor
 		//setting luck
 		luck[b] = new LRClickableAreaWTextComp();
 		luck[b]->pos = genRect(32, 32, pos.x + 213 + 490*b, pos.y + 45);
-		blitAt(graphics->luck30->ourImages[heroInst[b]->getCurrentLuck()+3].bitmap, 213 + 490*b, 45, bg);
 
 		mrl = heroInst[b]->getCurrentLuckModifiers();
 		mrlv = heroInst[b]->getCurrentLuck();
@@ -3885,10 +3913,6 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) //c-tor
 			luck[b]->text += mrl[it].second;
 	}
 
-	//printing portraits
-	blitAt(graphics->portraitLarge[heroInst[0]->portrait], 257, 13, bg);
-	blitAt(graphics->portraitLarge[heroInst[1]->portrait], 485, 13, bg);
-
 	//buttons
 	quit = new AdventureMapButton(CGI->generaltexth->tcommands[8], "", boost::bind(&CExchangeWindow::close, this), pos.x+732, pos.y+567, "IOKAY.DEF", SDLK_RETURN);
 	questlogButton[0] = new AdventureMapButton(CGI->generaltexth->heroscrn[0], std::string(), boost::bind(&CExchangeWindow::questlog,this, 0), pos.x+10, pos.y+44, "hsbtns4.def");
@@ -3899,9 +3923,6 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) //c-tor
 
 	//garrison interface
 	garr = new CGarrisonInt(pos.x + 69, pos.y + 131, 4, Point(418,0), bg, Point(0,0), heroInst[0],heroInst[1], true);
-
-	delete un32;
-	delete skilldef;
 }
 
 CExchangeWindow::~CExchangeWindow() //d-tor

+ 5 - 3
client/GUIClasses.h

@@ -678,15 +678,15 @@ class CExchangeWindow : public CIntObject, public CWindowWithGarrison
 	SDL_Surface *bg; //background
 	AdventureMapButton * quit, * questlogButton[2];
 
-	const CGHeroInstance * heroInst[2];
-	CArtifactsOfHero * artifs[2];
-
 	std::vector<LRClickableAreaWTextComp *> secSkillAreas[2], primSkillAreas;
 
 	LRClickableAreaWTextComp *morale[2], *luck[2];
 
 public:
 
+	const CGHeroInstance * heroInst[2];
+	CArtifactsOfHero * artifs[2];
+
 	void close();
 	void activate();
 	void deactivate();
@@ -694,6 +694,8 @@ public:
 
 	void questlog(int whichHero); //questlog button callback; whichHero: 0 - left, 1 - right
 
+	void prepareBackground(); //prepares or redraws bg
+
 	CExchangeWindow(si32 hero1, si32 hero2); //c-tor
 	~CExchangeWindow(); //d-tor
 };

+ 13 - 0
config/cr_abils.txt

@@ -0,0 +1,13 @@
+//creatures' abilities description
+//first line: use abilities from ZCRTRAIT.TXT [0 - no, 1 - yes]
+//next lines: + [CREATURE_ID] [ABILITY_ID] [value] [subtype] [additional info] [comment to the end of line] /*adding ability*/
+//		or:   - [CREATURE_ID] [ABILITY_ID] /*removing ability*/
+//		or:   0 /*end of ability descriptions*/
+1
++ 115 1 0 0 0 //water elemental should be treated as double-wide
++ 123 1 0 0 0 //ice elemental should be treated as double-wide
++ 140 1 0 0 0 //boar should be treated as double-wide
++ 142 1 0 0 0 //nomads should be treated as double-wide
+- 46 2 //hell hound doesn't fly
+- 47 2 //cerberus doesn't fly
+0

+ 150 - 233
hch/CCreatureHandler.cpp

@@ -90,10 +90,40 @@ si32 CCreature::maxAmount(const std::vector<si32> &res) const //how many creatur
 	return ret;
 }
 
+int readNumber(int & befi, int & i, int andame, std::string & buf) //helper function for void CCreatureHandler::loadCreatures()
+{
+	befi=i;
+	for(i; i<andame; ++i)
+	{
+		if(buf[i]=='\t')
+			break;
+	}
+	std::string tmp = buf.substr(befi, i-befi);
+	int ret = atoi(buf.substr(befi, i-befi).c_str());
+	++i;
+	return ret;
+}
+
 void CCreatureHandler::loadCreatures()
 {
 	notUsedMonsters += 122,124,126,128,145,146,147,148,149,160,161,162,163,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191;
-	tlog5 << "\t\tReading ZCRTRAIT.TXT" << std::endl;
+	tlog5 << "\t\tReading config/cr_abils.txt and ZCRTRAIT.TXT" << std::endl;
+
+	bool useCreAbilsFromZCRTRAIT = true;
+
+	////////////reading cr_abils.txt ///////////////////
+	std::ifstream abils("config" PATHSEPARATOR "cr_abils.txt", std::ios::in | std::ios::binary); //this file is not in lod
+	const int MAX_LINE_SIZE = 1000;
+	char abilLine[MAX_LINE_SIZE+1];
+	for(int i=0; i<5; ++i) //removing 5 comment lines
+	{
+		abils.getline(abilLine, MAX_LINE_SIZE);
+	}
+	//reading first line (determining if we should use creature abilities from ZCRTRAIT.TXT)
+	abils.getline(abilLine, MAX_LINE_SIZE);
+	useCreAbilsFromZCRTRAIT = atoi(abilLine);
+
+	////////////reading ZCRTRAIT.TXT ///////////////////
 	std::string buf = bitmaph->getTextFile("ZCRTRAIT.TXT");
 	int andame = buf.size();
 	int i=0; //buf iterator
@@ -141,194 +171,24 @@ void CCreatureHandler::loadCreatures()
 		ncre.namePl = buf.substr(befi, i-befi);
 		++i;
 
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.cost[0] = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.cost[1] = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.cost[2] = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.cost[3] = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.cost[4] = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.cost[5] = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.cost[6] = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.fightValue = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.AIValue = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.growth = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.hordeGrowth = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.hitPoints = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.speed = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.attack = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.defence = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.damageMin = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.damageMax = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.shots = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.spells = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.ammMin = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
-
-		befi=i;
-		for(i; i<andame; ++i)
-		{
-			if(buf[i]=='\t')
-				break;
-		}
-		ncre.ammMax = atoi(buf.substr(befi, i-befi).c_str());
-		++i;
+		for(int v=0; v<7; ++v)
+		{
+			ncre.cost[v] = readNumber(befi, i, andame, buf);
+		}
+		ncre.fightValue = readNumber(befi, i, andame, buf);
+		ncre.AIValue = readNumber(befi, i, andame, buf);
+		ncre.growth = readNumber(befi, i, andame, buf);
+		ncre.hordeGrowth = readNumber(befi, i, andame, buf);
+		ncre.hitPoints = readNumber(befi, i, andame, buf);
+		ncre.speed = readNumber(befi, i, andame, buf);
+		ncre.attack = readNumber(befi, i, andame, buf);
+		ncre.defence = readNumber(befi, i, andame, buf);
+		ncre.damageMin = readNumber(befi, i, andame, buf);
+		ncre.damageMax = readNumber(befi, i, andame, buf);
+		ncre.shots = readNumber(befi, i, andame, buf);
+		ncre.spells = readNumber(befi, i, andame, buf);
+		ncre.ammMin = readNumber(befi, i, andame, buf);
+		ncre.ammMax = readNumber(befi, i, andame, buf);
 
 		befi=i;
 		for(i; i<andame; ++i)
@@ -347,52 +207,55 @@ void CCreatureHandler::loadCreatures()
 		}
 		ncre.abilityRefs = buf.substr(befi, i-befi);
 		i+=2;
-		if(boost::algorithm::find_first(ncre.abilityRefs, "DOUBLE_WIDE"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::DOUBLE_WIDE, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "FLYING_ARMY"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::FLYING, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "SHOOTING_ARMY"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::SHOOTER, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "SIEGE_WEAPON"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::SIEGE_WEAPON, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "const_two_attacks"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::ADDITIONAL_ATTACK, 1));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::BLOCKS_RETAILATION, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::UNDEAD, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "const_no_melee_penalty"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::NO_MELEE_PENALTY, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "const_jousting"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::JOUSTING, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "const_raises_morale"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::RAISING_MORALE, 1));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "const_lowers_morale"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::ENEMY_MORALE_DECREASING, 1));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "KING_1"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING1, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "KING_2"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING2, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "KING_3"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING3, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "const_no_wall_penalty"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::NO_WALL_PENALTY, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "CATAPULT"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::CATAPULT, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "MULTI_HEADED"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::ATTACKS_ALL_ADAJCENT, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "IMMUNE_TO_MIND_SPELLS"))
-		{
-			std::vector<int> mindSpells = getMindSpells();
-			for(int g=0; g<mindSpells.size(); ++g)
+		if(useCreAbilsFromZCRTRAIT)
+		{ //adding abilities from ZCRTRAIT.TXT
+			if(boost::algorithm::find_first(ncre.abilityRefs, "DOUBLE_WIDE"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::DOUBLE_WIDE, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "FLYING_ARMY"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::FLYING, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "SHOOTING_ARMY"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::SHOOTER, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "SIEGE_WEAPON"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::SIEGE_WEAPON, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "const_two_attacks"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::ADDITIONAL_ATTACK, 1));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::BLOCKS_RETAILATION, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::UNDEAD, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "const_no_melee_penalty"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::NO_MELEE_PENALTY, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "const_jousting"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::JOUSTING, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "const_raises_morale"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::RAISING_MORALE, 1));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "const_lowers_morale"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::ENEMY_MORALE_DECREASING, 1));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "KING_1"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING1, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "KING_2"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING2, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "KING_3"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING3, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "const_no_wall_penalty"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::NO_WALL_PENALTY, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "CATAPULT"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::CATAPULT, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "MULTI_HEADED"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::ATTACKS_ALL_ADAJCENT, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "IMMUNE_TO_MIND_SPELLS"))
 			{
-				creatures[40].abilities += makeCreatureAbility(StackFeature::SPELL_IMMUNITY, 0, mindSpells[g]); //giants are immune to mind spells
+				std::vector<int> mindSpells = getMindSpells();
+				for(int g=0; g<mindSpells.size(); ++g)
+				{
+					creatures[40].abilities += makeCreatureAbility(StackFeature::SPELL_IMMUNITY, 0, mindSpells[g]); //giants are immune to mind spells
+				}
 			}
+			if(boost::algorithm::find_first(ncre.abilityRefs, "IMMUNE_TO_FIRE_SPELLS"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::FIRE_IMMUNITY, 0));
+			if(boost::algorithm::find_first(ncre.abilityRefs, "HAS_EXTENDED_ATTACK"))
+				ncre.abilities.push_back(makeCreatureAbility(StackFeature::TWO_HEX_ATTACK_BREATH, 0));
 		}
-		if(boost::algorithm::find_first(ncre.abilityRefs, "IMMUNE_TO_FIRE_SPELLS"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::FIRE_IMMUNITY, 0));
-		if(boost::algorithm::find_first(ncre.abilityRefs, "HAS_EXTENDED_ATTACK"))
-			ncre.abilities.push_back(makeCreatureAbility(StackFeature::TWO_HEX_ATTACK_BREATH, 0));
 
 		if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string(""))
 		{
@@ -401,6 +264,59 @@ void CCreatureHandler::loadCreatures()
 		}
 	}
 
+	////second part of reading cr_abils.txt////
+	bool contReading = true;
+	while(contReading) //main reading loop
+	{
+		abils.getline(abilLine, MAX_LINE_SIZE);
+		std::istringstream reader(abilLine);
+		char command;
+		reader >> command;
+		switch(command)
+		{
+		case '+': //add new ability
+			{
+				int creatureID;
+				StackFeature nsf;
+				si32 buf;
+				reader >> creatureID;
+				reader >> buf; nsf.type = buf; //it reads ui8 as byte, in file it has different format
+				reader >> buf; nsf.value = buf;
+				reader >> buf; nsf.subtype = buf;
+				reader >> buf; nsf.additionalInfo = buf;
+				nsf.source = StackFeature::CREATURE_ABILITY;
+				nsf.duration = StackFeature::WHOLE_BATTLE;
+				nsf.turnsRemain = 0;
+
+				creatures[creatureID].abilities += nsf;
+				break;
+			}
+		case '-': //remove ability
+			{
+				int creatureID;
+				ui32 type;
+				reader >> creatureID;
+				reader >> type;
+				StackFeature::ECombatFeatures ecf = static_cast<StackFeature::ECombatFeatures>(type);
+
+				creatures[creatureID].abilities -= ecf;
+				break;
+			}
+		case '0': //end reading
+			{
+				contReading = false;
+				break;
+			}
+		default: //invalid command
+			{
+				tlog1 << "Parse error in file config/cr_abils.txt" << std::endl;
+				break;
+			}
+		}
+	}
+
+	abils.close();
+
 	tlog5 << "\t\tReading config/crerefnam.txt" << std::endl;
 	//loading reference names
 	std::ifstream ifs("config/crerefnam.txt");
@@ -528,13 +444,14 @@ void CCreatureHandler::loadCreatures()
 	inp2.close();
 
 	//TODO: create a tidy configuration file to control fixing unit abilities
-	creatures[115].abilities.push_back(makeCreatureAbility(StackFeature::DOUBLE_WIDE, 0));//water elemental should be treated as double-wide
+/*	creatures[115].abilities.push_back(makeCreatureAbility(StackFeature::DOUBLE_WIDE, 0));//water elemental should be treated as double-wide
 	creatures[123].abilities.push_back(makeCreatureAbility(StackFeature::DOUBLE_WIDE, 0));//ice elemental should be treated as double-wide
 	creatures[140].abilities.push_back(makeCreatureAbility(StackFeature::DOUBLE_WIDE, 0));//boar should be treated as double-wide
 	creatures[142].abilities.push_back(makeCreatureAbility(StackFeature::DOUBLE_WIDE, 0));//nomads should be treated as double-wide
 
 	creatures[46].abilities -= StackFeature::FLYING; //hell hound
 	creatures[47].abilities -= StackFeature::FLYING; //cerberus
+*/
 	creatures[52].abilities += makeCreatureAbility(StackFeature::FLYING, 0); //Efreeti
 	creatures[53].abilities += makeCreatureAbility(StackFeature::FLYING, 0); //Efreet Sultan
 

+ 5 - 0
lib/CGameState.cpp

@@ -597,6 +597,11 @@ si32 CStack::Defense(bool withFrenzy /*= true*/) const
 	return ret;
 }
 
+ui16 CStack::MaxHealth() const
+{
+	return creature->hitPoints + valOfFeatures(StackFeature::HP_BONUS);
+}
+
 bool CStack::willMove()
 {
 	return !vstd::contains(state, DEFENDING)

+ 1 - 0
lib/CGameState.h

@@ -183,6 +183,7 @@ public:
 	si8 Luck() const; //get luck of stack with all modificators
 	si32 Attack() const; //get attack of stack with all modificators
 	si32 Defense(bool withFrenzy = true) const; //get defense of stack with all modificators
+	ui16 MaxHealth() const; //get max HP of stack with all modifiers
 	template <typename Handler> void save(Handler &h, const int version)
 	{
 		h & creature->idNumber;

+ 7 - 3
server/CGameHandler.cpp

@@ -437,13 +437,13 @@ askInterfaceForMove:
 }
 void CGameHandler::prepareAttacked(BattleStackAttacked &bsa, CStack *def)
 {	
-	bsa.killedAmount = bsa.damageAmount / def->creature->hitPoints;
-	unsigned damageFirst = bsa.damageAmount % def->creature->hitPoints;
+	bsa.killedAmount = bsa.damageAmount / def->MaxHealth();
+	unsigned damageFirst = bsa.damageAmount % def->MaxHealth();
 
 	if( def->firstHPleft <= damageFirst )
 	{
 		bsa.killedAmount++;
-		bsa.newHP = def->firstHPleft + def->creature->hitPoints - damageFirst;
+		bsa.newHP = def->firstHPleft + def->MaxHealth() - damageFirst;
 	}
 	else
 	{
@@ -849,6 +849,8 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, CCreatureSet &army
 			stacks.back()->luck = hero1->getCurrentLuck(i->first,false);
 			stacks.back()->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, hero1->getPrimSkillLevel(0), StackFeature::BONUS_FROM_HERO));
 			stacks.back()->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, hero1->getPrimSkillLevel(1), StackFeature::BONUS_FROM_HERO));
+			stacks.back()->features.push_back(makeFeature(StackFeature::HP_BONUS, StackFeature::WHOLE_BATTLE, 0, hero1->valOfBonuses(HeroBonus::STACK_HEALTH), StackFeature::BONUS_FROM_HERO));
+			stacks.back()->firstHPleft = stacks.back()->MaxHealth();
 		}
 		else
 		{
@@ -900,6 +902,8 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, CCreatureSet &army
 			stacks.back()->luck = hero2->getCurrentLuck(i->first,false);
 			stacks.back()->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, hero2->getPrimSkillLevel(0), StackFeature::BONUS_FROM_HERO));
 			stacks.back()->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, hero2->getPrimSkillLevel(1), StackFeature::BONUS_FROM_HERO));
+			stacks.back()->features.push_back(makeFeature(StackFeature::HP_BONUS, StackFeature::WHOLE_BATTLE, 0, hero2->valOfBonuses(HeroBonus::STACK_HEALTH), StackFeature::BONUS_FROM_HERO));
+			stacks.back()->firstHPleft = stacks.back()->MaxHealth();
 		}
 		else
 		{