mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-17 01:32:21 +02:00
Demon summon is now a spell. DEMON_SUMMONING bonus has been removed
This commit is contained in:
@ -171,8 +171,6 @@ void BattleActionsController::reorderPossibleActionsPriority(const CStack * stac
|
|||||||
break;
|
break;
|
||||||
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
||||||
return 2; break;
|
return 2; break;
|
||||||
case PossiblePlayerBattleAction::RISE_DEMONS:
|
|
||||||
return 3; break;
|
|
||||||
case PossiblePlayerBattleAction::SHOOT:
|
case PossiblePlayerBattleAction::SHOOT:
|
||||||
return 4; break;
|
return 4; break;
|
||||||
case PossiblePlayerBattleAction::ATTACK_AND_RETURN:
|
case PossiblePlayerBattleAction::ATTACK_AND_RETURN:
|
||||||
@ -374,19 +372,6 @@ void BattleActionsController::handleHex(BattleHex myNumber, int eventType)
|
|||||||
if (shere && ourStack && shere->canBeHealed())
|
if (shere && ourStack && shere->canBeHealed())
|
||||||
legalAction = true;
|
legalAction = true;
|
||||||
break;
|
break;
|
||||||
case PossiblePlayerBattleAction::RISE_DEMONS:
|
|
||||||
if (shere && ourStack && !shere->alive())
|
|
||||||
{
|
|
||||||
if (!(shere->hasBonusOfType(Bonus::UNDEAD)
|
|
||||||
|| shere->hasBonusOfType(Bonus::NON_LIVING)
|
|
||||||
|| shere->hasBonusOfType(Bonus::GARGOYLE)
|
|
||||||
|| shere->summoned
|
|
||||||
|| shere->isClone()
|
|
||||||
|| shere->hasBonusOfType(Bonus::SIEGE_WEAPON)
|
|
||||||
))
|
|
||||||
legalAction = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (legalAction)
|
if (legalAction)
|
||||||
localActions.push_back (action);
|
localActions.push_back (action);
|
||||||
@ -542,13 +527,6 @@ void BattleActionsController::handleHex(BattleHex myNumber, int eventType)
|
|||||||
newConsoleMsg = (boost::format(CGI->generaltexth->allTexts[419]) % shere->getName()).str(); //Apply first aid to the %s
|
newConsoleMsg = (boost::format(CGI->generaltexth->allTexts[419]) % shere->getName()).str(); //Apply first aid to the %s
|
||||||
realizeAction = [=](){ owner.giveCommand(EActionType::STACK_HEAL, myNumber); }; //command healing
|
realizeAction = [=](){ owner.giveCommand(EActionType::STACK_HEAL, myNumber); }; //command healing
|
||||||
break;
|
break;
|
||||||
case PossiblePlayerBattleAction::RISE_DEMONS:
|
|
||||||
spellcastingCursor = true;
|
|
||||||
realizeAction = [=]()
|
|
||||||
{
|
|
||||||
owner.giveCommand(EActionType::DAEMON_SUMMONING, myNumber);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case PossiblePlayerBattleAction::CATAPULT:
|
case PossiblePlayerBattleAction::CATAPULT:
|
||||||
cursorFrame = Cursor::Combat::SHOOT_CATAPULT;
|
cursorFrame = Cursor::Combat::SHOOT_CATAPULT;
|
||||||
realizeAction = [=](){ owner.giveCommand(EActionType::CATAPULT, myNumber); };
|
realizeAction = [=](){ owner.giveCommand(EActionType::CATAPULT, myNumber); };
|
||||||
|
@ -847,7 +847,7 @@ void BattleStacksController::updateHoveredStacks()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
stackAnimation[stack->ID]->setBorderColor(AnimationControls::getBlueBorder());
|
stackAnimation[stack->ID]->setBorderColor(AnimationControls::getBlueBorder());
|
||||||
if (stackAnimation[stack->ID]->framesInGroup(ECreatureAnimType::MOUSEON) > 0)
|
if (stackAnimation[stack->ID]->framesInGroup(ECreatureAnimType::MOUSEON) > 0 && stack->alive())
|
||||||
stackAnimation[stack->ID]->playOnce(ECreatureAnimType::MOUSEON);
|
stackAnimation[stack->ID]->playOnce(ECreatureAnimType::MOUSEON);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
|||||||
${MAIN_LIB_DIR}/spells/effects/Catapult.cpp
|
${MAIN_LIB_DIR}/spells/effects/Catapult.cpp
|
||||||
${MAIN_LIB_DIR}/spells/effects/Clone.cpp
|
${MAIN_LIB_DIR}/spells/effects/Clone.cpp
|
||||||
${MAIN_LIB_DIR}/spells/effects/Damage.cpp
|
${MAIN_LIB_DIR}/spells/effects/Damage.cpp
|
||||||
|
${MAIN_LIB_DIR}/spells/effects/DemonSummon.cpp
|
||||||
${MAIN_LIB_DIR}/spells/effects/Dispel.cpp
|
${MAIN_LIB_DIR}/spells/effects/Dispel.cpp
|
||||||
${MAIN_LIB_DIR}/spells/effects/Effect.cpp
|
${MAIN_LIB_DIR}/spells/effects/Effect.cpp
|
||||||
${MAIN_LIB_DIR}/spells/effects/Effects.cpp
|
${MAIN_LIB_DIR}/spells/effects/Effects.cpp
|
||||||
@ -374,6 +375,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
|||||||
${MAIN_LIB_DIR}/spells/effects/Catapult.h
|
${MAIN_LIB_DIR}/spells/effects/Catapult.h
|
||||||
${MAIN_LIB_DIR}/spells/effects/Clone.h
|
${MAIN_LIB_DIR}/spells/effects/Clone.h
|
||||||
${MAIN_LIB_DIR}/spells/effects/Damage.h
|
${MAIN_LIB_DIR}/spells/effects/Damage.h
|
||||||
|
${MAIN_LIB_DIR}/spells/effects/DemonSummon.h
|
||||||
${MAIN_LIB_DIR}/spells/effects/Dispel.h
|
${MAIN_LIB_DIR}/spells/effects/Dispel.h
|
||||||
${MAIN_LIB_DIR}/spells/effects/Effect.h
|
${MAIN_LIB_DIR}/spells/effects/Effect.h
|
||||||
${MAIN_LIB_DIR}/spells/effects/Effects.h
|
${MAIN_LIB_DIR}/spells/effects/Effects.h
|
||||||
|
@ -2240,12 +2240,6 @@
|
|||||||
"val" : 0,
|
"val" : 0,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"subtype" : "creature.vampireLord",
|
|
||||||
"type" : "DAEMON_SUMMONING",
|
|
||||||
"val" : 10,
|
|
||||||
"valueType" : "BASE_NUMBER"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"addInfo" : 2,
|
"addInfo" : 2,
|
||||||
"subtype" : "spell.lightningBolt",
|
"subtype" : "spell.lightningBolt",
|
||||||
|
@ -84,14 +84,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"DAEMON_SUMMONING":
|
|
||||||
{
|
|
||||||
"graphics":
|
|
||||||
{
|
|
||||||
"icon": "zvs/Lib1.res/RiseDemons"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"DARKNESS":
|
"DARKNESS":
|
||||||
{
|
{
|
||||||
},
|
},
|
||||||
|
@ -72,12 +72,6 @@
|
|||||||
"description": "Immune to Champion charge"
|
"description": "Immune to Champion charge"
|
||||||
},
|
},
|
||||||
|
|
||||||
"DAEMON_SUMMONING":
|
|
||||||
{
|
|
||||||
"name": "Summoner (${subtype.creature})",
|
|
||||||
"description": "Can rise creatures from corpses"
|
|
||||||
},
|
|
||||||
|
|
||||||
"DARKNESS":
|
"DARKNESS":
|
||||||
{
|
{
|
||||||
"name": "Darkness cover",
|
"name": "Darkness cover",
|
||||||
|
@ -216,12 +216,18 @@
|
|||||||
"faction": "inferno",
|
"faction": "inferno",
|
||||||
"abilities":
|
"abilities":
|
||||||
{
|
{
|
||||||
"demonSummon" :
|
"summons50HP" :
|
||||||
{
|
{
|
||||||
"type" : "DAEMON_SUMMONING",
|
"type" : "SPECIFIC_SPELL_POWER",
|
||||||
"subtype" : "creature.demon",
|
"subtype" : "spell.summonDemons",
|
||||||
"val" : 50
|
"val" : 50
|
||||||
},
|
},
|
||||||
|
"resurrects" :
|
||||||
|
{
|
||||||
|
"type" : "SPELLCASTER",
|
||||||
|
"subtype" : "spell.summonDemons",
|
||||||
|
"val" : 0
|
||||||
|
},
|
||||||
"castsAmount" :
|
"castsAmount" :
|
||||||
{
|
{
|
||||||
"type" : "CASTS",
|
"type" : "CASTS",
|
||||||
|
@ -430,5 +430,52 @@
|
|||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"summonDemons" : {
|
||||||
|
"type": "ability",
|
||||||
|
"targetType" : "CREATURE",
|
||||||
|
"name": "Summon Demons",
|
||||||
|
"school" : {},
|
||||||
|
"level": 2,
|
||||||
|
"power": 50,
|
||||||
|
"defaultGainChance": 0,
|
||||||
|
"gainChance": {},
|
||||||
|
"animation":{
|
||||||
|
},
|
||||||
|
"sounds": {
|
||||||
|
"cast": "RESURECT"
|
||||||
|
},
|
||||||
|
"levels" : {
|
||||||
|
"base": {
|
||||||
|
"description" : "",
|
||||||
|
"aiValue" : 0,
|
||||||
|
"power" : 40,
|
||||||
|
"cost" : 1,
|
||||||
|
"range" : "0",
|
||||||
|
"battleEffects":{
|
||||||
|
"demonSummon":{
|
||||||
|
"id":"demon",
|
||||||
|
"permanent":true,
|
||||||
|
"type":"core:demonSummon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"none" :{},
|
||||||
|
"basic" :{},
|
||||||
|
"advanced" :{},
|
||||||
|
"expert" :{}
|
||||||
|
},
|
||||||
|
"flags" : {
|
||||||
|
"rising": true,
|
||||||
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.NON_LIVING" : "absolute",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute",
|
||||||
|
"bonus.GARGOYLE" : "absolute"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -232,7 +232,6 @@ std::ostream & operator<<(std::ostream & os, const EActionType actionType)
|
|||||||
{EActionType::MONSTER_SPELL, "Monster spell"},
|
{EActionType::MONSTER_SPELL, "Monster spell"},
|
||||||
{EActionType::BAD_MORALE, "Bad morale"},
|
{EActionType::BAD_MORALE, "Bad morale"},
|
||||||
{EActionType::STACK_HEAL, "Stack heal"},
|
{EActionType::STACK_HEAL, "Stack heal"},
|
||||||
{EActionType::DAEMON_SUMMONING, "Daemon summoning"}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto it = actionTypeToString.find(actionType);
|
auto it = actionTypeToString.find(actionType);
|
||||||
|
@ -922,7 +922,6 @@ enum class EActionType : int32_t
|
|||||||
MONSTER_SPELL,
|
MONSTER_SPELL,
|
||||||
BAD_MORALE,
|
BAD_MORALE,
|
||||||
STACK_HEAL,
|
STACK_HEAL,
|
||||||
DAEMON_SUMMONING
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EActionType actionType);
|
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EActionType actionType);
|
||||||
|
@ -237,7 +237,6 @@ public:
|
|||||||
BONUS_NAME(MANA_CHANNELING) /*value in %, eg. familiar*/ \
|
BONUS_NAME(MANA_CHANNELING) /*value in %, eg. familiar*/ \
|
||||||
BONUS_NAME(SPELL_LIKE_ATTACK) /*subtype - spell, value - spell level; range is taken from spell, but damage from creature; eg. magog*/ \
|
BONUS_NAME(SPELL_LIKE_ATTACK) /*subtype - spell, value - spell level; range is taken from spell, but damage from creature; eg. magog*/ \
|
||||||
BONUS_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/ \
|
BONUS_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/ \
|
||||||
BONUS_NAME(DAEMON_SUMMONING) /*pit lord, subtype - type of creatures, val - hp per unit*/ \
|
|
||||||
BONUS_NAME(FIRE_IMMUNITY) /*subtype 0 - all, 1 - all except positive, 2 - only damage spells*/ \
|
BONUS_NAME(FIRE_IMMUNITY) /*subtype 0 - all, 1 - all except positive, 2 - only damage spells*/ \
|
||||||
BONUS_NAME(WATER_IMMUNITY) \
|
BONUS_NAME(WATER_IMMUNITY) \
|
||||||
BONUS_NAME(EARTH_IMMUNITY) \
|
BONUS_NAME(EARTH_IMMUNITY) \
|
||||||
|
@ -230,8 +230,6 @@ std::vector<PossiblePlayerBattleAction> CBattleInfoCallback::getClientActionsFor
|
|||||||
}
|
}
|
||||||
if(stack->hasBonusOfType(Bonus::RANDOM_SPELLCASTER))
|
if(stack->hasBonusOfType(Bonus::RANDOM_SPELLCASTER))
|
||||||
allowedActionList.push_back(PossiblePlayerBattleAction::RANDOM_GENIE_SPELL);
|
allowedActionList.push_back(PossiblePlayerBattleAction::RANDOM_GENIE_SPELL);
|
||||||
if(stack->hasBonusOfType(Bonus::DAEMON_SUMMONING))
|
|
||||||
allowedActionList.push_back(PossiblePlayerBattleAction::RISE_DEMONS);
|
|
||||||
}
|
}
|
||||||
if(stack->canShoot())
|
if(stack->canShoot())
|
||||||
allowedActionList.push_back(PossiblePlayerBattleAction::SHOOT);
|
allowedActionList.push_back(PossiblePlayerBattleAction::SHOOT);
|
||||||
|
@ -49,7 +49,7 @@ enum class PossiblePlayerBattleAction // actions performed at l-click
|
|||||||
MOVE_STACK, ATTACK, WALK_AND_ATTACK, ATTACK_AND_RETURN, SHOOT, //OPEN_GATE, //we can open castle gate during siege
|
MOVE_STACK, ATTACK, WALK_AND_ATTACK, ATTACK_AND_RETURN, SHOOT, //OPEN_GATE, //we can open castle gate during siege
|
||||||
NO_LOCATION, ANY_LOCATION, OBSTACLE, TELEPORT, SACRIFICE, RANDOM_GENIE_SPELL,
|
NO_LOCATION, ANY_LOCATION, OBSTACLE, TELEPORT, SACRIFICE, RANDOM_GENIE_SPELL,
|
||||||
FREE_LOCATION, //used with Force Field and Fire Wall - all tiles affected by spell must be free
|
FREE_LOCATION, //used with Force Field and Fire Wall - all tiles affected by spell must be free
|
||||||
CATAPULT, HEAL, RISE_DEMONS,
|
CATAPULT, HEAL,
|
||||||
AIMED_SPELL_CREATURE
|
AIMED_SPELL_CREATURE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -608,9 +608,9 @@ std::vector<Destination> BattleSpellMechanics::getPossibleDestinations(size_t in
|
|||||||
Target tmp = current;
|
Target tmp = current;
|
||||||
tmp.emplace_back(dest);
|
tmp.emplace_back(dest);
|
||||||
|
|
||||||
detail::ProblemImpl ingored;
|
detail::ProblemImpl ignored;
|
||||||
|
|
||||||
if(canBeCastAt(tmp, ingored))
|
if(canBeCastAt(tmp, ignored))
|
||||||
ret.emplace_back(dest);
|
ret.emplace_back(dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,7 +630,7 @@ bool BattleSpellMechanics::isReceptive(const battle::Unit * target) const
|
|||||||
return targetCondition->isReceptive(this, target);
|
return targetCondition->isReceptive(this, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<BattleHex> BattleSpellMechanics::rangeInHexes(BattleHex centralHex, bool * outDroppedHexes) const
|
std::vector<BattleHex> BattleSpellMechanics::rangeInHexes(BattleHex centralHex) const
|
||||||
{
|
{
|
||||||
if(isMassive() || !centralHex.isValid())
|
if(isMassive() || !centralHex.isValid())
|
||||||
return std::vector<BattleHex>(1, BattleHex::INVALID);
|
return std::vector<BattleHex>(1, BattleHex::INVALID);
|
||||||
|
@ -27,22 +27,36 @@ public:
|
|||||||
BattleSpellMechanics(const IBattleCast * event, std::shared_ptr<effects::Effects> effects_, std::shared_ptr<IReceptiveCheck> targetCondition_);
|
BattleSpellMechanics(const IBattleCast * event, std::shared_ptr<effects::Effects> effects_, std::shared_ptr<IReceptiveCheck> targetCondition_);
|
||||||
virtual ~BattleSpellMechanics();
|
virtual ~BattleSpellMechanics();
|
||||||
|
|
||||||
|
// TODO: ??? (what's the difference compared to cast?)
|
||||||
void applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const override;
|
void applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const override;
|
||||||
|
|
||||||
|
/// Returns false if spell can not be cast at all, e.g. due to not having any possible target on battlefield
|
||||||
bool canBeCast(Problem & problem) const override;
|
bool canBeCast(Problem & problem) const override;
|
||||||
|
|
||||||
|
/// Returns false if spell can not be cast at specifid target
|
||||||
bool canBeCastAt(const Target & target, Problem & problem) const override;
|
bool canBeCastAt(const Target & target, Problem & problem) const override;
|
||||||
|
|
||||||
|
// TODO: ??? (what's the difference compared to applyEffects?)
|
||||||
void cast(ServerCallback * server, const Target & target) override final;
|
void cast(ServerCallback * server, const Target & target) override final;
|
||||||
|
// TODO: ??? (what's the difference compared to cast?)
|
||||||
void castEval(ServerCallback * server, const Target & target) override final;
|
void castEval(ServerCallback * server, const Target & target) override final;
|
||||||
|
|
||||||
|
/// Returns list of affected stack using currently configured target
|
||||||
std::vector<const CStack *> getAffectedStacks(const Target & target) const override final;
|
std::vector<const CStack *> getAffectedStacks(const Target & target) const override final;
|
||||||
|
|
||||||
|
/// Returns list of target types that can be targeted by spell
|
||||||
std::vector<AimType> getTargetTypes() const override final;
|
std::vector<AimType> getTargetTypes() const override final;
|
||||||
|
|
||||||
|
/// Returns vector of all possible destinations for specified aim type
|
||||||
|
/// index - ???
|
||||||
|
/// current - ???
|
||||||
std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current) const override final;
|
std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current) const override final;
|
||||||
|
|
||||||
|
/// Returns true if spell can be cast on unit
|
||||||
bool isReceptive(const battle::Unit * target) const override;
|
bool isReceptive(const battle::Unit * target) const override;
|
||||||
|
|
||||||
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, bool * outDroppedHexes = nullptr) const override;
|
/// Returns list of hexes that are affected by spell assuming cast at centralHex
|
||||||
|
std::vector<BattleHex> rangeInHexes(BattleHex centralHex) const override;
|
||||||
|
|
||||||
const Spell * getSpell() const override;
|
const Spell * getSpell() const override;
|
||||||
|
|
||||||
|
@ -701,9 +701,9 @@ PlayerColor BaseMechanics::getCasterColor() const
|
|||||||
std::vector<AimType> BaseMechanics::getTargetTypes() const
|
std::vector<AimType> BaseMechanics::getTargetTypes() const
|
||||||
{
|
{
|
||||||
std::vector<AimType> ret;
|
std::vector<AimType> ret;
|
||||||
detail::ProblemImpl ingored;
|
detail::ProblemImpl ignored;
|
||||||
|
|
||||||
if(canBeCast(ingored))
|
if(canBeCast(ignored))
|
||||||
{
|
{
|
||||||
auto spellTargetType = owner->getTargetType();
|
auto spellTargetType = owner->getTargetType();
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ public:
|
|||||||
virtual bool adaptProblem(ESpellCastProblem::ESpellCastProblem source, Problem & target) const = 0;
|
virtual bool adaptProblem(ESpellCastProblem::ESpellCastProblem source, Problem & target) const = 0;
|
||||||
virtual bool adaptGenericProblem(Problem & target) const = 0;
|
virtual bool adaptGenericProblem(Problem & target) const = 0;
|
||||||
|
|
||||||
virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex, bool * outDroppedHexes = nullptr) const = 0;
|
virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex) const = 0;
|
||||||
virtual std::vector<const CStack *> getAffectedStacks(const Target & target) const = 0;
|
virtual std::vector<const CStack *> getAffectedStacks(const Target & target) const = 0;
|
||||||
|
|
||||||
virtual bool canBeCast(Problem & problem) const = 0;
|
virtual bool canBeCast(Problem & problem) const = 0;
|
||||||
|
130
lib/spells/effects/DemonSummon.cpp
Normal file
130
lib/spells/effects/DemonSummon.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* DemonSummon.cpp, 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 "StdInc.h"
|
||||||
|
|
||||||
|
#include "DemonSummon.h"
|
||||||
|
#include "Registry.h"
|
||||||
|
#include "../ISpellMechanics.h"
|
||||||
|
#include "../../NetPacks.h"
|
||||||
|
#include "../../battle/CBattleInfoCallback.h"
|
||||||
|
#include "../../battle/CUnitState.h"
|
||||||
|
#include "../../serializer/JsonSerializeFormat.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
static const std::string EFFECT_NAME = "core:demonSummon";
|
||||||
|
|
||||||
|
namespace spells
|
||||||
|
{
|
||||||
|
namespace effects
|
||||||
|
{
|
||||||
|
|
||||||
|
VCMI_REGISTER_SPELL_EFFECT(DemonSummon, EFFECT_NAME);
|
||||||
|
|
||||||
|
DemonSummon::DemonSummon()
|
||||||
|
: UnitEffect()
|
||||||
|
, creature(0)
|
||||||
|
, permanent(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DemonSummon::~DemonSummon() = default;
|
||||||
|
|
||||||
|
void DemonSummon::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const
|
||||||
|
{
|
||||||
|
BattleUnitsChanged pack;
|
||||||
|
|
||||||
|
for(const Destination & dest : target)
|
||||||
|
{
|
||||||
|
const battle::Unit * targetStack = dest.unitValue;
|
||||||
|
|
||||||
|
//we shall have all targets to be stacks
|
||||||
|
if(!targetStack || targetStack->alive() || targetStack->isGhost())
|
||||||
|
{
|
||||||
|
server->complain("No corpse to demonize! Invalid effect target transformation.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hex = m->battle()->getAvaliableHex(targetStack->creatureId(), m->casterSide, targetStack->getPosition());
|
||||||
|
|
||||||
|
if(!hex.isValid())
|
||||||
|
{
|
||||||
|
server->complain("No place to put new summon!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto creatureType = creature.toCreature(m->creatures());
|
||||||
|
|
||||||
|
int32_t deadCount = targetStack->unitBaseAmount();
|
||||||
|
int32_t deadTotalHealth = targetStack->getTotalHealth();
|
||||||
|
int32_t raisedMaxHealth = creatureType->getMaxHealth();
|
||||||
|
int32_t raisedTotalHealth = m->applySpellBonus(m->getEffectValue(), targetStack);
|
||||||
|
|
||||||
|
// Can't raise stack with more HP than original stack
|
||||||
|
int32_t maxAmountFromHealth = deadTotalHealth / raisedMaxHealth;
|
||||||
|
// Can't raise stack with more creatures than original stack
|
||||||
|
int32_t maxAmountFromAmount = deadCount;
|
||||||
|
// Can't raise stack with more HP than our spellpower
|
||||||
|
int32_t maxAmountFromSpellpower = raisedTotalHealth / raisedMaxHealth;
|
||||||
|
|
||||||
|
int32_t finalAmount = std::min( { maxAmountFromHealth, maxAmountFromAmount, maxAmountFromSpellpower } );
|
||||||
|
|
||||||
|
if(finalAmount < 1)
|
||||||
|
{
|
||||||
|
server->complain("Summoning didn't summon any!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
battle::UnitInfo info;
|
||||||
|
info.id = m->battle()->battleNextUnitId();
|
||||||
|
info.count = finalAmount;
|
||||||
|
info.type = creature;
|
||||||
|
info.side = m->casterSide;
|
||||||
|
info.position = dest.hexValue;
|
||||||
|
info.summoned = !permanent;
|
||||||
|
|
||||||
|
// add newly created creature
|
||||||
|
pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
|
||||||
|
info.save(pack.changedStacks.back().data);
|
||||||
|
|
||||||
|
// and remove corpse to prevent second raising or resurrection
|
||||||
|
pack.changedStacks.emplace_back(targetStack->unitId(), UnitChanges::EOperation::REMOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!pack.changedStacks.empty())
|
||||||
|
server->apply(&pack);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DemonSummon::isValidTarget(const Mechanics * m, const battle::Unit * s) const
|
||||||
|
{
|
||||||
|
if(!s->isDead())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (s->isGhost())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto creatureType = creature.toCreature(m->creatures());
|
||||||
|
|
||||||
|
if (s->getTotalHealth() < creatureType->getMaxHealth())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m->isReceptive(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DemonSummon::serializeJsonUnitEffect(JsonSerializeFormat & handler)
|
||||||
|
{
|
||||||
|
handler.serializeId("id", creature, CreatureID());
|
||||||
|
handler.serializeBool("permanent", permanent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
44
lib/spells/effects/DemonSummon.h
Normal file
44
lib/spells/effects/DemonSummon.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* DemonSummon.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 "UnitEffect.h"
|
||||||
|
#include "../../GameConstants.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
namespace spells
|
||||||
|
{
|
||||||
|
namespace effects
|
||||||
|
{
|
||||||
|
|
||||||
|
class DemonSummon : public UnitEffect
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DemonSummon();
|
||||||
|
virtual ~DemonSummon();
|
||||||
|
|
||||||
|
void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override;
|
||||||
|
protected:
|
||||||
|
bool isValidTarget(const Mechanics * m, const battle::Unit * s) const override;
|
||||||
|
|
||||||
|
void serializeJsonUnitEffect(JsonSerializeFormat & handler) override final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CreatureID creature;
|
||||||
|
|
||||||
|
bool permanent;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
@ -51,19 +51,27 @@ public:
|
|||||||
Effect();
|
Effect();
|
||||||
virtual ~Effect();
|
virtual ~Effect();
|
||||||
|
|
||||||
|
// TODO: document me
|
||||||
virtual void adjustTargetTypes(std::vector<TargetType> & types) const = 0;
|
virtual void adjustTargetTypes(std::vector<TargetType> & types) const = 0;
|
||||||
|
|
||||||
|
/// Generates list of hexes affected by spell, if spell were to cast at specified target
|
||||||
virtual void adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const = 0;
|
virtual void adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const = 0;
|
||||||
|
|
||||||
|
/// Returns whether effect has any valid targets on the battlefield
|
||||||
virtual bool applicable(Problem & problem, const Mechanics * m) const;
|
virtual bool applicable(Problem & problem, const Mechanics * m) const;
|
||||||
|
|
||||||
|
/// Returns whether effect is valid and can be applied onto selected target
|
||||||
virtual bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const;
|
virtual bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const;
|
||||||
|
|
||||||
virtual void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const = 0;
|
virtual void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const = 0;
|
||||||
|
|
||||||
|
/// Processes input target and generates subset-result that contains only valid targets
|
||||||
virtual EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const = 0;
|
virtual EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const = 0;
|
||||||
|
|
||||||
|
// TODO: document me
|
||||||
virtual EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const = 0;
|
virtual EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const = 0;
|
||||||
|
|
||||||
|
/// Serializes (or deserializes) parameters of Effect
|
||||||
void serializeJson(JsonSerializeFormat & handler);
|
void serializeJson(JsonSerializeFormat & handler);
|
||||||
|
|
||||||
static std::shared_ptr<Effect> create(const Registry * registry, const std::string & type);
|
static std::shared_ptr<Effect> create(const Registry * registry, const std::string & type);
|
||||||
|
@ -63,14 +63,14 @@ bool Heal::isValidTarget(const Mechanics * m, const battle::Unit * unit) const
|
|||||||
if(!validInGenaral)
|
if(!validInGenaral)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto insuries = unit->getTotalHealth() - unit->getAvailableHealth();
|
auto injuries = unit->getTotalHealth() - unit->getAvailableHealth();
|
||||||
|
|
||||||
if(insuries == 0)
|
if(injuries == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(minFullUnits > 0)
|
if(minFullUnits > 0)
|
||||||
{
|
{
|
||||||
auto hpGained = std::min(m->getEffectValue(), insuries);
|
auto hpGained = std::min(m->getEffectValue(), injuries);
|
||||||
if(hpGained < minFullUnits * unit->MaxHealth())
|
if(hpGained < minFullUnits * unit->MaxHealth())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -4472,7 +4472,6 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
case EActionType::SHOOT: //shoot
|
case EActionType::SHOOT: //shoot
|
||||||
case EActionType::CATAPULT: //catapult
|
case EActionType::CATAPULT: //catapult
|
||||||
case EActionType::STACK_HEAL: //healing with First Aid Tent
|
case EActionType::STACK_HEAL: //healing with First Aid Tent
|
||||||
case EActionType::DAEMON_SUMMONING:
|
|
||||||
case EActionType::MONSTER_SPELL:
|
case EActionType::MONSTER_SPELL:
|
||||||
|
|
||||||
if (!stack)
|
if (!stack)
|
||||||
@ -5006,58 +5005,6 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EActionType::DAEMON_SUMMONING:
|
|
||||||
//TODO: From Strategija:
|
|
||||||
//Summon Demon is a level 2 spell.
|
|
||||||
{
|
|
||||||
if(target.size() < 1)
|
|
||||||
{
|
|
||||||
complain("Destination required for summon action.");
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CStack * summoner = gs->curB->battleGetStackByID(ba.stackNumber);
|
|
||||||
const CStack * destStack = gs->curB->battleGetStackByPos(target.at(0).hexValue, false);
|
|
||||||
|
|
||||||
CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type()(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream!
|
|
||||||
|
|
||||||
ui64 risedHp = summoner->getCount() * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, summonedType.toEnum());
|
|
||||||
ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount;
|
|
||||||
|
|
||||||
ui64 canRiseHp = std::min(targetHealth, risedHp);
|
|
||||||
ui32 canRiseAmount = static_cast<ui32>(canRiseHp / summonedType.toCreature()->MaxHealth());
|
|
||||||
|
|
||||||
battle::UnitInfo info;
|
|
||||||
info.id = gs->curB->battleNextUnitId();
|
|
||||||
info.count = std::min(canRiseAmount, destStack->baseAmount);
|
|
||||||
info.type = summonedType;
|
|
||||||
info.side = summoner->side;
|
|
||||||
info.position = gs->curB->getAvaliableHex(summonedType, summoner->side, destStack->getPosition());
|
|
||||||
info.summoned = false;
|
|
||||||
|
|
||||||
BattleUnitsChanged addUnits;
|
|
||||||
addUnits.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
|
|
||||||
info.save(addUnits.changedStacks.back().data);
|
|
||||||
|
|
||||||
if(info.count > 0) //there's rare possibility single creature cannot rise desired type
|
|
||||||
{
|
|
||||||
auto wrapper = wrapAction(ba);
|
|
||||||
|
|
||||||
BattleUnitsChanged removeUnits;
|
|
||||||
removeUnits.changedStacks.emplace_back(destStack->unitId(), UnitChanges::EOperation::REMOVE);
|
|
||||||
sendAndApply(&removeUnits);
|
|
||||||
sendAndApply(&addUnits);
|
|
||||||
|
|
||||||
BattleSetStackProperty ssp;
|
|
||||||
ssp.stackID = ba.stackNumber;
|
|
||||||
ssp.which = BattleSetStackProperty::CASTS; //reduce number of casts
|
|
||||||
ssp.val = -1;
|
|
||||||
ssp.absolute = false;
|
|
||||||
sendAndApply(&ssp);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EActionType::MONSTER_SPELL:
|
case EActionType::MONSTER_SPELL:
|
||||||
{
|
{
|
||||||
auto wrapper = wrapAction(ba);
|
auto wrapper = wrapAction(ba);
|
||||||
@ -5089,7 +5036,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(ba.actionType == EActionType::DAEMON_SUMMONING || ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND
|
if(ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND
|
||||||
|| ba.actionType == EActionType::SHOOT || ba.actionType == EActionType::MONSTER_SPELL)
|
|| ba.actionType == EActionType::SHOOT || ba.actionType == EActionType::MONSTER_SPELL)
|
||||||
handleDamageFromObstacle(stack);
|
handleDamageFromObstacle(stack);
|
||||||
if(ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
|
if(ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
|
||||||
|
Reference in New Issue
Block a user