Browse Source

* Necromancy implemented. Not sure if they client/server communication is right, but it works.
* Cloak of the Undead King implemented as well.

OnionKnight 16 năm trước cách đây
mục cha
commit
e60c6785a4
3 tập tin đã thay đổi với 100 bổ sung2 xóa
  1. 73 0
      hch/CObjectHandler.cpp
  2. 2 0
      hch/CObjectHandler.h
  3. 25 2
      server/CGameHandler.cpp

+ 73 - 0
hch/CObjectHandler.cpp

@@ -1013,6 +1013,79 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
 	return false;
 }
 
+/**
+ * Calculates what creatures and how many to be raised from a battle.
+ * @param battleResult The results of the battle.
+ * @return Returns a pair with the first value indicating the ID of the creature
+ * type and second value the amount. Both values are returned as -1 if necromancy
+ * could not be applied.
+ */
+std::pair<ui32, si32> CGHeroInstance::calculateNecromancy (BattleResult &battleResult) const
+{
+	const ui8 necromancyLevel = getSecSkillLevel(12);
+
+	// Hero knows necromancy.
+	if (necromancyLevel > 0) {
+		double necromancySkill = necromancyLevel*0.1
+			+ valOfBonuses(HeroBonus::SECONDARY_SKILL_PREMY, 12)/100.0;
+		const std::set<std::pair<ui32, si32> > &casualties = battleResult.casualties[!battleResult.winner];
+		ui32 raisedUnits = 0;
+
+		// Get lost enemy hit points convertible to units.
+		for (std::set<std::pair<ui32, si32> >::const_iterator it = casualties.begin(); it != casualties.end(); it++)
+			raisedUnits += VLC->creh->creatures[it->first].hitPoints*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(HeroBonus::IMPROVED_NECROMANCY);
+		CCreature *raisedUnitType = &VLC->creh->creatures[creatureTypes[improvedNecromancy ? necromancyLevel : 0]];
+
+		raisedUnits /= raisedUnitType->hitPoints;
+
+		// Make room for new units.
+		int slot = army.getSlotFor(raisedUnitType->idNumber);
+		if (slot == -1) {
+			// If there's no room for unit, try it's upgraded version 2/3rds the size.
+			raisedUnitType = &VLC->creh->creatures[*raisedUnitType->upgrades.begin()];
+			raisedUnits = (raisedUnits*2)/3;
+
+			slot = army.getSlotFor(raisedUnitType->idNumber);
+		}
+		if (raisedUnits <= 0)
+			raisedUnits = 1;
+
+		return std::pair<ui32, si32>(raisedUnitType->idNumber, raisedUnits);
+	}
+
+	return std::pair<ui32, si32>(-1, -1);
+}
+
+/**
+ * Show the necromancy dialog with information about units raised.
+ * @param raisedStack Pair where the first element represents ID of the raised creature
+ * and the second element the amount.
+ */
+void CGHeroInstance::showNecromancyDialog (std::pair<ui32, si32> raisedStack) const
+{
+	const CCreature &unitType = VLC->creh->creatures[raisedStack.first];
+	InfoWindow iw;
+	iw.soundID = soundBase::GENIE;
+	iw.player = tempOwner;
+	iw.components.push_back(Component(3, unitType.idNumber, raisedStack.second, 0));
+
+	if (raisedStack.second > 1) { // Practicing the dark arts of necromancy, ... (plural)
+		iw.text.addTxt(MetaString::GENERAL_TXT, 145);
+		iw.text.addReplacement(raisedStack.second);
+		iw.text.addReplacement(MetaString::CRE_PL_NAMES, unitType.idNumber);
+	} else { // Practicing the dark arts of necromancy, ... (singular)
+		iw.text.addTxt(MetaString::GENERAL_TXT, 146);
+		iw.text.addReplacement(MetaString::CRE_SING_NAMES, unitType.idNumber);
+	}
+
+	cb->showInfoDialog(&iw);
+}
+
 int3 CGHeroInstance::getSightCenter() const
 {
 	return getPosition(false);

+ 2 - 0
hch/CObjectHandler.h

@@ -293,6 +293,8 @@ public:
 	int getTotalStrength() const;
 	ui8 getSpellSchoolLevel(const CSpell * spell) const; //returns level on which given spell would be cast by this hero
 	bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
+	std::pair<ui32, si32> calculateNecromancy (BattleResult &battleResult) const;
+	void showNecromancyDialog (std::pair<ui32, si32> raisedStack) const;
 
 	//////////////////////////////////////////////////////////////////////////
 

+ 25 - 2
server/CGameHandler.cpp

@@ -450,9 +450,32 @@ askInterfaceForMove:
 	if(cb)
 		cb(battleResult.data);
 
-	delete battleResult.data;
-	
 	sendAndApply(&resultsApplied);
+
+	// Necromancy if applicable.
+	const CGHeroInstance *winnerHero = battleResult.data->winner != 0 ? hero2 : hero1;
+	if (winnerHero) {
+		std::pair<ui32, si32> raisedStack = winnerHero->calculateNecromancy(*battleResult.data);
+
+		// Give raised units to winner and show dialog, if any were raised.
+		if (raisedStack.first != -1) {
+			int slot = winnerHero->army.getSlotFor(raisedStack.first);
+
+			if (slot != -1) {
+				SetGarrisons sg;
+
+				sg.garrs[winnerHero->id] = winnerHero->army;
+				if (vstd::contains(winnerHero->army.slots, slot)) // Add to existing stack.
+					sg.garrs[winnerHero->id].slots[slot].second += raisedStack.second;
+				else // Create a new stack.
+					sg.garrs[winnerHero->id].slots[slot] = raisedStack;
+				winnerHero->showNecromancyDialog(raisedStack);
+				sendAndApply(&sg);
+			}
+		}
+	}
+
+	delete battleResult.data;
 }
 
 void CGameHandler::prepareAttacked(BattleStackAttacked &bsa, CStack *def)