Преглед на файлове

Extract some spell imuunity mechanics + draft of overall design (UNTESTED)

AlexVinS преди 11 години
родител
ревизия
61d6bca3ff
променени са 2 файла, в които са добавени 162 реда и са изтрити 4 реда
  1. 137 2
      lib/CSpellHandler.cpp
  2. 25 2
      lib/CSpellHandler.h

+ 137 - 2
lib/CSpellHandler.cpp

@@ -10,6 +10,9 @@
 #include "CModHandler.h"
 #include "StringConstants.h"
 
+#include "mapObjects/CGHeroInstance.h"
+#include "BattleState.h"
+
 /*
  * CSpellHandler.cpp, part of VCMI engine
  *
@@ -128,6 +131,96 @@ namespace SRSLPraserHelpers
 	}
 }
 
+
+///CSpellMechanics
+CSpellMechanics::CSpellMechanics(CSpell * s):
+	owner(s)
+{
+	
+}
+
+CSpellMechanics::~CSpellMechanics()
+{
+	
+}
+
+ESpellCastProblem::ESpellCastProblem CSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj)
+{
+	//by default no immunity
+	return ESpellCastProblem::OK;
+}
+
+namespace
+{
+	class CloneMechnics: public CSpellMechanics
+	{
+	public:
+		CloneMechnics(CSpell * s): CSpellMechanics(s){};
+		ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
+	};
+	
+	class DispellHelpfulMechanics: public CSpellMechanics
+	{
+	public:
+		DispellHelpfulMechanics(CSpell * s): CSpellMechanics(s){};
+		ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;	
+	};
+	
+	
+	///CloneMechanics
+	ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack * obj)
+	{
+		//can't clone already cloned creature
+		if (vstd::contains(obj->state, EBattleStackState::CLONED))
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+		//TODO: how about stacks casting Clone?
+		//currently Clone casted by stack is assumed Expert level
+		ui8 schoolLevel;
+		if (caster)
+		{
+			schoolLevel = caster->getSpellSchoolLevel(owner);
+		}
+		else
+		{
+			schoolLevel = 3;
+		}
+
+		if (schoolLevel < 3)
+		{
+			int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
+			int creLevel = obj->getCreature()->level;
+			if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
+				return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+		}
+		
+		return CSpellMechanics::isImmuneByStack(caster,mode,obj);	
+	}
+	
+	///DispellHelpfulMechanics
+	ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj)
+	{
+		TBonusListPtr spellBon = obj->getSpellBonuses();
+		bool hasPositiveSpell = false;
+		for(const Bonus * b : *spellBon)
+		{
+			if(SpellID(b->sid).toSpell()->isPositive())
+			{
+				hasPositiveSpell = true;
+				break;
+			}
+		}
+		if(!hasPositiveSpell)
+		{
+			return ESpellCastProblem::NO_SPELLS_TO_DISPEL;
+		}		
+		return CSpellMechanics::isImmuneByStack(caster,mode,obj);	
+	}
+	
+	
+}
+
+
+///CSpell::LevelInfo
 CSpell::LevelInfo::LevelInfo()
 	:description(""),cost(0),power(0),AIValue(0),smartTarget(true),range("0")
 {
@@ -139,7 +232,7 @@ CSpell::LevelInfo::~LevelInfo()
 
 }
 
-
+///CSpell
 CSpell::CSpell():
 	id(SpellID::NONE), level(0),
 	earth(false), water(false), fire(false), air(false),
@@ -148,13 +241,15 @@ CSpell::CSpell():
 	mainEffectAnim(-1),
 	defaultProbability(0),
 	isRising(false), isDamage(false), isOffensive(false),
-	targetType(ETargetType::NO_TARGET)
+	targetType(ETargetType::NO_TARGET),
+	mechanics(nullptr)
 {
 	levels.resize(GameConstants::SPELL_SCHOOL_LEVELS);
 }
 
 CSpell::~CSpell()
 {
+	delete mechanics;
 }
 
 const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
@@ -495,6 +590,18 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj)
 	return ESpellCastProblem::NOT_DECIDED;
 }
 
+ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) const
+{
+	const auto immuneResult = isImmuneBy(obj);
+	
+	if (ESpellCastProblem::NOT_DECIDED != immuneResult) 
+		return immuneResult;
+		
+	return mechanics->isImmuneByStack(caster,mode,obj);
+
+}
+
+
 void CSpell::setIsOffensive(const bool val)
 {
 	isOffensive = val;
@@ -516,6 +623,30 @@ void CSpell::setIsRising(const bool val)
 	}
 }
 
+void CSpell::setupMechanics()
+{
+	if(nullptr != mechanics)
+	{
+		logGlobal->errorStream() << "Spell " << this->name << " mechanics already set";
+		delete mechanics;
+		mechanics = nullptr;	
+	}
+	
+	switch (id)
+	{
+	case SpellID::CLONE:
+		mechanics = new CloneMechnics(this);
+		break;
+	case SpellID::DISPEL_HELPFUL_SPELLS:
+		mechanics = new DispellHelpfulMechanics(this);
+		break;	
+	default:
+		mechanics = new CSpellMechanics(this);
+		break;
+	}
+	
+}
+
 
 
 bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
@@ -527,6 +658,7 @@ bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
 		return false;
 }
 
+///CSpellHandler
 CSpellHandler::CSpellHandler()
 {
 
@@ -854,9 +986,12 @@ void CSpellHandler::afterLoadFinalization()
 {
 	//FIXME: it is a bad place for this code, should refactor loadFromJson to know object id during loading
 	for(auto spell: objects)
+	{
 		for(auto & level: spell->levels)
 			for(auto & bonus: level.effects)
 				bonus.sid = spell->id;
+		spell->setupMechanics();
+	}
 }
 
 void CSpellHandler::beforeValidate(JsonNode & object)

+ 25 - 2
lib/CSpellHandler.h

@@ -19,6 +19,20 @@
 
 class CLegacyConfigParser;
 struct BattleHex;
+class CSpell;
+class CGHeroInstance;
+class CStack;
+
+class DLL_LINKAGE CSpellMechanics
+{
+public:
+	CSpellMechanics(CSpell * s);
+	virtual ~CSpellMechanics();	
+	
+	virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj);
+protected:
+	CSpell * owner;	
+};
 
 class DLL_LINKAGE CSpell
 {
@@ -109,6 +123,9 @@ public:
 	void getEffects(std::vector<Bonus> &lst, const int level) const;
 
 	ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
+	
+	//checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
+	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) const;
 
 	si32 getCost(const int skillLevel) const;
 
@@ -147,7 +164,9 @@ public:
 		h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll;
 
 		h & levels;
-
+		
+		if(!h.saving)
+			setupMechanics();
 	}
 	friend class CSpellHandler;
 	friend class Graphics;
@@ -155,7 +174,9 @@ public:
 private:
 	void setIsOffensive(const bool val);
 	void setIsRising(const bool val);
-
+	
+	//call this after load or deserialization. cant be done in constructor.
+	void setupMechanics();
 private:
 	si32 defaultProbability;
 
@@ -186,6 +207,8 @@ private:
 	std::string castSound;
 
 	std::vector<LevelInfo> levels;
+	
+	CSpellMechanics * mechanics;//(!) do not serialize
 };