浏览代码

Fixed #15, #224, #370, #516
Minor changes.

Michał W. Urbańczyk 15 年之前
父节点
当前提交
88e1636250

+ 25 - 0
client/CAdvmapInterface.cpp

@@ -1804,17 +1804,20 @@ void CAdvMapInt::tileLClicked(const int3 &mp)
 		if(currentHero == topBlocking) //clicked selected hero
 		{
 			LOCPLINT->openHeroWindow(currentHero);
+			return;
 		}
 		else if(topBlocking && (topBlocking->ID == HEROI_TYPE || topBlocking->ID == TOWNI_TYPE) //clicked our town or hero
 			&& pn->turns == 255 && topBlocking->tempOwner == LOCPLINT->playerID) //at inaccessible tile
 		{
 			select(static_cast<const CArmedInstance*>(topBlocking), false);
+			return;
 		}
 		else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
 		{
 			if (terrain.currentPath  &&  terrain.currentPath->endPos() == mp)//we'll be moving
 			{
 				LOCPLINT->moveHero(currentHero,*terrain.currentPath);
+				return;
 			}
 			else if(mp.z == currentHero->pos.z) //remove old path and find a new one if we clicked on the map level on which hero is present
 			{
@@ -1822,6 +1825,8 @@ void CAdvMapInt::tileLClicked(const int3 &mp)
 				terrain.currentPath = &path;
 				if(!LOCPLINT->cb->getPath2(mp, path)) //try getting path, erase if failed
 					LOCPLINT->eraseCurrentPathOf(currentHero);
+				else
+					return;
 			}
 		}
 	} //end of hero is selected "case"
@@ -1829,6 +1834,11 @@ void CAdvMapInt::tileLClicked(const int3 &mp)
 	{
 		throw std::string("Nothing is selected...");
 	}
+
+	if(const IShipyard *shipyard = ourInaccessibleShipyard(topBlocking))
+	{
+		LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
+	}
 }
 
 void CAdvMapInt::tileHovered(const int3 &tile)
@@ -2006,6 +2016,11 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 				CGI->curh->changeGraphic(0, 0);
 		}
 	}
+
+	if(const IShipyard *shipyard = ourInaccessibleShipyard(objAtTile))
+	{
+		CGI->curh->changeGraphic(0, 6);
+	}
 }
 
 void CAdvMapInt::tileRClicked(const int3 &mp)
@@ -2075,6 +2090,16 @@ const CGTownInstance * CAdvMapInt::curTown() const
 		return NULL;
 }
 
+const IShipyard * CAdvMapInt::ourInaccessibleShipyard(const CGObjectInstance *obj) const
+{
+	const IShipyard *ret = IShipyard::castFrom(obj);
+
+	if(!ret || obj->tempOwner != player || CGI->curh->mode || (CGI->curh->number != 6 && CGI->curh->number != 0))
+		return NULL;
+
+	return ret;
+}
+
 CAdventureOptions::CAdventureOptions()
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;

+ 3 - 0
client/CAdvmapInterface.h

@@ -14,6 +14,8 @@ class CGHeroInstance;
 class CGTownInstance;
 class CHeroWindow;
 class CSpell;
+class IShipyard;
+
 /*****************************/
 
 /*
@@ -218,6 +220,7 @@ public:
 	void leaveCastingMode(bool cast = false, int3 dest = int3(-1, -1, -1));
 	const CGHeroInstance * curHero() const;
 	const CGTownInstance * curTown() const;
+	const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or NULL else
 };
 
 extern CAdvMapInt *adventureInt;

+ 26 - 8
client/CHeroWindow.cpp

@@ -40,6 +40,24 @@
 
 extern SDL_Surface * screen;
 using namespace boost::assign;
+
+void CHeroSwitcher::clickLeft(tribool down, bool previousState)
+{
+	if(!down)
+	{
+		owner->deactivate();
+		const CGHeroInstance * buf = LOCPLINT->getWHero(id);
+		owner->setHero(buf);
+		owner->redrawCurBack();
+		owner->activate();
+	}
+}
+
+CHeroSwitcher::CHeroSwitcher()
+{
+	used = LCLICK;
+}
+
 CHeroWindow::CHeroWindow(int playerColor):
 	player(playerColor)
 {
@@ -76,7 +94,7 @@ CHeroWindow::CHeroWindow(int playerColor):
 	for(int g=0; g<8; ++g)
 	{
 		//heroList.push_back(new AdventureMapButton<CHeroWindow>(std::string(), std::string(), &CHeroWindow::switchHero, 677, 95+g*54, "hsbtns5.def", this));
-		heroListMi.push_back(new LClickableAreaHero());
+		heroListMi.push_back(new CHeroSwitcher());
 		heroListMi[g]->pos = genRect(32, 48, pos.x+677, pos.y  +  95+g*54);
 		heroListMi[g]->owner = this;
 		heroListMi[g]->id = g;
@@ -94,7 +112,7 @@ CHeroWindow::CHeroWindow(int playerColor):
 		primSkillAreas[v]->pos = genRect(64, 42, pos.x+95 + 70*v, pos.y  +  117);
 		primSkillAreas[v]->text = CGI->generaltexth->arraytxt[2+v];
 		primSkillAreas[v]->type = v;
-		primSkillAreas[v]->bonus = -1; // to be initilized when hero is being set
+		primSkillAreas[v]->bonusValue = -1; // to be initilized when hero is being set
 		primSkillAreas[v]->baseType = 0;
 		sprintf(bufor, CGI->generaltexth->heroscrn[1].c_str(), CGI->generaltexth->primarySkillNames[v].c_str());
 		primSkillAreas[v]->hoverText = std::string(bufor);
@@ -109,10 +127,10 @@ CHeroWindow::CHeroWindow(int playerColor):
 	expArea->pos = genRect(42, 136, pos.x+83, pos.y  +  236);
 	expArea->hoverText = CGI->generaltexth->heroscrn[9];
 
-	morale = new MoraleLuckBox();
+	morale = new MoraleLuckBox(true);
 	morale->pos = genRect(45,53,pos.x+240,pos.y+187);
 
-	luck = new MoraleLuckBox();
+	luck = new MoraleLuckBox(false);
 	luck->pos = genRect(45,53,pos.x+298,pos.y+187);
 
 	spellPointsArea = new LRClickableAreaWText();
@@ -230,7 +248,7 @@ void CHeroWindow::setHero(const CGHeroInstance *hero)
 	//primary skills support
 	for(size_t g=0; g<primSkillAreas.size(); ++g)
 	{
-		primSkillAreas[g]->bonus = hero->getPrimSkillLevel(g);
+		primSkillAreas[g]->bonusValue = hero->getPrimSkillLevel(g);
 	}
 
 	//secondary skills support
@@ -240,7 +258,7 @@ void CHeroWindow::setHero(const CGHeroInstance *hero)
 			level = hero->secSkills[g].second;
 
 		secSkillAreas[g]->type = skill;
-		secSkillAreas[g]->bonus = level;
+		secSkillAreas[g]->bonusValue = level;
 		secSkillAreas[g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
 
 		sprintf(bufor, CGI->generaltexth->heroscrn[21].c_str(), CGI->generaltexth->levels[level-1].c_str(), CGI->generaltexth->skillName[skill].c_str());
@@ -289,8 +307,8 @@ void CHeroWindow::setHero(const CGHeroInstance *hero)
 	formations->select(hero->formation,true);
 	formations->onChange = boost::bind(&CCallback::setFormation, LOCPLINT->cb, hero, _1);
 
-	morale->set(true, hero);
-	luck->set(false, hero);
+	morale->set(hero);
+	luck->set(hero);
 
 	//restoring pos
 	pos.x += 65;

+ 9 - 1
client/CHeroWindow.h

@@ -25,7 +25,15 @@ class LRClickableAreaWText;
 class LRClickableAreaWTextComp;
 class CArtifactsOfHero;
 
+class CHeroSwitcher : public CIntObject
+{
+public:
+	int id;
+	CHeroWindow * owner;
+	virtual void clickLeft(tribool down, bool previousState);
 
+	CHeroSwitcher();
+};
 
 class CHeroWindow: public CWindowWithGarrison
 {
@@ -37,7 +45,7 @@ class CHeroWindow: public CWindowWithGarrison
 
 	//buttons
 	//AdventureMapButton * gar4button; //splitting
-	std::vector<LClickableAreaHero *> heroListMi; //new better list of heroes
+	std::vector<CHeroSwitcher *> heroListMi; //new better list of heroes
 
 	CArtifactsOfHero * artifs;
 

+ 6 - 6
client/CKingdomInterface.cpp

@@ -747,10 +747,10 @@ CKingdomInterface::CHeroItem::CHeroItem(int num, CKingdomInterface * Owner)
 	experience->pos = genRect(33, 49, pos.x+322, pos.y+5);
 	experience->hoverText = CGI->generaltexth->heroscrn[9];
 
-	morale = new MoraleLuckBox();
+	morale = new MoraleLuckBox(true);
 	morale->pos = genRect(20,32,pos.x+221,pos.y+52);
 
-	luck = new MoraleLuckBox();
+	luck = new MoraleLuckBox(false);
 	luck->pos = genRect(20,32,pos.x+221,pos.y+28);
 
 	spellPoints = new LRClickableAreaWText();
@@ -842,7 +842,7 @@ void CKingdomInterface::CHeroItem::setHero(const CGHeroInstance * newHero)
 
 	//primary skills
 	for(size_t g=0; g<primarySkills.size(); ++g)
-		primarySkills[g]->bonus = hero->getPrimSkillLevel(g);
+		primarySkills[g]->bonusValue = hero->getPrimSkillLevel(g);
 
 	//secondary skills
 	for(size_t g=0; g<std::min(secondarySkills.size(),hero->secSkills.size()); ++g)
@@ -850,7 +850,7 @@ void CKingdomInterface::CHeroItem::setHero(const CGHeroInstance * newHero)
 		int skill = hero->secSkills[g].first,
 		    level = hero->secSkills[g].second;
 		secondarySkills[g]->type = skill;
-		secondarySkills[g]->bonus = level;
+		secondarySkills[g]->bonusValue = level;
 		secondarySkills[g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
 		sprintf(bufor, CGI->generaltexth->heroscrn[21].c_str(), CGI->generaltexth->levels[level-1].c_str(), CGI->generaltexth->skillName[skill].c_str());
 		secondarySkills[g]->hoverText = std::string(bufor);
@@ -866,8 +866,8 @@ void CKingdomInterface::CHeroItem::setHero(const CGHeroInstance * newHero)
 	spellPoints->text = std::string(bufor);
 
 	//setting morale and luck
-	morale->set(true,hero);
-	luck->set(false,hero);
+	morale->set(hero);
+	luck->set(hero);
 }
 
 void CKingdomInterface::CHeroItem::scrollArts(int move)

+ 12 - 0
client/CPlayerInterface.cpp

@@ -2001,4 +2001,16 @@ void CPlayerInterface::showTavernWindow(const CGObjectInstance *townOrTavern)
 {
 	CTavernWindow *tv = new CTavernWindow(townOrTavern);
 	GH.pushInt(tv);
+}
+
+void CPlayerInterface::showShipyardDialogOrProblemPopup(const IShipyard *obj)
+{
+	if(obj->state())
+	{
+		MetaString txt;
+		obj->getProblemText(txt);
+		showInfoDialog(txt.toString());
+	}
+	else
+		showShipyardDialog(obj);
 }

+ 1 - 0
client/CPlayerInterface.h

@@ -227,6 +227,7 @@ public:
 	CGPath *getAndVerifyPath( const CGHeroInstance * h );
 	void acceptTurn(); //used during hot seat after your turn message is close
 	void tryDiggging(const CGHeroInstance *h);
+	void showShipyardDialogOrProblemPopup(const IShipyard *obj); //obj may be town or shipyard; 
 
 	CPlayerInterface(int Player, int serial);//c-tor
 	~CPlayerInterface();//d-tor

+ 67 - 121
client/GUIClasses.cpp

@@ -2329,6 +2329,16 @@ void CCreInfoWindow::init(const CCreature *cre, const CStackInstance *stack, int
 	printLine(4, CGI->generaltexth->allTexts[388], cre->valOfBonuses(Bonus::STACK_HEALTH), finalNode->valOfBonuses(Bonus::STACK_HEALTH));
 	printLine(6, CGI->generaltexth->zelp[441].first, cre->valOfBonuses(Bonus::STACKS_SPEED), finalNode->valOfBonuses(Bonus::STACKS_SPEED));
 
+
+	//setting morale
+	morale = new MoraleLuckBox(true);
+	morale->pos = genRect(42, 42, pos.x + 24, pos.y + 189);
+	morale->set(stack);
+	//setting luck
+	luck = new MoraleLuckBox(false);
+	luck->pos =  genRect(42, 42, pos.x + 77, pos.y + 189);
+	luck->set(stack);
+
 	//luck and morale
 	int luck = 3, morale = 3;
 	if(stack)
@@ -4156,109 +4166,43 @@ void HoverableArea::hover (bool on)
 		GH.statusbar->clear();
 }
 
-void HoverableArea::activate()
+HoverableArea::HoverableArea()
 {
-	activateHover();
+	used |= HOVER;
 }
 
-void HoverableArea::deactivate()
+HoverableArea::~HoverableArea()
 {
-	deactivateHover();
-}
-
-void LClickableArea::activate()
-{
-	activateLClick();
-}
-void LClickableArea::deactivate()
-{
-	deactivateLClick();
-}
-void LClickableArea::clickLeft(tribool down, bool previousState)
-{
-	//if(!down)
-	//{
-	//	LOCPLINT->showInfoDialog("TEST TEST AAA", std::vector<SComponent*>());
-	//}
-}
-
-void RClickableArea::activate()
-{
-	activateRClick();
-}
-void RClickableArea::deactivate()
-{
-	deactivateRClick();
-}
-void RClickableArea::clickRight(tribool down, bool previousState)
-{
-	//if(!down)
-	//{
-	//	LOCPLINT->showInfoDialog("TEST TEST AAA", std::vector<SComponent*>());
-	//}
 }
 
 void LRClickableAreaWText::clickLeft(tribool down, bool previousState)
 {
 	if(!down && previousState)
 	{
-		LOCPLINT->showInfoDialog(text, std::vector<SComponent*>(), soundBase::sound_todo);
+		LOCPLINT->showInfoDialog(text);
 	}
-	//ClickableL::clickLeft(down);
 }
 void LRClickableAreaWText::clickRight(tribool down, bool previousState)
 {
 	adventureInt->handleRightClick(text, down);
 }
-void LRClickableAreaWText::activate()
-{
-	LClickableArea::activate();
-	RClickableArea::activate();
-	activateHover();
-}
-void LRClickableAreaWText::deactivate()
+
+LRClickableAreaWText::LRClickableAreaWText()
 {
-	LClickableArea::deactivate();
-	RClickableArea::deactivate();
-	deactivateHover();
+	used = LCLICK | RCLICK | HOVER;
 }
 
-void LClickableAreaHero::clickLeft(tribool down, bool previousState)
+LRClickableAreaWText::~LRClickableAreaWText()
 {
-	if(!down)
-	{
-		owner->deactivate();
-		const CGHeroInstance * buf = LOCPLINT->getWHero(id);
-		owner->setHero(buf);
-		owner->redrawCurBack();
-		owner->activate();
-	}
 }
 
 void LRClickableAreaWTextComp::clickLeft(tribool down, bool previousState)
 {
 	if((!down) && previousState)
 	{
-		std::vector<SComponent*> comp(1, new SComponent(SComponent::Etype(baseType), type, bonus));
-		LOCPLINT->showInfoDialog(text, comp, soundBase::sound_todo);
+		std::vector<SComponent*> comp(1, new SComponent(SComponent::Etype(baseType), type, bonusValue));
+		LOCPLINT->showInfoDialog(text, comp);
 	}
-	//ClickableL::clickLeft(down);
-}
-void LRClickableAreaWTextComp::clickRight(tribool down, bool previousState)
-{
-	adventureInt->handleRightClick(text, down);
-}
-void LRClickableAreaWTextComp::activate()
-{
-	LClickableArea::activate();
-	RClickableArea::activate();
-	activateHover();
-}
-void LRClickableAreaWTextComp::deactivate()
-{
-	LClickableArea::deactivate();
-	RClickableArea::deactivate();
-	deactivateHover();
 }
 
 void LRClickableAreaOpenHero::clickLeft(tribool down, bool previousState)
@@ -4829,7 +4773,7 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) : bg(NULL)
 		primSkillAreas[g]->pos = genRect(32, 140, pos.x+329, pos.y + 19 + 36 * g);
 		primSkillAreas[g]->text = CGI->generaltexth->arraytxt[2+g];
 		primSkillAreas[g]->type = g;
-		primSkillAreas[g]->bonus = -1;
+		primSkillAreas[g]->bonusValue = -1;
 		primSkillAreas[g]->baseType = 0;
 		sprintf(bufor, CGI->generaltexth->heroscrn[1].c_str(), CGI->generaltexth->primarySkillNames[g].c_str());
 		primSkillAreas[g]->hoverText = std::string(bufor);
@@ -4848,7 +4792,7 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) : bg(NULL)
 			secSkillAreas[b][g]->baseType = 1;
 
 			secSkillAreas[b][g]->type = skill;
-			secSkillAreas[b][g]->bonus = level;
+			secSkillAreas[b][g]->bonusValue = level;
 			secSkillAreas[b][g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
 
 			sprintf(bufor, CGI->generaltexth->heroscrn[21].c_str(), CGI->generaltexth->levels[level - 1].c_str(), CGI->generaltexth->skillName[skill].c_str());
@@ -4881,13 +4825,13 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) : bg(NULL)
 		spellPoints[b]->text = std::string(bufor);
 
 		//setting morale
-		morale[b] = new MoraleLuckBox();
+		morale[b] = new MoraleLuckBox(true);
 		morale[b]->pos = genRect(32, 32, pos.x + 177 + 490*b, pos.y + 45);
-		morale[b]->set(true,heroInst[b]);
+		morale[b]->set(heroInst[b]);
 		//setting luck
-		luck[b] = new MoraleLuckBox();
+		luck[b] = new MoraleLuckBox(false);
 		luck[b]->pos = genRect(32, 32, pos.x + 213 + 490*b, pos.y + 45);
-		luck[b]->set(false,heroInst[b]);
+		luck[b]->set(heroInst[b]);
 	}
 
 	//buttons
@@ -5457,50 +5401,47 @@ CThievesGuildWindow::~CThievesGuildWindow()
 // 	delete resdatabar;
 }
 
+void MoraleLuckBox::set(const CBonusSystemNode *hero)
+{
+	const int textId[] = {62, 88}; //eg %s \n\n\n {Current Luck Modifiers:}
+	const int noneTxtId[] = {77, 108}; //I don't know why we have separate "none" texts for luck and morale...
+	const int neutralDescr[] = {60, 86}; //eg {Neutral Morale} \n\n Neutral morale means your armies will neither be blessed with extra attacks or freeze in combat.
+	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 mrlt = -9;
+	TModDescr mrl;
+	hero->getModifiersWDescr(mrl, bonusType[morale]);
+	bonusValue = (hero->*getValue[morale])();
+	mrlt = (bonusValue>0)-(bonusValue<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
+	hoverText = CGI->generaltexth->heroscrn[hoverTextBase[morale] - mrlt];
+	baseType = componentType[morale];
+	text = CGI->generaltexth->arraytxt[textId[morale]];
+	boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[neutralDescr[morale]-mrlt]);
+	if (!mrl.size())
+		text += CGI->generaltexth->arraytxt[noneTxtId[morale]];
+	else
+		for(int it=0; it < mrl.size(); it++)
+			text += "\n" + mrl[it].second;
+}
 
+void MoraleLuckBox::showAll(SDL_Surface * to)
+{
+	CDefEssential *def = morale ? graphics->morale42 : graphics->luck42;
+	blitAt(def->ourImages[bonusValue].bitmap, pos, to);
+}
 
+MoraleLuckBox::MoraleLuckBox(bool Morale)
+	:morale(Morale)
+{
+	bonusValue = 0;
+}
 
-
-void MoraleLuckBox::set(bool morale, const CGHeroInstance *hero)
+MoraleLuckBox::~MoraleLuckBox()
 {
-	int mrlv = -9, mrlt = -9;
-	TModDescr mrl;
 
-	if(morale)
-	{
-		//setting morale
-		hero->getModifiersWDescr(mrl, Bonus::MORALE);
-		mrlv = hero->MoraleVal();
-		mrlt = (mrlv>0)-(mrlv<0); //signum: -1 - bad morale, 0 - neutral, 1 - good
-		hoverText = CGI->generaltexth->heroscrn[4 - mrlt];
-		baseType = SComponent::morale;
-		bonus = mrlv;
-		text = CGI->generaltexth->arraytxt[88];
-		boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[86-mrlt]);
-		if (!mrl.size())
-			text += CGI->generaltexth->arraytxt[108];
-		else
-			for(int it=0; it < mrl.size(); it++)
-				text += "\n" + mrl[it].second;
-	}
-	else
-	{
-		//setting luck
-		hero->getModifiersWDescr(mrl, Bonus::LUCK);
-		mrlv = hero->LuckVal();
-		mrlt = (mrlv>0)-(mrlv<0); //signum: -1 - bad luck, 0 - neutral, 1 - good
-		hoverText = CGI->generaltexth->heroscrn[7 - mrlt];
-		baseType = SComponent::luck;
-		bonus = mrlv;
-		text = CGI->generaltexth->arraytxt[62];
-		boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[60-mrlt]);
-		if (!mrl.size())
-			text += CGI->generaltexth->arraytxt[77];
-		else
-			for(int it=0; it < mrl.size(); it++)
-				text += "\n" + mrl[it].second;
-	}
 }
 
 void CLabel::showAll(SDL_Surface * to)
@@ -5571,6 +5512,8 @@ void CTextBox::showAll(SDL_Surface * to)
 	for (int i = 0; i < howManyLinesToPrint; i++)
 	{
 		const std::string &line = lines[i + firstLineToPrint];
+		if(!line.size()) continue;
+
 		int x = pos.x;
 		if(alignment == CENTER)
 		{
@@ -5579,7 +5522,10 @@ void CTextBox::showAll(SDL_Surface * to)
 				x -= slider->pos.w / 2 + 5;
 		}
 
-		printAt(line, x, base_y + i*dy, font, color, to);
+		if(line.front() == '{' && line.back() == '}')
+			printAt(line, x, base_y + i*dy, font, tytulowy, to);
+		else
+			printAt(line, x, base_y + i*dy, font, color, to);
 	}
 
 }

+ 16 - 35
client/GUIClasses.h

@@ -714,62 +714,43 @@ class HoverableArea: public virtual CIntObject
 {
 public:
 	std::string hoverText;
+
 	virtual void hover (bool on);
-	virtual void activate();
-	virtual void deactivate();
-};
 
-class LClickableArea: public virtual CIntObject
-{
-public:
-	virtual void clickLeft(tribool down, bool previousState);
-	virtual void activate();
-	virtual void deactivate();
+	HoverableArea();
+	virtual ~HoverableArea();
 };
 
-class RClickableArea: public virtual CIntObject
+class LRClickableAreaWText: public HoverableArea
 {
 public:
-	virtual void clickRight(tribool down, bool previousState);
-	virtual void activate();
-	virtual void deactivate();
-};
+	std::string text;
 
-class LClickableAreaHero : public LClickableArea
-{
-public:
-	int id;
-	CHeroWindow * owner;
-	virtual void clickLeft(tribool down, bool previousState);
-};
+	LRClickableAreaWText();
+	virtual ~LRClickableAreaWText();
 
-class LRClickableAreaWText: public LClickableArea, public RClickableArea, public HoverableArea
-{
-public:
-	std::string text;
-	virtual void activate();
-	virtual void deactivate();
 	virtual void clickLeft(tribool down, bool previousState);
 	virtual void clickRight(tribool down, bool previousState);
 };
 
-class LRClickableAreaWTextComp: public LClickableArea, public RClickableArea, public HoverableArea
+class LRClickableAreaWTextComp: public LRClickableAreaWText
 {
 public:
-	std::string text;
 	int baseType;
-	int bonus, type;
-	virtual void activate();
-	virtual void deactivate();
+	int bonusValue, type;
 	virtual void clickLeft(tribool down, bool previousState);
-	virtual void clickRight(tribool down, bool previousState);
 };
 
 class MoraleLuckBox : public LRClickableAreaWTextComp
 {
 public:
+	bool morale; //true if morale, false if luck
 	
-	void set(bool morale, const CGHeroInstance *hero);
+	void set(const CBonusSystemNode*hero);
+	void showAll(SDL_Surface * to);
+
+	MoraleLuckBox(bool Morale);
+	~MoraleLuckBox();
 };
 
 class LRClickableAreaOpenHero: public LRClickableAreaWTextComp
@@ -803,7 +784,7 @@ public:
 	const CCreature *c; //related creature
 	std::vector<SComponent*> upgResCost; //cost of upgrade (if not possible then empty)
 
-	//MoraleLuckBox *luck, *morale;
+	MoraleLuckBox *luck, *morale;
 
 	AdventureMapButton *dismiss, *upgrade, *ok;
 	CCreInfoWindow(const CStackInstance &st, int Type = 0, boost::function<void()> Upg = 0, boost::function<void()> Dsm = 0, UpgradeInfo *ui = NULL); //c-tor

+ 35 - 18
hch/CObjectHandler.cpp

@@ -5901,7 +5901,7 @@ int3 IBoatGenerator::bestLocation() const
 	{
 		if (tile = IObjectInterface::cb->getTile(o->pos + offsets[i])) //tile is in the map 
 		{
-			if (tile->tertype == TerrainTile::water) //and is water
+			if (tile->tertype == TerrainTile::water  &&  (!tile->blocked || tile->blockingObjects.front()->ID == 8)) //and is water and is not blocked or is blocked by boat
 				return o->pos + offsets[i];
 		}
 	}
@@ -5913,7 +5913,7 @@ int IBoatGenerator::state() const
 	int3 tile = bestLocation();
 	TerrainTile *t = IObjectInterface::cb->getTile(tile);
 	if(!t)
-		return 3; //no water
+		return 2; //no available water
 	else if(!t->blockingObjects.size())
 		return 0; //OK
 	else if(t->blockingObjects.front()->ID == 8)
@@ -5934,6 +5934,28 @@ IBoatGenerator::IBoatGenerator(const CGObjectInstance *O)
 {
 }
 
+void IBoatGenerator::getProblemText(MetaString &out, const CGHeroInstance *visitor) const
+{
+	switch(state())
+	{
+	case 1: 
+		out.addTxt(MetaString::GENERAL_TXT, 51);
+		break;
+	case 2:
+		if(visitor)
+		{
+			out.addTxt(MetaString::GENERAL_TXT, 134);
+			out.addReplacement(visitor->name);
+		}
+		else
+			out.addTxt(MetaString::ADVOB_TXT, 189);
+		break;
+	case 3:
+		tlog1 << "Shipyard without water!!! " << o->pos << "\t" << o->id << std::endl;
+		return;
+	}
+}
+
 void IShipyard::getBoatCost( std::vector<si32> &cost ) const
 {
 	cost.resize(RESOURCE_QUANTITY);
@@ -5948,6 +5970,9 @@ IShipyard::IShipyard(const CGObjectInstance *O)
 
 IShipyard * IShipyard::castFrom( CGObjectInstance *obj )
 {
+	if(!obj)
+		return NULL;
+
 	if(obj->ID == TOWNI_TYPE)
 	{
 		return static_cast<CGTownInstance*>(obj);
@@ -5958,7 +5983,7 @@ IShipyard * IShipyard::castFrom( CGObjectInstance *obj )
 	}
 	else
 	{
-		tlog1 << "Cannot cast to IShipyard object with ID " << obj->ID << std::endl;
+		//tlog1 << "Cannot cast to IShipyard object with ID " << obj->ID << std::endl;
 		return NULL;
 	}
 }
@@ -5975,8 +6000,12 @@ CGShipyard::CGShipyard()
 
 void CGShipyard::getOutOffsets( std::vector<int3> &offsets ) const
 {
-	offsets += int3(1,0,0), int3(-3,0,0), int3(-3,1,0), int3(-2,1,0), int3(1,1,0), int3(1,-1,0), int3(-3,-1,0), 
-		int3(-2,-1,0), int3(0,-1,0), int3(-1,-1,0), int3(0,1,0), int3(-1,1,0);
+	// H J L K I
+	// A x S x B
+	// C E G F D
+	offsets += int3(-3,0,0), int3(1,0,0), //AB
+		int3(-3,1,0), int3(1,1,0), int3(-2,1,0), int3(0,1,0), int3(-1,1,0), //CDEFG
+		int3(-3,-1,0), int3(1,-1,0), int3(-2,-1,0), int3(0,-1,0), int3(-1,-1,0); //HIJKL
 }
 
 void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
@@ -5989,19 +6018,7 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
 	{
 		InfoWindow iw;
 		iw.player = tempOwner;
-		switch(s)
-		{
-		case 1: 
-			iw.text.addTxt(MetaString::GENERAL_TXT, 51);
-			break;
-		case 2:
-			iw.text.addTxt(MetaString::ADVOB_TXT, 189);
-			break;
-		case 3:
-			tlog1 << "Shipyard without water!!! " << pos << "\t" << id << std::endl;
-			return;
-		}
-
+		getProblemText(iw.text, h);
 		cb->showInfoDialog(&iw);
 	}
 	else

+ 2 - 0
hch/CObjectHandler.h

@@ -25,6 +25,7 @@
  *
  */
 
+struct MetaString;
 class BattleInfo;
 class IGameCallback;
 struct BattleResult;
@@ -129,6 +130,7 @@ public:
 	virtual void getOutOffsets(std::vector<int3> &offsets) const =0; //offsets to obj pos when we boat can be placed
 	int3 bestLocation() const; //returns location when the boat should be placed
 	int state() const; //0 - can buid, 1 - there is already a boat at dest tile, 2 - dest tile is blocked, 3 - no water
+	void getProblemText(MetaString &out, const CGHeroInstance *visitor = NULL) const; 
 };
 
 class DLL_EXPORT IShipyard : public IBoatGenerator

+ 7 - 0
lib/CGameState.cpp

@@ -276,6 +276,13 @@ DLL_EXPORT void MetaString::toString(std::string &dst) const
 	}
 }
 
+DLL_EXPORT std::string MetaString::toString() const
+{
+	std::string ret;
+	toString(ret);
+	return ret;
+}
+
 DLL_EXPORT std::string MetaString::buildList () const
 ///used to handle loot from creature bank
 {

+ 31 - 1
lib/HeroBonus.cpp

@@ -7,6 +7,7 @@
 #include "../hch/CCreatureHandler.h"
 #include <boost/assign/list_of.hpp>
 #include "CCreatureSet.h"
+#include <boost/algorithm/string/trim.hpp>
 
 #define FOREACH_CONST_PARENT(pname, source) 	TCNodes parents; getParents(parents, source); BOOST_FOREACH(const CBonusSystemNode *pname, parents)
 #define FOREACH_PARENT(pname, source) 	TNodes parents; getParents(parents, source); BOOST_FOREACH(CBonusSystemNode *pname, parents)
@@ -337,12 +338,41 @@ std::string Bonus::Description() const
 	return str.str();
 }
 
+Bonus::Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype/*=-1*/) 
+	: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), id(ID), description(Desc)
+{
+	additionalInfo = -1;
+	turnsRemain = 0;
+	valType = ADDITIVE_VALUE;
+	effectRange = NO_LIMIT;
+	limiter = NULL;
+	boost::algorithm::trim(description);
+}
+
+Bonus::Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype/*=-1*/, ui8 ValType /*= ADDITIVE_VALUE*/) 
+	: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), id(ID), valType(ValType)
+{
+	additionalInfo = -1;
+	turnsRemain = 0;
+	effectRange = NO_LIMIT;
+	limiter = NULL;
+}
+
+Bonus::Bonus()
+{
+	subtype = -1;
+	additionalInfo = -1;
+	turnsRemain = 0;
+	valType = ADDITIVE_VALUE;
+	effectRange = NO_LIMIT;
+	limiter = NULL;
+}
+
 CSelector DLL_EXPORT operator&&(const CSelector &first, const CSelector &second)
 {
 	return CSelectorsConjunction(first, second);
 }
 
-
 namespace Selector
 {
 	DLL_EXPORT CSelectFieldEqual<TBonusType> type(&Bonus::type, 0);

+ 3 - 26
lib/HeroBonus.h

@@ -227,32 +227,9 @@ struct DLL_EXPORT Bonus
 
 	std::string description; 
 
-	Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1)
-		:duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), id(ID), description(Desc) 
-	{
-		additionalInfo = -1;
-		turnsRemain = 0;
-		valType = ADDITIVE_VALUE;
-		effectRange = NO_LIMIT;
-		limiter = NULL;
-	}
-	Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype=-1, ui8 ValType = ADDITIVE_VALUE)
-		:duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), id(ID), valType(ValType)
-	{
-		additionalInfo = -1;
-		turnsRemain = 0;
-		effectRange = NO_LIMIT;
-		limiter = NULL;
-	}
-	Bonus()
-	{
-		subtype = -1;
-		additionalInfo = -1;
-		turnsRemain = 0;
-		valType = ADDITIVE_VALUE;
-		effectRange = NO_LIMIT;
-		limiter = NULL;
-	}
+	Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1);
+	Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype=-1, ui8 ValType = ADDITIVE_VALUE);
+	Bonus();
 
 // 	//comparison
 // 	bool operator==(const HeroBonus &other)

+ 1 - 0
lib/NetPacks.h

@@ -136,6 +136,7 @@ public:
 		numbers.clear();
 	}
 	DLL_EXPORT void toString(std::string &dst) const;
+	DLL_EXPORT std::string toString() const;
 	void getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const;
 
 	MetaString()