1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Get rid of handleSpellCasting

This commit is contained in:
AlexVinS 2014-11-25 22:00:04 +03:00
parent 2a75c432e0
commit ca5391cde6
4 changed files with 176 additions and 118 deletions

View File

@ -47,6 +47,7 @@ struct SpellSchoolInfo
class DLL_LINKAGE SpellCastEnvironment class DLL_LINKAGE SpellCastEnvironment
{ {
public: public:
virtual ~SpellCastEnvironment(){};
virtual void sendAndApply(CPackForClient * info) const = 0; virtual void sendAndApply(CPackForClient * info) const = 0;
virtual CRandomGenerator & getRandomGenerator() const = 0; virtual CRandomGenerator & getRandomGenerator() const = 0;

View File

@ -230,7 +230,9 @@ public:
class SacrificeMechanics: public RisingSpellMechanics class SacrificeMechanics: public RisingSpellMechanics
{ {
public: public:
SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){}; SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};
protected:
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
}; };
///all rising spells but SACRIFICE ///all rising spells but SACRIFICE
@ -452,7 +454,6 @@ int DefaultSpellMechanics::calculateDuration(const CGHeroInstance * caster, int
} }
} }
void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
{ {
//applying effects //applying effects
@ -627,33 +628,6 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
} }
if(!shr.healedStacks.empty()) if(!shr.healedStacks.empty())
env->sendAndApply(&shr); env->sendAndApply(&shr);
if(owner->id == SpellID::SACRIFICE) //remove victim
{
if(parameters.selectedStack == parameters.cb->battleActiveStack())
//set another active stack than the one removed, or bad things will happen
//TODO: make that part of BattleStacksRemoved? what about client update?
{
//makeStackDoNothing(gs->curB->getStack (selectedStack));
BattleSetActiveStack sas;
//std::vector<const CStack *> hlp;
//battleGetStackQueue(hlp, 1, selectedStack); //next after this one
//if(hlp.size())
//{
// sas.stack = hlp[0]->ID;
//}
//else
// complain ("No new stack to activate!");
sas.stack = parameters.cb->getNextStack()->ID; //why the hell next stack has same ID as current?
env->sendAndApply(&sas);
}
BattleStacksRemoved bsr;
bsr.stackIDs.insert(parameters.selectedStack->ID); //somehow it works for teleport?
env->sendAndApply(&bsr);
}
} }
} }
@ -1119,6 +1093,38 @@ void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * en
env->complain("There's no obstacle to remove!"); env->complain("There's no obstacle to remove!");
} }
///SpecialRisingSpellMechanics
void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
{
RisingSpellMechanics::applyBattleEffects(env, parameters, ctx);
if(parameters.selectedStack == parameters.cb->battleActiveStack())
//set another active stack than the one removed, or bad things will happen
//TODO: make that part of BattleStacksRemoved? what about client update?
{
//makeStackDoNothing(gs->curB->getStack (selectedStack));
BattleSetActiveStack sas;
//std::vector<const CStack *> hlp;
//battleGetStackQueue(hlp, 1, selectedStack); //next after this one
//if(hlp.size())
//{
// sas.stack = hlp[0]->ID;
//}
//else
// complain ("No new stack to activate!");
sas.stack = parameters.cb->getNextStack()->ID; //why the hell next stack has same ID as current?
env->sendAndApply(&sas);
}
BattleStacksRemoved bsr;
bsr.stackIDs.insert(parameters.selectedStack->ID); //somehow it works for teleport?
env->sendAndApply(&bsr);
}
///SpecialRisingSpellMechanics ///SpecialRisingSpellMechanics
ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const

View File

@ -1064,10 +1064,13 @@ CGameHandler::CGameHandler(void)
registerTypesServerPacks(*applier); registerTypesServerPacks(*applier);
visitObjectAfterVictory = false; visitObjectAfterVictory = false;
queries.gh = this; queries.gh = this;
spellEnv = new ServerSpellCastEnvironment(this);
} }
CGameHandler::~CGameHandler(void) CGameHandler::~CGameHandler(void)
{ {
delete spellEnv;
delete applier; delete applier;
applier = nullptr; applier = nullptr;
delete gs; delete gs;
@ -3770,17 +3773,27 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
complain("That stack can't cast spells!"); complain("That stack can't cast spells!");
else else
{ {
int spellLvl = 0; BattleSpellCastParameters p;
p.spellLvl = 0;
if (spellcaster) if (spellcaster)
vstd::amax(spellLvl, spellcaster->val); vstd::amax(p.spellLvl, spellcaster->val);
if (randSpellcaster) if (randSpellcaster)
vstd::amax(spellLvl, randSpellcaster->val); vstd::amax(p.spellLvl, randSpellcaster->val);
vstd::amin (spellLvl, 3); vstd::amin (p.spellLvl, 3);
int casterSide = gs->curB->whatSide(stack->owner); p.casterSide = gs->curB->whatSide(stack->owner);
const CGHeroInstance * secHero = gs->curB->getHero(gs->curB->theOtherPlayer(stack->owner)); p.secHero = gs->curB->getHero(gs->curB->theOtherPlayer(stack->owner));
p.mode = ECastingMode::CREATURE_ACTIVE_CASTING;
p.destination = destination;
p.casterColor = stack->owner;
p.caster = nullptr;
p.usedSpellPower = 0;
p.casterStack = stack;
p.selectedStack = nullptr;
handleSpellCasting(spellID, spellLvl, destination, casterSide, stack->owner, nullptr, secHero, 0, ECastingMode::CREATURE_ACTIVE_CASTING, stack); const CSpell * spell = SpellID(spellID).toSpell();
spell->battleCast(spellEnv, p);
} }
sendAndApply(&end_action); sendAndApply(&end_action);
break; break;
@ -3944,30 +3957,6 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
} }
} }
void CGameHandler::handleSpellCasting(SpellID spellID, int spellLvl, BattleHex destination, ui8 casterSide, PlayerColor casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero,
int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack)
{
const CSpell * spell = SpellID(spellID).toSpell();
ServerSpellCastEnvironment spellEnvironment(this);
BattleSpellCastParameters parameters;
parameters.spellLvl = spellLvl;
parameters.destination = destination;
parameters.casterSide = casterSide;
parameters.casterColor = casterColor;
parameters.caster = caster;
parameters.secHero = secHero;
parameters.usedSpellPower = usedSpellPower;
parameters.mode = mode;
parameters.casterStack = stack;
parameters.selectedStack = gs->curB->battleGetStackByID(selectedStack, false);
spell->battleCast(&spellEnvironment, parameters);
}
bool CGameHandler::makeCustomAction( BattleAction &ba ) bool CGameHandler::makeCustomAction( BattleAction &ba )
{ {
switch(ba.actionType) switch(ba.actionType)
@ -3990,52 +3979,49 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
return false; return false;
} }
const CSpell *s = SpellID(ba.additionalInfo).toSpell(); const CSpell * s = SpellID(ba.additionalInfo).toSpell();
if (s->mainEffectAnim > -1
|| s->id == SpellID::CLONE BattleSpellCastParameters parameters;
|| s->id >= SpellID::SUMMON_FIRE_ELEMENTAL parameters.spellLvl = h->getSpellSchoolLevel(s);
|| s->id <= SpellID::SUMMON_AIR_ELEMENTAL parameters.destination = ba.destinationTile;
|| s->id <= SpellID::SUMMON_EARTH_ELEMENTAL parameters.casterSide = ba.side;
|| s->id <= SpellID::SUMMON_WATER_ELEMENTAL) parameters.casterColor = h->tempOwner;
//TODO: special effects, like Clone parameters.caster = h;
parameters.secHero = secondHero;
parameters.usedSpellPower = h->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
parameters.mode = ECastingMode::HERO_CASTING;
parameters.casterStack = nullptr;
parameters.selectedStack = gs->curB->battleGetStackByID(ba.selectedStack, false);
ESpellCastProblem::ESpellCastProblem escp = gs->curB->battleCanCastThisSpell(h->tempOwner, s, ECastingMode::HERO_CASTING);
if(escp != ESpellCastProblem::OK)
{ {
ui8 skill = h->getSpellSchoolLevel(s); //skill level logGlobal->warnStream() << "Spell cannot be cast!";
logGlobal->warnStream() << "Problem : " << escp;
ESpellCastProblem::ESpellCastProblem escp = gs->curB->battleCanCastThisSpell(h->tempOwner, s, ECastingMode::HERO_CASTING);
if(escp != ESpellCastProblem::OK)
{
logGlobal->warnStream() << "Spell cannot be cast!";
logGlobal->warnStream() << "Problem : " << escp;
return false;
}
StartAction start_action(ba);
sendAndApply(&start_action); //start spell casting
handleSpellCasting (SpellID(ba.additionalInfo), skill, ba.destinationTile, ba.side, h->tempOwner,
h, secondHero, h->getPrimSkillLevel(PrimarySkill::SPELL_POWER),
ECastingMode::HERO_CASTING, nullptr, ba.selectedStack);
sendAndApply(&end_action);
if( !gs->curB->battleGetStackByID(gs->curB->activeStack, true))
{
battleMadeAction.setn(true);
}
checkForBattleEnd();
if(battleResult.get())
{
battleMadeAction.setn(true);
//battle will be ended by startBattle function
//endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]);
}
return true;
}
else
{
logGlobal->warnStream() << "Spell " << s->name << " is not yet supported!";
return false; return false;
} }
StartAction start_action(ba);
sendAndApply(&start_action); //start spell casting
s->battleCast(spellEnv, parameters);
sendAndApply(&end_action);
if( !gs->curB->battleGetStackByID(gs->curB->activeStack, true))
{
battleMadeAction.setn(true);
}
checkForBattleEnd();
if(battleResult.get())
{
battleMadeAction.setn(true);
//battle will be ended by startBattle function
//endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]);
}
return true;
} }
} }
return false; return false;
@ -4146,11 +4132,22 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
{ {
auto bonus = *RandomGeneratorUtil::nextItem(bl, gs->getRandomGenerator()); auto bonus = *RandomGeneratorUtil::nextItem(bl, gs->getRandomGenerator());
auto spellID = SpellID(bonus->subtype); auto spellID = SpellID(bonus->subtype);
if (gs->curB->battleCanCastThisSpell(st->owner, SpellID(spellID).toSpell(), ECastingMode::ENCHANTER_CASTING) == ESpellCastProblem::OK) //TODO: select another available? const CSpell * spell = SpellID(spellID).toSpell();
if (gs->curB->battleCanCastThisSpell(st->owner, spell, ECastingMode::ENCHANTER_CASTING) == ESpellCastProblem::OK) //TODO: select another available?
{ {
int spellLeveL = bonus->val; //spell level BattleSpellCastParameters parameters;
const CGHeroInstance * enemyHero = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner)); parameters.spellLvl = bonus->val;
handleSpellCasting(spellID, spellLeveL, -1, side, st->owner, nullptr, enemyHero, 0, ECastingMode::ENCHANTER_CASTING, st); parameters.destination = BattleHex::INVALID;
parameters.casterSide = side;
parameters.casterColor = st->owner;
parameters.caster = nullptr;
parameters.secHero = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner));
parameters.usedSpellPower = 0;
parameters.mode = ECastingMode::ENCHANTER_CASTING;
parameters.casterStack = st;
parameters.selectedStack = nullptr;
spell->battleCast(spellEnv, parameters);
BattleSetStackProperty ssp; BattleSetStackProperty ssp;
ssp.which = BattleSetStackProperty::ENCHANTER_COUNTER; ssp.which = BattleSetStackProperty::ENCHANTER_COUNTER;
@ -4832,9 +4829,26 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
if(gs->getRandomGenerator().nextInt(99) >= chance) if(gs->getRandomGenerator().nextInt(99) >= chance)
continue; continue;
//casting //TODO: check if spell can be blocked or target is immune //casting
if (castMe) //stacks use 0 spell power. If needed, default = 3 or custom value is used if (castMe) //stacks use 0 spell power. If needed, default = 3 or custom value is used
handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, nullptr, nullptr, 0, ECastingMode::AFTER_ATTACK_CASTING, attacker); {
const CSpell * spell = SpellID(spellID).toSpell();
BattleSpellCastParameters parameters;
parameters.spellLvl = spellLevel;
parameters.destination = destination;
parameters.casterSide = !attacker->attackerOwned;
parameters.casterColor = attacker->owner;
parameters.caster = nullptr;
parameters.secHero = nullptr;
parameters.usedSpellPower = 0;
parameters.mode = ECastingMode::AFTER_ATTACK_CASTING;
parameters.casterStack = attacker;
parameters.selectedStack = nullptr;
spell->battleCast(spellEnv, parameters);
}
} }
} }
} }
@ -4850,6 +4864,27 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
const CStack * attacker = gs->curB->battleGetStackByID(bat.stackAttacking); const CStack * attacker = gs->curB->battleGetStackByID(bat.stackAttacking);
if (!attacker) //could be already dead if (!attacker) //could be already dead
return; return;
auto cast = [=](SpellID spellID, int power)
{
const CSpell * spell = SpellID(spellID).toSpell();
BattleSpellCastParameters parameters;
parameters.spellLvl = 0;
parameters.destination = gs->curB->battleGetStackByID(bat.bsa.at(0).stackAttacked)->position;
parameters.casterSide = !attacker->attackerOwned;
parameters.casterColor = attacker->owner;
parameters.caster = nullptr;
parameters.secHero = nullptr;
parameters.usedSpellPower = power;
parameters.mode = ECastingMode::AFTER_ATTACK_CASTING;
parameters.casterStack = attacker;
parameters.selectedStack = nullptr;
spell->battleCast(this->spellEnv, parameters);
};
attackCasting(bat, Bonus::SPELL_AFTER_ATTACK, attacker); attackCasting(bat, Bonus::SPELL_AFTER_ATTACK, attacker);
if(bat.bsa.at(0).newAmount <= 0) if(bat.bsa.at(0).newAmount <= 0)
@ -4880,8 +4915,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
if (staredCreatures) if (staredCreatures)
{ {
if (bat.bsa.at(0).newAmount > 0) //TODO: death stare was not originally available for multiple-hex attacks, but... if (bat.bsa.at(0).newAmount > 0) //TODO: death stare was not originally available for multiple-hex attacks, but...
handleSpellCasting(SpellID::DEATH_STARE, 0, gs->curB->battleGetStackByID(bat.bsa.at(0).stackAttacked)->position, cast(SpellID::DEATH_STARE, staredCreatures);
!attacker->attackerOwned, attacker->owner, nullptr, nullptr, staredCreatures, ECastingMode::AFTER_ATTACK_CASTING, attacker);
} }
} }
@ -4894,9 +4928,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
} }
if (acidDamage) if (acidDamage)
{ {
handleSpellCasting(SpellID::ACID_BREATH_DAMAGE, 0, gs->curB->battleGetStackByID(bat.bsa.at(0).stackAttacked)->position, cast(SpellID::ACID_BREATH_DAMAGE, acidDamage * attacker->count);
!attacker->attackerOwned, attacker->owner, nullptr, nullptr,
acidDamage * attacker->count, ECastingMode::AFTER_ATTACK_CASTING, attacker);
} }
} }
@ -5354,10 +5386,28 @@ void CGameHandler::runBattle()
auto h = gs->curB->battleGetFightingHero(i); auto h = gs->curB->battleGetFightingHero(i);
if(h && h->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL)) if(h && h->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL))
{ {
TBonusListPtr bl = h->getBonuses(Selector::type(Bonus::OPENING_BATTLE_SPELL)); TBonusListPtr bl = h->getBonuses(Selector::type(Bonus::OPENING_BATTLE_SPELL));
BattleSpellCastParameters parameters;
parameters.spellLvl = 3;
parameters.destination = BattleHex::INVALID;
parameters.casterSide = 0;
parameters.casterColor = h->tempOwner;
parameters.caster = nullptr;
parameters.secHero = gs->curB->battleGetFightingHero(1-i);
parameters.mode = ECastingMode::HERO_CASTING;
parameters.casterStack = nullptr;
parameters.selectedStack = nullptr;
for (Bonus *b : *bl) for (Bonus *b : *bl)
{ {
handleSpellCasting(SpellID(b->subtype), 3, -1, 0, h->tempOwner, nullptr, gs->curB->battleGetFightingHero(1-i), b->val, ECastingMode::HERO_CASTING, nullptr); parameters.usedSpellPower = b->val;
const CSpell * spell = SpellID(b->subtype).toSpell();
spell->battleCast(spellEnv, parameters);
} }
} }
} }

View File

@ -36,6 +36,8 @@ struct NewStructures;
class CGHeroInstance; class CGHeroInstance;
class IMarket; class IMarket;
class ServerSpellCastEnvironment;
extern std::map<ui32, CFunctionList<void(ui32)> > callbacks; //question id => callback functions - for selection dialogs extern std::map<ui32, CFunctionList<void(ui32)> > callbacks; //question id => callback functions - for selection dialogs
extern boost::mutex gsm; extern boost::mutex gsm;
@ -201,8 +203,6 @@ public:
void playerMessage( PlayerColor player, const std::string &message, ObjectInstanceID currObj); void playerMessage( PlayerColor player, const std::string &message, ObjectInstanceID currObj);
bool makeBattleAction(BattleAction &ba); bool makeBattleAction(BattleAction &ba);
bool makeAutomaticAction(const CStack *stack, BattleAction &ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack) bool makeAutomaticAction(const CStack *stack, BattleAction &ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack)
void handleSpellCasting(SpellID spellID, int spellLvl, BattleHex destination, ui8 casterSide, PlayerColor casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero,
int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack = -1);
bool makeCustomAction(BattleAction &ba); bool makeCustomAction(BattleAction &ba);
void stackTurnTrigger(const CStack * stack); void stackTurnTrigger(const CStack * stack);
void handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack); //checks if obstacle is land mine and handles possible consequences void handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack); //checks if obstacle is land mine and handles possible consequences
@ -288,6 +288,7 @@ public:
friend class CVCMIServer; friend class CVCMIServer;
private: private:
ServerSpellCastEnvironment * spellEnv;
std::list<PlayerColor> generatePlayerTurnOrder() const; std::list<PlayerColor> generatePlayerTurnOrder() const;
void makeStackDoNothing(const CStack * next); void makeStackDoNothing(const CStack * next);