2
0
Эх сурвалжийг харах

* New file: lib/HeroBonus.cpp - updated project files for MSVC
* Updated changelog
* Support for Lighthosues and Obelisks
* Bonus system extended on players
* Army speed won't affect movement points when sailing
* Picking grail position (digging not implemented though, puzzle map only partially)
* Minor improvements

Michał W. Urbańczyk 15 жил өмнө
parent
commit
0fdbe787dc

+ 7 - 1
CCallback.cpp

@@ -317,7 +317,7 @@ std::vector< std::vector< std::vector<unsigned char> > > & CCallback::getVisibil
 bool CCallback::isVisible(int3 pos, int Player) const
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	return gs->isVisible(pos, Player);
+	return gs->map->isInTheMap(pos) && gs->isVisible(pos, Player);
 }
 
 std::vector < const CGTownInstance *> CCallback::getTownsInfo(bool onlyOur) const
@@ -905,6 +905,12 @@ void CCallback::calculatePaths( const CGHeroInstance *hero, CPathsInfo &out, int
 	gs->calculatePaths(hero, out, src, movement);
 }
 
+int3 CCallback::getGrailPos( float &outKnownRatio )
+{
+	outKnownRatio = (float)CGObelisk::visited[player] / CGObelisk::obeliskCount;
+	return gs->map->grailPos;
+}
+
 InfoAboutTown::InfoAboutTown()
 {
 	tType = NULL;

+ 2 - 0
CCallback.h

@@ -114,6 +114,7 @@ public:
 	virtual int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const =0; //when called during battle, takes into account creatures' spell cost reduction
 	virtual int estimateSpellDamage(const CSpell * sp) const =0; //estimates damage of given spell; returns 0 if spell causes no dmg
 	virtual void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj)=0; //get thieves' guild info obtainable while visiting given object
+	virtual int3 getGrailPos(float &outKnownRatio)=0;
 
 	//hero
 	virtual int howManyHeroes(bool includeGarrisoned = true)const =0;
@@ -247,6 +248,7 @@ public:
 	int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
 	int estimateSpellDamage(const CSpell * sp) const; //estimates damage of given spell; returns 0 if spell causes no dmg
 	void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object
+	int3 getGrailPos(float &outKnownRatio); //returns pos and (via arg) percent of discovered obelisks; TODO: relies on fairness of GUI/AI... :/
 
 	std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos) const;
 	std::vector < const CGObjectInstance * > getVisitableObjs(int3 pos) const;

+ 3 - 1
CGameInterface.h

@@ -21,7 +21,7 @@ using namespace boost::logic;
 class CCallback;
 class ICallback;
 class CGlobalAI;
-class Component;
+struct Component;
 class CSelectableComponent;
 struct TryMoveHero;
 class CGHeroInstance;
@@ -91,6 +91,7 @@ public:
 	virtual void showShipyardDialog(const IShipyard *obj){} //obj may be town or shipyard; state: 0 - can buid, 1 - lack of resources, 2 - dest tile is blocked, 3 - no water
 	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const int soundID, bool selection, bool cancel) = 0; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, boost::function<void()> &onEnd) = 0; //all stacks operations between these objects become allowed, interface has to call onEnd when done
+	virtual void showPuzzleMap(){};
 	virtual void tileHidden(const std::set<int3> &pos){};
 	virtual void tileRevealed(const std::set<int3> &pos){};
 	virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard
@@ -98,6 +99,7 @@ public:
 	virtual void centerView (int3 pos, int focusTime){};
 	virtual void availableCreaturesChanged(const CGDwelling *town){};
 	virtual void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
+	virtual void playerBonusChanged(const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
 	virtual void requestRealized(PackageApplied *pa){};
 	virtual void heroExchangeStarted(si32 hero1, si32 hero2){};
 	virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged

+ 39 - 0
ChangeLog

@@ -1,3 +1,42 @@
+0.75 -> 0.8
+GENERAL:
+* Victory and loss conditions are supported. It's now possible to win or lose the game.
+* Kingdom Overview screen is now available.
+* Replaced TTF fonts with original ones.
+
+ADVENTURE MAP:
+* Implemented rivers animations (thx to GrayFace).
+
+HERO:
+* Partial implementation of scholar skill (spell exchange works, but no message).
+
+TOWN:
+* New left-bottom info panel functionalities.
+TOWNS:
+* new town structures supported:
+- Ballista Yard
+- Blood Obelisk
+- Dwarven Treasury
+- Glyphs of Fear
+- Mystic Pond
+- Thieves Guild
+
+OBJECTS:
+New objects supported:
+- Border gate
+- Den of Thieves
+- Lighthouse
+- Obelisk
+- Quest Guard
+- Seer hut
+
+
+
+A lot of of various bugfixes and improvements:
+http://vcmi.antypika.aplus.pl/bugs/view_all_bug_page.php?filter=699
+
+
+
 0.74 -> 0.75 (Dec 01 2009)
 GENERAL:
 * Implemented "main menu" in-game option.

+ 2 - 7
client/CAdvmapInterface.cpp

@@ -2060,7 +2060,7 @@ CAdventureOptions::CAdventureOptions()
 	//viewWorld = new AdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally, &GH, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
 
 	puzzle = new AdventureMapButton("","", boost::bind(&CGuiHandler::popIntTotally, &GH, this), 24, 81, "ADVPUZ.DEF");;
-	puzzle->callback += CAdventureOptions::showPuzzleMap;
+	puzzle->callback += boost::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT);
 }
 
 CAdventureOptions::~CAdventureOptions()
@@ -2070,9 +2070,4 @@ CAdventureOptions::~CAdventureOptions()
 void CAdventureOptions::showScenarioInfo()
 {
 	GH.pushInt(new CScenarioInfo(LOCPLINT->cb->getMapHeader(), LOCPLINT->cb->getStartInfo()));
-}
-
-void CAdventureOptions::showPuzzleMap()
-{
-	GH.pushInt(new CPuzzleWindow());
-}
+}

+ 0 - 1
client/CAdvmapInterface.h

@@ -34,7 +34,6 @@ public:
 	CAdventureOptions();
 	~CAdventureOptions();
 	static void showScenarioInfo();
-	static void showPuzzleMap();
 };
 	 
 

+ 2 - 0
client/CCastleInterface.cpp

@@ -1169,6 +1169,8 @@ void CCastleInterface::CCreaInfo::clickRight(tribool down, bool previousState)
 			ch = ci->town->visitingHero;
 		};
 
+		//TODO player bonuses
+
 		if(bld.find(26)!=bld.end()) //grail - +50% to ALL growth
 			summ+=AddToString(CGI->buildh->buildings[ci->town->subID][26]->Name()+" %+d",descr,summ/2);
 

+ 17 - 1
client/CPlayerInterface.cpp

@@ -230,7 +230,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 
 	if(makingTurn  &&  ho->tempOwner == playerID) //we are moving our hero - we may need to update assigned path
 	{
-		if(details.result == TryMoveHero::TELEPORTATION	||  details.start == details.end)
+		if(details.result == TryMoveHero::TELEPORTATION/*	||  details.start == details.end*/)
 		{
 			adventureInt->eraseCurrentPathOf(ho);
 			return; //teleport - no fancy moving animation
@@ -1629,6 +1629,22 @@ void CPlayerInterface::gameOver(ui8 player, bool victory )
 	}
 }
 
+void CPlayerInterface::playerBonusChanged( const HeroBonus &bonus, bool gain )
+{
+
+}
+
+void CPlayerInterface::showPuzzleMap()
+{
+	waitWhileDialog();
+
+	//TODO: interface should not know the real position of Grail...
+	float ratio = 0;
+	int3 grailPos = cb->getGrailPos(ratio);
+
+	GH.pushInt(new CPuzzleWindow(grailPos, ratio));
+}
+
 void SystemOptions::setMusicVolume( int newVolume )
 {
 	musicVolume = newVolume;

+ 2 - 0
client/CPlayerInterface.h

@@ -159,12 +159,14 @@ public:
 	void showShipyardDialog(const IShipyard *obj); //obj may be town or shipyard; 
 	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, int soundID, bool selection, bool cancel); //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, boost::function<void()> &onEnd);
+	void showPuzzleMap();
 	void tileHidden(const std::set<int3> &pos); //called when given tiles become hidden under fog of war
 	void tileRevealed(const std::set<int3> &pos); //called when fog of war disappears from given tiles
 	void newObject(const CGObjectInstance * obj);
 	void yourTurn();
 	void availableCreaturesChanged(const CGDwelling *town);
 	void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain);//if gain hero received bonus, else he lost it
+	void playerBonusChanged(const HeroBonus &bonus, bool gain);
 	void requestRealized(PackageApplied *pa);
 	void heroExchangeStarted(si32 hero1, si32 hero2);
 	void centerView (int3 pos, int focusTime);

+ 3 - 5
client/GUIClasses.cpp

@@ -4710,7 +4710,7 @@ CShipyardWindow::CShipyardWindow(const std::vector<si32> &cost, int state, const
 	printAtMiddle(CGI->generaltexth->jktexts[14], 164, 220, FONT_MEDIUM, zwykly, bg); //Resource cost:
 }
 
-CPuzzleWindow::CPuzzleWindow()
+CPuzzleWindow::CPuzzleWindow(const int3 &grailPos, float discoveredRatio)
 :animCount(0)
 {
 	SDL_Surface * back = BitmapHandler::loadBitmap("PUZZLE.BMP", false);
@@ -4728,19 +4728,17 @@ CPuzzleWindow::CPuzzleWindow()
 	//printing necessary thinks to background
 	
 	CGI->mh->terrainRect
-		(int3(14, 15, 0), LOCPLINT->adventureInt->anim,
+		(grailPos, LOCPLINT->adventureInt->anim,
 		 &LOCPLINT->cb->getVisibilityMap(), true, LOCPLINT->adventureInt->heroAnim,
 		 background, &genRect(544, 591, 8, 8), 0, 0, true);
 
-	
-	float discoveryRatio = 0.5f;
 	int faction = LOCPLINT->cb->getStartInfo()->playerInfos[LOCPLINT->serialID].castle;
 
 	std::vector<SPuzzleInfo> puzzlesToPrint;
 
 	for(int g=0; g<PUZZLES_PER_FACTION; ++g)
 	{
-		if(CGI->heroh->puzzleInfo[faction][g].whenUncovered >= PUZZLES_PER_FACTION * discoveryRatio)
+		if(CGI->heroh->puzzleInfo[faction][g].whenUncovered > PUZZLES_PER_FACTION * discoveredRatio)
 		{
 			puzzlesToPrint.push_back(CGI->heroh->puzzleInfo[faction][g]);
 		}

+ 1 - 1
client/GUIClasses.h

@@ -825,7 +825,7 @@ public:
 	void deactivate();
 	void show(SDL_Surface * to);
 
-	CPuzzleWindow();
+	CPuzzleWindow(const int3 &grailPos, float discoveredRatio);
 	~CPuzzleWindow();
 };
 

+ 39 - 3
client/NetPacksClient.cpp

@@ -109,9 +109,21 @@ void SetAvailableHeroes::applyCl( CClient *cl )
 
 void GiveBonus::applyCl( CClient *cl )
 {
-	CGHeroInstance *h = GS(cl)->getHero(hid);
-	if(vstd::contains(cl->playerint,h->tempOwner))
-		cl->playerint[h->tempOwner]->heroBonusChanged(h,h->bonuses.back(),true);
+	switch(who)
+	{
+	case HERO:
+		{
+			const CGHeroInstance *h = GS(cl)->getHero(id);
+			INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroBonusChanged, h, h->bonuses.back(),true);
+		}
+		break;
+	case PLAYER:
+		{
+			const PlayerState *p = GS(cl)->getPlayer(id);
+			INTERFACE_CALL_IF_PRESENT(id, playerBonusChanged, p->bonuses.back(), true);
+		}
+		break;
+	}
 }
 
 void ChangeObjPos::applyFirstCl( CClient *cl )
@@ -133,6 +145,25 @@ void PlayerEndsGame::applyCl( CClient *cl )
 		i->second->gameOver(player,	victory);
 }
 
+void RemoveBonus::applyCl( CClient *cl )
+{
+	switch(who)
+	{
+	case HERO:
+		{
+			const CGHeroInstance *h = GS(cl)->getHero(id);
+			INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroBonusChanged, h, bonus,false);
+		}
+		break;
+	case PLAYER:
+		{
+			const PlayerState *p = GS(cl)->getPlayer(id);
+			INTERFACE_CALL_IF_PRESENT(id, playerBonusChanged, bonus, false);
+		}
+		break;
+	}
+}
+
 void RemoveObject::applyFirstCl( CClient *cl )
 {
 	const CGObjectInstance *o = cl->getObj(id);
@@ -635,6 +666,11 @@ void OpenWindow::applyCl(CClient *cl)
 			GH.pushInt( new CThievesGuildWindow(obj) );
 		}
 		break;
+	case PUZZLE_MAP:
+		{
+			INTERFACE_CALL_IF_PRESENT(id1, showPuzzleMap);
+		}
+		break;
 	}
 
 }

+ 1 - 1
config/settings.txt

@@ -253,4 +253,4 @@ GUISettings
 			ButtonEndTurn: x=1559 y=491 graphic=IAM001.DEF playerColoured=1; 
 		}; 
 	}
-}
+}

+ 14 - 3
global.h

@@ -75,9 +75,10 @@ enum ECombatInfo{ALIVE = 180, SUMMONED, CLONED, HAD_MORALE, WAITING, MOVED, DEFE
 class CGameInfo;
 extern CGameInfo* CGI;
 
-#define HEROI_TYPE (34)
-#define TOWNI_TYPE (98)
-#define CREI_TYPE (54)
+const int HEROI_TYPE = 34, 
+	TOWNI_TYPE = 98,
+	CREI_TYPE = 54,
+	EVENTI_TYPE = 26;
 
 const int F_NUMBER = 9; //factions (town types) quantity
 const int PLAYER_LIMIT = 8; //player limit per map
@@ -265,6 +266,7 @@ t1 & amax(t1 &a, const t2 &b) //assigns greater of (a, b) to a and returns maxim
 		return a;
 	}
 }
+
 template <typename t1, typename t2>
 t1 & amin(t1 &a, const t2 &b) //assigns smaller of (a, b) to a and returns minimum of (a, b)
 {
@@ -276,6 +278,15 @@ t1 & amin(t1 &a, const t2 &b) //assigns smaller of (a, b) to a and returns minim
 		return a;
 	}
 }
+
+template <typename t1, typename t2, typename t3>
+t1 & abetw(t1 &a, const t2 &b, const t3 &c) //makes a to fit the range <b, c>
+{
+	amax(a,b);
+	amin(a,c);
+	return a;
+}
+
 #include "CConsoleHandler.h"
 extern DLL_EXPORT std::ostream *logfile;
 extern DLL_EXPORT CConsoleHandler *console;

+ 170 - 92
hch/CObjectHandler.cpp

@@ -44,6 +44,9 @@ extern boost::rand48 ran;
 std::map <ui8, std::set <ui8> > CGKeys::playerKeyMap;
 std::map <si32, std::vector<si32> > CGMagi::eyelist;
 BankConfig CGPyramid::pyramidConfig;
+ui8 CGObelisk::obeliskCount; //how many obelisks are on map
+std::map<ui8, ui8> CGObelisk::visited; //map: color_id => how many obelisks has been visited
+
 
 void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const 
 {};
@@ -429,7 +432,7 @@ void CGObjectInstance::giveDummyBonus(int heroID, ui8 duration) const
 {
 	GiveBonus gbonus;
 	gbonus.bonus.type = HeroBonus::NONE;
-	gbonus.hid = heroID;
+	gbonus.id = heroID;
 	gbonus.bonus.duration = duration;
 	gbonus.bonus.source = HeroBonus::OBJECT;
 	gbonus.bonus.id = ID;
@@ -563,10 +566,8 @@ bool CGHeroInstance::canWalkOnSea() const
 int CGHeroInstance::getPrimSkillLevel(int id) const
 {
 	int ret = primSkills[id];
-	for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-		if(i->type == HeroBonus::PRIMARY_SKILL && i->subtype==id)
-			ret += i->val;
-	amax(ret, id/2);//minimum value for attack and defense is 0 and for spell power and knowledge - 1
+	ret += valOfBonuses(HeroBonus::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(const int & ID) const
@@ -578,13 +579,21 @@ ui8 CGHeroInstance::getSecSkillLevel(const int & ID) const
 }
 int CGHeroInstance::maxMovePoints(bool onLand) const
 {
-	static const int moveForSpeed[] = { 1500, 1560, 1630, 1700, 1760, 1830, 1900, 1960, 2000 }; //first element for 3 and lower; last for 11 and more
-	int index = lowestSpeed(this) - 3;
-	amin(index, ARRAY_COUNT(moveForSpeed)-1);
-	amax(index, 0);
-
-	int ret = moveForSpeed[index],
-		bonus = valOfBonuses(HeroBonus::MOVEMENT) + (onLand ? valOfBonuses(HeroBonus::LAND_MOVEMENT) : valOfBonuses(HeroBonus::SEA_MOVEMENT));
+	int base = -1;
+	if(onLand)
+	{
+		static const int moveForSpeed[] = { 1500, 1560, 1630, 1700, 1760, 1830, 1900, 1960, 2000 }; //first element for 3 and lower; last for 11 and more
+		int index = lowestSpeed(this) - 3;
+		amin(index, ARRAY_COUNT(moveForSpeed)-1);
+		amax(index, 0);
+		base = moveForSpeed[index];
+	}
+	else
+	{
+		base = 1500; //on water base movement is always 1500 (speed of army doesn't matter)
+	}
+	
+	int bonus = valOfBonuses(HeroBonus::MOVEMENT) + (onLand ? valOfBonuses(HeroBonus::LAND_MOVEMENT) : valOfBonuses(HeroBonus::SEA_MOVEMENT));
 
 	double modifier = 0;
 	if(onLand)
@@ -619,7 +628,7 @@ int CGHeroInstance::maxMovePoints(bool onLand) const
 			break;
 		}
 	}
-	return int(ret + ret*modifier) + bonus;
+	return int(base + base*modifier) + bonus;
 }
 
 ui32 CGHeroInstance::getArtAtPos(ui16 pos) const
@@ -879,15 +888,7 @@ std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentMoraleModifie
 {
 	//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<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-	{
-		if(i->type == HeroBonus::MORALE   ||   i->type == HeroBonus::MORALE_AND_LUCK)
-		{
-			ret.push_back(std::make_pair(i->val, i->description));
-		}
-	}
+	getModifiersWDescr(ret, MORALE_AFFECTING);
 
 	//leadership
 	if(getSecSkillLevel(6)) 
@@ -961,42 +962,29 @@ int CGHeroInstance::getCurrentLuck( int stack/*=-1*/, bool town/*=false*/ ) cons
 	std::vector<std::pair<int,std::string> > mods = getCurrentLuckModifiers(stack,town);
 	for(int i=0; i < mods.size(); i++)
 		ret += mods[i].first;
-	if(ret > 3)
-		return 3;
-	if(ret < -3)
-		return -3;
+
+	abetw(ret, -3, 3);
 	return ret;
 }
 
 std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentLuckModifiers( int stack/*=-1*/, bool town/*=false*/ ) const
 {
 	std::vector<std::pair<int,std::string> > ret;
-
-	//various morale bonuses (from buildings, artifacts, etc)
-	for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-		if(i->type == HeroBonus::LUCK   ||   i->type == HeroBonus::MORALE_AND_LUCK)
-			ret.push_back(std::make_pair(i->val, i->description));
+	getModifiersWDescr(ret, LUCK_AFFECTING);
 
 	//luck skill
 	if(getSecSkillLevel(9)) 
 		ret.push_back(std::make_pair(getSecSkillLevel(9),VLC->generaltexth->arraytxt[73+getSecSkillLevel(9)]));
 
-	if(visitedTown)
-	{
-		if(visitedTown->subID == 1  &&  vstd::contains(visitedTown->builtBuildings,21)) //castle, brotherhood of sword built
-			ret.push_back(std::pair<int,std::string>(2,VLC->generaltexth->buildings[1][21].first + " +2"));
-	}
-
-
+	if(visitedTown && visitedTown->subID == 1  &&  vstd::contains(visitedTown->builtBuildings,21)) //rampart, fountain of fortune
+		ret.push_back(std::pair<int,std::string>(2,VLC->generaltexth->buildings[1][21].first + " +2"));
+	
 	return ret;
 }
 
 const HeroBonus * CGHeroInstance::getBonus( int from, int id ) const
 {
-	for (std::list<HeroBonus>::const_iterator i=bonuses.begin(); i!=bonuses.end(); i++)
-		if(i->source == from  &&  i->id == id)
-			return &*i;
-	return NULL;
+	return bonuses.getBonus(from, id);
 }
 
 void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
@@ -1048,26 +1036,16 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
 	if(!getArt(17)) //if hero has no spellbook
 		return false;
 
-	if(vstd::contains(spells, spell->id) //hero does not have this spell in spellbook
+	if(vstd::contains(spells, spell->id) //hero has this spell in spellbook
 		|| (spell->air && hasBonusOfType(HeroBonus::AIR_SPELLS)) // this is air spell and hero can cast all air spells
 		|| (spell->fire && hasBonusOfType(HeroBonus::FIRE_SPELLS)) // this is fire spell and hero can cast all fire spells
 		|| (spell->water && hasBonusOfType(HeroBonus::WATER_SPELLS)) // this is water spell and hero can cast all water spells
 		|| (spell->earth && hasBonusOfType(HeroBonus::EARTH_SPELLS)) // this is earth spell and hero can cast all earth spells
+		|| hasBonusOfType(HeroBonus::SPELL, spell->id)
+		|| hasBonusOfType(HeroBonus::SPELLS_OF_LEVEL, spell->level)
 		)
 		return true;
 
-	for(std::list<HeroBonus>::const_iterator it = bonuses.begin(); it != bonuses.end(); ++it)
-	{
-		if(it->type == HeroBonus::SPELL && it->subtype == spell->id)
-		{
-			return true;
-		}
-		if(it->type == HeroBonus::SPELLS_OF_LEVEL && it->subtype == spell->level)
-		{
-			return true;
-		}
-	}
-
 	return false;
 }
 
@@ -1165,40 +1143,15 @@ si32 CGHeroInstance::manaRegain() const
 
 int CGHeroInstance::valOfBonuses( HeroBonus::BonusType type, int subtype /*= -1*/ ) const
 {
-	int ret = 0;
-	if(subtype == -1)
-	{
-		for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-			if(i->type == type)
-				ret += i->val;
-	}
-	else
-	{
-		for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-			if(i->type == type && i->subtype == subtype)
-				ret += i->val;
-	}
-	return ret;
+	return bonuses.valOfBonuses(type, subtype) + ownerBonuses()->valOfBonuses(type, subtype);
 }
 
 bool CGHeroInstance::hasBonusOfType(HeroBonus::BonusType type, int subtype /*= -1*/) const
 {
 	if(!this) //to allow calls on NULL and avoid checking duplication
 		return false; //if hero doesn't exist then bonus neither can
-
-	if(subtype == -1) //any subtype
-	{
-		for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-			if(i->type == type)
-				return true;
-	}
-	else //given subtype
-	{
-		for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-			if(i->type == type && i->subtype == subtype)
-				return true;
-	}
-	return false;
+	else
+		return bonuses.hasBonusOfType(type, subtype) || ownerBonuses()->hasBonusOfType(type, subtype);
 }
 
 si32 CGHeroInstance::getArtPos(int aid) const
@@ -1252,6 +1205,21 @@ bool CGHeroInstance::hasArt( ui32 aid ) const
 	return false;
 }
 
+void CGHeroInstance::getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype /*= -1*/ ) const
+{
+	bonuses.getModifiersWDescr(out, type, subtype);
+	ownerBonuses()->getModifiersWDescr(out, type, subtype);
+}
+
+const BonusList * CGHeroInstance::ownerBonuses() const
+{
+	const PlayerState *p = cb->getPlayerState(tempOwner);
+	if(!p)
+		return NULL;
+	else
+		return &p->bonuses;
+}
+
 void CGDwelling::initObj()
 {
 	switch(ID)
@@ -2166,7 +2134,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
 				{
 					GiveBonus gb;
 					gb.bonus = HeroBonus(HeroBonus::ONE_WEEK, HeroBonus::LAND_MOVEMENT, HeroBonus::OBJECT, 600, 94, VLC->generaltexth->arraytxt[100]);
-					gb.hid = heroID;
+					gb.id = heroID;
 					cb->giveHeroBonus(&gb);
 					iw.text << VLC->generaltexth->allTexts[580];
 					cb->showInfoDialog(&iw);
@@ -3632,7 +3600,7 @@ void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward
 			HeroBonus hb(HeroBonus::ONE_WEEK, (rewardType == 3 ? HeroBonus::MORALE : HeroBonus::LUCK),
 				HeroBonus::OBJECT, rVal, h->id, "", -1);
 			GiveBonus gb;
-			gb.hid = h->id;
+			gb.id = h->id;
 			gb.bonus = hb;
 			//gb.descr = "";
 			cb->giveHeroBonus(&gb);
@@ -3752,7 +3720,7 @@ void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
 	InfoWindow iw;
 	iw.player = h->tempOwner;
 	GiveBonus gbonus;
-	gbonus.hid = h->id;
+	gbonus.id = h->id;
 	gbonus.bonus.duration = HeroBonus::ONE_BATTLE;
 	gbonus.bonus.source = HeroBonus::OBJECT;
 	gbonus.bonus.id = ID;
@@ -4121,7 +4089,7 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 		cb->showInfoDialog(&iw);
 		GiveBonus gb;
 		gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::MORALE,HeroBonus::OBJECT,moraleDiff,id,"");
-		gb.hid = h->id;
+		gb.id = h->id;
 		cb->giveHeroBonus(&gb);
 	}
 
@@ -4132,7 +4100,7 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 		cb->showInfoDialog(&iw);
 		GiveBonus gb;
 		gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::LUCK,HeroBonus::OBJECT,luckDiff,id,"");
-		gb.hid = h->id;
+		gb.id = h->id;
 		cb->giveHeroBonus(&gb);
 	}
 
@@ -4688,7 +4656,7 @@ void CGOnceVisitable::searchTomb(const CGHeroInstance *h, ui32 accept) const
 		{
 			//ruin morale 
 			GiveBonus gb;
-			gb.hid = h->id;
+			gb.id = h->id;
 			gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::MORALE,HeroBonus::OBJECT,-3,id,"");
 			gb.bdescr.addTxt(MetaString::ARRAY_TXT,104); //Warrior Tomb Visited -3
 			cb->giveHeroBonus(&gb);
@@ -4912,7 +4880,7 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons
 				{
 					iw.components.push_back (Component (Component::MORALE, 0 , -2, 0));
 					GiveBonus gbonus;
-					gbonus.hid = h->id;
+					gbonus.id = h->id;
 					gbonus.bonus.duration = HeroBonus::ONE_BATTLE;
 					gbonus.bonus.source = HeroBonus::OBJECT;
 					gbonus.bonus.id = ID;
@@ -4931,7 +4899,7 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons
 				{
 					iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
 					GiveBonus gbonus;
-					gbonus.hid = h->id;
+					gbonus.id = h->id;
 					gbonus.bonus.duration = HeroBonus::ONE_BATTLE;
 					gbonus.bonus.source = HeroBonus::OBJECT;
 					gbonus.bonus.id = ID;
@@ -5077,7 +5045,7 @@ void CGPyramid::onHeroVisit (const CGHeroInstance * h) const
 		iw.components.push_back (Component (Component::LUCK, 0 , -2, 0));
 		GiveBonus gb;
 		gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::LUCK,HeroBonus::OBJECT,-2,id,VLC->generaltexth->arraytxt[70]);
-		gb.hid = h->id;
+		gb.id = h->id;
 		cb->giveHeroBonus(&gb);
 		cb->showInfoDialog(&iw);
 	}
@@ -5569,4 +5537,114 @@ void CGRefugeeCamp::reset(ui32 val)
 void CGDenOfthieves::onHeroVisit (const CGHeroInstance * h) const
 {
 	cb->showThievesGuildWindow(id);
+}
+
+void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
+{
+	InfoWindow iw;
+	iw.player = h->tempOwner;
+	
+	if(!hasVisited(h->tempOwner))
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT, 96);
+		cb->sendAndApply(&iw);
+
+		cb->setObjProperty(id,20,h->tempOwner); //increment general visited obelisks counter
+
+		OpenWindow ow;
+		ow.id1 = h->tempOwner;
+		ow.window = OpenWindow::PUZZLE_MAP;
+		cb->sendAndApply(&ow);
+
+		cb->setObjProperty(id,10,h->tempOwner); //mark that particular obelisk as visited
+	}
+	else
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT, 97);
+		cb->sendAndApply(&iw);
+	}
+
+}
+
+void CGObelisk::initObj()
+{
+	obeliskCount++;
+}
+
+const std::string & CGObelisk::getHoverText() const
+{
+	hoverName = VLC->generaltexth->names[ID];
+	if(hasVisited(cb->getCurrentPlayer()))
+		hoverName += " " + VLC->generaltexth->allTexts[352]; //not visited
+	else
+		hoverName += " " + VLC->generaltexth->allTexts[353]; //visited
+	return hoverName;
+}
+
+void CGObelisk::setPropertyDer( ui8 what, ui32 val )
+{
+	CPlayersVisited::setPropertyDer(what, val);
+	switch(what)
+	{
+	case 20:
+		assert(val < PLAYER_LIMIT);
+		visited[val]++;
+		assert(visited[val] <= obeliskCount);
+		break;
+	}
+}
+
+void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const
+{
+	if(h->tempOwner != tempOwner)
+	{
+		ui8 oldOwner = tempOwner;
+		cb->setOwner(id,h->tempOwner); //not ours? flag it!
+
+
+		InfoWindow iw;
+		iw.player = h->tempOwner;
+		iw.text.addTxt(MetaString::ADVOB_TXT, 69);
+		iw.soundID = soundBase::LIGHTHOUSE;
+		cb->sendAndApply(&iw);
+
+		giveBonusTo(h->tempOwner);
+
+		if(oldOwner < PLAYER_LIMIT) //remove bonus from old owner
+		{
+			RemoveBonus rb(RemoveBonus::PLAYER);
+			rb.whoID = oldOwner;
+			rb.source = HeroBonus::OBJECT;
+			rb.id = id;
+			cb->sendAndApply(&rb);
+		}
+	}
+}
+
+void CGLighthouse::initObj()
+{
+	if(tempOwner < PLAYER_LIMIT)
+	{
+		giveBonusTo(tempOwner);
+	}
+}
+
+const std::string & CGLighthouse::getHoverText() const
+{
+	hoverName = VLC->generaltexth->names[ID];
+	//TODO: owned by %s player
+	return hoverName;
+}
+
+void CGLighthouse::giveBonusTo( ui8 player ) const
+{
+	GiveBonus gb(GiveBonus::PLAYER);
+	gb.bonus.type = HeroBonus::SEA_MOVEMENT;
+	gb.bonus.val = 500;
+	gb.id = player;
+	gb.bonus.duration = HeroBonus::PERMANENT;
+	gb.bonus.source = HeroBonus::OBJECT;
+	gb.bonus.id = id;
+	cb->sendAndApply(&gb);
+
 }

+ 46 - 5
hch/CObjectHandler.h

@@ -70,7 +70,7 @@ public:
 	}
 };
 
-class CQuest
+class DLL_EXPORT CQuest
 {
 public:
 	enum Emission {MISSION_NONE = 0, MISSION_LEVEL = 1, MISSION_PRIMARY_STAT = 2, MISSION_KILL_HERO = 3, MISSION_KILL_CREATURE = 4,
@@ -267,7 +267,7 @@ public:
 		}
 	} patrol;
 
-	std::list<HeroBonus> bonuses;
+	BonusList bonuses;
 	//////////////////////////////////////////////////////////////////////////
 
 
@@ -292,9 +292,7 @@ public:
 	int getSightRadious() const; //sight distance (should be used if player-owned structure)
 
 	//////////////////////////////////////////////////////////////////////////
-	const HeroBonus *getBonus(int from, int id) const;
-	int valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
-	bool hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const; //determines if hero has a bonus of given type (and optionally subtype)
+
 	const std::string &getBiography() const;
 	bool needsLastStack()const;
 	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
@@ -304,6 +302,18 @@ public:
 	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;
+
+	const BonusList *ownerBonuses() const;
+	const HeroBonus *getBonus(int from, int id) const;
+	int valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
+	bool hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const; //determines if hero has a bonus of given type (and optionally subtype)
+	void getModifiersWDescr(std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype = -1) const; //out: pairs<modifier value, modifier description>
+	template<int N> void getModifiersWDescr(std::vector<std::pair<int,std::string> > &out, const HeroBonus::BonusType (&types)[N]) const //retreive array of types
+	{
+		for (int i = 0; i < N; i++)
+			getModifiersWDescr(out, types[i]);
+	}
+
 	std::vector<std::pair<int,std::string> > 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
 	std::vector<std::pair<int,std::string> > getCurrentMoraleModifiers(int stack=-1, bool town=false) const; //args as above
@@ -1078,6 +1088,37 @@ class DLL_EXPORT CGDenOfthieves : public CGObjectInstance
 	void onHeroVisit (const CGHeroInstance * h) const;
 };
 
+class DLL_EXPORT CGObelisk : public CPlayersVisited
+{
+public:
+	static ui8 obeliskCount; //how many obelisks are on map
+	static std::map<ui8, ui8> visited; //map: color_id => how many obelisks has been visited
+
+	void setPropertyDer (ui8 what, ui32 val);
+	void onHeroVisit(const CGHeroInstance * h) const;
+	void initObj();
+	const std::string & getHoverText() const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CPlayersVisited&>(*this);;
+	}
+};
+
+class DLL_EXPORT CGLighthouse : public CGObjectInstance
+{
+public:
+	void onHeroVisit(const CGHeroInstance * h) const;
+	void initObj();
+	const std::string & getHoverText() const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this);;
+	}
+	void giveBonusTo( ui8 player ) const;
+};
+
 struct BankConfig
 {
 	BankConfig() {level = chance = upgradeChance = combatValue = value = rewardDifficulty = easiest = 0; };

+ 47 - 6
lib/CGameState.cpp

@@ -1280,6 +1280,45 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
 	scenarioOps = si;
 	this->map = map;
 	loadTownDInfos();
+
+ 	//pick grail location
+ 	if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
+ 	{
+		if(!map->grailRadious) //radius not given -> anywhere on map
+			map->grailRadious = map->width * 2;
+
+
+ 		std::vector<int3> allowedPos;
+ 
+		// add all not blocked tiles in range
+ 		for (int i = 0; i < map->width ; i++)
+ 		{
+ 			for (int j = 0; j < map->height ; j++)
+ 			{
+ 				for (int k = 0; k <= map->twoLevel ; k++)
+ 				{
+ 					const TerrainTile &t = map->terrain[i][j][k];
+ 					if(!t.blocked 
+						&& !t.visitable 
+						&& t.tertype != TerrainTile::water 
+						&& t.tertype != TerrainTile::rock
+						&& map->grailPos.dist2d(int3(i,j,k)) <= map->grailRadious)
+ 						allowedPos.push_back(int3(i,j,k));
+ 				}
+ 			}
+ 		}
+ 
+		//remove tiles with holes
+		for(unsigned int no=0; no<map->objects.size(); ++no)
+			if(map->objects[no]->ID == 124)
+				allowedPos -= map->objects[no]->pos;
+
+		if(allowedPos.size())
+			map->grailPos = allowedPos[ran() % allowedPos.size()];
+		else
+			tlog2 << "Warning: Grail cannot be placed, no appropriate tile found!\n";
+ 	}
+
 	//picking random factions for players
 	for(unsigned int i=0;i<scenarioOps->playerInfos.size();i++)
 	{
@@ -1293,11 +1332,12 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
 			scenarioOps->playerInfos[i].castle = f;
 		}
 	}
+
 	//randomizing objects
 	for(unsigned int no=0; no<map->objects.size(); ++no)
 	{
 		randomizeObject(map->objects[no]);
-		if(map->objects[no]->ID==26)
+		if(map->objects[no]->ID==EVENTI_TYPE)
 		{
 			map->objects[no]->defInfo->handler=NULL;
 		}
@@ -1940,7 +1980,7 @@ void CGameState::apply(CPack *pack)
 	applierGs->apps[typ]->applyOnGS(this,pack);
 }
 
-PlayerState * CGameState::getPlayer( ui8 color )
+PlayerState * CGameState::getPlayer( ui8 color, bool verbose )
 {
 	if(vstd::contains(players,color))
 	{
@@ -1948,14 +1988,15 @@ PlayerState * CGameState::getPlayer( ui8 color )
 	}
 	else 
 	{
-		tlog2 << "Warning: Cannot find info for player " << int(color) << std::endl;
+		if(verbose)
+			tlog2 << "Warning: Cannot find info for player " << int(color) << std::endl;
 		return NULL;
 	}
 }
 
-const PlayerState * CGameState::getPlayer( ui8 color ) const
+const PlayerState * CGameState::getPlayer( ui8 color, bool verbose ) const
 {
-	return (const_cast<CGameState *>(this))->getPlayer(color);
+	return (const_cast<CGameState *>(this))->getPlayer(color, verbose);
 }
 
 bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret)
@@ -2163,7 +2204,7 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 							node.accessible = CGPathNode::BLOCKVIS;
 							break;
 						}
-						else if(obj->ID != 26) //pathfinder should ignore placed events
+						else if(obj->ID != EVENTI_TYPE) //pathfinder should ignore placed events
 						{
 							node.accessible = CGPathNode::VISITABLE;
 						}

+ 5 - 3
lib/CGameState.h

@@ -11,6 +11,7 @@
 #include <vector>
 #include <list>
 #include "StackFeature.h"
+#include "HeroBonus.h"
 #ifdef _WIN32
 #include <tchar.h>
 #else
@@ -117,6 +118,7 @@ public:
 	std::vector<CGTownInstance *> towns;
 	std::vector<CGHeroInstance *> availableHeroes; //heroes available in taverns
 	std::vector<CGDwelling *> dwellings; //used for town growth
+	BonusList bonuses; //player bonuses
 
 	ui8 status; //0 - in game, 1 - loser, 2 - winner <- uses EStatus enum
 	ui8 daysWithoutCastle;
@@ -126,7 +128,7 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & color & serial & human & currentSelection & fogOfWarMap & resources & status;
-		h & heroes & towns & availableHeroes & dwellings & status & daysWithoutCastle;
+		h & heroes & towns & availableHeroes & dwellings & bonuses & status & daysWithoutCastle;
 
 // 		ui32 size;
 // 		if(h.saving) //write subids of available heroes
@@ -405,8 +407,8 @@ public:
 
 	boost::shared_mutex *mx;
 
-	PlayerState *getPlayer(ui8 color);
-	const PlayerState *getPlayer(ui8 color) const;
+	PlayerState *getPlayer(ui8 color, bool verbose = true);
+	const PlayerState *getPlayer(ui8 color, bool verbose = true) const;
 	void init(StartInfo * si, Mapa * map, int Seed);
 	void loadTownDInfos();
 	void randomizeObject(CGObjectInstance *cur);

+ 73 - 0
lib/HeroBonus.cpp

@@ -0,0 +1,73 @@
+#define VCMI_DLL
+#include "HeroBonus.h"
+
+int BonusList::valOfBonuses( HeroBonus::BonusType type, int subtype /*= -1*/ ) const /*subtype -> subtype of bonus, if -1 then any */
+{
+	if(!this) //to avoid null-checking in maany places -> no bonus list means 0 bonus value
+		return 0;
+
+	int ret = 0;
+	if(subtype == -1)
+	{
+		for(const_iterator i = begin(); i != end(); i++)
+			if(i->type == type)
+				ret += i->val;
+	}
+	else
+	{
+		for(const_iterator i = begin(); i != end(); i++)
+			if(i->type == type && i->subtype == subtype)
+				ret += i->val;
+	}
+	return ret;
+}
+
+bool BonusList::hasBonusOfType( HeroBonus::BonusType type, int subtype /*= -1*/ ) const 
+{
+	if(!this) //to avoid null-checking in maany places -> no bonus list means there is no searched bonus
+		return 0;
+
+	if(subtype == -1) //any subtype
+	{
+		for(const_iterator i = begin(); i != end(); i++)
+			if(i->type == type)
+				return true;
+	}
+	else //given subtype
+	{
+		for(const_iterator i = begin(); i != end(); i++)
+			if(i->type == type && i->subtype == subtype)
+				return true;
+	}
+	return false;
+}
+
+const HeroBonus * BonusList::getBonus( int from, int id ) const
+{
+	if(!this) //to avoid null-checking in maany places -> no bonus list means bonus cannot be retreived
+		return NULL;
+
+	for (const_iterator i = begin(); i != end(); i++)
+		if(i->source == from  &&  i->id == id)
+			return &*i;
+	return NULL;
+}
+
+void BonusList::getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype /*= -1 */ ) const
+{
+	if(!this) //to avoid null-checking in maany places -> no bonus list means nothing has to be done here
+		return;
+
+	if(subtype == -1)
+	{
+		for(const_iterator i = begin(); i != end(); i++)
+			if(i->type == type)
+				out.push_back(std::make_pair(i->val, i->description));
+	}
+	else
+	{
+		for(const_iterator i = begin(); i != end(); i++)
+			if(i->type == type && i->subtype == subtype)
+				out.push_back(std::make_pair(i->val, i->description));
+	}
+}

+ 33 - 0
lib/HeroBonus.h

@@ -1,6 +1,7 @@
 #pragma once
 #include "../global.h"
 #include <string>
+#include <list>
 
 /*
  * HeroBonus.h, part of VCMI engine
@@ -75,6 +76,19 @@ struct DLL_EXPORT HeroBonus
 	{
 		subtype = -1;
 	}
+
+// 	//comparison
+// 	bool operator==(const HeroBonus &other)
+// 	{
+// 		return &other == this;
+// 		//TODO: what is best logic for that?
+// 	}
+// 	bool operator<(const HeroBonus &other)
+// 	{
+// 		return &other < this;
+// 		//TODO: what is best logic for that?
+// 	}
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & duration & type & subtype & source & val & id & description;
@@ -96,4 +110,23 @@ struct DLL_EXPORT HeroBonus
 	{
 		return hb.source==source && (id==0xffffff  ||  hb.id==id);
 	}
+
 };
+
+static const HeroBonus::BonusType MORALE_AFFECTING[] =  {HeroBonus::LUCK, HeroBonus::MORALE_AND_LUCK};
+static const HeroBonus::BonusType LUCK_AFFECTING[] =  {HeroBonus::MORALE, HeroBonus::MORALE_AND_LUCK};
+typedef std::vector<std::pair<int,std::string> > TModDescr; //modifiers values and their descriptions
+
+class BonusList : public std::list<HeroBonus>
+{
+public:
+	int DLL_EXPORT valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
+	bool DLL_EXPORT hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const;
+	const DLL_EXPORT HeroBonus * getBonus( int from, int id ) const;
+	void DLL_EXPORT getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype = -1 ) const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<std::list<HeroBonus>&>(*this);
+	}
+};

+ 5 - 0
lib/IGameCallback.cpp

@@ -256,4 +256,9 @@ inline TerrainTile * IGameCallback::getTile( int3 pos )
 	if(!gs->map->isInTheMap(pos))
 		return NULL;
 	return &gs->map->getTile(pos);
+}
+
+const PlayerState * IGameCallback::getPlayerState( int color )
+{
+	return gs->getPlayer(color, false);
 }

+ 2 - 0
lib/IGameCallback.h

@@ -34,6 +34,7 @@ class CArtHandler;
 class CArtifact;
 class CArmedInstance;
 struct TerrainTile;
+struct PlayerState;
 
 class DLL_EXPORT IGameCallback
 {
@@ -63,6 +64,7 @@ public:
 	virtual void getAllowedSpells(std::vector<ui16> &out, ui16 level);
 	virtual int3 getMapSize(); //returns size of the map
 	virtual TerrainTile * getTile(int3 pos);
+	virtual const PlayerState * getPlayerState(int color);
 
 	//do sth
 	virtual void changeSpells(int hid, bool give, const std::set<ui32> &spells)=0;

+ 39 - 4
lib/NetPacks.h

@@ -357,17 +357,24 @@ struct SetAvailableHeroes : public CPackForClient //113
 
 struct GiveBonus :  public CPackForClient //115
 {
-	GiveBonus(){type = 115;};
+	GiveBonus(ui8 Who = 0)
+	{
+		who = Who; 
+		type = 115;
+	}
+
 	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
-	ui32 hid;
+	enum {HERO, PLAYER};
+	ui8 who; //who receives bonus, uses enum above
+	ui32 id; //hero or player id
 	HeroBonus bonus;
 	MetaString bdescr;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & bonus & hid & bdescr;
+		h & bonus & id & bdescr & who;
 	}
 };
 
@@ -408,6 +415,34 @@ struct PlayerEndsGame : public CPackForClient //117
 };
 
 
+struct RemoveBonus :  public CPackForClient //118
+{
+	RemoveBonus(ui8 Who = 0)
+	{
+		who = Who; 
+		type = 118;
+	}
+
+	void applyCl(CClient *cl);
+	DLL_EXPORT void applyGs(CGameState *gs);
+
+	enum {HERO, PLAYER};
+	ui8 who; //who receives bonus, uses enum above
+	ui32 whoID; //hero or player id
+
+	//vars to identify bonus: its source
+	ui8 source;
+	ui32 id; //source id
+
+	//used locally: copy of removed bonus
+	HeroBonus bonus;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & source & id & who & whoID;
+	}
+};
+
 struct RemoveObject : public CPackForClient //500
 {
 	RemoveObject(){type = 500;};
@@ -573,7 +608,7 @@ struct OpenWindow : public CPackForClient //517
 	OpenWindow(){type = 517;};
 	void applyCl(CClient *cl);
 
-	enum EWindow {EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL, SHIPYARD_WINDOW, THIEVES_GUILD};
+	enum EWindow {EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL, SHIPYARD_WINDOW, THIEVES_GUILD, PUZZLE_MAP};
 	ui8 window;
 	ui32 id1, id2;
 

+ 40 - 5
lib/NetPacksLib.cpp

@@ -186,16 +186,32 @@ DLL_EXPORT void SetAvailableHeroes::applyGs( CGameState *gs )
 
 DLL_EXPORT void GiveBonus::applyGs( CGameState *gs )
 {
-	CGHeroInstance *h = gs->getHero(hid);
-	h->bonuses.push_back(bonus);
-
+	BonusList *bonuses = NULL;
+	switch(who)
+	{
+	case HERO:
+		{
+			CGHeroInstance *h = gs->getHero(id);
+			assert(h);
+			bonuses = &h->bonuses;
+		}
+		break;
+	case PLAYER:
+		{
+			PlayerState *p = gs->getPlayer(id);
+			assert(p);
+			bonuses = &p->bonuses;
+		}
+		break;
+	}
 
-	std::string &descr = h->bonuses.back().description;
+	bonuses->push_back(bonus);
+	std::string &descr = bonuses->back().description;
 
 	if(!bdescr.message.size() 
 		&& bonus.source == HeroBonus::OBJECT 
 		&& (bonus.type == HeroBonus::LUCK || bonus.type == HeroBonus::MORALE || bonus.type == HeroBonus::MORALE_AND_LUCK)
-		&& gs->map->objects[bonus.id]->ID == 26) //it's morale/luck bonus from an event without description
+		&& gs->map->objects[bonus.id]->ID == EVENTI_TYPE) //it's morale/luck bonus from an event without description
 	{
 		descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
 		boost::replace_first(descr,"%d",boost::lexical_cast<std::string>(std::abs(bonus.val)));
@@ -225,6 +241,21 @@ DLL_EXPORT void PlayerEndsGame::applyGs( CGameState *gs )
 	p->status = victory ? 2 : 1;
 }
 
+DLL_EXPORT void RemoveBonus::applyGs( CGameState *gs )
+{
+	std::list<HeroBonus> &bonuses = (who == HERO ? gs->getHero(whoID)->bonuses : gs->getPlayer(whoID)->bonuses);
+
+	for(std::list<HeroBonus>::iterator i = bonuses.begin(); i != bonuses.end(); i++)
+	{
+		if(i->source == source && i->id == id)
+		{
+			bonus = *i; //backup bonus (to show to interfaces later)
+			bonuses.erase(i);
+			break;
+		}
+	}
+}
+
 DLL_EXPORT void RemoveObject::applyGs( CGameState *gs )
 {
 	CGObjectInstance *obj = gs->map->objects[id];
@@ -588,6 +619,10 @@ DLL_EXPORT void NewTurn::applyGs( CGameState *gs )
 			i->second.daysWithoutCastle = 0;
 		else
 			i->second.daysWithoutCastle++;
+
+		i->second.bonuses.remove_if(HeroBonus::OneDay);
+		if(gs->getDate(1) == 7) //new week
+			i->second.bonuses.remove_if(HeroBonus::OneWeek);
 	}
 }
 

+ 3 - 0
lib/RegisterTypes.cpp

@@ -60,6 +60,8 @@ void registerTypes1(Serializer &s)
 	s.template registerType<CGObjectInstance>();
 	s.template registerType<COPWBonus>();
 	s.template registerType<CGDenOfthieves>();
+	s.template registerType<CGObelisk>();
+	s.template registerType<CGLighthouse>();
 }
 
 template<typename Serializer> DLL_EXPORT 
@@ -82,6 +84,7 @@ void registerTypes2(Serializer &s)
 	s.template registerType<GiveBonus>();
 	s.template registerType<ChangeObjPos>();
 	s.template registerType<PlayerEndsGame>();
+	s.template registerType<RemoveBonus>();
 	s.template registerType<RemoveObject>();
 	s.template registerType<TryMoveHero>();
 	s.template registerType<SetGarrisons>();

+ 12 - 0
lib/VCMI_lib.vcproj

@@ -270,6 +270,10 @@
 				RelativePath="..\hch\CBuildingHandler.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\hch\CCampaignHandler.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\CConsoleHandler.cpp"
 				>
@@ -314,6 +318,10 @@
 				RelativePath="..\hch\CTownHandler.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\HeroBonus.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\IGameCallback.cpp"
 				>
@@ -356,6 +364,10 @@
 				RelativePath="..\hch\CBuildingHandler.h"
 				>
 			</File>
+			<File
+				RelativePath="..\hch\CCampaignHandler.h"
+				>
+			</File>
 			<File
 				RelativePath="..\CConsoleHandler.h"
 				>

+ 15 - 3
lib/map.cpp

@@ -529,6 +529,7 @@ void Mapa::addBlockVisTiles(CGObjectInstance * obj)
 	}
 }
 Mapa::Mapa(std::string filename)
+	:grailPos(-1, -1, -1)
 {
 	int mapsize = 0;
 
@@ -1318,7 +1319,7 @@ void Mapa::readDefInfo( const unsigned char * bufor, int &i)
 			vinya->visitDir = 0xff;
 		}
 
-		if(vinya->id == 26)
+		if(vinya->id == EVENTI_TYPE)
 			std::memset(vinya->blockMap,255,6);
 
 		//calculating coverageMap
@@ -1353,7 +1354,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 
 		switch(defInfo->id)
 		{
-		case 26: //for event objects
+		case EVENTI_TYPE: //for event objects
 			{
 				CGEvent *evnt = new CGEvent();
 				nobj = evnt;
@@ -1648,7 +1649,6 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 				i+=3;
 				break;
 			}
-		case 42: //lighthouse
 		case 220://mine (?)
 			{
 				nobj = new CGObjectInstance();
@@ -1911,11 +1911,23 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 				nobj = new CGDenOfthieves();
 				break;
 			}
+		case 57: //Obelisk
+			{
+				nobj = new CGObelisk();
+				break;
+			}
+		case 42: //Lighthouse
+			{
+				nobj = new CGLighthouse();
+				nobj->tempOwner = readNormalNr(bufor,i); i+=4;
+				break;
+			}
 		default: //any other object
 			{
 				nobj = new CGObjectInstance();
 				break;
 			}
+
 		} //end of main switch
 
 		nobj->pos = pos;

+ 2 - 1
lib/map.h

@@ -375,12 +375,13 @@ struct DLL_EXPORT Mapa : public CMapHeader
 			objects.resize(hlp);
 		}
 
-		//static structures
+		//static members
 		h & CGTeleport::objs;
 		h & CGTeleport::gates;
 		h & CGKeys::playerKeyMap;
 		h & CGMagi::eyelist;
 		h & CGPyramid::pyramidConfig;
+		h & CGObelisk::obeliskCount & CGObelisk::visited;
 
 		for(unsigned int i=0; i<objects.size(); i++)
 		{

+ 1 - 1
mapHandler.cpp

@@ -301,7 +301,7 @@ void CMapHandler::initObjectRects()
 }
 static void processDef (CGDefInfo* def)
 {
-	if(def->id == 26) //if it's event, return from function
+	if(def->id == EVENTI_TYPE)
 		return;
 
 	if(!def->handler) //if object has already set handler (eg. heroes) it should not be overwritten

+ 13 - 4
server/CGameHandler.cpp

@@ -856,6 +856,9 @@ void CGameHandler::newTurn()
 				for(std::list<HeroBonus>::iterator  j = h->bonuses.begin(); j != h->bonuses.end(); j++)
 					if(j->type == HeroBonus::GENERATE_RESOURCE)
 						n.res[i->first][j->subtype] += j->val;
+
+				//TODO player bonuses
+
 			}
 		}
 		//n.res.push_back(r);
@@ -1276,7 +1279,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 				{
 					GiveBonus gs;
 					gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::PRIMARY_SKILL, HeroBonus::OBJECT, val, -1, "", i);
-					gs.hid = hero2->id;
+					gs.id = hero2->id;
 					sendAndApply(&gs);
 				}
 			}
@@ -1334,7 +1337,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 
 				GiveBonus gs;
 				gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::MAGIC_SCHOOL_SKILL, HeroBonus::OBJECT, 3, -1, "", bonusSubtype);
-				gs.hid = cHero->id;
+				gs.id = cHero->id;
 
 				sendAndApply(&gs);
 			}
@@ -1393,7 +1396,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 
 				GiveBonus gs;
 				gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL, HeroBonus::OBJECT, 1, -1, "", bonusSubtype);
-				gs.hid = cHero->id;
+				gs.id = cHero->id;
 
 				sendAndApply(&gs);
 			}
@@ -1612,7 +1615,13 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 			//call objects if they are visited
 
 			if(t.visitableObjects.size())
-				objectVisited(t.visitableObjects.back(), h);
+			{
+				//to prevent self-visiting heroes on space press
+				if(t.visitableObjects.back() != h)
+					objectVisited(t.visitableObjects.back(), h);
+				else if(t.visitableObjects.size() > 1)
+					objectVisited(*(t.visitableObjects.end()-2),h);
+			}
 // 			BOOST_FOREACH(CGObjectInstance *obj, t.visitableObjects)
 // 			{
 // 				objectVisited(obj, h);