瀏覽代碼

* support for build grail victory condition
* improved formula for necromancy to match better OH3
* Support for new town structures:
- Lighthouse
- Colossus
- Guardian Spirit
- Necromancy Amplifier
- Soul Prison

Michał W. Urbańczyk 15 年之前
父節點
當前提交
bb80d4bc02
共有 9 個文件被更改,包括 84 次插入23 次删除
  1. 12 5
      client/CPlayerInterface.cpp
  2. 1 1
      client/CPlayerInterface.h
  3. 44 8
      hch/CObjectHandler.cpp
  4. 8 4
      lib/CGameState.cpp
  5. 4 1
      lib/CGameState.h
  6. 1 1
      lib/Connection.h
  7. 9 1
      lib/HeroBonus.cpp
  8. 3 1
      lib/HeroBonus.h
  9. 2 1
      server/CGameHandler.cpp

+ 12 - 5
client/CPlayerInterface.cpp

@@ -832,10 +832,8 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
 {
 	waitWhileDialog();
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-
-	if(stillMoveHero.get() == DURING_MOVE)//if we are in the middle of hero movement
-		stillMoveHero.setn(STOP_MOVE); //after showing dialog movement will be stopped
-
+	
+	stopMovement();
 	CInfoWindow *temp = CInfoWindow::create(text, playerID, &components);
 	if(makingTurn && GH.listInt.size() && LOCPLINT == this)
 	{
@@ -852,6 +850,8 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
 void CPlayerInterface::showYesNoDialog(const std::string &text, const std::vector<SComponent*> & components, CFunctionList<void()> onYes, CFunctionList<void()> onNo, bool DelComps)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
+
+	stopMovement();
 	LOCPLINT->showingDialog->setn(true);
 	CInfoWindow::showYesNoDialog(text, &components, onYes, onNo, DelComps, playerID);
 }
@@ -860,7 +860,8 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v
 {
 	waitWhileDialog();
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-
+	
+	stopMovement();
 	CGI->soundh->playSound(static_cast<soundBase::soundID>(soundID));
 
 	if(!selection && cancel) //simple yes/no dialog
@@ -1942,3 +1943,9 @@ void CPlayerInterface::battleNewRoundFirst( int round )
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	battleInt->newRoundFirst(round);
 }
+
+void CPlayerInterface::stopMovement()
+{
+	if(stillMoveHero.get() == DURING_MOVE)//if we are in the middle of hero movement
+		stillMoveHero.setn(STOP_MOVE); //after showing dialog movement will be stopped
+}

+ 1 - 1
client/CPlayerInterface.h

@@ -213,7 +213,7 @@ public:
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
 	void showInfoDialog(const std::string &text, const std::vector<SComponent*> & components = std::vector<SComponent*>(), int soundID = 0);
 	void showYesNoDialog(const std::string &text, const std::vector<SComponent*> & components, CFunctionList<void()> onYes, CFunctionList<void()> onNo, bool DelComps); //deactivateCur - whether current main interface should be deactivated; delComps - if components will be deleted on window close
-
+	void stopMovement();
 	bool moveHero(const CGHeroInstance *h, CGPath path);
 	void initMovement(const TryMoveHero &details, const CGHeroInstance * ho, const int3 &hp );//initializing objects and performing first step of move
 	void movementPxStep( const TryMoveHero &details, int i, const int3 &hp, const CGHeroInstance * ho );//performing step of movement

+ 44 - 8
hch/CObjectHandler.cpp

@@ -960,23 +960,27 @@ CStackInstance CGHeroInstance::calculateNecromancy (const BattleResult &battleRe
 	const ui8 necromancyLevel = getSecSkillLevel(12);
 
 	// Hero knows necromancy.
-	if (necromancyLevel > 0) {
+	if (necromancyLevel > 0) 
+	{
 		double necromancySkill = necromancyLevel*0.1
 			+ valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 12)/100.0;
+		amin(necromancySkill, 1.0); //it's impossible to raise more creatures than all...
 		const std::map<ui32,si32> &casualties = battleResult.casualties[!battleResult.winner];
 		ui32 raisedUnits = 0;
 
-		// Get lost enemy hit points convertible to units.
-		for (std::map<ui32,si32>::const_iterator it = casualties.begin(); it != casualties.end(); it++)
-			raisedUnits += VLC->creh->creatures[it->first]->valOfBonuses(Bonus::STACK_HEALTH) * it->second;
-		raisedUnits *= necromancySkill;
-
 		// Figure out what to raise and how many.
 		const ui32 creatureTypes[] = {56, 58, 60, 64}; // IDs for Skeletons, Walking Dead, Wights and Liches respectively.
 		const bool improvedNecromancy = hasBonusOfType(Bonus::IMPROVED_NECROMANCY);
-		CCreature *raisedUnitType = VLC->creh->creatures[creatureTypes[improvedNecromancy ? necromancyLevel : 0]];
+		const CCreature *raisedUnitType = VLC->creh->creatures[creatureTypes[improvedNecromancy ? necromancyLevel : 0]];
+		const ui32 raisedUnitHP = raisedUnitType->valOfBonuses(Bonus::STACK_HEALTH);
 
-		raisedUnits /= raisedUnitType->valOfBonuses(Bonus::STACK_HEALTH);
+		//calculate creatures raised from each defeated stack
+		for (std::map<ui32,si32>::const_iterator it = casualties.begin(); it != casualties.end(); it++)
+		{
+			// Get lost enemy hit points convertible to units.
+			const ui32 raisedHP = VLC->creh->creatures[it->first]->valOfBonuses(Bonus::STACK_HEALTH) * it->second * necromancySkill;
+			raisedUnits += std::min<ui32>(raisedHP / raisedUnitHP, it->second * necromancySkill); //limit to % of HP and % of original stack count
+		}
 
 		// Make room for new units.
 		int slot = getSlotFor(raisedUnitType->idNumber);
@@ -1144,6 +1148,19 @@ void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const
 		//luck skill
 		if(int luckSkill = getSecSkillLevel(9)) 
 			out.push_back(Bonus(Bonus::PERMANENT, Bonus::LUCK, Bonus::SECONDARY_SKILL, luckSkill, 9, VLC->generaltexth->arraytxt[73+luckSkill]));
+
+		//guardian spirit
+		BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns)
+			if(t->subID ==1 && vstd::contains(t->builtBuildings,26)) //rampart with grail
+				out.push_back(Bonus(Bonus::PERMANENT, Bonus::LUCK, Bonus::TOWN_STRUCTURE, +2, 26, VLC->generaltexth->buildings[1][26].first + " +2"));
+	}
+
+	if(Selector::matchesType(selector, Bonus::SEA_MOVEMENT))
+	{
+		//lighthouses
+		BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns)
+			if(t->subID == 0 && vstd::contains(t->builtBuildings,17)) //castle
+				out.push_back(Bonus(Bonus::PERMANENT, Bonus::SEA_MOVEMENT, Bonus::TOWN_STRUCTURE, +500, 17, VLC->generaltexth->buildings[0][17].first + " +500"));
 	}
 
 	if(Selector::matchesType(selector, Bonus::MORALE))
@@ -1151,6 +1168,25 @@ void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const
 		//leadership
 		if(int moraleSkill = getSecSkillLevel(6)) 
 			out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::SECONDARY_SKILL, moraleSkill, 6, VLC->generaltexth->arraytxt[104+moraleSkill]));
+
+		//colossus
+		BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns)
+			if(t->subID == 0 && vstd::contains(t->builtBuildings,26)) //castle
+				out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::TOWN_STRUCTURE, +2, 26, VLC->generaltexth->buildings[0][26].first + " +2"));
+	}
+
+	if(Selector::matchesTypeSubtype(selector, Bonus::SECONDARY_SKILL_PREMY, 12)) //necromancy
+	{
+		BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns)
+		{
+			if(t->subID == 4) //necropolis
+			{
+				if(vstd::contains(t->builtBuildings,21)) //necromancy amplifier
+					out.push_back(Bonus(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, Bonus::TOWN_STRUCTURE, +10, 21, VLC->generaltexth->buildings[4][21].first + " +10%", 12));
+				if(vstd::contains(t->builtBuildings,26)) //grail - Soul prison
+					out.push_back(Bonus(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, Bonus::TOWN_STRUCTURE, +20, 26, VLC->generaltexth->buildings[4][26].first + " +20%", 12));
+			}
+		}
 	}
 }
 

+ 8 - 4
lib/CGameState.cpp

@@ -3061,10 +3061,10 @@ int CGameState::victoryCheck( ui8 player ) const
 			break;
 
 		case buildGrail:
-			for(size_t i = 0; i < map->towns.size(); i++)
-				if(map->towns[i]->pos == map->victoryCondition.pos
-					&& map->towns[i]->tempOwner == player 
-					&& vstd::contains(map->towns[i]->builtBuildings, 26))
+			BOOST_FOREACH(const CGTownInstance *t, map->towns)
+				if((t == map->victoryCondition.obj || !map->victoryCondition.obj)
+					&& t->tempOwner == player 
+					&& vstd::contains(t->builtBuildings, 26))
 					return 1;
 			break;
 
@@ -3760,6 +3760,10 @@ void PlayerState::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL
 	//TODO: global effects
 }
 
+void PlayerState::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
+{
+}
+
 InfoAboutHero::InfoAboutHero()
 {
 	details = NULL;

+ 4 - 1
lib/CGameState.h

@@ -128,7 +128,10 @@ public:
 	ui8 daysWithoutCastle;
 
 	PlayerState();
-	virtual void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
+
+	//override
+	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const; 
+	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 1 - 1
lib/Connection.h

@@ -21,7 +21,7 @@
 #include <boost/mpl/int.hpp>
 #include <boost/mpl/identity.hpp>
 
-const ui32 version = 720;
+const ui32 version = 721;
 class CConnection;
 class CGObjectInstance;
 class CGameState;

+ 9 - 1
lib/HeroBonus.cpp

@@ -332,10 +332,18 @@ namespace Selector
 		return CSelectFieldEqual<ui8>(&Bonus::source, source) && CSelectFieldEqual<ui32>(&Bonus::id, sourceID);
 	}
 
-	bool matchesType(const CSelector &sel, TBonusType type)
+	bool DLL_EXPORT matchesType(const CSelector &sel, TBonusType type)
 	{
 		Bonus dummy;
 		dummy.type = type;
 		return sel(dummy);
 	}
+
+	bool DLL_EXPORT matchesTypeSubtype(const CSelector &sel, TBonusType type, TBonusSubtype subtype)
+	{
+		Bonus dummy;
+		dummy.type = type;
+		dummy.subtype = subtype;
+		return sel(dummy);
+	}
 }

+ 3 - 1
lib/HeroBonus.h

@@ -186,7 +186,8 @@ struct DLL_EXPORT Bonus
 	{
 		NO_LIMIT = 0, 
 		ONLY_DISTANCE_FIGHT=1, ONLY_MELEE_FIGHT, //used to mark bonuses for attack/defense primary skills from spells like Precision (distance only)
-		ONLY_ALLIED_ARMY, ONLY_ENEMY_ARMY
+		ONLY_ALLIED_ARMY, ONLY_ENEMY_ARMY,
+		PLAYR_HEROES
 	};
 
 	enum ValueType
@@ -443,4 +444,5 @@ namespace Selector
 	CSelector DLL_EXPORT source(ui8 source, ui32 sourceID);
 
 	bool DLL_EXPORT matchesType(const CSelector &sel, TBonusType type);
+	bool DLL_EXPORT matchesTypeSubtype(const CSelector &sel, TBonusType type, TBonusSubtype subtype);
 }

+ 2 - 1
server/CGameHandler.cpp

@@ -2482,7 +2482,8 @@ bool CGameHandler::buildStructure( si32 tid, si32 bid )
 		vistiCastleObjects (t, t->visitingHero);
 	if(t->garrisonHero)
 		vistiCastleObjects (t, t->garrisonHero);
-	
+
+	checkLossVictory(t->tempOwner);
 	return true;
 }
 bool CGameHandler::razeStructure (si32 tid, si32 bid)