Przeglądaj źródła

- Some work for commanders
- New creature window - visit forum for package with necessary graphics: http://forum.vcmi.eu/viewtopic.php?p=6908#6908
- More AI optimizations - heroes will be processed starting from the fastest one.
- AI will check is GATHER_ARMY object is reachable

DjWarmonger 13 lat temu
rodzic
commit
ee2768ab07

+ 26 - 7
AI/VCAI/VCAI.cpp

@@ -305,10 +305,10 @@ bool isCloser(const CGObjectInstance *lhs, const CGObjectInstance *rhs)
 	return (ln->moveRemains > rn->moveRemains);
 };
 
-//bool isCloser (int3 pos1, int3 pos, const CGHeroInstance * h)
-//{ //TODO
-//	return false;
-//};
+bool compareMovement(const CGHeroInstance *lhs, const CGHeroInstance *rhs)
+{
+	return lhs->movement > rhs->movement;
+};
 
 ui64 evaluateDanger(const CGObjectInstance *obj);
 
@@ -966,13 +966,29 @@ void VCAI::makeTurnInternal()
 	{
 		striveToGoal(CGoal(WIN));
 
-		auto safeCopy = lockedHeroes; //heroes tend to die in the process and loose their goals, unsafe to iterate it
+		std::vector<std::pair<const CGHeroInstance *, CGoal> > safeCopy; //heroes tend to die in the process and loose their goals, unsafe to iterate it
+		BOOST_FOREACH (auto h, lockedHeroes)
+		{
+			safeCopy.push_back(h);
+		}
+
+		//auto compareReinforcements = [h](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool
+		//   {
+		//		return howManyReinforcementsCanGet(h, lhs) < howManyReinforcementsCanGet(h, rhs);
+		//   };
+
+		auto lockedHeroesSorter = [](std::pair<const CGHeroInstance *, CGoal> h1, std::pair<const CGHeroInstance *, CGoal> h2) -> bool
+		{
+			return compareMovement (h1.first, h2.first);
+		};
+		boost::sort(safeCopy, lockedHeroesSorter);
+
 		while (safeCopy.size()) //continue our goals
 		{
 			auto it = safeCopy.begin();
 			if (it->first && it->first->tempOwner == playerID && vstd::contains(lockedHeroes, it->first)) //make sure hero still has his goal
 			{
-				cb->recalculatePaths(); //every time we change a hero
+				cb->setSelection(it->first);
 				striveToGoal (it->second);
 			}
 			safeCopy.erase(it);
@@ -2291,7 +2307,7 @@ TSubgoal CGoal::whatToDoToAchieve()
 						else
 							throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
 					}
-					boost::sort(hs, isCloser); //closer to what?
+					boost::sort(hs, compareMovement); //closer to what?
 				}
 			}
 
@@ -2507,7 +2523,10 @@ TSubgoal CGoal::whatToDoToAchieve()
 		I_AM_ELEMENTAR;
 	case GATHER_ARMY:
 		{
+			//TODO: find hero if none set
+
 			const CGHeroInstance *h = hero;
+			cb->setSelection(h);
 			auto compareReinforcements = [h](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool
 			{
 				return howManyReinforcementsCanGet(h, lhs) < howManyReinforcementsCanGet(h, rhs);

+ 1 - 0
AI/VCAI/VCAI.h

@@ -93,6 +93,7 @@ struct CGoal
 
 	static TSubgoal goVisitOrLookFor(const CGObjectInstance *obj); //if obj is NULL, then we'll explore
 	static TSubgoal lookForArtSmart(int aid); //checks non-standard ways of obtaining art (merchants, quests, etc.)
+	static TSubgoal tryRecruitHero();
 
 	int value; SETTER(int, value)
 	int resID; SETTER(int, resID)

+ 14 - 4
client/CCreatureWindow.cpp

@@ -207,14 +207,14 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 	luck = new MoraleLuckBox(false, genRect(42, 42, 387, 100));
 	luck->set(stack);
 
-	new CPicture(graphics->pskillsm->ourImages[4].bitmap, 335, 50, false); //exp icon - Print it always?
+	new CPicture(graphics->pskillsm->ourImages[4].bitmap, 387, 51, false); //exp icon - Print it always?
 	if (type) //not in fort window
 	{
 		if (GameConstants::STACK_EXP)
 		{
 			int rank = std::min(stack->getExpRank(), 10); //hopefully nobody adds more
-			printAtMiddle(CGI->generaltexth->zcrexp[rank] + " [" + boost::lexical_cast<std::string>(rank) + "]", 436, 62, FONT_MEDIUM, Colors::Jasmine,*bitmap);
-			printAtMiddle(boost::lexical_cast<std::string>(stack->experience), 436, 82, FONT_SMALL, Colors::Cornsilk,*bitmap);
+			printAtMiddle(CGI->generaltexth->zcrexp[rank] + " [" + boost::lexical_cast<std::string>(rank) + "]", 488, 62, FONT_MEDIUM, Colors::Jasmine,*bitmap);
+			printAtMiddle(boost::lexical_cast<std::string>(stack->experience), 488, 82, FONT_SMALL, Colors::Cornsilk,*bitmap);
 			if (type > BATTLE) //we need it only on adv. map
 			{
 				int tier = stack->type->level;
@@ -361,13 +361,23 @@ void CCreatureWindow::showAll(SDL_Surface * to)
 	printLine(0, CGI->generaltexth->primarySkillNames[0], c->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), stackNode->Attack());
 	printLine(1, CGI->generaltexth->primarySkillNames[1], c->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), stackNode->Defense());
 
-	if(stackNode->valOfBonuses(Bonus::SHOTS) && stackNode->hasBonusOfType(Bonus::SHOOTER))
+	if (stackNode->valOfBonuses(Bonus::SHOTS) && stackNode->hasBonusOfType(Bonus::SHOOTER))
 	{//only for shooting units - important with wog exp shooters
 		if (type == BATTLE)
 			printLine(2, CGI->generaltexth->allTexts[198], dynamic_cast<const CStack*>(stackNode)->shots);
 		else
 			printLine(2, CGI->generaltexth->allTexts[198], stackNode->valOfBonuses(Bonus::SHOTS));
 	}
+	if (stackNode->valOfBonuses(Bonus::CASTS))
+	{
+		printAtMiddle(CGI->generaltexth->allTexts[399], 356, 61, FONT_SMALL, Colors::Cornsilk,*bitmap);
+		std::string casts;
+		if (type == BATTLE)
+			casts = boost::lexical_cast<std::string>((ui16)dynamic_cast<const CStack*>(stackNode)->casts); //ui8 is converted to char :(
+		else
+			casts = boost::lexical_cast<std::string>(stackNode->valOfBonuses(Bonus::CASTS));
+		printAtMiddle(casts, 356, 82, FONT_SMALL, Colors::Cornsilk,*bitmap);
+	}
 
 	//TODO
 	int dmgMultiply = 1;

+ 1 - 1
lib/BattleState.cpp

@@ -2640,7 +2640,7 @@ void CStack::postInit()
 	firstHPleft = MaxHealth();
 	shots = getCreature()->valOfBonuses(Bonus::SHOTS);
 	counterAttacks = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
-	casts = valOfBonuses(Bonus::CASTS); //TODO: set them in cr_abils.txt
+	casts = valOfBonuses(Bonus::CASTS);
 	state.insert(EBattleStackState::ALIVE);  //alive state indication
 }
 

+ 43 - 1
lib/CCreatureSet.cpp

@@ -8,6 +8,7 @@
 #include "CGameState.h"
 #include "CGeneralTextHandler.h"
 #include "CSpellHandler.h"
+#include "CHeroHandler.h"
 
 const CStackInstance &CCreatureSet::operator[](TSlot slot) const
 {
@@ -687,8 +688,9 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
 		case Bonus::SPELL_AFTER_ATTACK:
 			fileName = "E_CAST.bmp"; break;
 		case Bonus::ENCHANTER:
-		case Bonus::RANDOM_SPELLCASTER:
 			fileName = "E_CAST1.bmp"; break;
+		case Bonus::RANDOM_SPELLCASTER:
+			fileName = "RandomBoost.bmp"; break;
 		case Bonus::SPELL_BEFORE_ATTACK:
 			fileName ="E_CAST2.bmp"; break;
 		case Bonus::SPELLCASTER:
@@ -735,6 +737,8 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
 			fileName = "E_KING3.bmp"; break;
 		case Bonus::CHANGES_SPELL_COST_FOR_ALLY:
 			fileName = "E_MANA.bmp"; break;
+		case Bonus::CHANGES_SPELL_COST_FOR_ENEMY:
+			fileName = "MagicDamper.bpm"; break;
 		case Bonus::NO_MELEE_PENALTY:
 			fileName = "E_MELEE.bmp"; break;
 		case Bonus::MIND_IMMUNITY:
@@ -858,6 +862,20 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
 			fileName = "E_UNDEAD.bmp"; break;
 		case Bonus::SPELL_RESISTANCE_AURA:
 			fileName = "E_UNIC.bmp"; break;
+		case Bonus::THREE_HEADED_ATTACK:
+			fileName = "ThreeHeaded.bmp"; break;
+		case Bonus::DAEMON_SUMMONING:
+			fileName = "RiseDemons.bmp"; break;
+		case Bonus::CHARGE_IMMUNITY:
+			fileName = "ChargeImmune.bmp"; break;
+		case Bonus::HEALER:
+			fileName = "Healer.bmp"; break;
+		case Bonus::CATAPULT:
+			fileName = "Catapult.bmp"; break;
+		case Bonus::MANA_CHANNELING:
+			fileName = "ManaChannel.bmp"; break;
+		case Bonus::MANA_DRAIN:
+			fileName = "ManaDrain.bmp"; break;
 	}
 	if(!fileName.empty())
 		fileName = "zvs/Lib1.res/" + fileName;
@@ -977,7 +995,31 @@ CCommanderInstance::~CCommanderInstance()
 
 void CCommanderInstance::setAlive (bool Alive)
 {
+	//TODO: helm of immortality
 	alive = Alive;
+	if (!alive)
+	{
+		//remove all bonuses from artifacts
+	}
+}
+
+void CCommanderInstance::giveStackExp (expType exp)
+{
+	if (alive)
+		experience += exp;
+}
+
+int CCommanderInstance::getExpRank() const
+{
+	return VLC->heroh->level (experience);
+}
+
+void CCommanderInstance::levelUp ()
+{
+	BOOST_FOREACH (auto bonus, VLC->creh->commanderLevelPremy)
+	{ //grant all regular level-up bonuses
+		accumulateBonus (*bonus);
+	}
 }
 
 CStackBasicDescriptor::CStackBasicDescriptor()

+ 3 - 1
lib/CCreatureSet.h

@@ -88,9 +88,11 @@ public:
 	CCommanderInstance (TCreature id);
 	~CCommanderInstance();
 	void setAlive (bool alive);
+	void giveStackExp (expType exp);
+	void levelUp ();
 
 	ui64 getPower() const {return 0;};
-	int getExpRank() const {return 0;};
+	int getExpRank() const;
 	ui8 bearerType() const OVERRIDE {return ArtBearer::COMMANDER;}; //from CArtifactSet
 
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 35 - 1
lib/NetPacks.h

@@ -286,7 +286,7 @@ struct SetSecSkill : public CPackForClient //106
 	{
 		h & abs & id & which & val;
 	}
-}; 
+};
 struct HeroVisitCastle : public CPackForClient //108
 {
 	HeroVisitCastle(){flags=0;type = 108;};
@@ -498,6 +498,23 @@ struct UpdateCampaignState : public CPackForClient //119
 		h & camp;
 	}
 };
+struct SetCommanderproperty : public CPackForClient //120
+{
+	enum ECommanderProperty {ALIVE, BONUS};
+
+	SetCommanderproperty(){type = 120;};
+	void applyCl(CClient *cl){};
+	DLL_LINKAGE void applyGs(CGameState *gs){};
+
+	ui8 which;
+	ui8 alive;
+	Bonus accumulatedBonus;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & which & alive & accumulatedBonus;
+	}
+}; 
 
 struct RemoveObject : public CPackForClient //500
 {
@@ -1122,6 +1139,23 @@ struct HeroLevelUp : public Query//2000
 	}
 };
 
+struct CommanderLevelUp : public Query
+{
+	void applyCl(CClient *cl){};
+	DLL_LINKAGE void applyGs(CGameState *gs){};
+
+	CCommanderInstance * commander;
+	std::vector<std::pair<ui8, ui8> > secondarySkills;
+	std::vector<Bonus *> specialSkills;
+
+	CommanderLevelUp(){type = 2005;};
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & id & commander & secondarySkills & specialSkills;
+	}
+};
+
 struct TradeComponents : public CPackForClient, public CPackForServer
 {
 ///used to handle info about components available in shops

+ 14 - 8
lib/NetPacksLib.cpp

@@ -991,18 +991,24 @@ void BattleResult::applyGs( CGameState *gs )
 			const_cast<CArmedInstance*>(s->base->armyObj)->eraseStack(s->slot);
 		}
 	}
-	for(unsigned i=0;i<gs->curB->stacks.size();i++)
+	for (unsigned i = 0; i < gs->curB->stacks.size(); i++)
 		delete gs->curB->stacks[i];
 
 	//remove any "until next battle" bonuses
 	CGHeroInstance *h;
-	h = gs->curB->heroes[0];
-	if(h)
-		h->getBonusList().remove_if(Bonus::OneBattle);
-
-	h = gs->curB->heroes[1];
-	if(h) 
-		h->getBonusList().remove_if(Bonus::OneBattle);
+	for (int i = 0; i < 2; ++i)
+	{
+		h = gs->curB->heroes[i];
+		if (h)
+		{
+			h->getBonusList().remove_if(Bonus::OneBattle);
+			if (h->commander)
+			{
+				h->commander->giveStackExp(exp[i]);
+				CBonusSystemNode::treeHasChanged();
+			}
+		}
+	}
 
 	if (GameConstants::STACK_EXP)
 	{

+ 1 - 0
lib/RegisterTypes.h

@@ -138,6 +138,7 @@ void registerTypes2(Serializer &s)
 	s.template registerType<SetObjectProperty>();
 	s.template registerType<SetHoverName>();
 	s.template registerType<HeroLevelUp>();
+	s.template registerType<CommanderLevelUp>();
 	s.template registerType<BlockingDialog>();
 	s.template registerType<GarrisonDialog>();
 	s.template registerType<BattleStart>();

+ 47 - 3
server/CGameHandler.cpp

@@ -285,6 +285,50 @@ void CGameHandler::levelUpHero(int ID)
 	}
 }
 
+void CGameHandler::levelUpCommander (const CCommanderInstance * c, int secondarySkill, int specialSKill)
+{
+	if (secondarySkill >=0 )
+	{
+		//c->secondarySkills[secondarySkill]++; //TODO: make sure to resize vector in first place
+	}
+	if (specialSKill >= 0)
+	{
+		auto it = VLC->creh->skillRequirements.begin();
+		std::advance(it, specialSKill); //suboptimal, use bmap?
+		//c->accumulateBonus(it->first);
+	}
+	levelUpCommander (c);
+	//c->levelUp(); //change standard parameters
+}
+
+void CGameHandler::levelUpCommander(const CCommanderInstance * c)
+{
+	return;
+	CommanderLevelUp clu;
+
+	//picking sec. skills for choice
+
+	int secondarySkill = -1, specialSkill = -1;
+
+	int skills = clu.secondarySkills.size() + clu.specialSkills.size();
+
+	if (skills > 1) //apply and ask for secondary skill
+	{
+		//auto callback = boost::bind (callWith<ui16>, clu.specialSkills, boost::bind(&CGameHandler::levelUpCommander, this, c, _1), _1);
+		//applyAndAsk (&clu, c->armyObj->tempOwner, callback); //call levelUpCommander when client responds
+	}
+	else if (skills == 1) //apply, give only possible skill  and send info
+	{
+		sendAndApply(&clu);
+		levelUpCommander(c, secondarySkill, specialSkill);
+	}
+	else //apply and send info
+	{
+		sendAndApply(&clu);
+		levelUpCommander(c);
+	}
+}
+
 void CGameHandler::changePrimSkill(int ID, int which, si64 val, bool abs)
 {
 	SetPrimSkill sps;
@@ -298,9 +342,9 @@ void CGameHandler::changePrimSkill(int ID, int which, si64 val, bool abs)
 	if(which==4)
 	{
 		levelUpHero(ID);
-
-		//TODO: Commander
-		//TODO: Stack Experience only after battle
+		CGHeroInstance *h = static_cast<CGHeroInstance *>(gs->map->objects[ID].get());
+		if (h->commander)
+			levelUpCommander (h->commander);
 	}
 }
 

+ 2 - 0
server/CGameHandler.h

@@ -182,6 +182,8 @@ public:
 	void vistiCastleObjects (const CGTownInstance *t, const CGHeroInstance *h);
 	void levelUpHero(int ID, int skill);//handle client respond and send one more request if needed
 	void levelUpHero(int ID);//initial call - check if hero have remaining levelups & handle them
+	void levelUpCommander (const CCommanderInstance * c, int secondarySkill, int specialSKill);
+	void levelUpCommander (const CCommanderInstance * c);
 	void afterBattleCallback(); // called after level-ups are finished
 	//////////////////////////////////////////////////////////////////////////