Browse Source

Start moving server side spell mechanics

AlexVinS 11 năm trước cách đây
mục cha
commit
0fda90c67d
4 tập tin đã thay đổi với 172 bổ sung19 xóa
  1. 9 0
      lib/CSpellHandler.cpp
  2. 14 7
      lib/CSpellHandler.h
  3. 136 6
      lib/SpellMechanics.cpp
  4. 13 6
      lib/SpellMechanics.h

+ 9 - 0
lib/CSpellHandler.cpp

@@ -101,6 +101,12 @@ CSpell::~CSpell()
 	delete mechanics;
 }
 
+void CSpell::battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const
+{
+	if(!mechanics->battleCast(env, parameters))
+		logGlobal->errorStream() << "Internal error during spell cast";	
+}
+
 bool CSpell::isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const
 {
 	if(!hasSpellBook)
@@ -552,6 +558,9 @@ void CSpell::setupMechanics()
 	case SpellID::FORCE_FIELD:
 		mechanics = new WallMechanics(this);
 		break;
+	case SpellID::LAND_MINE:
+	case SpellID::QUICKSAND:
+		mechanics = new ObstacleMechanics(this);
 	default:		
 		if(isRisingSpell())
 			mechanics = new SpecialRisingSpellMechanics(this);

+ 14 - 7
lib/CSpellHandler.h

@@ -29,6 +29,7 @@ class CStack;
 class CBattleInfoCallback;
 
 struct CPackForClient;
+class CRandomGenerator;
 
 struct SpellSchoolInfo
 {
@@ -44,25 +45,28 @@ struct SpellSchoolInfo
 class DLL_LINKAGE SpellCastEnvironment
 {
 public:
-	virtual void sendAndApply(CPackForClient * info) = 0;
+	virtual void sendAndApply(CPackForClient * info) const  = 0;
+	
+	virtual CRandomGenerator & getRandomGenerator() const  = 0;
 };
 
 ///helper struct
-struct DLL_LINKAGE SpellCastContext
+struct DLL_LINKAGE BattleSpellCastParameters
 {
 public:
-	const SpellCastEnvironment * env;
-	
 	int spellLvl;
-//	BattleHex destination;
+	BattleHex destination;
 	ui8 casterSide;
 	PlayerColor casterColor;
 	const CGHeroInstance * caster;
 	const CGHeroInstance * secHero;
 	int usedSpellPower;
 	ECastingMode::ECastingMode mode;
-	const CStack * targetStack;
-	const CStack * selectedStack;	
+	const CStack * casterStack;
+	const CStack * selectedStack;
+	
+	const CBattleInfoCallback * cb;
+		
 };
 
 
@@ -148,6 +152,9 @@ public:
 	CSpell();
 	~CSpell();
 	
+	//void adventureCast() const; 
+	void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const; 	
+	
 	bool isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const;
 
 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr ) const; //convert range to specific hexes; last optional out parameter is set to true, if spell would cover unavailable hexes (that are not included in ret)

+ 136 - 6
lib/SpellMechanics.cpp

@@ -13,6 +13,7 @@
 
 #include "mapObjects/CGHeroInstance.h"
 #include "BattleState.h"
+#include "CRandomGenerator.h"
 
 #include "NetPacks.h"
 
@@ -129,14 +130,143 @@ ISpellMechanics::ISpellMechanics(CSpell * s):
 
 ///DefaultSpellMechanics
 
-bool DefaultSpellMechanics::adventureCast(const SpellCastContext& context) const
-{
-	return false; //there is no general algorithm for casting adventure spells
-}
+//bool DefaultSpellMechanics::adventureCast(const SpellCastContext& context) const
+//{
+//	return false; //there is no general algorithm for casting adventure spells
+//}
 
-bool DefaultSpellMechanics::battleCast(const SpellCastContext& context) const
+bool DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const
 {
-	return false; //todo; DefaultSpellMechanics::battleCast
+	BattleSpellCast sc;
+	sc.side = parameters.casterSide;
+	sc.id = owner->id;
+	sc.skill = parameters.spellLvl;
+	sc.tile = parameters.destination;
+	sc.dmgToDisplay = 0;
+	sc.castedByHero = nullptr != parameters.caster;
+	sc.attackerType = (parameters.casterStack ? parameters.casterStack->type->idNumber : CreatureID(CreatureID::NONE));
+	sc.manaGained = 0;
+	sc.spellCost = 0;	
+	
+	//calculate spell cost
+	if (parameters.caster) 
+	{
+		sc.spellCost = parameters.cb->battleGetSpellCost(owner, parameters.caster);
+
+		if (parameters.secHero && parameters.mode == ECastingMode::HERO_CASTING) //handle mana channel
+		{
+			int manaChannel = 0;
+			for(const CStack * stack : parameters.cb->battleGetAllStacks(true)) //TODO: shouldn't bonus system handle it somehow?
+			{
+				if (stack->owner == parameters.secHero->tempOwner)
+				{
+					vstd::amax(manaChannel, stack->valOfBonuses(Bonus::MANA_CHANNELING));
+				}
+			}
+			sc.manaGained = (manaChannel * sc.spellCost) / 100;
+		}
+	}	
+	
+	
+	//calculating affected creatures for all spells
+	//must be vector, as in Chain Lightning order matters
+	std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
+
+	auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, parameters.destination, parameters.caster);
+	std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
+	
+	for (auto cre : attackedCres)
+	{
+		sc.affectedCres.insert (cre->ID);
+	}
+	
+	//checking if creatures resist
+	//resistance is applied only to negative spells
+	if(owner->isNegative())
+	{
+		for(auto s : attackedCres)
+		{
+			const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in %
+			
+			if(env->getRandomGenerator().nextInt(99) < prob)
+			{
+				sc.resisted.push_back(s->ID);
+			}
+		}
+	}
+	
+	//TODO: extract dmg to display calculation	
+	//calculating dmg to display
+	if (owner->id == SpellID::DEATH_STARE || owner->id == SpellID::ACID_BREATH_DAMAGE)
+	{
+		sc.dmgToDisplay = parameters.usedSpellPower;
+		if (owner->id == SpellID::DEATH_STARE)
+			vstd::amin(sc.dmgToDisplay, (*attackedCres.begin())->count); //stack is already reduced after attack
+	}	
+	
+	StacksInjured si;
+	
+	//TODO:applying effects
+	
+	
+	env->sendAndApply(&sc);
+	if(!si.stacks.empty()) //after spellcast info shows
+		env->sendAndApply(&si);
+	
+	//reduce number of casts remaining
+	//TODO: this should be part of BattleSpellCast apply
+	if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING) 
+	{
+		assert(parameters.casterStack);
+		
+		BattleSetStackProperty ssp;
+		ssp.stackID = parameters.casterStack->ID;
+		ssp.which = BattleSetStackProperty::CASTS;
+		ssp.val = -1;
+		ssp.absolute = false;
+		env->sendAndApply(&ssp);
+	}
+
+	//Magic Mirror effect
+	if (owner->isNegative() && parameters.mode != ECastingMode::MAGIC_MIRROR && owner->level && owner->getLevelInfo(0).range == "0") //it is actual spell and can be reflected to single target, no recurrence
+	{
+		for(auto & attackedCre : attackedCres)
+		{
+			int mirrorChance = (attackedCre)->valOfBonuses(Bonus::MAGIC_MIRROR);
+			if(mirrorChance > env->getRandomGenerator().nextInt(99))
+			{
+				std::vector<const CStack *> mirrorTargets;
+				auto battleStacks = parameters.cb->battleGetAllStacks(true);
+				for (auto & battleStack : battleStacks)
+				{
+					if(battleStack->owner == parameters.casterColor) //get enemy stacks which can be affected by this spell
+					{
+						if (ESpellCastProblem::OK == owner->isImmuneByStack(nullptr, battleStack))
+							mirrorTargets.push_back(battleStack);
+					}
+				}
+				if (!mirrorTargets.empty())
+				{
+					int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position;
+					
+					BattleSpellCastParameters mirrorParameters = parameters;
+					mirrorParameters.spellLvl = 0;
+					mirrorParameters.casterSide = 1-parameters.casterSide;
+					mirrorParameters.casterColor = (attackedCre)->owner;
+					mirrorParameters.caster = nullptr;
+					mirrorParameters.destination = targetHex;
+					mirrorParameters.secHero = parameters.caster;
+					mirrorParameters.mode = ECastingMode::MAGIC_MIRROR;
+					mirrorParameters.casterStack = (attackedCre);
+					mirrorParameters.selectedStack = nullptr;
+					
+					battleCast(env, mirrorParameters);					
+				}
+			}
+		}
+	}	
+	
+	return true;
 }
 
 std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const

+ 13 - 6
lib/SpellMechanics.h

@@ -48,8 +48,8 @@ public:
      * \return true if no error
      *
      */                           
-	virtual bool adventureCast(const SpellCastContext & context) const = 0; 
-	virtual bool battleCast(const SpellCastContext & context) const = 0; 	
+	//virtual bool adventureCast(const SpellCastContext & context) const = 0; 
+	virtual bool battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const = 0; 	
 	
 protected:
 	CSpell * owner;	
@@ -65,14 +65,21 @@ public:
 	
 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
 	
-	bool adventureCast(const SpellCastContext & context) const override; 
-	bool battleCast(const SpellCastContext & context) const override; 
+	//bool adventureCast(const SpellCastContext & context) const override; 
+	bool battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const override; 
 };
 
-class WallMechanics: public DefaultSpellMechanics
+class ObstacleMechanics: public DefaultSpellMechanics
 {
 public:
-	WallMechanics(CSpell * s): DefaultSpellMechanics(s){};	
+	ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};		
+	
+};
+
+class WallMechanics: public ObstacleMechanics
+{
+public:
+	WallMechanics(CSpell * s): ObstacleMechanics(s){};	
 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override;	
 	
 };