diff --git a/lib/CObstacleInstance.cpp b/lib/CObstacleInstance.cpp index 168490e12..0c9d27208 100644 --- a/lib/CObstacleInstance.cpp +++ b/lib/CObstacleInstance.cpp @@ -131,7 +131,6 @@ std::vector SpellCreatedObstacle::getAffectedTiles() const return std::vector(1, pos); case FORCE_FIELD: return SpellID(SpellID::FORCE_FIELD).toSpell()->rangeInHexes(pos, spellLevel, casterSide); - //TODO Fire Wall default: assert(0); return std::vector(); diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 705149c67..db7f37540 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -410,120 +410,69 @@ ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleI return ESpellCastProblem::OK; } -void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const +void ObstacleMechanics::placeObstacle(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const BattleHex & pos) const { - auto placeObstacle = [&, this](const BattleHex & pos) - { - static int obstacleIdToGive = parameters.cb->obstacles.size() - ? (parameters.cb->obstacles.back()->uniqueID+1) - : 0; + const int obstacleIdToGive = parameters.cb->obstacles.size()+1; - auto obstacle = std::make_shared(); - switch(owner->id) // :/ - { - case SpellID::QUICKSAND: - obstacle->obstacleType = CObstacleInstance::QUICKSAND; - obstacle->turnsRemaining = -1; - obstacle->visibleForAnotherSide = false; - break; - case SpellID::LAND_MINE: - obstacle->obstacleType = CObstacleInstance::LAND_MINE; - obstacle->turnsRemaining = -1; - obstacle->visibleForAnotherSide = false; - break; - case SpellID::FIRE_WALL: - obstacle->obstacleType = CObstacleInstance::FIRE_WALL; - obstacle->turnsRemaining = 2; - obstacle->visibleForAnotherSide = true; - break; - case SpellID::FORCE_FIELD: - obstacle->obstacleType = CObstacleInstance::FORCE_FIELD; - obstacle->turnsRemaining = 2; - obstacle->visibleForAnotherSide = true; - break; - default: - //this function cannot be used with spells that do not create obstacles - assert(0); - } + auto obstacle = std::make_shared(); + setupObstacle(obstacle.get()); - obstacle->pos = pos; - obstacle->casterSide = parameters.casterSide; - obstacle->ID = owner->id; - obstacle->spellLevel = parameters.effectLevel; - obstacle->casterSpellPower = parameters.effectPower; - obstacle->uniqueID = obstacleIdToGive++; + obstacle->pos = pos; + obstacle->casterSide = parameters.casterSide; + obstacle->ID = owner->id; + obstacle->spellLevel = parameters.effectLevel; + obstacle->casterSpellPower = parameters.effectPower; + obstacle->uniqueID = obstacleIdToGive; - BattleObstaclePlaced bop; - bop.obstacle = obstacle; - env->sendAndApply(&bop); - }; - - const BattleHex destination = parameters.getFirstDestinationHex(); - - switch(owner->id) - { - case SpellID::QUICKSAND: - case SpellID::LAND_MINE: - { - std::vector availableTiles; - for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1) - { - BattleHex hex = i; - if(hex.getX() > 0 && hex.getX() < 16 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) - availableTiles.push_back(hex); - } - boost::range::random_shuffle(availableTiles); - - const int patchesForSkill[] = {4, 4, 6, 8}; - const int patchesToPut = std::min(patchesForSkill[parameters.spellLvl], availableTiles.size()); - - //land mines or quicksand patches are handled as spell created obstacles - for (int i = 0; i < patchesToPut; i++) - placeObstacle(availableTiles.at(i)); - } - - break; - case SpellID::FORCE_FIELD: - if(!destination.isValid()) - { - env->complain("Invalid destination for FORCE_FIELD"); - return; - } - placeObstacle(destination); - break; - case SpellID::FIRE_WALL: - { - if(!destination.isValid()) - { - env->complain("Invalid destination for FIRE_WALL"); - return; - } - //fire wall is build from multiple obstacles - one fire piece for each affected hex - auto affectedHexes = owner->rangeInHexes(destination, parameters.spellLvl, parameters.casterSide); - for(BattleHex hex : affectedHexes) - placeObstacle(hex); - } - break; - default: - assert(0); - } + BattleObstaclePlaced bop; + bop.obstacle = obstacle; + env->sendAndApply(&bop); } -bool ObstacleMechanics::requiresCreatureTarget() const +///PatchObstacleMechanics +void PatchObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { - switch(owner->id) + std::vector availableTiles; + for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1) { - case SpellID::QUICKSAND: - return false; - case SpellID::LAND_MINE: - return true; - case SpellID::FORCE_FIELD: - return false; - case SpellID::FIRE_WALL: - return true; - default: - return false; + BattleHex hex = i; + if(hex.getX() > 0 && hex.getX() < 16 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) + availableTiles.push_back(hex); } + boost::range::random_shuffle(availableTiles); + + const int patchesForSkill[] = {4, 4, 6, 8}; + const int patchesToPut = std::min(patchesForSkill[parameters.spellLvl], availableTiles.size()); + + //land mines or quicksand patches are handled as spell created obstacles + for (int i = 0; i < patchesToPut; i++) + placeObstacle(env, parameters, availableTiles.at(i)); +} + +///LandMineMechanics +bool LandMineMechanics::requiresCreatureTarget() const +{ + return true; +} + +void LandMineMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const +{ + obstacle->obstacleType = CObstacleInstance::LAND_MINE; + obstacle->turnsRemaining = -1; + obstacle->visibleForAnotherSide = false; +} + +///QuicksandMechanics +bool QuicksandMechanics::requiresCreatureTarget() const +{ + return false; +} + +void QuicksandMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const +{ + obstacle->obstacleType = CObstacleInstance::QUICKSAND; + obstacle->turnsRemaining = -1; + obstacle->visibleForAnotherSide = false; } ///WallMechanics @@ -563,6 +512,59 @@ std::vector WallMechanics::rangeInHexes(BattleHex centralHex, ui8 sch return ret; } +///FireWallMechanics +bool FireWallMechanics::requiresCreatureTarget() const +{ + return true; +} + +void FireWallMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const +{ + const BattleHex destination = parameters.getFirstDestinationHex(); + + if(!destination.isValid()) + { + env->complain("Invalid destination for FIRE_WALL"); + return; + } + //firewall is build from multiple obstacles - one fire piece for each affected hex + auto affectedHexes = owner->rangeInHexes(destination, parameters.spellLvl, parameters.casterSide); + for(BattleHex hex : affectedHexes) + placeObstacle(env, parameters, hex); +} + +void FireWallMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const +{ + obstacle->obstacleType = CObstacleInstance::FIRE_WALL; + obstacle->turnsRemaining = 2; + obstacle->visibleForAnotherSide = true; +} + +///ForceFieldMechanics +bool ForceFieldMechanics::requiresCreatureTarget() const +{ + return false; +} + +void ForceFieldMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const +{ + const BattleHex destination = parameters.getFirstDestinationHex(); + + if(!destination.isValid()) + { + env->complain("Invalid destination for FORCE_FIELD"); + return; + } + placeObstacle(env, parameters, destination); +} + +void ForceFieldMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const +{ + obstacle->obstacleType = CObstacleInstance::FORCE_FIELD; + obstacle->turnsRemaining = 2; + obstacle->visibleForAnotherSide = true; +} + ///RemoveObstacleMechanics void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 6f93b31ea..85d3fc41d 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -13,6 +13,7 @@ #include "CDefaultSpellMechanics.h" class CObstacleInstance; +class SpellCreatedObstacle; class DLL_LINKAGE HealingSpellMechanics : public DefaultSpellMechanics { @@ -98,11 +99,37 @@ class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics public: ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; - bool requiresCreatureTarget() const override; +protected: + void placeObstacle(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const BattleHex & pos) const; + virtual void setupObstacle(SpellCreatedObstacle * obstacle) const = 0; +}; + +class PatchObstacleMechanics : public ObstacleMechanics +{ +public: + PatchObstacleMechanics(CSpell * s): ObstacleMechanics(s){}; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; +class DLL_LINKAGE LandMineMechanics : public PatchObstacleMechanics +{ +public: + LandMineMechanics(CSpell * s): PatchObstacleMechanics(s){}; + bool requiresCreatureTarget() const override; +protected: + void setupObstacle(SpellCreatedObstacle * obstacle) const override; +}; + +class DLL_LINKAGE QuicksandMechanics : public PatchObstacleMechanics +{ +public: + QuicksandMechanics(CSpell * s): PatchObstacleMechanics(s){}; + bool requiresCreatureTarget() const override; +protected: + void setupObstacle(SpellCreatedObstacle * obstacle) const override; +}; + class DLL_LINKAGE WallMechanics : public ObstacleMechanics { public: @@ -110,6 +137,26 @@ public: std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override; }; +class DLL_LINKAGE FireWallMechanics : public WallMechanics +{ +public: + FireWallMechanics(CSpell * s): WallMechanics(s){}; + bool requiresCreatureTarget() const override; +protected: + void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; + void setupObstacle(SpellCreatedObstacle * obstacle) const override; +}; + +class DLL_LINKAGE ForceFieldMechanics : public WallMechanics +{ +public: + ForceFieldMechanics(CSpell * s): WallMechanics(s){}; + bool requiresCreatureTarget() const override; +protected: + void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; + void setupObstacle(SpellCreatedObstacle * obstacle) const override; +}; + class DLL_LINKAGE RemoveObstacleMechanics : public DefaultSpellMechanics { public: diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 7aca151b3..185e4935d 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -258,7 +258,6 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS 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); diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index ef0b7a0f1..8f3dbac5c 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -106,13 +106,15 @@ std::unique_ptr ISpellMechanics::createMechanics(CSpell * s) case SpellID::EARTHQUAKE: return make_unique(s); case SpellID::FIRE_WALL: + return make_unique(s); case SpellID::FORCE_FIELD: - return make_unique(s); + return make_unique(s); case SpellID::HYPNOTIZE: return make_unique(s); case SpellID::LAND_MINE: + return make_unique(s); case SpellID::QUICKSAND: - return make_unique(s); + return make_unique(s); case SpellID::REMOVE_OBSTACLE: return make_unique(s); case SpellID::SACRIFICE: