1
0
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:
DjWarmonger 2015-03-28 10:27:35 +01:00
commit 6001a89632
12 changed files with 172 additions and 13 deletions

View File

@ -1188,19 +1188,32 @@ void CBattleInterface::hexLclicked(int whichOne)
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);
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();
for(auto it = ca.attackedParts.begin(); it != ca.attackedParts.end(); ++it)
for(auto attackInfo : ca.attackedParts)
{
SDL_FreeSurface(siegeH->walls[it->attackedPart + 2]);
siegeH->walls[it->attackedPart + 2] = BitmapHandler::loadBitmap(
siegeH->getSiegeName(it->attackedPart + 2, curInt->cb->battleGetWallState(it->attackedPart)) );
SDL_FreeSurface(siegeH->walls[attackInfo.attackedPart + 2]);
siegeH->walls[attackInfo.attackedPart + 2] = BitmapHandler::loadBitmap(
siegeH->getSiegeName(attackInfo.attackedPart + 2, curInt->cb->battleGetWallState(attackInfo.attackedPart)));
}
}

View File

@ -86,6 +86,7 @@
},
"levels" : {
"base":{
"targetModifier":{"smart":true},
"range" : "X"
}
},

View File

@ -1599,6 +1599,11 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
if(!spell->combatSpell)
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())
{
bool allStacksImmune = true;
@ -1633,7 +1638,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
if(arpos < ARRAY_COUNT(spellIDs))
{
//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])
return ESpellCastProblem::ANOTHER_ELEMENTAL_SUMMONED;
}

View File

@ -1555,7 +1555,6 @@ struct ObstaclesRemoved : public CPackForClient //3014
};
struct ELF_VISIBILITY CatapultAttack : public CPackForClient //3015
{
struct AttackInfo
{

View File

@ -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
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const

View File

@ -44,6 +44,15 @@ public:
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
{
public:

View File

@ -712,6 +712,13 @@ std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargeting
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
{
//by default use general algorithm

View File

@ -33,6 +33,8 @@ public:
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) 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;
virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;

View File

@ -234,6 +234,11 @@ ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affec
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
{
return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes);

View File

@ -246,9 +246,6 @@ public:
//internal, for use only by Mechanics classes
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
ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
@ -301,6 +298,14 @@ public:
}
friend class CSpellHandler;
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:
///Server logic. Has write access to GameState via packets.
///May be executed on client side by (future) non-cheat-proof scripts.

View File

@ -42,6 +42,8 @@ ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s)
return new DispellMechanics(s);
case SpellID::DISPEL_HELPFUL_SPELLS:
return new DispellHelpfulMechanics(s);
case SpellID::EARTHQUAKE:
return new EarthquakeMechanics(s);
case SpellID::FIRE_WALL:
case SpellID::FORCE_FIELD:
return new WallMechanics(s);

View File

@ -38,9 +38,11 @@ public:
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 ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) 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 bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;