Ver Fonte

Fixed some issues with hiring heroes: #25, #232, #333

Michał W. Urbańczyk há 15 anos atrás
pai
commit
94f7ee41e2
8 ficheiros alterados com 166 adições e 116 exclusões
  1. 1 0
      global.h
  2. 45 38
      hch/CObjectHandler.cpp
  3. 1 0
      hch/CObjectHandler.h
  4. 24 5
      lib/CGameState.cpp
  5. 2 27
      lib/CGameState.h
  6. 14 4
      lib/NetPacks.h
  7. 11 15
      lib/NetPacksLib.cpp
  8. 68 27
      server/CGameHandler.cpp

+ 1 - 0
global.h

@@ -102,6 +102,7 @@ const int MAX_BUILDING_PER_TURN = 1;
 const int SPELL_LEVELS = 5;
 const int CREEP_SIZE = 4000; // neutral stacks won't grow beyon this number
 const int WEEKLY_GROWTH = 10; //percent
+const int AVAILABLE_HEROES_PER_PLAYER = 2;
 
 #define BFIELD_WIDTH (17)
 #define BFIELD_HEIGHT (11)

+ 45 - 38
hch/CObjectHandler.cpp

@@ -791,46 +791,10 @@ void CGHeroInstance::initHero()
 	setFormation(false);
 	if (!stacksCount()) //standard army//initial army
 	{
-		int howManyStacks = 0; //how many stacks will hero receives <1 - 3>
-		int pom = ran()%100;
-		int warMachinesGiven = 0;
-
-		if(pom < 9)
-			howManyStacks = 1;
-		else if(pom < 79)
-			howManyStacks = 2;
-		else
-			howManyStacks = 3;
-
-		for(int stackNo=0; stackNo<3; stackNo++)
-		{
-			int creID = (VLC->creh->nameToID[type->refTypeStack[stackNo]]);
-			int range = type->highStack[stackNo] - type->lowStack[stackNo];
-			int count = ran()%(range+1) + type->lowStack[stackNo];
-
-			if(creID>=145 && creID<=149) //war machine
-			{
-				warMachinesGiven++;
-				switch (creID)
-				{
-				case 145: //catapult
-					VLC->arth->equipArtifact(artifWorn, 16, 3, &bonuses);
-					break;
-				default:
-					VLC->arth->equipArtifact(
-						artifWorn,
-						9+CArtHandler::convertMachineID(creID,true),
-						CArtHandler::convertMachineID(creID,true),
-						&bonuses);
-					break;
-				}
-			}
-			else
-				addStack(stackNo-warMachinesGiven, CStackInstance(creID, count));
-		}
+		initArmy();
 	}
-
 	assert(validTypes());
+
 	hoverName = VLC->generaltexth->allTexts[15];
 	boost::algorithm::replace_first(hoverName,"%s",name);
 	boost::algorithm::replace_first(hoverName,"%s", type->heroClass->name);
@@ -840,6 +804,49 @@ void CGHeroInstance::initHero()
 		mana = manaLimit(); //after all bonuses are taken into account
 }
 
+void CGHeroInstance::initArmy(CCreatureSet *dst /*= NULL*/)
+{
+	if(!dst)
+		dst = this;
+
+	int howManyStacks = 0; //how many stacks will hero receives <1 - 3>
+	int pom = ran()%100;
+	int warMachinesGiven = 0;
+
+	if(pom < 9)
+		howManyStacks = 1;
+	else if(pom < 79)
+		howManyStacks = 2;
+	else
+		howManyStacks = 3;
+
+	for(int stackNo=0; stackNo<3; stackNo++)
+	{
+		int creID = (VLC->creh->nameToID[type->refTypeStack[stackNo]]);
+		int range = type->highStack[stackNo] - type->lowStack[stackNo];
+		int count = ran()%(range+1) + type->lowStack[stackNo];
+
+		if(creID>=145 && creID<=149) //war machine
+		{
+			warMachinesGiven++;
+			switch (creID)
+			{
+			case 145: //catapult
+				VLC->arth->equipArtifact(artifWorn, 16, 3, &bonuses);
+				break;
+			default:
+				VLC->arth->equipArtifact(
+					artifWorn,
+					9+CArtHandler::convertMachineID(creID,true),
+					CArtHandler::convertMachineID(creID,true),
+					&bonuses);
+				break;
+			}
+		}
+		else
+			dst->addStack(stackNo-warMachinesGiven, CStackInstance(creID, count));
+	}
+}
 void CGHeroInstance::initHeroDefInfo()
 {
 	if(!defInfo  ||  defInfo->id != HEROI_TYPE)

+ 1 - 0
hch/CObjectHandler.h

@@ -375,6 +375,7 @@ public:
 
 	void initHero(); 
 	void initHero(int SUBID); 
+	void initArmy(CCreatureSet *dst = NULL);
 	void recreateArtBonuses();
 	void giveArtifact (ui32 aid);
 	void initHeroDefInfo();

+ 24 - 5
lib/CGameState.cpp

@@ -778,7 +778,7 @@ bool CStack::doubleWide() const
 	return type->doubleWide;
 }
 
-CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available) const
+CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available, const CHeroClass *bannedClass /*= NULL*/) const
 {
 	CGHeroInstance *ret = NULL;
 
@@ -816,7 +816,8 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, co
 
 		for(std::map<ui32,CGHeroInstance *>::iterator i=available.begin(); i!=available.end(); i++)
 		{
-			if(pavailable.find(i->first)->second & 1<<player)
+			if(pavailable.find(i->first)->second & 1<<player
+				&& !bannedClass || i->second->type->heroClass != bannedClass)
 			{
 				pool.push_back(i->second);
 				sum += i->second->type->heroClass->selectionProbability[town->typeID]; //total weight
@@ -1351,10 +1352,12 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 
 
 	/*************************HEROES************************************************/
-	std::set<int> hids;
+	std::set<int> hids; //hero ids to create pool
+
 	for(unsigned int i=0; i<map->allowedHeroes.size(); i++) //add to hids all allowed heroes
 		if(map->allowedHeroes[i])
 			hids.insert(i);
+
 	for (unsigned int i=0; i<map->heroes.size();i++) //heroes instances initialization
 	{
 		if (map->heroes[i]->getOwner()<0)
@@ -1367,11 +1370,13 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 		players.find(vhi->getOwner())->second.heroes.push_back(vhi);
 		hids.erase(vhi->subID);
 	}
-	for (unsigned int i=0; i<map->objects.size();i++) //heroes instances initialization
+
+	for (unsigned int i=0; i<map->objects.size();i++) //prisons
 	{
 		if (map->objects[i]->ID == 62)
 			hids.erase(map->objects[i]->subID);
 	}
+
 	for(unsigned int i=0; i<map->predefinedHeroes.size(); i++)
 	{
 		if(!vstd::contains(hids,map->predefinedHeroes[i]->subID))
@@ -1381,17 +1386,20 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 		hpool.pavailable[map->predefinedHeroes[i]->subID] = 0xff;
 		hids.erase(map->predefinedHeroes[i]->subID);
 	}
-	BOOST_FOREACH(int hid, hids) //all not used allowed heroes go into the pool
+
+	BOOST_FOREACH(int hid, hids) //all not used allowed heroes go with default state into the pool
 	{
 		CGHeroInstance * vhi = new CGHeroInstance();
 		vhi->initHero(hid);
 		hpool.heroesPool[hid] = vhi;
 		hpool.pavailable[hid] = 0xff;
 	}
+
 	for(unsigned int i=0; i<map->disposedHeroes.size(); i++)
 	{
 		hpool.pavailable[map->disposedHeroes[i].ID] = map->disposedHeroes[i].players;
 	}
+
 	/*************************FOG**OF**WAR******************************************/		
 	for(std::map<ui8, PlayerState>::iterator k=players.begin(); k!=players.end(); ++k)
 	{
@@ -3422,6 +3430,17 @@ int CGameState::lossCheck( ui8 player ) const
 	return false;
 }
 
+std::map<ui32,CGHeroInstance *> CGameState::unusedHeroesFromPool()
+{
+	std::map<ui32,CGHeroInstance *> pool = hpool.heroesPool;
+	for ( std::map<ui8, PlayerState>::iterator i = players.begin() ; i != players.end();i++)
+		for(std::vector<CGHeroInstance *>::iterator j = i->second.availableHeroes.begin(); j != i->second.availableHeroes.end(); j++)
+			if(*j)
+				pool.erase((**j).subID);
+
+	return pool;
+}
+
 const CStack * BattleInfo::getNextStack() const
 {
 	std::vector<const CStack *> hlp;

+ 2 - 27
lib/CGameState.h

@@ -386,7 +386,7 @@ public:
 		std::map<ui32,CGHeroInstance *> heroesPool; //[subID] - heroes available to buy; NULL if not available
 		std::map<ui32,ui8> pavailable; // [subid] -> which players can recruit hero (binary flags)
 
-		CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available) const;
+		CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available, const CHeroClass *bannedClass = NULL) const;
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
@@ -430,6 +430,7 @@ public:
 	ui8 checkForStandardWin() const; //returns color of player that accomplished standard victory conditions or 255 if no winner
 	bool checkForStandardLoss(ui8 player) const; //checks if given player lost the game
 	void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild
+	std::map<ui32,CGHeroInstance *> unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
 
 	bool isVisible(int3 pos, int player);
 	bool isVisible(const CGObjectInstance *obj, int player);
@@ -445,32 +446,6 @@ public:
 		if(!h.saving)
 		{
 			loadTownDInfos();
-
-// 			//recreating towns/heroes vectors in players entries
-// 			for(int i=0; i<map->towns.size(); i++)
-// 				if(map->towns[i]->tempOwner < PLAYER_LIMIT)
-// 					getPlayer(map->towns[i]->tempOwner)->towns.push_back(map->towns[i]);
-// 			for(int i=0; i<map->heroes.size(); i++)
-// 				if(map->heroes[i]->tempOwner < PLAYER_LIMIT)
-// 					getPlayer(map->heroes[i]->tempOwner)->heroes.push_back(map->heroes[i]);
-// 			//recreating available heroes
-// 			for(std::map<ui8,PlayerState>::iterator i=players.begin(); i!=players.end(); i++)
-// 			{
-// 				for(size_t j=0; j < i->second.availableHeroes.size(); j++)
-// 				{
-// 					ui32 hlp = i->second.availableHeroes[j]->subID;
-// 					delete i->second.availableHeroes[j];
-// 					if(hlp != 0xffffffff)
-// 					{
-// 						assert(vstd::contains(hpool.heroesPool, hlp));
-// 						i->second.availableHeroes[j] = hpool.heroesPool[hlp];
-// 					}
-// 					else
-// 					{
-// 						i->second.availableHeroes[j] = NULL;
-// 					}
-// 				}
-// 			}
 		}
 	}
 

+ 14 - 4
lib/NetPacks.h

@@ -346,16 +346,26 @@ struct FoWChange : public CPackForClient //112
 
 struct SetAvailableHeroes : public CPackForClient //113
 {
-	SetAvailableHeroes(){type = 113;flags=0;};
+	SetAvailableHeroes()
+	{
+		type = 113;
+		for (int i = 0; i < AVAILABLE_HEROES_PER_PLAYER; i++)
+			army[i] = NULL;
+	}
+	~SetAvailableHeroes()
+	{
+		for (int i = 0; i < AVAILABLE_HEROES_PER_PLAYER; i++)
+			delete army[i];
+	}
 	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 	ui8 player;
-	si32 hid1, hid2;
-	ui8 flags; //1 - reset army of hero1; 2 - reset army of hero 2
+	si32 hid[AVAILABLE_HEROES_PER_PLAYER]; //-1 if no hero
+	CCreatureSet *army[AVAILABLE_HEROES_PER_PLAYER];
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & player & hid1 & hid2 & flags;
+		h & player & hid & army;
 	}
 };
 

+ 11 - 15
lib/NetPacksLib.cpp

@@ -190,22 +190,15 @@ DLL_EXPORT void FoWChange::applyGs( CGameState *gs )
 }
 DLL_EXPORT void SetAvailableHeroes::applyGs( CGameState *gs )
 {
-	gs->getPlayer(player)->availableHeroes.clear();
-
-	CGHeroInstance *h = (hid1>=0 ?  gs->hpool.heroesPool[hid1] : NULL);
-	gs->getPlayer(player)->availableHeroes.push_back(h);
-	if(h  &&  flags & 1)
-	{
-		h->clear();
-		h->addStack(0, CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1));
-	}
+	PlayerState *p = gs->getPlayer(player);
+	p->availableHeroes.clear();
 
-	h = (hid2>=0 ?  gs->hpool.heroesPool[hid2] : NULL);
-	gs->getPlayer(player)->availableHeroes.push_back(h);
-	if(flags & 2)
+	for (int i = 0; i < AVAILABLE_HEROES_PER_PLAYER; i++)
 	{
-		h->clear();
-		h->addStack(0, CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1));
+		CGHeroInstance *h = (hid[i]>=0 ?  gs->hpool.heroesPool[hid[i]] : NULL);
+		if(h && army[i])
+			h->setArmy(*army[i]);
+		p->availableHeroes.push_back(h);
 	}
 }
 
@@ -315,7 +308,10 @@ DLL_EXPORT void RemoveObject::applyGs( CGameState *gs )
 			h->visitedTown = NULL;
 		}
 
-		//TODO: add to the pool?
+		//return hero to the pool, so he may reappear in tavern
+		gs->hpool.heroesPool[h->subID] = h;
+		if(!vstd::contains(gs->hpool.pavailable, h->subID))
+			gs->hpool.pavailable[h->subID] = 0xff;
 	}
 	else if (obj->ID==CREI_TYPE  &&  gs->map->version > CMapHeader::RoE) //only fixed monsters can be a part of quest
 	{

+ 68 - 27
server/CGameHandler.cpp

@@ -545,6 +545,7 @@ askInterfaceForMove:
 
 void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
 {
+
 	BattleResultsApplied resultsApplied;
 	resultsApplied.player1 = bEndArmy1->tempOwner;
 	resultsApplied.player2 = bEndArmy2->tempOwner;
@@ -565,6 +566,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 	sides[0] = gs->curB->side1;
 	sides[1] = gs->curB->side2;
 
+	ui8 loser = sides[!battleResult.data->winner];
+
 	//end battle, remove all info, free memory
 	giveExp(*battleResult.data);
 	sendAndApply(battleResult.data);
@@ -598,6 +601,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 
 	// Necromancy if applicable.
 	const CGHeroInstance *winnerHero = battleResult.data->winner != 0 ? hero2 : hero1;
+	const CGHeroInstance *loserHero = battleResult.data->winner != 0 ? hero1 : hero2;
+
 	if (winnerHero) 
 	{
 		CStackInstance raisedStack = winnerHero->calculateNecromancy(*battleResult.data);
@@ -630,6 +635,27 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 	visitObjectAfterVictory = false; 
 
 	winLoseHandle(1<<sides[0] | 1<<sides[1]); //handle victory/loss of engaged players
+
+	int result = battleResult.get()->result;
+	if(result == 1 || result == 2) //loser has escaped or surrendered
+	{
+		SetAvailableHeroes sah;
+		sah.player = loser;
+		sah.hid[0] = loserHero->subID;
+		if(result == 1) //retreat
+		{
+			sah.army[0] = new CCreatureSet();
+			sah.army[0]->addToSlot(0, VLC->creh->nameToID[loserHero->type->refTypeStack[0]],1);
+		}
+
+		if(const CGHeroInstance *another =  getPlayerState(loser)->availableHeroes[1])
+			sah.hid[1] = another->subID;
+		else
+			sah.hid[1] = -1;
+
+		sendAndApply(&sah);
+	}
+
 	delete battleResult.data;
 }
 
@@ -916,29 +942,37 @@ void CGameHandler::newTurn()
 
 	for ( std::map<ui8, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
 	{
-		if(i->first == 255) continue;
-		else if(i->first > PLAYER_LIMIT) assert(0); //illegal player number!
+		if(i->first == 255) 
+			continue;
+		else if(i->first >= PLAYER_LIMIT) 
+			assert(0); //illegal player number!
 
 		std::pair<ui8,si32> playerGold(i->first,i->second.resources[6]);
 		hadGold.insert(playerGold); 
 
 		if(gs->getDate(1)==7) //first day of week - new heroes in tavern
 		{
+			const CTown *nativeTownType = &VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(i->first).castle];
+
 			SetAvailableHeroes sah;
 			sah.player = i->first;
-			CGHeroInstance *h = gs->hpool.pickHeroFor(true,i->first,&VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(i->first).castle], pool);
-			if(h)
-				sah.hid1 = h->subID;
-			else
-				sah.hid1 = -1;
-			h = gs->hpool.pickHeroFor(false,i->first,&VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(i->first).castle], pool);
-			if(h)
-				sah.hid2 = h->subID;
-			else
-				sah.hid2 = -1;
+
+			//pick heroes and their armies
+			CHeroClass *banned = NULL;
+			for (int j = 0; j < AVAILABLE_HEROES_PER_PLAYER; j++)
+			{
+				if(CGHeroInstance *h = gs->hpool.pickHeroFor(j == 0, i->first, nativeTownType, pool, banned)) //first hero - native if possible, second hero -> any other class
+				{
+					sah.hid[j] = h->subID;
+					h->initArmy(sah.army[j] = new CCreatureSet());
+					banned = h->type->heroClass;
+				}
+				else
+					sah.hid[j] = -1;
+			}
+
 			sendAndApply(&sah);
 		}
-		if(i->first>=PLAYER_LIMIT) continue;
 
 		n.res[i->first] = i->second.resources;
 // 		SetResources r;
@@ -3247,14 +3281,16 @@ bool CGameHandler::setFormation( si32 hid, ui8 formation )
 
 bool CGameHandler::hireHero( ui32 tid, ui8 hid )
 {
-	CGTownInstance *t = gs->getTown(tid);
+	const CGTownInstance *t = gs->getTown(tid);
+	const PlayerState *p = gs->getPlayer(t->tempOwner);
+
 	if(!vstd::contains(t->builtBuildings,5)  && complain("No tavern!")
-		|| gs->getPlayer(t->tempOwner)->resources[6]<2500  && complain("Not enough gold for buying hero!")
+		|| p->resources[6]<2500  && complain("Not enough gold for buying hero!")
 		|| t->visitingHero  && complain("There is visiting hero - no place!")
 		|| getHeroCount(t->tempOwner,false) >= 8 && complain("Cannot hire hero, only 8 wandering heroes are allowed!")
 		)
 		return false;
-	CGHeroInstance *nh = gs->getPlayer(t->tempOwner)->availableHeroes[hid];
+	CGHeroInstance *nh = p->availableHeroes[hid];
 	assert(nh);
 
 	HeroRecruited hr;
@@ -3265,25 +3301,30 @@ bool CGameHandler::hireHero( ui32 tid, ui8 hid )
 	sendAndApply(&hr);
 
 
-	std::map<ui32,CGHeroInstance *> pool = gs->hpool.heroesPool;
-	for ( std::map<ui8, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
-		for(std::vector<CGHeroInstance *>::iterator j = i->second.availableHeroes.begin(); j != i->second.availableHeroes.end(); j++)
-			if(*j)
-				pool.erase((**j).subID);
+	std::map<ui32,CGHeroInstance *> pool = gs->unusedHeroesFromPool();
+
+	const CGHeroInstance *theOtherHero = p->availableHeroes[!hid];
+	const CGHeroInstance *newHero = gs->hpool.pickHeroFor(false, t->tempOwner,t->town, pool, theOtherHero->type->heroClass);
 
 	SetAvailableHeroes sah;
-	CGHeroInstance *h1 = gs->hpool.pickHeroFor(false,t->tempOwner,t->town, pool),
-				*h2 = gs->getPlayer(t->tempOwner)->availableHeroes[!hid];
-	(hid ? sah.hid2 : sah.hid1) = h1 ? h1->subID : -1;
-	(hid ? sah.hid1 : sah.hid2) = h2 ? h2->subID : -1;
 	sah.player = t->tempOwner;
-	sah.flags = hid+1;
+
+	if(newHero)
+	{
+		sah.hid[hid] = newHero->subID;
+		sah.army[hid] = new CCreatureSet();
+		sah.army[hid]->addToSlot(0, VLC->creh->nameToID[newHero->type->refTypeStack[0]],1);
+	}
+	else
+		sah.hid[hid] = -1;
+
+	sah.hid[!hid] = theOtherHero ? theOtherHero->subID : -1;
 	sendAndApply(&sah);
 
 	SetResource sr;
 	sr.player = t->tempOwner;
 	sr.resid = 6;
-	sr.val = gs->getPlayer(t->tempOwner)->resources[6] - 2500;
+	sr.val = p->resources[6] - 2500;
 	sendAndApply(&sr);
 
 	vistiCastleObjects (t, nh);