瀏覽代碼

Rewritten code handling growth to eliminate duplication and make it conformant to OH3 mechanics. Proper support for Statue of Legion.
Displaying shield over minimap during AI turn.

Michał W. Urbańczyk 14 年之前
父節點
當前提交
18444fc72f

+ 6 - 4
ChangeLog

@@ -1,11 +1,13 @@
-0.85 -> 0.86 (Aug 01 2011)
+0.85 -> 0.86 (Sep 01 2011?)
 GENERAL:
+* Reinstated music support
+* Bonus system optimizations (caching)
+* converted many config files to JSON
+* .tga file support
 * New artifacts supported
 - Admiral's Hat
+- Statue of Legion
 - Titan's Thunder
-* vERM parser & interpreter
-* Bonus caching system
-* .tga file support
 
 BATTLES:
 * Correct handling of siege obstacles

+ 47 - 52
client/CAdvmapInterface.cpp

@@ -58,6 +58,8 @@ CAdvMapInt *adventureInt;
 
 CMinimap::CMinimap(bool draw)
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	used = LCLICK | RCLICK | HOVER;
 	int3 mapSizes = LOCPLINT->cb->getMapSize();
 	statusbarTxt = CGI->generaltexth->zelp[291].first;
 	rcText = CGI->generaltexth->zelp[291].second;
@@ -67,6 +69,7 @@ CMinimap::CMinimap(bool draw)
 	pos.w=ADVOPT.minimapH;
 
 	temps = newSurface(pos.w,pos.h);
+	aiShield = new CPicture("AISHIELD.bmp");
 
 	const JsonNode config(DATA_DIR "/config/minimap.json");
 	const JsonVector &minimap_vec = config["MinimapColors"].Vector();
@@ -114,48 +117,55 @@ CMinimap::~CMinimap()
 
 void CMinimap::draw(SDL_Surface * to)
 {
-	int3 mapSizes = LOCPLINT->cb->getMapSize();
-	//draw terrain
-	blitAt(map[adventureInt->position.z],0,0,temps);
+	if(LOCPLINT->makingTurn)
+	{
+		int3 mapSizes = LOCPLINT->cb->getMapSize();
+		//draw terrain
+		blitAt(map[adventureInt->position.z],0,0,temps);
 
-	//draw heroes
-	std::vector <const CGHeroInstance *> hh = LOCPLINT->cb->getHeroesInfo(false);
-	int mw = map[0]->w, mh = map[0]->h,
-		wo = mw/mapSizes.x, ho = mh/mapSizes.y;
+		//draw heroes
+		std::vector <const CGHeroInstance *> hh = LOCPLINT->cb->getHeroesInfo(false);
+		int mw = map[0]->w, mh = map[0]->h,
+			wo = mw/mapSizes.x, ho = mh/mapSizes.y;
 
-	for (size_t i=0; i < hh.size(); ++i)
-	{
-		int3 hpos = hh[i]->getPosition(false);
-		if(hpos.z!=adventureInt->position.z)
-			continue;
-		//float zawx = ((float)hpos.x/CGI->mh->sizes.x), zawy = ((float)hpos.y/CGI->mh->sizes.y);
-		int3 maplgp ( (hpos.x*mw)/mapSizes.x, (hpos.y*mh)/mapSizes.y, hpos.z );
-		for (int ii=0; ii<wo; ii++)
+		for (size_t i=0; i < hh.size(); ++i)
 		{
-			for (int jj=0; jj<ho; jj++)
+			int3 hpos = hh[i]->getPosition(false);
+			if(hpos.z!=adventureInt->position.z)
+				continue;
+			//float zawx = ((float)hpos.x/CGI->mh->sizes.x), zawy = ((float)hpos.y/CGI->mh->sizes.y);
+			int3 maplgp ( (hpos.x*mw)/mapSizes.x, (hpos.y*mh)/mapSizes.y, hpos.z );
+			for (int ii=0; ii<wo; ii++)
 			{
-				SDL_PutPixelWithoutRefresh(temps,maplgp.x+ii,maplgp.y+jj,graphics->playerColors[hh[i]->getOwner()].r,
+				for (int jj=0; jj<ho; jj++)
+				{
+					SDL_PutPixelWithoutRefresh(temps,maplgp.x+ii,maplgp.y+jj,graphics->playerColors[hh[i]->getOwner()].r,
 						graphics->playerColors[hh[i]->getOwner()].g,graphics->playerColors[hh[i]->getOwner()].b);
+				}
 			}
 		}
-	}
 
-	blitAt(flObjs[adventureInt->position.z],0,0,temps);
+		blitAt(flObjs[adventureInt->position.z],0,0,temps);
 
-	blitAt(FoW[adventureInt->position.z],0,0,temps);
+		blitAt(FoW[adventureInt->position.z],0,0,temps);
 
-	//draw radar
-	const int tilesw=(ADVOPT.advmapW+31)/32;
-	const int tilesh=(ADVOPT.advmapH+31)/32;
-	int bx = (((float)adventureInt->position.x)/(((float)mapSizes.x)))*pos.w,
-		by = (((float)adventureInt->position.y)/(((float)mapSizes.y)))*pos.h,
-		rx = (((float)tilesw)/(mapSizes.x))*((float)pos.w), //width
-		ry = (((float)tilesh)/(mapSizes.y))*((float)pos.h); //height
+		//draw radar
+		const int tilesw=(ADVOPT.advmapW+31)/32;
+		const int tilesh=(ADVOPT.advmapH+31)/32;
+		int bx = (((float)adventureInt->position.x)/(((float)mapSizes.x)))*pos.w,
+			by = (((float)adventureInt->position.y)/(((float)mapSizes.y)))*pos.h,
+			rx = (((float)tilesw)/(mapSizes.x))*((float)pos.w), //width
+			ry = (((float)tilesh)/(mapSizes.y))*((float)pos.h); //height
 
-	CSDL_Ext::drawDashedBorder(temps, Rect(bx, by, rx, ry), int3(255,75,125));
+		CSDL_Ext::drawDashedBorder(temps, Rect(bx, by, rx, ry), int3(255,75,125));
 
-	//blitAt(radar,bx,by,temps);
-	blitAt(temps,pos.x,pos.y,to);
+		//blitAt(radar,bx,by,temps);
+		blitAt(temps,pos.x,pos.y,to);
+	}
+	else
+	{
+		aiShield->showAll(to);
+	}
 }
 void CMinimap::redraw(int level)// (level==-1) => redraw all levels
 {
@@ -275,13 +285,11 @@ void CMinimap::clickRight(tribool down, bool previousState)
 
 void CMinimap::clickLeft(tribool down, bool previousState)
 {
-	if (down && (!previousState))
-		activateMouseMove();
-	else if (!down)
-	{
-		if (std::find(GH.motioninterested.begin(),GH.motioninterested.end(),this)!=GH.motioninterested.end())
-			deactivateMouseMove();
-	}
+	if (down && !(used & MOVE))
+		changeUsedEvents(MOVE, true);
+	else if (!down  &&  used & MOVE)
+		changeUsedEvents(MOVE, false);
+
 	//ClickableL::clickLeft(down);
 	if (!((bool)down))
 		return;
@@ -314,20 +322,12 @@ void CMinimap::mouseMoved (const SDL_MouseMotionEvent & sEvent)
 }
 void CMinimap::activate()
 {
-	activateLClick();
-	activateRClick();
-	activateHover();
-	if (pressedL)
-		activateMouseMove();
+	CIntObject::activate();
 }
 
 void CMinimap::deactivate()
 {
-	if (pressedL)
-		deactivateMouseMove();
-	deactivateLClick();
-	deactivateRClick();
-	deactivateHover();
+	CIntObject::deactivate();
 }
 
 void CMinimap::showTile(const int3 &pos)
@@ -439,11 +439,6 @@ void CMinimap::hideTile(const int3 &pos)
 	}
 }
 
-void CMinimap::show( SDL_Surface * to )
-{
-
-}
-
 CTerrainRect::CTerrainRect()
 	:curHoveredTile(-1,-1,-1), currentPath(NULL)
 {

+ 1 - 1
client/CAdvmapInterface.h

@@ -44,6 +44,7 @@ public:
 class CMinimap : public CIntObject
 {
 public:
+	CPicture *aiShield; //the graphic displayed during AI turn
 	SDL_Surface * temps;
 	std::map<int,SDL_Color> colors;
 	std::map<int,SDL_Color> colorsBlocked;
@@ -52,7 +53,6 @@ public:
 
 	CMinimap(bool draw=true);
 	~CMinimap();
-	void show(SDL_Surface * to);
 	void draw(SDL_Surface * to);
 	void redraw(int level=-1);// (level==-1) => redraw all levels
 	void initMap(int level=-1);// (level==-1) => redraw all levels

+ 6 - 67
client/CCastleInterface.cpp

@@ -5,6 +5,7 @@
 #include <boost/assign/std/vector.hpp>
 #include <boost/format.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
 
 #include "CCastleInterface.h"
 #include "../CCallback.h"
@@ -1130,76 +1131,14 @@ int CCreaInfo::AddToString(std::string from, std::string & to, int numb)
 
 std::string CCreaInfo::genGrowthText()
 {
-	int summ=0;
-	std::string descr=CGI->generaltexth->allTexts[589];//Growth of creature is number
-	boost::algorithm::replace_first(descr,"%s", creature->nameSing);
-	boost::algorithm::replace_first(descr,"%d", boost::lexical_cast<std::string>(town->creatureGrowth(level)));
+	GrowthInfo gi = town->getGrowthInfo(level);
+	std::string descr = boost::str(boost::format(CGI->generaltexth->allTexts[589]) % creature->nameSing % gi.totalGrowth());
 
-	descr +="\n"+CGI->generaltexth->allTexts[590];
-	summ = creature->growth;
-	boost::algorithm::replace_first(descr,"%d", boost::lexical_cast<std::string>(summ));
-
-	if ( level>=0 && level<CREATURES_PER_TOWN)
+	BOOST_FOREACH(const GrowthInfo::Entry &entry, gi.entries)
 	{
-
-		if ( vstd::contains(town->builtBuildings, Buildings::CASTLE))
-			summ+=AddToString(CGI->buildh->buildings[town->subID][Buildings::CASTLE]->Name()+" %+d",descr,summ);
-		else if ( vstd::contains(town->builtBuildings, Buildings::CITADEL))
-			summ+=AddToString(CGI->buildh->buildings[town->subID][Buildings::CITADEL]->Name()+" %+d",descr,summ/2);
-
-		summ+=AddToString(CGI->generaltexth->allTexts[63] + " %+d",descr, //double growth or plague
-			summ * creature->valOfBonuses(Bonus::CREATURE_GROWTH_PERCENT)/100);
-
-		summ+=AddToString(CGI->generaltexth->artifNames[133] + " %+d",descr,
-			summ * town->valOfGlobalBonuses
-			(Selector::type(Bonus::CREATURE_GROWTH_PERCENT) && Selector::sourceType(Bonus::ARTIFACT))/100); //Statue of Legion
-
-		if(town->town->hordeLvl[0]==level)//horde, x to summ
-		if( vstd::contains(town->builtBuildings, Buildings::HORDE_1) || vstd::contains(town->builtBuildings, Buildings::HORDE_1_UPGR))
-			summ+=AddToString(CGI->buildh->buildings[town->subID][Buildings::HORDE_1]->Name()+" %+d",descr,
-				creature->hordeGrowth);
-
-		if(town->town->hordeLvl[1]==level)//horde, x to summ
-		if( vstd::contains(town->builtBuildings, Buildings::HORDE_2) || vstd::contains(town->builtBuildings, Buildings::HORDE_2_UPGR))
-			summ+=AddToString(CGI->buildh->buildings[town->subID][Buildings::HORDE_2]->Name()+" %+d",descr,
-				creature->hordeGrowth);
-
-		int cnt = 0;
-
-		std::vector< const CGDwelling * > myDwellings = LOCPLINT->cb->getMyDwellings();
-		for (std::vector<const CGDwelling*>::const_iterator it = myDwellings.begin(); it != myDwellings.end(); ++it)
-			if (CGI->creh->creatures[town->town->basicCreatures[level]]->idNumber == (*it)->creatures[0].second[0])
-				cnt++;//external dwellings count to summ
-		summ+=AddToString(CGI->generaltexth->allTexts[591],descr,cnt);
-
-		boost::shared_ptr<BonusList> bl;
-		const CGHeroInstance *hero = town->garrisonHero;
-		if (hero)
-		{
-			bl = hero->getAllBonuses(Selector::type(Bonus::CREATURE_GROWTH) && Selector::subtype(level) 
-							  && Selector::sourceType(Bonus::ARTIFACT), 0, hero);
-		}
-		hero = town->visitingHero;
-		if (hero)
-		{
-			boost::shared_ptr<BonusList> blAppend = hero->getAllBonuses(Selector::type(Bonus::CREATURE_GROWTH) && Selector::subtype(level) 
-				&& Selector::sourceType(Bonus::ARTIFACT), 0, hero);
-			if (town->garrisonHero)
-				bl->insert(bl->size(), blAppend->begin(), blAppend->end());
-			else
-				bl = blAppend;
-		}
-		
-		if (bl->size())
-			summ+=AddToString (CGI->arth->artifacts[bl->front()->sid]->Name()+" %+d", descr, bl->totalValue());
-
-		//TODO: player bonuses
-
-		if(vstd::contains(town->builtBuildings, Buildings::GRAIL)) //grail - +50% to ALL growth
-			summ+=AddToString(CGI->buildh->buildings[town->subID][Buildings::GRAIL]->Name()+" %+d",descr,summ/2);
-
-		summ+=AddToString(CGI->generaltexth->allTexts[63] + " %+d",descr, creature->valOfBonuses(Bonus::CREATURE_GROWTH));
+		descr +="\n" + entry.description;
 	}
+	
 	return descr;
 }
 

+ 22 - 8
lib/CArtHandler.cpp

@@ -502,8 +502,22 @@ void CArtHandler::giveArtBonus( int aid, Bonus::BonusType type, int val, int sub
 	Bonus *added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,aid,subtype);
 	added->valType = valType;
 	added->limiter.reset(limiter);
-	if(type == Bonus::MORALE || Bonus::LUCK)
+	if(type == Bonus::MORALE || type == Bonus::LUCK)
 		added->description = artifacts[aid]->Name()  + (val > 0 ? " +" : " ") + boost::lexical_cast<std::string>(val);
+	else
+		added->description = artifacts[aid]->Name();
+	artifacts[aid]->addNewBonus(added);
+}
+
+void CArtHandler::giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype, IPropagator* propagator /*= NULL*/)
+{
+	Bonus *added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,aid,subtype);
+	added->valType = Bonus::BASE_NUMBER;
+	added->propagator.reset(propagator);
+	if(type == Bonus::MORALE || type == Bonus::LUCK)
+		added->description = artifacts[aid]->Name()  + (val > 0 ? " +" : " ") + boost::lexical_cast<std::string>(val);
+	else
+		added->description = artifacts[aid]->Name();
 	artifacts[aid]->addNewBonus(added);
 }
 
@@ -677,11 +691,11 @@ void CArtHandler::addBonuses()
 	giveArtBonus(116,Bonus::GENERATE_RESOURCE,+750, Res::GOLD); //Endless Bag of Gold
 	giveArtBonus(117,Bonus::GENERATE_RESOURCE,+500, Res::GOLD); //Endless Purse of Gold
 
-	giveArtBonus(118,Bonus::CREATURE_GROWTH,+5,1); //Legs of Legion
-	giveArtBonus(119,Bonus::CREATURE_GROWTH,+4,2); //Loins of Legion
-	giveArtBonus(120,Bonus::CREATURE_GROWTH,+3,3); //Torso of Legion
-	giveArtBonus(121,Bonus::CREATURE_GROWTH,+2,4); //Arms of Legion
-	giveArtBonus(122,Bonus::CREATURE_GROWTH,+1,5); //Head of Legion
+	giveArtBonus(118,Bonus::CREATURE_GROWTH,+5,1, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Legs of Legion
+	giveArtBonus(119,Bonus::CREATURE_GROWTH,+4,2, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Loins of Legion
+	giveArtBonus(120,Bonus::CREATURE_GROWTH,+3,3, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Torso of Legion
+	giveArtBonus(121,Bonus::CREATURE_GROWTH,+2,4, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Arms of Legion
+	giveArtBonus(122,Bonus::CREATURE_GROWTH,+1,5, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Head of Legion
 
 	//Sea Captain's Hat 
 	giveArtBonus(123,Bonus::WHIRLPOOL_PROTECTION,0); 
@@ -722,7 +736,7 @@ void CArtHandler::addBonuses()
 	giveArtBonus(132, Bonus::OPENING_BATTLE_SPELL, 50, 52); // Misfortune
 
 	// Statue of Legion - gives only 50% growth
-	giveArtBonus(133, Bonus::CREATURE_GROWTH_PERCENT, 50, -1);
+	giveArtBonus(133, Bonus::CREATURE_GROWTH_PERCENT, 50, -1, new CPropagatorNodeType(CBonusSystemNode::PLAYER));
 
 	//Power of the Dragon Father
 	giveArtBonus(134, Bonus::LEVEL_SPELL_IMMUNITY, 4, -1, Bonus::INDEPENDENT_MAX);
@@ -880,7 +894,6 @@ CArtifactInstance::CArtifactInstance( CArtifact *Art)
 {
 	init();
 	setType(Art);
-
 }
 
 // CArtifactInstance::CArtifactInstance(int aid)
@@ -911,6 +924,7 @@ CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s)
 void CArtifactInstance::init()
 {
 	id = -1;
+	setNodeType(ARTIFACT_INSTANCE);
 }
 
 int CArtifactInstance::firstAvailableSlot(const CGHeroInstance *h) const

+ 1 - 0
lib/CArtHandler.h

@@ -198,6 +198,7 @@ public:
 class DLL_EXPORT CArtHandler //handles artifacts
 {
 	void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype = -1, int valType = Bonus::BASE_NUMBER, ILimiter * limiter = NULL);
+	void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype, IPropagator* propagator);
 public:
 	std::vector<CArtifact*> treasures, minors, majors, relics;
 	std::vector< ConstTransitivePtr<CArtifact> > artifacts;

+ 79 - 37
lib/CObjectHandler.cpp

@@ -26,6 +26,7 @@
 #include <boost/foreach.hpp>
 #include <boost/format.hpp>
 #include <boost/algorithm/string/trim.hpp>
+#include "CBuildingHandler.h"
 
 using namespace boost::assign;
 
@@ -1871,49 +1872,64 @@ int CGTownInstance::getHordeLevel(const int & HID)  const//HID - 0 or 1; returns
 }
 int CGTownInstance::creatureGrowth(const int & level) const
 {
+	return getGrowthInfo(level).totalGrowth();
+}
+
+GrowthInfo CGTownInstance::getGrowthInfo(int level) const
+{
+	GrowthInfo ret;
+
 	if (level<0 || level >=CREATURES_PER_TOWN)
-		return 0;
+		return ret;
 	if (!vstd::contains(builtBuildings, Buildings::DWELL_FIRST+level))
-		return 0; //no dwelling
-	TCreature creid = town->basicCreatures[level];
-		
-	int ret = VLC->creh->creatures[creid]->growth;
-	switch(fortLevel())
-	{
-	case 3:
-		ret*=2;break;
-	case 2:
-		ret*=(1.5); break;
-	}
-	ret *= (1 + VLC->creh->creatures[creid]->valOfBonuses(Bonus::CREATURE_GROWTH_PERCENT)/100); // double growth or plague
-	if(tempOwner != NEUTRAL_PLAYER)
+		return ret; //no dwelling
+
+	const CCreature *creature = VLC->creh->creatures[town->basicCreatures[level]];
+	const int base = creature->growth;
+	int castleBonus = 0;
+
+	ret.entries.push_back(GrowthInfo::Entry(VLC->generaltexth->allTexts[590], base));// \n\nBasic growth %d"
+
+	if ( vstd::contains(builtBuildings, Buildings::CASTLE))
+		ret.entries.push_back(GrowthInfo::Entry(subID, Buildings::CASTLE, castleBonus = base));
+	else if ( vstd::contains(builtBuildings, Buildings::CITADEL))
+		ret.entries.push_back(GrowthInfo::Entry(subID, Buildings::CITADEL, castleBonus = base / 2));
+
+	if(town->hordeLvl[0] == level)//horde 1
+		if( vstd::contains(builtBuildings, Buildings::HORDE_1) || vstd::contains(builtBuildings, Buildings::HORDE_1_UPGR))
+			ret.entries.push_back(GrowthInfo::Entry(subID, Buildings::HORDE_1, creature->hordeGrowth));
+
+	if(town->hordeLvl[1] == level)//horde 2
+		if(vstd::contains(builtBuildings, Buildings::HORDE_2) || vstd::contains(builtBuildings, Buildings::HORDE_2_UPGR))
+			ret.entries.push_back(GrowthInfo::Entry(subID, Buildings::HORDE_2, creature->hordeGrowth));
+
+	int dwellingBonus = 0;
+	if(const PlayerState *p = cb->getPlayer(tempOwner, false))
 	{
-		ret *= (100.0f + cb->gameState()->players[tempOwner].valOfBonuses
-			(Selector::type(Bonus::CREATURE_GROWTH_PERCENT) && Selector::sourceType(Bonus::ARTIFACT)))/100; //Statue of Legion
-		for (std::vector<ConstTransitivePtr<CGDwelling> >::const_iterator it = cb->gameState()->players[tempOwner].dwellings.begin(); it != cb->gameState()->players[tempOwner].dwellings.end(); ++it)
-		{ //+1 for each dwelling
-			if (VLC->creh->creatures[creid]->idNumber == (*it)->creatures[0].second[0])
-				++ret;
-		}
+		BOOST_FOREACH(const CGDwelling *dwelling, p->dwellings)
+			if(creature->idNumber == dwelling->creatures[0].second[0])
+				dwellingBonus++;
 	}
-	if(getHordeLevel(0)==level)
-		if((builtBuildings.find(18)!=builtBuildings.end()) || (builtBuildings.find(19)!=builtBuildings.end()))
-			ret+=VLC->creh->creatures[creid]->hordeGrowth;
-	if(getHordeLevel(1)==level)
-		if((builtBuildings.find(24)!=builtBuildings.end()) || (builtBuildings.find(25)!=builtBuildings.end()))
-			ret+=VLC->creh->creatures[creid]->hordeGrowth;
 
-	//support for legs of legion etc.
-	if(garrisonHero)
-		ret += garrisonHero->valOfBonuses(Bonus::CREATURE_GROWTH, level);
-	if(visitingHero)
-		ret += visitingHero->valOfBonuses(Bonus::CREATURE_GROWTH, level);
-	if(builtBuildings.find(26)!=builtBuildings.end()) //grail - +50% to ALL growth
-		ret*=1.5;
-	//spcecial week unaffected by grail (bug in original game?)
-	ret += VLC->creh->creatures[creid]->valOfBonuses(Bonus::CREATURE_GROWTH);
-	return ret;//check CCastleInterface.cpp->CCastleInterface::CCreaInfo::clickRight if this one will be modified
+	if(dwellingBonus)
+		ret.entries.push_back(GrowthInfo::Entry(VLC->generaltexth->allTexts[591], dwellingBonus));// \nExternal dwellings %+d
+		
+	//other *-of-legion-like bonuses (%d to growth cumulative with grail)
+	boost::shared_ptr<BonusList> bonuses = getBonuses(Selector::type(Bonus::CREATURE_GROWTH) && Selector::subtype(level));
+	BOOST_FOREACH(const Bonus *b, *bonuses)
+		ret.entries.push_back(GrowthInfo::Entry(b->Description() + " %+d", b->val));
+
+	//statue-of-legion-like bonus: % to base+castle
+	boost::shared_ptr<BonusList> bonuses2 = getBonuses(Selector::type(Bonus::CREATURE_GROWTH_PERCENT));
+	BOOST_FOREACH(const Bonus *b, *bonuses2)
+		ret.entries.push_back(GrowthInfo::Entry(b->Description() + " %+d", b->val * (base + castleBonus) / 100));
+
+	if(vstd::contains(builtBuildings, Buildings::GRAIL)) //grail - +50% to ALL (so far added) growth
+		ret.entries.push_back(GrowthInfo::Entry(subID, Buildings::GRAIL, ret.totalGrowth() / 2));
+
+	return ret;
 }
+
 int CGTownInstance::dailyIncome() const
 {
 	int ret = 0;
@@ -7070,3 +7086,29 @@ void CGUniversity::onHeroVisit(const CGHeroInstance * h) const
 	ow.window = OpenWindow::UNIVERSITY_WINDOW;
 	cb->sendAndApply(&ow);
 }
+
+GrowthInfo::Entry::Entry(const std::string &format, int _count)
+	: count(_count)
+{
+	description = boost::str(boost::format(format) % count);
+}
+
+GrowthInfo::Entry::Entry(int subID, Buildings::EBuilding building, int _count)
+	: count(_count)
+{
+	description = boost::str(boost::format("%s %+d") % VLC->buildh->buildings[subID][building]->Name() % count);
+}
+
+CTownAndVisitingHero::CTownAndVisitingHero()
+{
+	setNodeType(TOWN_AND_VISITOR);
+}
+
+int GrowthInfo::totalGrowth() const
+{
+	int ret = 0;
+	BOOST_FOREACH(const Entry &entry, entries)
+		ret += entry.count;
+
+	return ret;
+}

+ 17 - 0
lib/CObjectHandler.h

@@ -519,6 +519,22 @@ public:
 
 class DLL_EXPORT CTownAndVisitingHero : public CBonusSystemNode
 {
+public:
+	CTownAndVisitingHero();
+};
+
+struct DLL_EXPORT GrowthInfo
+{
+	struct Entry
+	{
+		int count;
+		std::string description;
+		Entry(const std::string &format, int _count);
+		Entry(int subID, Buildings::EBuilding building, int _count);
+	};
+
+	std::vector<Entry> entries;
+	int totalGrowth() const;
 };
 
 class DLL_EXPORT CGTownInstance : public CGDwelling, public IShipyard, public IMarket
@@ -591,6 +607,7 @@ public:
 	bool creatureDwelling(const int & level, bool upgraded=false) const;
 	int getHordeLevel(const int & HID) const; //HID - 0 or 1; returns creature level or -1 if that horde structure is not present
 	int creatureGrowth(const int & level) const;
+	GrowthInfo getGrowthInfo(int level) const;
 	bool hasFort() const;
 	bool hasCapitol() const;
 	int dailyIncome() const; //calculates daily income of this town

+ 1 - 1
lib/HeroBonus.h

@@ -587,7 +587,7 @@ public:
 	}
 	enum ENodeTypes
 	{
-		UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALITY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM
+		UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALITY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM, TOWN_AND_VISITOR
 	};
 };
 

+ 1 - 1
lib/NetPacks.h

@@ -950,7 +950,7 @@ struct HeroVisit : CPackForClient //531
 
 struct NewTurn : public CPackForClient //101
 {
-	enum weekType {NORMAL, DOUBLE_GROWTH, BONUS_GROWTH, DEITYOFFIRE, PLAGUE, CUSTOM, NO_ACTION, NONE};
+	enum weekType {NORMAL, DOUBLE_GROWTH, BONUS_GROWTH, DEITYOFFIRE, PLAGUE, NO_ACTION};
 
 	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);

+ 13 - 55
lib/NetPacksLib.cpp

@@ -766,65 +766,23 @@ DLL_EXPORT void NewTurn::applyGs( CGameState *gs )
 	BOOST_FOREACH(SetAvailableCreatures h, cres) //set available creatures in towns
 		h.applyGs(gs);
 
-	if (specialWeek != NO_ACTION) //first pack applied, reset all effects and aplly new
-	{
-		//TODO? won't work for battles lasting several turns (not an issue now but...)
-		gs->globalEffects.popBonuses(Bonus::OneDay); //works for children -> all game objs
+	gs->globalEffects.popBonuses(Bonus::OneDay); //works for children -> all game objs
+	if(gs->getDate(1)) //new week
+		gs->globalEffects.popBonuses(Bonus::OneWeek); //works for children -> all game objs
 
-		if(gs->getDate(1)) //new week, Monday that is
-		{
-			gs->globalEffects.popBonuses(Bonus::OneWeek); //works for children -> all game objs
-
-			Bonus *b = new Bonus();
-			b->duration = Bonus::ONE_WEEK;
-			b->source = Bonus::SPECIAL_WEEK;
-			b->effectRange = Bonus::NO_LIMIT;
-			b->valType = Bonus::BASE_NUMBER; //certainly not intuitive
-			switch (specialWeek)
-			{
-				case DOUBLE_GROWTH:
-					b->val = 100;
-					b->type = Bonus::CREATURE_GROWTH_PERCENT;
-					b->limiter.reset(new CCreatureTypeLimiter(*VLC->creh->creatures[creatureid], false));
-					break;
-				case BONUS_GROWTH:
-					b->val = 5;
-					b->type = Bonus::CREATURE_GROWTH;
-					b->limiter.reset(new CCreatureTypeLimiter(*VLC->creh->creatures[creatureid], false));
-					break;
-				case DEITYOFFIRE:
-					b->val = 15;
-					b->type = Bonus::CREATURE_GROWTH;
-					b->limiter.reset(new CCreatureTypeLimiter(*VLC->creh->creatures[42], true));
-					break;
-				case PLAGUE:
-					b->val = -100; //no basic creatures
-					b->type = Bonus::CREATURE_GROWTH_PERCENT;
-					break;
-				default:
-					b->val = 0;
+	//TODO not really a single root hierarchy, what about bonuses placed elsewhere? [not an issue with H3 mechanics but in the future...]
 
-			}
-			if (b->val)
-				gs->globalEffects.addNewBonus(b);
-		}
-	}
-	else //second pack is applied
+	//count days without town
+	for( std::map<ui8, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
 	{
-		//count days without town
-		for( std::map<ui8, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
-		{
-			if(i->second.towns.size() || gs->day == 1)
-				i->second.daysWithoutCastle = 0;
-			else
-				i->second.daysWithoutCastle++;
-		}
-		if(resetBuilded) //reset amount of structures set in this turn in towns
-		{
-			BOOST_FOREACH(CGTownInstance* t, gs->map->towns)
-				t->builded = 0;
-		}
+		if(i->second.towns.size() || gs->day == 1)
+			i->second.daysWithoutCastle = 0;
+		else
+			i->second.daysWithoutCastle++;
 	}
+
+	BOOST_FOREACH(CGTownInstance* t, gs->map->towns)
+		t->builded = 0;
 }
 
 DLL_EXPORT void SetObjectProperty::applyGs( CGameState *gs )

+ 144 - 130
server/CGameHandler.cpp

@@ -853,55 +853,70 @@ void CGameHandler::newTurn()
 {
 	tlog5 << "Turn " << gs->day+1 << std::endl;
 	NewTurn n;
+	n.specialWeek = NewTurn::NO_ACTION;
 	n.creatureid = -1;
 	n.day = gs->day + 1;
-	n.resetBuilded = true;
-	bool newmonth = false;
+
+	bool firstTurn = !getDate(0);
+	bool newWeek = getDate(1) == 7; //day numbers are confusing, as day was not yet switched
+	bool newMonth = getDate(4) == 28;
 	
 	std::map<ui8, si32> hadGold;//starting gold - for buildings like dwarven treasury
 	srand(time(NULL));
 
-	if (getDate(1) == 7 && getDate(0)>1) //new week (day numbers are confusing, as day was not yet switched)
+	if (newWeek && !firstTurn)
 	{
-		int monthType = rand()%100;
-		if(getDate(4) == 28) //new month
+		n.specialWeek = NewTurn::NORMAL;
+		bool deityOfFireBuilt = false;
+		BOOST_FOREACH(const CGTownInstance *t, gs->map->towns)
 		{
-			newmonth = true;
-			if (monthType < 40) //double growth
+			if(t->subID == 3 && vstd::contains(t->builtBuildings, Buildings::GRAIL))
 			{
-				n.specialWeek = NewTurn::DOUBLE_GROWTH;
-				if (ALLCREATURESGETDOUBLEMONTHS)
-				{
-					std::pair<int,int> newMonster(54, VLC->creh->pickRandomMonster(boost::ref(rand)));
-					n.creatureid = newMonster.second;
-				}
-				else
-				{
-					std::set<TCreature>::const_iterator it = VLC->creh->doubledCreatures.begin();
-					std::advance (it, rand() % VLC->creh->doubledCreatures.size()); //picking random elelemnt of set is tiring
-					n.creatureid = *it;
-				}
+				deityOfFireBuilt = true;
+				break;
 			}
-			else if (monthType < 90)
-				n.specialWeek = NewTurn::NORMAL;
-			else
-				n.specialWeek = NewTurn::PLAGUE;
 		}
-		else //it's a week, but not full month
+
+		if(deityOfFireBuilt)
 		{
-			newmonth = false;
-			if (monthType < 25)
+			n.specialWeek = NewTurn::DEITYOFFIRE;
+			n.creatureid = 42;
+		}
+		else
+		{
+			int monthType = rand()%100;
+			if(newMonth) //new month
 			{
-				n.specialWeek = NewTurn::BONUS_GROWTH; //+5
-				std::pair<int,int> newMonster (54, VLC->creh->pickRandomMonster(boost::ref(rand)));
-				n.creatureid = newMonster.second;
+				if (monthType < 40) //double growth
+				{
+					n.specialWeek = NewTurn::DOUBLE_GROWTH;
+					if (ALLCREATURESGETDOUBLEMONTHS)
+					{
+						std::pair<int,int> newMonster(54, VLC->creh->pickRandomMonster(boost::ref(rand)));
+						n.creatureid = newMonster.second;
+					}
+					else
+					{
+						std::set<TCreature>::const_iterator it = VLC->creh->doubledCreatures.begin();
+						std::advance (it, rand() % VLC->creh->doubledCreatures.size()); //picking random element of set is tiring
+						n.creatureid = *it;
+					}
+				}
+				else if (monthType < 50)
+					n.specialWeek = NewTurn::PLAGUE;
+			}
+			else //it's a week, but not full month
+			{
+				if (monthType < 25)
+				{
+					n.specialWeek = NewTurn::BONUS_GROWTH; //+5
+					std::pair<int,int> newMonster (54, VLC->creh->pickRandomMonster(boost::ref(rand)));
+					//TODO do not pick neutrals
+					n.creatureid = newMonster.second;
+				}
 			}
-			else
-				n.specialWeek = NewTurn::NORMAL;
 		}
 	}
-	else
-		n.specialWeek = NewTurn::NO_ACTION; //don't remove bonuses
 
 	bmap<ui32, ConstTransitivePtr<CGHeroInstance> > pool = gs->hpool.heroesPool;
 
@@ -915,7 +930,7 @@ void CGameHandler::newTurn()
 		std::pair<ui8,si32> playerGold(i->first,i->second.resources[Res::GOLD]);
 		hadGold.insert(playerGold); 
 
-		if(gs->getDate(1)==7) //first day of week - new heroes in tavern
+		if(newWeek) //new heroes in tavern
 		{
 			SetAvailableHeroes sah;
 			sah.player = i->first;
@@ -955,7 +970,7 @@ void CGameHandler::newTurn()
 
 			n.heroes.insert(hth);
 			
-			if(gs->day) //not first day
+			if(!firstTurn) //not first day
 			{
 				n.res[i->first][Res::GOLD] += h->valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::ESTATES)); //estates
 
@@ -969,131 +984,113 @@ void CGameHandler::newTurn()
 	//      townID,    creatureID, amount
 	std::map<si32, std::map<si32, si32> > newCreas;//creatures that needs to be added by town events
 	
-	for(std::vector<ConstTransitivePtr<CGTownInstance> >::iterator j = gs->map->towns.begin(); j!=gs->map->towns.end(); j++)//handle towns
+	BOOST_FOREACH(CGTownInstance *t, gs->map->towns)
 	{
-		ui8 player = (*j)->tempOwner;
-		if(gs->getDate(1)==7) //first day of week
+		ui8 player = t->tempOwner;
+		handleTownEvents(t, n, newCreas);
+		if(newWeek) //first day of week
 		{
-			if ((*j)->subID == 5 && vstd::contains((*j)->builtBuildings, 22))
-				setPortalDwelling(*j, true, (n.specialWeek == NewTurn::PLAGUE ? true : false)); //set creatures for Portal of Summoning
+			if(t->subID == 5 && vstd::contains(t->builtBuildings, Buildings::SPECIAL_3))
+				setPortalDwelling(t, true, (n.specialWeek == NewTurn::PLAGUE ? true : false)); //set creatures for Portal of Summoning
+
+			if(!firstTurn)
+				if (t->subID == 1  && player < PLAYER_LIMIT && vstd::contains(t->builtBuildings, Buildings::SPECIAL_3))//dwarven treasury
+						n.res[player][Res::GOLD] += hadGold[player]/10; //give 10% of starting gold
+
+			SetAvailableCreatures sac;
+			sac.tid = t->id;
+			sac.creatures = t->creatures;
+			for (int k=0; k < CREATURES_PER_TOWN; k++) //creature growths
+			{
+				if(t->creatureDwelling(k))//there is dwelling (k-level)
+				{
+					ui32 &availableCount = sac.creatures[k].first;
+					const CCreature *cre = VLC->creh->creatures[t->creatureDwelling(k, true) ? t->town->upgradedCreatures[k] : t->town->basicCreatures[k]];
+
+					if (n.specialWeek == NewTurn::PLAGUE)
+						availableCount = t->creatures[k].first / 2; //halve their number, no growth
+					else
+					{
+						if(firstTurn) //first day of game: use only basic growths
+							availableCount = cre->growth;
+						else
+							availableCount += t->creatureGrowth(k);
+
+						if(n.creatureid == cre->idNumber 
+							|| n.specialWeek == NewTurn::DEITYOFFIRE && (cre->idNumber == 42 || cre->idNumber == 43))
+						{
+							if(n.specialWeek == NewTurn::DOUBLE_GROWTH)
+								availableCount *= 2;
+							else if(n.specialWeek == NewTurn::BONUS_GROWTH)
+								availableCount += 5;
+							else if(n.specialWeek == NewTurn::DEITYOFFIRE)
+								availableCount += 15;
+						}
+					}
+				}
+			}
+			//add creatures from town events
+			if (vstd::contains(newCreas, t->id))
+				for(std::map<si32, si32>::iterator i=newCreas[t->id].begin() ; i!=newCreas[t->id].end(); i++)
+					sac.creatures[i->first].first += i->second;
 
-			if  ((**j).subID == 1 && gs->getDate(0) && player < PLAYER_LIMIT && vstd::contains((**j).builtBuildings, 22))//dwarven treasury
-					n.res[player][Res::GOLD] += hadGold[player]/10; //give 10% of starting gold
+			n.cres.push_back(sac);
 		}
-		if(gs->day  &&  player < PLAYER_LIMIT)//not the first day and town not neutral
+		if(!firstTurn  &&  player < PLAYER_LIMIT)//not the first day and town not neutral
 		{
-			////SetResources r;
-			//r.player = (**j).tempOwner;
-			if(vstd::contains((**j).builtBuildings, Buildings::RESOURCE_SILO)) //there is resource silo
+			if(vstd::contains(t->builtBuildings, Buildings::RESOURCE_SILO)) //there is resource silo
 			{
-				if((**j).town->primaryRes == 127) //we'll give wood and ore
+				if(t->town->primaryRes == 127) //we'll give wood and ore
 				{
 					n.res[player][Res::WOOD] ++;
 					n.res[player][Res::ORE] ++;
 				}
 				else
 				{
-					n.res[player][(**j).town->primaryRes] ++;;
+					n.res[player][t->town->primaryRes] ++;
 				}
 			}
-			n.res[player][Res::GOLD] += (**j).dailyIncome();
+
+			n.res[player][Res::GOLD] += t->dailyIncome();
 		}
-		handleTownEvents(*j, n, newCreas);
-		if (vstd::contains((**j).builtBuildings, Buildings::GRAIL)) 
+		if(vstd::contains(t->builtBuildings, Buildings::GRAIL) && t->subID == 2) 
 		{
-			switch ((**j).subID)
-			{
-				case 2: // Skyship, probably easier to handle same as Veil of darkness
-					{ //do it every new day after veils apply
-						FoWChange fw;
-						fw.mode = 1;
-						fw.player = player;
-						getAllTiles(fw.tiles, player, -1, 0);
-						sendAndApply (&fw);
-					}
-					break;
-				case 3: //Deity of Fire
-					{
-						if (getDate(0) > 1)
-						{
-							n.specialWeek = NewTurn::DEITYOFFIRE; //spawn familiars on new month
-							n.creatureid = 42; //familiar
-						}
-					}
-					break;
-			}
+			// Skyship, probably easier to handle same as Veil of darkness
+			//do it every new day after veils apply
+			FoWChange fw;
+			fw.mode = 1;
+			fw.player = player;
+			getAllTiles(fw.tiles, player, -1, 0);
+			sendAndApply (&fw);
 		}
-		if ((**j).hasBonusOfType (Bonus::DARKNESS))
+		if (t->hasBonusOfType (Bonus::DARKNESS))
 		{
-			(**j).hideTiles((**j).getOwner(), (**j).getBonus(Selector::type(Bonus::DARKNESS))->val);
+			t->hideTiles(t->getOwner(), t->getBonus(Selector::type(Bonus::DARKNESS))->val);
 		}
 		//unhiding what shouldn't be hidden? //that's handled in netpacks client
 	}
 
-	if(getDate(2) == 1) //first week
+	if(newMonth)
 	{
 		SetAvailableArtifacts saa; 
 		saa.id = -1;
 		pickAllowedArtsSet(saa.arts);
 		sendAndApply(&saa);
 	}
-
 	sendAndApply(&n);
 
-	if (gs->getDate(1)==1) //first day of week, day has already been changed
-	{
-		if (getDate(4) == 1 && (n.specialWeek == NewTurn::DOUBLE_GROWTH || n.specialWeek == NewTurn::DEITYOFFIRE))
-		{ //spawn wandering monsters
-			std::vector<int3>::iterator tile;
-			std::vector<int3> tiles;
-			getFreeTiles(tiles);
-			ui32 amount = (tiles.size()) >> 6;
-			std::random_shuffle(tiles.begin(), tiles.end(), p_myrandom);
-			tlog5 << "Spawning wandering monsters. Found " << tiles.size() << " free tiles. Creature type: " << n.creatureid << std::endl;
-			const CCreature *cre = VLC->creh->creatures[n.creatureid];
-			for (int i = 0; i < amount; ++i)
-			{
-				tile = tiles.begin();
-				tlog5 << "\tSpawning monster at " << *tile << std::endl;
-				putNewMonster(n.creatureid, cre->getRandomAmount(std::rand), *tile);
-				tiles.erase(tile); //not use it again
-			}
-		}
-
-		NewTurn n2; //just to handle  creature growths after bonuses are applied
-		n2.specialWeek = NewTurn::NO_ACTION;
-		n2.day = gs->day;
-		n2.resetBuilded = true;
-
-		for(std::vector<ConstTransitivePtr<CGTownInstance> >::iterator j = gs->map->towns.begin(); j!=gs->map->towns.end(); j++)//handle towns
+	if(newWeek)
+	{
+		//spawn wandering monsters
+		if (newMonth && (n.specialWeek == NewTurn::DOUBLE_GROWTH || n.specialWeek == NewTurn::DEITYOFFIRE))
 		{
-			SetAvailableCreatures sac;
-			sac.tid = (**j).id;
-			sac.creatures = (**j).creatures;
-			for (int k=0; k < CREATURES_PER_TOWN; k++) //creature growths
-			{
-				if((**j).creatureDwelling(k))//there is dwelling (k-level)
-				{
-					if (n.specialWeek == NewTurn::PLAGUE)
-						sac.creatures[k].first = (**j).creatures[k].first / 2; //halve their number, no growth
-					else
-					{
-						sac.creatures[k].first += (**j).creatureGrowth(k);
-						if(gs->getDate(0) == 1) //first day of game: use only basic growths
-							amin(sac.creatures[k].first, VLC->creh->creatures[(*j)->town->basicCreatures[k]]->growth);
-					}
-				}
-			}
-			//creatures from town events
-			if (vstd::contains(newCreas, (**j).id))
-				for(std::map<si32, si32>::iterator i=newCreas[(**j).id].begin() ; i!=newCreas[(**j).id].end(); i++)
-					sac.creatures[i->first].first += i->second;
-			
-			n2.cres.push_back(sac);
+			spawnWanderingMonsters(n.creatureid);
 		}
-		if (gs->getDate(0) > 1)
+
+		//new week info popup
+		if(!firstTurn)
 		{
-			InfoWindow iw; //new week info
+			InfoWindow iw;
 			switch (n.specialWeek)
 			{
 				case NewTurn::DOUBLE_GROWTH:
@@ -1118,7 +1115,7 @@ void CGameHandler::newTurn()
 					iw.text.addReplacement2(15);							//%+d 15
 					break;
 				default:
-					iw.text.addTxt(MetaString::ARRAY_TXT, (newmonth ? 130 : 133));
+					iw.text.addTxt(MetaString::ARRAY_TXT, (newMonth ? 130 : 133));
 					iw.text.addReplacement(MetaString::ARRAY_TXT, 43 + rand()%15);
 			}
 			for (std::map<ui8, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end(); i++)
@@ -1127,9 +1124,8 @@ void CGameHandler::newTurn()
 				sendAndApply(&iw);
 			}
 		}
-
-		sendAndApply(&n2);
 	}
+
 	tlog5 << "Info about turn " << n.day << "has been sent!" << std::endl;
 	handleTimeEvents();
 	//call objects
@@ -3884,6 +3880,7 @@ void CGameHandler::handleTimeEvents()
 
 void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n, std::map<si32, std::map<si32, si32> > &newCreas)
 {
+	//TODO event removing desync!!!
 	town->events.sort(evntCmp);
 	while(town->events.size() && town->events.front()->firstOccurence == gs->day)
 	{
@@ -5208,6 +5205,23 @@ void CGameHandler::commitPackage( CPackForClient *pack )
 	sendAndApply(pack);
 }
 
+void CGameHandler::spawnWanderingMonsters(int creatureID)
+{
+	std::vector<int3>::iterator tile;
+	std::vector<int3> tiles;
+	getFreeTiles(tiles);
+	ui32 amount = (tiles.size()) >> 6;
+	std::random_shuffle(tiles.begin(), tiles.end(), p_myrandom);
+	tlog5 << "Spawning wandering monsters. Found " << tiles.size() << " free tiles. Creature type: " << creatureID << std::endl;
+	const CCreature *cre = VLC->creh->creatures[creatureID];
+	for (int i = 0; i < amount; ++i)
+	{
+		tile = tiles.begin();
+		tlog5 << "\tSpawning monster at " << *tile << std::endl;
+		putNewMonster(creatureID, cre->getRandomAmount(std::rand), *tile);
+		tiles.erase(tile); //not use it again
+	}
+}
 CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat)
 {
 	int color = army->tempOwner;

+ 1 - 0
server/CGameHandler.h

@@ -254,6 +254,7 @@ public:
 	void handleAfterAttackCasting (const BattleAttack & bat);
 	void attackCasting(const BattleAttack & bat, Bonus::BonusType attackMode, const CStack * attacker);
 	bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, int slot);
+	void spawnWanderingMonsters(int creatureID);
 	friend class CVCMIServer;
 	friend class CScriptCallback;
 };