Browse Source

Version set to 0.83c, to be released as development build.
Code reorganizations in bonus system, allowing defining bonusNode-like classes.
Fixed some issues with artifact handling (proper updating of GUI when moving, minor fixes).
Restoring battle AI for neutrals after loading game.

Michał W. Urbańczyk 14 years ago
parent
commit
03fdd22b1a
13 changed files with 237 additions and 168 deletions
  1. 17 5
      ChangeLog
  2. 49 28
      client/CHeroWindow.cpp
  3. 11 0
      client/CHeroWindow.h
  4. 10 4
      client/Client.cpp
  5. 2 0
      client/Client.h
  6. 30 22
      client/GUIClasses.cpp
  7. 5 3
      client/GUIClasses.h
  8. 1 1
      global.h
  9. 0 19
      lib/CObjectHandler.cpp
  10. 0 5
      lib/CObjectHandler.h
  11. 84 65
      lib/HeroBonus.cpp
  12. 27 15
      lib/HeroBonus.h
  13. 1 1
      server/CGameHandler.cpp

+ 17 - 5
ChangeLog

@@ -1,14 +1,26 @@
 0.83 -> 0.84 (Mar 01 2011)
 GENERAL:
 * Bonus system has been rewritten
-* Support for new artifacts (spell scrolls, angelic alliance)
-* Preliminary support for stack experience (no UI yet)
-* Partial support for running VCMI in duel mode (no adventure map, only one battle)
+* Partial support for running VCMI in duel mode (no adventure map, only one battle, ATM only AI-AI battles)
+* New artifacts supported:
+- Angellic Alliance
+- Bird of Perception
+- Emblem of Cognizance
+- Spell Scroll
+- Stoic Watchman
 
 BATTLES:
-* Support for eagle eye, tactics and artillery secondary skills (together with corresponding artifacts)
+* Better animations handling
 * Defensive stance is supported
-* New, improved AI for neutral player
+
+HERO:
+* New secondary skills supported:
+- Artillery
+- Eagle Eye
+- Tactics
+
+AI PLAYER:
+* new AI leading neutral creatures in combat, slightly better then previous
 
 
 0.82 -> 0.83 (Nov 01 2010)

+ 49 - 28
client/CHeroWindow.cpp

@@ -44,6 +44,28 @@
 extern SDL_Surface * screen;
 using namespace boost::assign;
 
+void CHeroWithMaybePickedArtifact::getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+{
+	BonusList heroBonuses, bonusesFromPickedUpArtifact;
+	hero->getAllBonuses(heroBonuses, selector, limit, hero);
+
+	CArtifactsOfHero::SCommonPart *cp = cww->artSets.size() ? cww->artSets.front()->commonInfo : NULL;
+	if(cp && cp->src.art && cp->src.AOH && cp->src.AOH->getHero() == hero)
+	{
+		cp->src.art->getAllBonuses(bonusesFromPickedUpArtifact, selector, limit, hero);
+	}
+
+	BOOST_FOREACH(Bonus *b, bonusesFromPickedUpArtifact)
+		heroBonuses -= b;
+	BOOST_FOREACH(Bonus *b, heroBonuses)
+		out.push_back(b);
+}
+
+CHeroWithMaybePickedArtifact::CHeroWithMaybePickedArtifact(CWindowWithArtifacts *Cww, const CGHeroInstance *Hero)
+	: cww(Cww), hero(Hero)
+{
+}
+
 void CHeroSwitcher::clickLeft(tribool down, bool previousState)
 {
 	if(!down)
@@ -69,6 +91,7 @@ CHeroSwitcher::CHeroSwitcher(int serial)
 }
 
 CHeroWindow::CHeroWindow(const CGHeroInstance *hero)
+	:  heroWArt(this, hero)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	garr = NULL;
@@ -97,9 +120,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero)
 	//right list of heroes
 	for(int g=0; g<8; ++g)
 		heroListMi.push_back(new CHeroSwitcher(g));
-
-
-
+	
 	flags = CDefHandler::giveDefEss("CREST58.DEF");
 
 	//areas
@@ -161,17 +182,16 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
 
 	assert(hero == curHero);
 	//assert(hero->tempOwner == LOCPLINT->playerID || hero->tempOwner == NEUTRAL_PLAYER); //for now we won't show hero windows for non-our heroes
-
-	specArea->text = CGI->generaltexth->hTxts[hero->subID].longBonus;
+	
+	specArea->text = CGI->generaltexth->hTxts[curHero->subID].longBonus;
 
 	tacticsButton->callback.clear();
 	tacticsButton->callback2.clear();
 
 	dismissButton->hoverTexts[0] = boost::str(boost::format(CGI->generaltexth->heroscrn[16]) % curHero->name % curHero->type->heroClass->name);
 	portraitArea->hoverText = boost::str(boost::format(CGI->generaltexth->allTexts[15]) % curHero->name % curHero->type->heroClass->name);
-	portraitArea->text = hero->getBiography();
+	portraitArea->text = curHero->getBiography();
 
-	
 	{
 		AdventureMapButton * split = NULL;
 		{
@@ -189,7 +209,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
 		if(!artSets.size())
 		{
 			CArtifactsOfHero *arts = new CArtifactsOfHero(Point(-65, -8), true);
-			arts->setHero(hero);
+			arts->setHero(curHero);
 			artSets.push_back(arts);
 		}
 	}
@@ -198,14 +218,14 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
 	//primary skills support
 	for(size_t g=0; g<primSkillAreas.size(); ++g)
 	{
-		primSkillAreas[g]->bonusValue = hero->getPrimSkillLevel(g);
+		primSkillAreas[g]->bonusValue = heroWArt.getPrimSkillLevel(g);
 	}
 
 	//secondary skills support
 	for(size_t g=0; g< secSkillAreas.size(); ++g)
 	{
-		int skill = hero->secSkills[g].first,
-			level = hero->getSecSkillLevel(static_cast<CGHeroInstance::SecondarySkill>(hero->secSkills[g].first));
+		int skill = curHero->secSkills[g].first,
+			level = curHero->getSecSkillLevel(static_cast<CGHeroInstance::SecondarySkill>(curHero->secSkills[g].first));
 		secSkillAreas[g]->type = skill;
 		secSkillAreas[g]->bonusValue = level;
 		secSkillAreas[g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
@@ -214,43 +234,43 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
 
 	//printing experience - original format does not support ui64
 	expArea->text = CGI->generaltexth->allTexts[2];
-	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(hero->level));
-	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(CGI->heroh->reqExp(hero->level+1)));
-	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(hero->exp));
+	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(curHero->level));
+	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(CGI->heroh->reqExp(curHero->level+1)));
+	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(curHero->exp));
 
 	//printing spell points
-	spellPointsArea->text = boost::str(boost::format(CGI->generaltexth->allTexts[205]) % hero->name % hero->mana % hero->manaLimit());
+	spellPointsArea->text = boost::str(boost::format(CGI->generaltexth->allTexts[205]) % curHero->name % curHero->mana % heroWArt.manaLimit());
 
-	//if we have exchange window with this hero open
+	//if we have exchange window with this curHero open
 	bool noDismiss=false;
 	BOOST_FOREACH(IShowActivable *isa, GH.listInt)
 	{
 		if(CExchangeWindow * cew = dynamic_cast<CExchangeWindow*>(isa))
 			for(int g=0; g < ARRAY_COUNT(cew->heroInst); ++g)
-				if(cew->heroInst[g] == hero)
+				if(cew->heroInst[g] == curHero)
 					noDismiss = true;
 
 		if (dynamic_cast<CKingdomInterface*>(isa))
 			noDismiss = true;
 	}
-	dismissButton->block(!!hero->visitedTown || noDismiss);
+	dismissButton->block(!!curHero->visitedTown || noDismiss);
 
-	if(hero->getSecSkillLevel(CGHeroInstance::TACTICS) == 0)
+	if(curHero->getSecSkillLevel(CGHeroInstance::TACTICS) == 0)
 		tacticsButton->block(true);
 	else
 	{
 		tacticsButton->block(false);
-		tacticsButton->callback = vstd::assigno(hero->tacticFormationEnabled,true);
-		tacticsButton->callback2 = vstd::assigno(hero->tacticFormationEnabled,false);
+		tacticsButton->callback = vstd::assigno(curHero->tacticFormationEnabled,true);
+		tacticsButton->callback2 = vstd::assigno(curHero->tacticFormationEnabled,false);
 	}
 
 	//setting formations
 	formations->onChange = 0;
-	formations->select(hero->formation,true);
-	formations->onChange = boost::bind(&CCallback::setFormation, LOCPLINT->cb, hero, _1);
+	formations->select(curHero->formation,true);
+	formations->onChange = boost::bind(&CCallback::setFormation, LOCPLINT->cb, curHero, _1);
 
-	morale->set(hero);
-	luck->set(hero);
+	morale->set(&heroWArt);
+	luck->set(&heroWArt);
 
 	if(redrawNeeded)
 		redraw();
@@ -271,6 +291,7 @@ void CHeroWindow::dismissCurrent()
 void CHeroWindow::questlog()
 {
 }
+
 void CHeroWindow::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
@@ -319,7 +340,7 @@ void CHeroWindow::showAll(SDL_Surface * to)
 	for(int m=0; m<4; ++m)
 	{
 	 	std::ostringstream primarySkill;
-	 	primarySkill<<curHero->getPrimSkillLevel(m);
+	 	primarySkill << primSkillAreas[m]->bonusValue;
 	 	printAtMiddleLoc(primarySkill.str(), 53 + 70 * m, 166, FONT_SMALL, zwykly, to);
 	}
 	 
@@ -365,10 +386,10 @@ void CHeroWindow::showAll(SDL_Surface * to)
 	//printing necessery texts
 	printAtLoc(CGI->generaltexth->jktexts[6].substr(1, CGI->generaltexth->jktexts[6].size()-2), 69, 232, FONT_SMALL, tytulowy, to);
 	std::ostringstream expstr;
-	expstr<<curHero->exp;
+	expstr << curHero->exp;
 	printAtLoc(expstr.str(), 68, 252, FONT_SMALL, zwykly, to);
 	printAtLoc(CGI->generaltexth->jktexts[7].substr(1, CGI->generaltexth->jktexts[7].size()-2), 213, 232, FONT_SMALL, tytulowy, to);
 	std::ostringstream manastr;
-	manastr << curHero->mana << '/' << curHero->manaLimit();
+	manastr << curHero->mana << '/' << heroWArt.manaLimit();
 	printAtLoc(manastr.str(), 211, 252, FONT_SMALL, zwykly, to);
 }

+ 11 - 0
client/CHeroWindow.h

@@ -37,7 +37,16 @@ public:
 	CHeroSwitcher(int serial);
 };
 
+//helper class for calculating values of hero bonuses without bonuses from picked up artifact
+class CHeroWithMaybePickedArtifact : public IBonusBearer
+{
+public:
+	const CGHeroInstance *hero;
+	CWindowWithArtifacts *cww;
 
+	CHeroWithMaybePickedArtifact(CWindowWithArtifacts *Cww, const CGHeroInstance *Hero);
+	void getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const OVERRIDE;
+};
 
 class CHeroWindow: public CWindowWithGarrison, public CWindowWithArtifacts
 {
@@ -61,6 +70,8 @@ class CHeroWindow: public CWindowWithGarrison, public CWindowWithArtifacts
 	LRClickableAreaWText * specArea;//speciality
 	MoraleLuckBox * morale, * luck;
 	std::vector<LRClickableAreaWTextComp *> secSkillAreas;
+	CHeroWithMaybePickedArtifact heroWArt;
+
 public:
 	const CGHeroInstance * curHero;
 	AdventureMapButton * quitButton, * dismissButton, * questlogButton; //general

+ 10 - 4
client/Client.cpp

@@ -430,10 +430,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	}
 	else
 	{
-		battleints[255] = CAIHandler::getNewBattleAI(conf.cc.defaultBattleAI);
-		battleints[255]->init(new CBattleCallback(gs, 255, this));
-// 		playerint[255]->init(new CCallback(gs,255,this));
-// 		battleints[255] = playerint[255];
+		loadNeutralBattleAI();
 	}
 
 	serv->addStdVecItems(const_cast<CGameInfo*>(CGI)->state);
@@ -493,6 +490,9 @@ void CClient::serialize( Handler &h, const int version )
 			nInt->init(callback);
 			nInt->serialize(h, version);
 		}
+
+		if(!vstd::contains(battleints, NEUTRAL_PLAYER))
+			loadNeutralBattleAI();
 	}
 }
 
@@ -588,6 +588,12 @@ void CClient::battleStarted(const BattleInfo * info)
 		battleints[254]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
 }
 
+void CClient::loadNeutralBattleAI()
+{
+	battleints[255] = CAIHandler::getNewBattleAI(conf.cc.defaultBattleAI);
+	battleints[255]->init(new CBattleCallback(gs, 255, this));
+}
+
 template void CClient::serialize( CISer<CLoadFile> &h, const int version );
 template void CClient::serialize( COSer<CSaveFile> &h, const int version );
 

+ 2 - 0
client/Client.h

@@ -82,6 +82,8 @@ public:
 
 	void init();
 	void newGame(CConnection *con, StartInfo *si); //con - connection to server
+
+	void loadNeutralBattleAI();
 	void endGame(bool closeConnection = true);
 	void stopConnection();
 	void save(const std::string & fname);

+ 30 - 22
client/GUIClasses.cpp

@@ -74,6 +74,7 @@ CFocusable * CFocusable::inputWithFocus;
 #undef min
 #undef max
 
+
 void CGarrisonSlot::hover (bool on)
 {
 	////Hoverable::hover(on);
@@ -4663,7 +4664,6 @@ void CArtPlace::select ()
 	if(slotID >= Arts::BACKPACK_START)
 		ourOwner->scrollBackpack(0); //will update slots
 
-	// Update the hero bonuses.
 	ourOwner->updateParentWindow();
 	ourOwner->safeRedraw();
 }
@@ -4695,7 +4695,7 @@ void CArtPlace::deactivate()
 
 void CArtPlace::showAll(SDL_Surface *to)
 {
-	if (ourArt && !picked)
+	if (ourArt && !picked && ourArt == ourOwner->curHero->getArt(slotID)) //last condition is needed for disassembling -> artifact may be gone, but we don't know yet TODO: real, nice solution
 	{
 		int graphic = locked ? 145 : ourArt->artType->id;
 		blitAt(graphics->artDefs->ourImages[graphic].bitmap, pos.x, pos.y, to);
@@ -5161,16 +5161,17 @@ void CArtifactsOfHero::updateParentWindow()
 		if(!updateState)
 		{
 			cew->deactivate();
-			for(int g=0; g<ARRAY_COUNT(cew->heroInst); ++g)
-			{
-				if(cew->heroInst[g] == curHero)
-				{
-					cew->artifs[g]->setHero(curHero);
-				}
-			}
+// 			for(int g=0; g<ARRAY_COUNT(cew->heroInst); ++g)
+// 			{
+// 				if(cew->heroInst[g] == curHero)
+// 				{
+// 					cew->artifs[g]->setHero(curHero);
+// 				}
+// 			}
 
 
 			cew->prepareBackground();
+			cew->redraw();
 			cew->activate();
 		}
 	}
@@ -5199,13 +5200,14 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact
 	if(dst.hero == curHero && dst.slot >= Arts::BACKPACK_START)
 		setSlotData(getArtPlace(dst.slot), dst.slot);
 	if(src.hero == curHero  ||  dst.hero == curHero) //we need to update all slots, artifact might be combined and affect more slots
-		updateWornSlots();
+		updateWornSlots(false);
 
 	if(commonInfo->src == src) //artifact was taken from us
 	{
 		assert(commonInfo->dst == dst  ||  dst.slot == dst.hero->artifactsInBackpack.size() + Arts::BACKPACK_START);
 		commonInfo->reset();
 		unmarkSlots();
+		updateParentWindow();
 	}
 	else if(commonInfo->dst == src) //the dest artifact was moved -> we are picking it
 	{
@@ -5289,12 +5291,18 @@ void CArtifactsOfHero::artifactDisassembled(const ArtifactLocation &al)
 		updateWornSlots();
 }
 
-void CArtifactsOfHero::updateWornSlots()
+void CArtifactsOfHero::updateWornSlots(bool redrawParent /*= true*/)
 {
 	for(int i = 0; i < Arts::BACKPACK_START; i++)
 		setSlotData(getArtPlace(i), i);
 
-	updateParentWindow();
+	if(redrawParent)
+		updateParentWindow();
+}
+
+const CGHeroInstance * CArtifactsOfHero::getHero() const
+{
+	return curHero;
 }
 
 void CExchangeWindow::close()
@@ -5307,9 +5315,9 @@ void CExchangeWindow::activate()
 	quit->activate();
 	garr->activate();
 
-	artifs[0]->setHero(heroInst[0]);
+	//artifs[0]->setHero(heroInst[0]);
 	artifs[0]->activate();
-	artifs[1]->setHero(heroInst[1]);
+	//artifs[1]->setHero(heroInst[1]);
 	artifs[1]->activate();
 
 	for(int g=0; g<ARRAY_COUNT(secSkillAreas); g++)
@@ -5446,11 +5454,12 @@ void CExchangeWindow::prepareBackground()
 	//heroes related thing
 	for(int b=0; b<ARRAY_COUNT(heroInst); b++)
 	{
+		CHeroWithMaybePickedArtifact heroWArt = CHeroWithMaybePickedArtifact(this, heroInst[b]);
 		//printing primary skills' amounts
 		for(int m=0; m<4; ++m)
 		{
 			std::ostringstream primarySkill;
-			primarySkill<<heroInst[b]->getPrimSkillLevel(m);
+			primarySkill << heroWArt.getPrimSkillLevel(m);
 			CSDL_Ext::printAtMiddle(primarySkill.str(), 352 + 93 * b, 35 + 36 * m, FONT_SMALL, zwykly, bg);
 		}
 
@@ -5472,10 +5481,10 @@ void CExchangeWindow::prepareBackground()
 		printAtMiddle( makeNumberShort(heroInst[b]->mana), 155 + 490*b, 71, FONT_SMALL, zwykly, bg );
 
 		//setting morale
-		blitAt(graphics->morale30->ourImages[heroInst[b]->MoraleVal()+3].bitmap, 177 + 490*b, 45, bg);
+		blitAt(graphics->morale30->ourImages[heroWArt.MoraleVal()+3].bitmap, 177 + 490*b, 45, bg);
 
 		//setting luck
-		blitAt(graphics->luck30->ourImages[heroInst[b]->LuckVal()+3].bitmap, 213 + 490*b, 45, bg);
+		blitAt(graphics->luck30->ourImages[heroWArt.LuckVal()+3].bitmap, 213 + 490*b, 45, bg);
 	}
 
 	//printing portraits
@@ -6453,7 +6462,7 @@ CThievesGuildWindow::~CThievesGuildWindow()
 // 	delete resdatabar;
 }
 
-void MoraleLuckBox::set(const CBonusSystemNode *node)
+void MoraleLuckBox::set(const IBonusBearer *node)
 {
 	const int textId[] = {62, 88}; //eg %s \n\n\n {Current Luck Modifiers:}
 	const int noneTxtId = 108; //Russian version uses same text for neutral morale\luck
@@ -6461,7 +6470,7 @@ void MoraleLuckBox::set(const CBonusSystemNode *node)
 	const int componentType[] = {SComponent::luck, SComponent::morale};
 	const int hoverTextBase[] = {7, 4};
 	const Bonus::BonusType bonusType[] = {Bonus::LUCK, Bonus::MORALE};
-	int (CBonusSystemNode::*getValue[])() const = {&CBonusSystemNode::LuckVal, &CBonusSystemNode::MoraleVal};
+	int (IBonusBearer::*getValue[])() const = {&IBonusBearer::LuckVal, &IBonusBearer::MoraleVal};
 
 	int mrlt = -9;
 	TModDescr mrl;
@@ -6484,8 +6493,7 @@ void MoraleLuckBox::set(const CBonusSystemNode *node)
 		text += CGI->generaltexth->arraytxt[noneTxtId];
 	else
 	{
-		if (node->nodeType == CBonusSystemNode::STACK_INSTANCE &&
-			(node->hasBonusOfType (Bonus::UNDEAD) || node->hasBonusOfType(Bonus::BLOCK_MORALE) || node->hasBonusOfType(Bonus::NON_LIVING))) //it's a creature window
+		if (node->hasBonusOfType (Bonus::UNDEAD) || node->hasBonusOfType(Bonus::BLOCK_MORALE) || node->hasBonusOfType(Bonus::NON_LIVING)) //it's a creature window
 		{
 			text += CGI->generaltexth->arraytxt[113]; //unaffected by morale
 		}
@@ -6899,4 +6907,4 @@ bool CArtifactsOfHero::SCommonPart::Artpos::valid()
 {
 	assert(AOH && art);
 	return art == AOH->curHero->getArt(slotID);
-}
+}

+ 5 - 3
client/GUIClasses.h

@@ -70,6 +70,7 @@ class CStackInstance;
 class IMarket;
 class CTextBox;
 class CArtifactInstance;
+class IBonusBearer;
 
 extern SDL_Color tytulowy, tlo, zwykly ;
 
@@ -878,7 +879,7 @@ class MoraleLuckBox : public LRClickableAreaWTextComp
 public:
 	bool morale; //true if morale, false if luck
 	
-	void set(const CBonusSystemNode *node);
+	void set(const IBonusBearer *node);
 	void showAll(SDL_Surface * to);
 
 	MoraleLuckBox(bool Morale, const Rect &r);
@@ -972,7 +973,7 @@ public:
 /// Contains artifacts of hero. Distincts which artifacts are worn or backpacked
 class CArtifactsOfHero : public CIntObject
 {
-	const CGHeroInstance * curHero; //local copy of hero on which we operate
+	const CGHeroInstance * curHero;
 
 	std::vector<CArtPlace *> artWorn; // 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::vector<CArtPlace *> backpack; //hero's visible backpack (only 5 elements!)
@@ -1012,6 +1013,7 @@ public:
 	CArtPlace *getArtPlace(int slot);
 
 	void setHero(const CGHeroInstance * hero);
+	const CGHeroInstance *getHero() const;
 	void dispose(); //free resources not needed after closing windows and reset state
 	void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right
 
@@ -1019,7 +1021,7 @@ public:
 	void markPossibleSlots(const CArtifactInstance* art);
 	void unmarkSlots(bool withRedraw = true);
 	void setSlotData (CArtPlace* artPlace, int slotID);
-	void updateWornSlots ();
+	void updateWornSlots (bool redrawParent = true);
 	void eraseSlotData (CArtPlace* artPlace, int slotID);
 
 	CArtifactsOfHero(const Point& position, bool createCommonPart = false); //c-tor

+ 1 - 1
global.h

@@ -33,7 +33,7 @@ typedef si32 TBonusSubtype;
 #define THC
 #endif
 
-#define NAME_VER ("VCMI 0.83b")
+#define NAME_VER ("VCMI 0.83c")
 extern std::string NAME; //full name
 extern std::string NAME_AFFIX; //client / server
 #define CONSOLE_LOGGING_LEVEL 5

+ 0 - 19
lib/CObjectHandler.cpp

@@ -622,30 +622,11 @@ int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position o
 	}
 }
 
-si32 CGHeroInstance::manaLimit() const
-{
-	return si32(getPrimSkillLevel(3) * (100.0f + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 24)) / 10.0f);
-}
-//void CGHeroInstance::setPosition(int3 Pos, bool h3m) //as above, but sets position
-//{
-//	if (h3m)
-//		pos = Pos;
-//	else
-//		pos = convertPosition(Pos,true);
-//}
-
 bool CGHeroInstance::canWalkOnSea() const
 {
 	return hasBonusOfType(Bonus::FLYING_MOVEMENT) || hasBonusOfType(Bonus::WATER_WALKING);
 }
 
-int CGHeroInstance::getPrimSkillLevel(int id) const
-{
-	int ret = valOfBonuses(Bonus::PRIMARY_SKILL, id);
-	amax(ret, id/2); //minimal value is 0 for attack and defense and 1 for spell power and knowledge
-	return ret;
-}
-
 ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const
 {
 	for(size_t i=0; i < secSkills.size(); ++i)

+ 0 - 5
lib/CObjectHandler.h

@@ -386,16 +386,11 @@ public:
 	unsigned int getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
 	unsigned int getLowestCreatureSpeed() const;
 	int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
-	si32 manaLimit() const; //maximum mana value for this hero (basically 10*knowledge)
 	si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
 	bool canWalkOnSea() const;
 	int getCurrentLuck(int stack=-1, bool town=false) const;
 	int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
 
-	TModDescr getCurrentLuckModifiers(int stack=-1, bool town=false) const; //args as above
-	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
-	TModDescr getCurrentMoraleModifiers(int stack=-1, bool town=false) const; //args as above
-	int getPrimSkillLevel(int id) const; //0-attack, 1-defence, 2-spell power, 3-knowledge
 	ui8 getSecSkillLevel(SecondarySkill skill) const; //0 - no skill
 	void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
 

+ 84 - 65
lib/HeroBonus.cpp

@@ -140,12 +140,12 @@ void DLL_EXPORT BonusList::eliminateDuplicates()
 	unique();
 }
 
-int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const
+int IBonusBearer::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const
 {
 	return valOfBonuses(Selector::type(type) && selector);
 }
 
-int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) const
+int IBonusBearer::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) const
 {
 	CSelector s = Selector::type(type);
 	if(subtype != -1)
@@ -154,18 +154,18 @@ int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/)
 	return valOfBonuses(s);
 }
 
-int CBonusSystemNode::valOfBonuses(const CSelector &selector) const
+int IBonusBearer::valOfBonuses(const CSelector &selector) const
 {
 	BonusList hlp;
 	getBonuses(hlp, selector);
 	return hlp.totalValue();
 }
-bool CBonusSystemNode::hasBonus(const CSelector &selector) const
+bool IBonusBearer::hasBonus(const CSelector &selector) const
 {
 	return getBonuses(selector).size() > 0;
 }
 
-bool CBonusSystemNode::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*/) const
+bool IBonusBearer::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*/) const
 {
 	CSelector s = Selector::type(type);
 	if(subtype != -1)
@@ -174,59 +174,26 @@ bool CBonusSystemNode::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*
 	return hasBonus(s);
 }
 
-Bonus * CBonusSystemNode::getBonus(const CSelector &selector)
-{
-	Bonus *ret = bonuses.getFirst(selector);
-	if(ret)
-		return ret;
-
-	FOREACH_PARENT(pname)
-	{
-		ret = pname->getBonus(selector);
-		if (ret)
-			return ret;
-	}
-
-	return NULL;
-}
-
-const Bonus * CBonusSystemNode::getBonus( const CSelector &selector ) const
-{
-	return (const_cast<CBonusSystemNode*>(this))->getBonus(selector);
-}
-
-void CBonusSystemNode::getModifiersWDescr(TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */) const
+void IBonusBearer::getModifiersWDescr(TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */) const
 {
 	getModifiersWDescr(out, subtype != -1 ? Selector::typeSybtype(type, subtype) : Selector::type(type));
 }
 
-void CBonusSystemNode::getModifiersWDescr(TModDescr &out, const CSelector &selector) const
+void IBonusBearer::getModifiersWDescr(TModDescr &out, const CSelector &selector) const
 {
 	getBonuses(selector).getModifiersWDescr(out);
 }
-int CBonusSystemNode::getBonusesCount(int from, int id) const
+int IBonusBearer::getBonusesCount(int from, int id) const
 {
 	return getBonusesCount(Selector::source(from, id));
 }
 
-int CBonusSystemNode::getBonusesCount(const CSelector &selector) const
+int IBonusBearer::getBonusesCount(const CSelector &selector) const
 {
 	return getBonuses(selector).size();
 }
 
-void CBonusSystemNode::getParents(TCNodes &out) const /*retreives list of parent nodes (nodes to inherit bonuses from) */
-{
-	BOOST_FOREACH(const CBonusSystemNode *parent, parents)
-		out.insert(parent);
-}
-
-void CBonusSystemNode::getParents(TNodes &out)
-{
-	BOOST_FOREACH(const CBonusSystemNode *parent, parents)
-		out.insert(const_cast<CBonusSystemNode*>(parent));
-}
-
-void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
+void IBonusBearer::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
 {
 	getBonuses(out, selector, 0, root);
 // 	FOREACH_CONST_PARENT(p)
@@ -238,14 +205,14 @@ void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, con
 // 		out.limit(*this);
 }
 
-BonusList CBonusSystemNode::getBonuses(const CSelector &selector) const
+BonusList IBonusBearer::getBonuses(const CSelector &selector) const
 {
 	BonusList ret;
 	getBonuses(ret, selector);
 	return ret;
 }
 
-void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+void IBonusBearer::getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
 {
 	getAllBonuses(out, selector, limit, root);
 	out.eliminateDuplicates();
@@ -255,30 +222,19 @@ void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, con
 // 	out.limit(*this); //apply bonuses' limiters
 }
 
-BonusList CBonusSystemNode::getBonuses(const CSelector &selector, const CSelector &limit) const
+BonusList IBonusBearer::getBonuses(const CSelector &selector, const CSelector &limit) const
 {
 	BonusList ret;
 	getBonuses(ret, selector, limit);
 	return ret;
 }
 
-void CBonusSystemNode::getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
-{
-	FOREACH_CONST_PARENT(p)
-		p->getBonuses(out, selector, limit, root ? root : this);
-
-	bonuses.getBonuses(out, selector, limit);
-
-	if(!root)
-		out.limit(*this);
-}
-
-bool CBonusSystemNode::hasBonusFrom(ui8 source, ui32 sourceID) const
+bool IBonusBearer::hasBonusFrom(ui8 source, ui32 sourceID) const
 {
 	return hasBonus(Selector::source(source,sourceID));
 }
 
-int CBonusSystemNode::MoraleVal() const
+int IBonusBearer::MoraleVal() const
 {
 	if(hasBonusOfType(Bonus::NON_LIVING) || hasBonusOfType(Bonus::UNDEAD) ||
 		hasBonusOfType(Bonus::NO_MORALE) || hasBonusOfType(Bonus::SIEGE_WEAPON))
@@ -292,7 +248,7 @@ int CBonusSystemNode::MoraleVal() const
 	return abetw(ret, -3, +3);
 }
 
-int CBonusSystemNode::LuckVal() const
+int IBonusBearer::LuckVal() const
 {
 	if(hasBonusOfType(Bonus::NO_LUCK))
 		return 0;
@@ -305,7 +261,7 @@ int CBonusSystemNode::LuckVal() const
 	return abetw(ret, -3, +3);
 }
 
-si32 CBonusSystemNode::Attack() const
+si32 IBonusBearer::Attack() const
 {
 	si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
 
@@ -317,7 +273,7 @@ si32 CBonusSystemNode::Attack() const
 	return ret;
 }
 
-si32 CBonusSystemNode::Defense(bool withFrenzy /*= true*/) const
+si32 IBonusBearer::Defense(bool withFrenzy /*= true*/) const
 {
 	si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
 
@@ -329,20 +285,83 @@ si32 CBonusSystemNode::Defense(bool withFrenzy /*= true*/) const
 	return ret;
 }
 
-ui16 CBonusSystemNode::MaxHealth() const
+ui16 IBonusBearer::MaxHealth() const
 {
 	return valOfBonuses(Bonus::STACK_HEALTH);
 }
 
-ui32 CBonusSystemNode::getMinDamage() const
+ui32 IBonusBearer::getMinDamage() const
 {
 	return valOfBonuses(Selector::typeSybtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSybtype(Bonus::CREATURE_DAMAGE, 1));
 }
-ui32 CBonusSystemNode::getMaxDamage() const
+ui32 IBonusBearer::getMaxDamage() const
 {
 	return valOfBonuses(Selector::typeSybtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSybtype(Bonus::CREATURE_DAMAGE, 2));
 }
 
+si32 IBonusBearer::manaLimit() const
+{
+	return si32(getPrimSkillLevel(3) * (100.0f + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 24)) / 10.0f);
+}
+
+int IBonusBearer::getPrimSkillLevel(int id) const
+{
+	int ret = 0;
+	if(id == PrimarySkill::ATTACK)
+		ret = Attack();
+	else if(id == PrimarySkill::DEFENSE)
+		ret = Defense();
+	else
+		ret = valOfBonuses(Bonus::PRIMARY_SKILL, id);
+
+	amax(ret, id/2); //minimal value is 0 for attack and defense and 1 for spell power and knowledge
+	return ret;
+}
+
+Bonus * CBonusSystemNode::getBonus(const CSelector &selector)
+{
+	Bonus *ret = bonuses.getFirst(selector);
+	if(ret)
+		return ret;
+
+	FOREACH_PARENT(pname)
+	{
+		ret = pname->getBonus(selector);
+		if (ret)
+			return ret;
+	}
+
+	return NULL;
+}
+
+const Bonus * CBonusSystemNode::getBonus( const CSelector &selector ) const
+{
+	return (const_cast<CBonusSystemNode*>(this))->getBonus(selector);
+}
+
+void CBonusSystemNode::getParents(TCNodes &out) const /*retreives list of parent nodes (nodes to inherit bonuses from) */
+{
+	BOOST_FOREACH(const CBonusSystemNode *parent, parents)
+		out.insert(parent);
+}
+
+void CBonusSystemNode::getParents(TNodes &out)
+{
+	BOOST_FOREACH(const CBonusSystemNode *parent, parents)
+		out.insert(const_cast<CBonusSystemNode*>(parent));
+}
+
+void CBonusSystemNode::getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+{
+	FOREACH_CONST_PARENT(p)
+		p->getBonuses(out, selector, limit, root ? root : this);
+
+	bonuses.getBonuses(out, selector, limit);
+
+	if(!root)
+		out.limit(*this);
+}
+
 CBonusSystemNode::CBonusSystemNode()
 {
 	nodeType = UNKNOWN;

+ 27 - 15
lib/HeroBonus.h

@@ -369,26 +369,15 @@ public:
 	{}
 };
 
-class DLL_EXPORT CBonusSystemNode
+class DLL_EXPORT IBonusBearer
 {
 public:
-	BonusList bonuses; //wielded bonuses (local or up-propagated here)
-	BonusList exportedBonuses; //bonuses coming from this node (wielded or propagated away)
-
-	TNodesVector parents; //parents -> we inherit bonuses from them, we may attach our bonuses to them
-	TNodesVector children;
-
-	ui8 nodeType;
-	std::string description;
-
-	CBonusSystemNode();
-	virtual ~CBonusSystemNode();
+	virtual void getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const = 0; 
 
 	//new bonusing node interface
 	// * selector is predicate that tests if HeroBonus matches our criteria
 	// * root is node on which call was made (NULL will be replaced with this)
 	//interface
-	void getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const; 
 	void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const; //as above but without duplicates
 	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
 	void getModifiersWDescr(TModDescr &out, const CSelector &selector) const;  //out: pairs<modifier value, modifier description>
@@ -397,7 +386,6 @@ public:
 	bool hasBonus(const CSelector &selector) const;
 	BonusList getBonuses(const CSelector &selector, const CSelector &limit) const;
 	BonusList getBonuses(const CSelector &selector) const;
-	void getParents(TCNodes &out) const;  //retrieves list of parent nodes (nodes to inherit bonuses from),
 
 	//legacy interface 
 	int valOfBonuses(Bonus::BonusType type, const CSelector &selector) const;
@@ -406,17 +394,41 @@ public:
 	bool hasBonusFrom(ui8 source, ui32 sourceID) const;
 	void getModifiersWDescr( TModDescr &out, Bonus::BonusType type, int subtype = -1 ) const;  //out: pairs<modifier value, modifier description>
 	int getBonusesCount(int from, int id) const;
+
+	//various hlp functions for non-trivial values
 	ui32 getMinDamage() const; //used for stacks and creatures only
 	ui32 getMaxDamage() const;
-
 	int MoraleVal() const; //range [-3, +3]
 	int LuckVal() const; //range [-3, +3]
 	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
 
+	si32 manaLimit() const; //maximum mana value for this hero (basically 10*knowledge)
+	int getPrimSkillLevel(int id) const; //0-attack, 1-defence, 2-spell power, 3-knowledge
+
+};
 
+class DLL_EXPORT CBonusSystemNode : public IBonusBearer
+{
+public:
+	BonusList bonuses; //wielded bonuses (local or up-propagated here)
+	BonusList exportedBonuses; //bonuses coming from this node (wielded or propagated away)
+
+	TNodesVector parents; //parents -> we inherit bonuses from them, we may attach our bonuses to them
+	TNodesVector children;
+
+	ui8 nodeType;
+	std::string description;
+
+	CBonusSystemNode();
+	virtual ~CBonusSystemNode();
+
+	void getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const;
+
+	void getParents(TCNodes &out) const;  //retrieves list of parent nodes (nodes to inherit bonuses from),
 	const Bonus *getBonus(const CSelector &selector) const;
+
 	//non-const interface
 	void getParents(TNodes &out);  //retrieves list of parent nodes (nodes to inherit bonuses from)
 	void getRedParents(TNodes &out);  //retrieves list of red parent nodes (nodes bonuses propagate from)

+ 1 - 1
server/CGameHandler.cpp

@@ -2578,7 +2578,7 @@ bool CGameHandler::moveArtifact(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, u
 //  	if (src.slot >= 19 && dst.slot >= 19 && src.slot < dst.slot)
 //  		dst.slot--;
 
-	if (src.slot == dst.slot)
+	if (src.slot == dst.slot  &&  src.hero == dst.hero)
 		COMPLAIN_RET("Won't move artifact: Dest same as source!");
 
 	//moving art to backpack is always allowed (we've ruled out exceptions)