1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Initial experiments on hero & creature casting unification

This commit is contained in:
AlexVinS 2015-03-18 22:22:52 +03:00
parent 28087099b8
commit fb5903d610
10 changed files with 105 additions and 51 deletions

View File

@ -2218,7 +2218,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
{
ui8 skill = 0;
if (creatureCasting)
skill = sactive->valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, SpellID::TELEPORT));
skill = sactive->getSpellSchoolLevel(SpellID(SpellID::TELEPORT).toSpell());
else
skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->objects[spellToCast->additionalInfo]);
//TODO: explicitely save power, skill

View File

@ -1152,6 +1152,21 @@ bool CStack::canBeHealed() const
&& !hasBonusOfType(Bonus::SIEGE_WEAPON);
}
ui8 CStack::getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool) const
{
int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->id));
vstd::abetween(skill, 0, 3);
return skill;
}
ui32 CStack::getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const
{
//stacks does not have spellpower etc. (yet?)
return base;
}
bool CMP_stack::operator()( const CStack* a, const CStack* b )
{
switch(phase)

View File

@ -1,5 +1,14 @@
#pragma once
/*
* BattleState.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "BattleHex.h"
#include "HeroBonus.h"
@ -12,16 +21,7 @@
#include "GameConstants.h"
#include "CBattleCallback.h"
#include "int3.h"
/*
* BattleState.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "spells/Magic.h"
class CGHeroInstance;
class CStack;
@ -159,7 +159,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format
};
class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor
class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor, public ISpellCaster
{
public:
const CStackInstance *base; //garrison slot from which stack originates (nullptr for war machines, summoned cres, etc)
@ -222,6 +222,10 @@ public:
std::pair<int,int> countKilledByAttack(int damageReceived) const; //returns pair<killed count, new left HP>
void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional<int> customCount = boost::none) const; //requires bsa.damageAmout filled
///ISpellCaster
ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
assert(isIndependentNode());

View File

@ -21,6 +21,7 @@
#include "../IGameCallback.h"
#include "../CGameState.h"
#include "../CCreatureHandler.h"
#include "../BattleState.h"
///helpers
static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
@ -883,6 +884,26 @@ ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSc
return skill;
}
ui32 CGHeroInstance::getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const
{
//applying sorcery secondary skill
base *= (100.0 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0;
base *= (100.0 + valOfBonuses(Bonus::SPELL_DAMAGE) + valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, spell->id.toEnum())) / 100.0;
spell->forEachSchool([&base, this](const SpellSchoolInfo & cnf, bool & stop)
{
base *= (100.0 + valOfBonuses(cnf.damagePremyBonus)) / 100.0;
stop = true; //only bonus from one school is used
});
if (affectedStack && affectedStack->getCreature()->level) //Hero specials like Solmyr, Deemer
base *= (100. + ((valOfBonuses(Bonus::SPECIAL_SPELL_LEV, spell->id.toEnum()) * level) / affectedStack->getCreature()->level)) / 100.0;
return base;
}
bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
{
return spell->isCastableBy(this, nullptr !=getArt(ArtifactPosition::SPELLBOOK), spells);

View File

@ -2,6 +2,7 @@
#include "CObjectHandler.h"
#include "CArmedInstance.h"
#include "../spells/Magic.h"
#include "../CArtHandler.h" // For CArtifactSet
#include "../CRandomGenerator.h"
@ -35,7 +36,7 @@ public:
};
class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet
class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public ISpellCaster
{
public:
enum ECanDig
@ -162,14 +163,13 @@ public:
int maxMovePoints(bool onLand) const;
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
//int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
double getFightingStrength() const; // takes attack / defense skill into account
double getMagicStrength() const; // takes knowledge / spell power skill into account
double getHeroStrength() const; // includes fighting and magic strength
ui64 getTotalStrength() const; // includes fighting strength and army strength
TExpType calculateXp(TExpType exp) const; //apply learning skill
ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const; //returns level on which given spell would be cast by this hero (0 - none, 1 - basic etc); optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
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
CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
@ -198,13 +198,18 @@ public:
CGHeroInstance();
virtual ~CGHeroInstance();
//////////////////////////////////////////////////////////////////////////
//
///ArtBearer
ArtBearer::ArtBearer bearerType() const override;
//////////////////////////////////////////////////////////////////////////
///IBonusBearer
CBonusSystemNode *whereShouldBeAttached(CGameState *gs) override;
std::string nodeName() const override;
///ISpellCaster
ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
void deserializationFix();
void initObj() override;

View File

@ -310,8 +310,8 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const C
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
ui64 subjectHealth = (obj->count - 1) * obj->MaxHealth() + obj->firstHPleft;
//apply 'damage' bonus for hypnotize, including hero specialty
ui64 maxHealth = owner->calculateBonus(caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), caster, obj);
ui64 maxHealth = caster->getSpellBonus(owner, caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), obj);
if (subjectHealth > maxHealth)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}

View File

@ -402,7 +402,7 @@ ui32 DefaultSpellMechanics::calculateHealedHP(const CGHeroInstance* caster, cons
healedHealth = (spellPowerSkill + sacrificedStack->MaxHealth() + levelPower) * sacrificedStack->count;
else
healedHealth = spellPowerSkill * owner->power + levelPower; //???
healedHealth = owner->calculateBonus(healedHealth, caster, stack);
healedHealth = caster->getSpellBonus(owner, healedHealth, stack);
return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (owner->isRisingSpell() ? stack->baseAmount * stack->MaxHealth() : 0));
}

View File

@ -169,29 +169,7 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
return levels.at(level);
}
ui32 CSpell::calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const
{
ui32 ret = baseDamage;
//applying sorcery secondary skill
if(caster)
{
ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0;
ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, id.toEnum())) / 100.0;
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
{
ret *= (100.0 + caster->valOfBonuses(cnf.damagePremyBonus)) / 100.0;
stop = true; //only bonus from one school is used
});
if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, id.toEnum()) * caster->level) / affectedCreature->getCreature()->level)) / 100.0;
}
return ret;
}
ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const
ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const
{
ui32 ret = 0; //value to return
@ -230,7 +208,9 @@ ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affec
ret /= 100;
}
}
ret = calculateBonus(ret, caster, affectedCreature);
if(nullptr != caster) //todo: make sure that caster always present
ret = caster->getSpellBonus(this, ret, affectedCreature);
return ret;
}

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "Magic.h"
#include "../IHandlerBase.h"
#include "../ConstTransitivePtr.h"
#include "../int3.h"
@ -248,11 +248,8 @@ public:
//internal, for use only by Mechanics classes
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
//internal, for use only by Mechanics classes. applying secondary skills
ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
ui32 calculateDamage(const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
ui32 calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
///selects from allStacks actually affected stacks
std::set<const CStack *> getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster = nullptr) const;

32
lib/spells/Magic.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Magic.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
/**
* High-level interface for spells subsystem
*/
class CSpell;
class CStack;
class DLL_LINKAGE ISpellCaster
{
public:
virtual ~ISpellCaster(){};
/// returns level on which given spell would be cast by this(0 - none, 1 - basic etc);
/// caster may not know this spell at all
/// optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic
virtual ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const = 0;
virtual ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const = 0;
};