mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Merge pull request #94 from vcmi/feature/spell.earthQuake
Okay, let's get this all together for 0.98.
This commit is contained in:
commit
6001a89632
@ -1188,19 +1188,32 @@ void CBattleInterface::hexLclicked(int whichOne)
|
|||||||
|
|
||||||
void CBattleInterface::stackIsCatapulting(const CatapultAttack & ca)
|
void CBattleInterface::stackIsCatapulting(const CatapultAttack & ca)
|
||||||
{
|
{
|
||||||
for(auto it = ca.attackedParts.begin(); it != ca.attackedParts.end(); ++it)
|
if(ca.attacker != -1)
|
||||||
{
|
{
|
||||||
const CStack * stack = curInt->cb->battleGetStackByID(ca.attacker);
|
const CStack * stack = curInt->cb->battleGetStackByID(ca.attacker);
|
||||||
addNewAnim(new CShootingAnimation(this, stack, it->destinationTile, nullptr, true, it->damageDealt));
|
for(auto attackInfo : ca.attackedParts)
|
||||||
|
{
|
||||||
|
addNewAnim(new CShootingAnimation(this, stack, attackInfo.destinationTile, nullptr, true, attackInfo.damageDealt));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//no attacker stack, assume spell-related (earthquake) - only hit animation
|
||||||
|
for(auto attackInfo : ca.attackedParts)
|
||||||
|
{
|
||||||
|
Point destPos = CClickableHex::getXYUnitAnim(attackInfo.destinationTile, nullptr, this) + Point(99, 120);
|
||||||
|
|
||||||
|
addNewAnim(new CSpellEffectAnimation(this, "SGEXPL.DEF", destPos.x, destPos.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
waitForAnims();
|
waitForAnims();
|
||||||
|
|
||||||
for(auto it = ca.attackedParts.begin(); it != ca.attackedParts.end(); ++it)
|
for(auto attackInfo : ca.attackedParts)
|
||||||
{
|
{
|
||||||
SDL_FreeSurface(siegeH->walls[it->attackedPart + 2]);
|
SDL_FreeSurface(siegeH->walls[attackInfo.attackedPart + 2]);
|
||||||
siegeH->walls[it->attackedPart + 2] = BitmapHandler::loadBitmap(
|
siegeH->walls[attackInfo.attackedPart + 2] = BitmapHandler::loadBitmap(
|
||||||
siegeH->getSiegeName(it->attackedPart + 2, curInt->cb->battleGetWallState(it->attackedPart)) );
|
siegeH->getSiegeName(attackInfo.attackedPart + 2, curInt->cb->battleGetWallState(attackInfo.attackedPart)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,7 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
|
"targetModifier":{"smart":true},
|
||||||
"range" : "X"
|
"range" : "X"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1599,6 +1599,11 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
|||||||
if(!spell->combatSpell)
|
if(!spell->combatSpell)
|
||||||
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
||||||
|
|
||||||
|
const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCasted(this, player);
|
||||||
|
|
||||||
|
if(specificProblem != ESpellCastProblem::OK)
|
||||||
|
return specificProblem;
|
||||||
|
|
||||||
if(spell->isNegative() || spell->hasEffects())
|
if(spell->isNegative() || spell->hasEffects())
|
||||||
{
|
{
|
||||||
bool allStacksImmune = true;
|
bool allStacksImmune = true;
|
||||||
@ -1633,7 +1638,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
|||||||
if(arpos < ARRAY_COUNT(spellIDs))
|
if(arpos < ARRAY_COUNT(spellIDs))
|
||||||
{
|
{
|
||||||
//check if there are summoned elementals of other type
|
//check if there are summoned elementals of other type
|
||||||
for( const CStack * st : battleAliveStacks())
|
for(const CStack * st : battleAliveStacks(side))
|
||||||
if(vstd::contains(st->state, EBattleStackState::SUMMONED) && st->getCreature()->idNumber != creIDs[arpos])
|
if(vstd::contains(st->state, EBattleStackState::SUMMONED) && st->getCreature()->idNumber != creIDs[arpos])
|
||||||
return ESpellCastProblem::ANOTHER_ELEMENTAL_SUMMONED;
|
return ESpellCastProblem::ANOTHER_ELEMENTAL_SUMMONED;
|
||||||
}
|
}
|
||||||
|
@ -1555,7 +1555,6 @@ struct ObstaclesRemoved : public CPackForClient //3014
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ELF_VISIBILITY CatapultAttack : public CPackForClient //3015
|
struct ELF_VISIBILITY CatapultAttack : public CPackForClient //3015
|
||||||
|
|
||||||
{
|
{
|
||||||
struct AttackInfo
|
struct AttackInfo
|
||||||
{
|
{
|
||||||
|
@ -152,6 +152,115 @@ void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///EarthquakeMechanics
|
||||||
|
void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||||
|
{
|
||||||
|
if(nullptr == parameters.cb->town)
|
||||||
|
{
|
||||||
|
env->complain("EarthquakeMechanics: not town siege");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CGTownInstance::NONE == parameters.cb->town->fortLevel())
|
||||||
|
{
|
||||||
|
env->complain("EarthquakeMechanics: town has no fort");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//start with all destructible parts
|
||||||
|
std::set<EWallPart::EWallPart> possibleTargets =
|
||||||
|
{
|
||||||
|
EWallPart::KEEP,
|
||||||
|
EWallPart::BOTTOM_TOWER,
|
||||||
|
EWallPart::BOTTOM_WALL,
|
||||||
|
EWallPart::BELOW_GATE,
|
||||||
|
EWallPart::OVER_GATE,
|
||||||
|
EWallPart::UPPER_WALL,
|
||||||
|
EWallPart::UPPER_TOWER,
|
||||||
|
EWallPart::GATE
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(possibleTargets.size() == EWallPart::PARTS_COUNT);
|
||||||
|
|
||||||
|
const int targetsToAttack = 2 + std::max<int>(parameters.spellLvl - 1, 0);
|
||||||
|
|
||||||
|
CatapultAttack ca;
|
||||||
|
ca.attacker = -1;
|
||||||
|
|
||||||
|
for(int i = 0; i < targetsToAttack; i++)
|
||||||
|
{
|
||||||
|
//Any destructible part can be hit regardless of its HP. Multiple hit on same target is allowed.
|
||||||
|
EWallPart::EWallPart target = *RandomGeneratorUtil::nextItem(possibleTargets, env->getRandomGenerator());
|
||||||
|
|
||||||
|
auto & currentHP = parameters.cb->si.wallState;
|
||||||
|
|
||||||
|
if(currentHP.at(target) == EWallState::DESTROYED || currentHP.at(target) == EWallState::NONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CatapultAttack::AttackInfo attackInfo;
|
||||||
|
|
||||||
|
attackInfo.damageDealt = 1;
|
||||||
|
attackInfo.attackedPart = target;
|
||||||
|
attackInfo.destinationTile = parameters.cb->wallPartToBattleHex(target);
|
||||||
|
|
||||||
|
ca.attackedParts.push_back(attackInfo);
|
||||||
|
|
||||||
|
//removing creatures in turrets / keep if one is destroyed
|
||||||
|
BattleHex posRemove;
|
||||||
|
|
||||||
|
switch(target)
|
||||||
|
{
|
||||||
|
case EWallPart::KEEP:
|
||||||
|
posRemove = -2;
|
||||||
|
break;
|
||||||
|
case EWallPart::BOTTOM_TOWER:
|
||||||
|
posRemove = -3;
|
||||||
|
break;
|
||||||
|
case EWallPart::UPPER_TOWER:
|
||||||
|
posRemove = -4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(posRemove != BattleHex::INVALID)
|
||||||
|
{
|
||||||
|
BattleStacksRemoved bsr;
|
||||||
|
for(auto & elem : parameters.cb->stacks)
|
||||||
|
{
|
||||||
|
if(elem->position == posRemove)
|
||||||
|
{
|
||||||
|
bsr.stackIDs.insert(elem->ID);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(bsr.stackIDs.size() > 0)
|
||||||
|
env->sendAndApply(&bsr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
env->sendAndApply(&ca);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
|
||||||
|
{
|
||||||
|
if(nullptr == cb->battleGetDefendedTown())
|
||||||
|
{
|
||||||
|
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CGTownInstance::NONE == cb->battleGetDefendedTown()->fortLevel())
|
||||||
|
{
|
||||||
|
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(owner->getTargetInfo(0).smart) //TODO: use real spell level
|
||||||
|
{
|
||||||
|
//if spell targeting is smart, then only attacker can use it
|
||||||
|
if(cb->playerToSide(player) != 0)
|
||||||
|
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESpellCastProblem::OK;
|
||||||
|
}
|
||||||
|
|
||||||
///HypnotizeMechanics
|
///HypnotizeMechanics
|
||||||
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
||||||
|
@ -44,6 +44,15 @@ public:
|
|||||||
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final;
|
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DLL_LINKAGE EarthquakeMechanics : public DefaultSpellMechanics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||||
|
ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const override;
|
||||||
|
protected:
|
||||||
|
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||||
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE HypnotizeMechanics : public DefaultSpellMechanics
|
class DLL_LINKAGE HypnotizeMechanics : public DefaultSpellMechanics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -712,6 +712,13 @@ std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargeting
|
|||||||
return attackedCres;
|
return attackedCres;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
|
||||||
|
{
|
||||||
|
//no problems by default, this method is for spell-specific problems
|
||||||
|
return ESpellCastProblem::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
||||||
{
|
{
|
||||||
//by default use general algorithm
|
//by default use general algorithm
|
||||||
|
@ -33,6 +33,8 @@ public:
|
|||||||
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override;
|
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override;
|
||||||
std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override;
|
std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override;
|
||||||
|
|
||||||
|
ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const override;
|
||||||
|
|
||||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
|
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
|
||||||
|
|
||||||
virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;
|
virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;
|
||||||
|
@ -234,6 +234,11 @@ ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affec
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ESpellCastProblem::ESpellCastProblem CSpell::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
|
||||||
|
{
|
||||||
|
return mechanics->canBeCasted(cb, player);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
||||||
{
|
{
|
||||||
return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes);
|
return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes);
|
||||||
|
@ -246,9 +246,6 @@ public:
|
|||||||
//internal, for use only by Mechanics classes
|
//internal, for use only by Mechanics classes
|
||||||
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
|
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
|
||||||
|
|
||||||
//checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc.
|
|
||||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const;
|
|
||||||
|
|
||||||
//internal, for use only by Mechanics classes. applying secondary skills
|
//internal, for use only by Mechanics classes. applying secondary skills
|
||||||
ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
|
ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
|
||||||
|
|
||||||
@ -301,6 +298,14 @@ public:
|
|||||||
}
|
}
|
||||||
friend class CSpellHandler;
|
friend class CSpellHandler;
|
||||||
friend class Graphics;
|
friend class Graphics;
|
||||||
|
public:
|
||||||
|
///internal interface (for callbacks)
|
||||||
|
|
||||||
|
///Checks general but spell-specific problems for all casting modes. Use only during battle.
|
||||||
|
ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const;
|
||||||
|
|
||||||
|
///checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc.
|
||||||
|
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const;
|
||||||
public:
|
public:
|
||||||
///Server logic. Has write access to GameState via packets.
|
///Server logic. Has write access to GameState via packets.
|
||||||
///May be executed on client side by (future) non-cheat-proof scripts.
|
///May be executed on client side by (future) non-cheat-proof scripts.
|
||||||
|
@ -42,6 +42,8 @@ ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s)
|
|||||||
return new DispellMechanics(s);
|
return new DispellMechanics(s);
|
||||||
case SpellID::DISPEL_HELPFUL_SPELLS:
|
case SpellID::DISPEL_HELPFUL_SPELLS:
|
||||||
return new DispellHelpfulMechanics(s);
|
return new DispellHelpfulMechanics(s);
|
||||||
|
case SpellID::EARTHQUAKE:
|
||||||
|
return new EarthquakeMechanics(s);
|
||||||
case SpellID::FIRE_WALL:
|
case SpellID::FIRE_WALL:
|
||||||
case SpellID::FORCE_FIELD:
|
case SpellID::FORCE_FIELD:
|
||||||
return new WallMechanics(s);
|
return new WallMechanics(s);
|
||||||
|
@ -38,9 +38,11 @@ public:
|
|||||||
|
|
||||||
virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0;
|
virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0;
|
||||||
virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0;
|
virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0;
|
||||||
|
|
||||||
|
virtual ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const = 0;
|
||||||
|
|
||||||
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0;
|
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0;
|
||||||
|
|
||||||
virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0;
|
virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0;
|
||||||
virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;
|
virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;
|
||||||
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;
|
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user