mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Implemented support for multi-spell casters
This commit is contained in:
@@ -42,8 +42,7 @@ static std::string formatDmgRange(std::pair<ui32, ui32> dmgRange)
|
|||||||
|
|
||||||
BattleActionsController::BattleActionsController(BattleInterface & owner):
|
BattleActionsController::BattleActionsController(BattleInterface & owner):
|
||||||
owner(owner),
|
owner(owner),
|
||||||
heroSpellToCast(nullptr),
|
heroSpellToCast(nullptr)
|
||||||
creatureSpellToCast(nullptr)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void BattleActionsController::endCastingSpell()
|
void BattleActionsController::endCastingSpell()
|
||||||
@@ -63,8 +62,8 @@ bool BattleActionsController::isActiveStackSpellcaster() const
|
|||||||
if (!casterStack)
|
if (!casterStack)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const auto randomSpellcaster = casterStack->getBonusLocalFirst(Selector::type()(Bonus::SPELLCASTER));
|
bool spellcaster = casterStack->hasBonusOfType(Bonus::SPELLCASTER);
|
||||||
return (randomSpellcaster && casterStack->canCast());
|
return (spellcaster && casterStack->canCast());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleActionsController::enterCreatureCastingMode()
|
void BattleActionsController::enterCreatureCastingMode()
|
||||||
@@ -83,10 +82,13 @@ void BattleActionsController::enterCreatureCastingMode()
|
|||||||
if (!isActiveStackSpellcaster())
|
if (!isActiveStackSpellcaster())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (vstd::contains(possibleActions, PossiblePlayerBattleAction::NO_LOCATION))
|
for (auto const & action : possibleActions)
|
||||||
{
|
{
|
||||||
|
if (action.get() != PossiblePlayerBattleAction::NO_LOCATION)
|
||||||
|
continue;
|
||||||
|
|
||||||
const spells::Caster * caster = owner.stacksController->getActiveStack();
|
const spells::Caster * caster = owner.stacksController->getActiveStack();
|
||||||
const CSpell * spell = getStackSpellToCast();
|
const CSpell * spell = action.spell().toSpell();
|
||||||
|
|
||||||
spells::Target target;
|
spells::Target target;
|
||||||
target.emplace_back();
|
target.emplace_back();
|
||||||
@@ -105,31 +107,26 @@ void BattleActionsController::enterCreatureCastingMode()
|
|||||||
|
|
||||||
CCS->curh->set(Cursor::Combat::POINTER);
|
CCS->curh->set(Cursor::Combat::POINTER);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack());
|
possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack());
|
||||||
|
|
||||||
auto actionFilterPredicate = [](const PossiblePlayerBattleAction x)
|
auto actionFilterPredicate = [](const PossiblePlayerBattleAction x)
|
||||||
{
|
{
|
||||||
return (x != PossiblePlayerBattleAction::ANY_LOCATION) && (x != PossiblePlayerBattleAction::NO_LOCATION) &&
|
return !x.spellcast();
|
||||||
(x != PossiblePlayerBattleAction::FREE_LOCATION) && (x != PossiblePlayerBattleAction::AIMED_SPELL_CREATURE) &&
|
|
||||||
(x != PossiblePlayerBattleAction::OBSTACLE);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
vstd::erase_if(possibleActions, actionFilterPredicate);
|
vstd::erase_if(possibleActions, actionFilterPredicate);
|
||||||
GH.fakeMouseMove();
|
GH.fakeMouseMove();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<PossiblePlayerBattleAction> BattleActionsController::getPossibleActionsForStack(const CStack *stack) const
|
std::vector<PossiblePlayerBattleAction> BattleActionsController::getPossibleActionsForStack(const CStack *stack) const
|
||||||
{
|
{
|
||||||
BattleClientInterfaceData data; //hard to get rid of these things so for now they're required data to pass
|
BattleClientInterfaceData data; //hard to get rid of these things so for now they're required data to pass
|
||||||
|
|
||||||
if (getStackSpellToCast())
|
for (auto const & spell : creatureSpells)
|
||||||
data.creatureSpellToCast = getStackSpellToCast()->getId();
|
data.creatureSpellsToCast.push_back(spell->id);
|
||||||
else
|
|
||||||
data.creatureSpellToCast = SpellID::NONE;
|
|
||||||
|
|
||||||
data.tacticsMode = owner.tacticsMode;
|
data.tacticsMode = owner.tacticsMode;
|
||||||
auto allActions = owner.curInt->cb->getClientActionsForStack(stack, data);
|
auto allActions = owner.curInt->cb->getClientActionsForStack(stack, data);
|
||||||
@@ -146,7 +143,7 @@ void BattleActionsController::reorderPossibleActionsPriority(const CStack * stac
|
|||||||
|
|
||||||
auto assignPriority = [&](PossiblePlayerBattleAction const & item) -> uint8_t //large lambda assigning priority which would have to be part of possibleActions without it
|
auto assignPriority = [&](PossiblePlayerBattleAction const & item) -> uint8_t //large lambda assigning priority which would have to be part of possibleActions without it
|
||||||
{
|
{
|
||||||
switch(item)
|
switch(item.get())
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
||||||
case PossiblePlayerBattleAction::ANY_LOCATION:
|
case PossiblePlayerBattleAction::ANY_LOCATION:
|
||||||
@@ -207,7 +204,7 @@ void BattleActionsController::castThisSpell(SpellID spellID)
|
|||||||
assert(castingHero); // code below assumes non-null hero
|
assert(castingHero); // code below assumes non-null hero
|
||||||
PossiblePlayerBattleAction spellSelMode = owner.curInt->cb->getCasterAction(spellID.toSpell(), castingHero, spells::Mode::HERO);
|
PossiblePlayerBattleAction spellSelMode = owner.curInt->cb->getCasterAction(spellID.toSpell(), castingHero, spells::Mode::HERO);
|
||||||
|
|
||||||
if (spellSelMode == PossiblePlayerBattleAction::NO_LOCATION) //user does not have to select location
|
if (spellSelMode.get() == PossiblePlayerBattleAction::NO_LOCATION) //user does not have to select location
|
||||||
{
|
{
|
||||||
heroSpellToCast->aimToHex(BattleHex::INVALID);
|
heroSpellToCast->aimToHex(BattleHex::INVALID);
|
||||||
owner.curInt->cb->battleMakeAction(heroSpellToCast.get());
|
owner.curInt->cb->battleMakeAction(heroSpellToCast.get());
|
||||||
@@ -228,19 +225,30 @@ const CSpell * BattleActionsController::getHeroSpellToCast( ) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CSpell * BattleActionsController::getStackSpellToCast( ) const
|
const CSpell * BattleActionsController::getStackSpellToCast(BattleHex hoveredHex)
|
||||||
{
|
{
|
||||||
if (isActiveStackSpellcaster())
|
if (heroSpellToCast)
|
||||||
return creatureSpellToCast;
|
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
if (!owner.stacksController->getActiveStack())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!hoveredHex.isValid())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto action = selectAction(hoveredHex);
|
||||||
|
|
||||||
|
if (action.spell() == SpellID::NONE)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return action.spell().toSpell();
|
||||||
}
|
}
|
||||||
|
|
||||||
const CSpell * BattleActionsController::getCurrentSpell( ) const
|
const CSpell * BattleActionsController::getCurrentSpell(BattleHex hoveredHex)
|
||||||
{
|
{
|
||||||
if (getHeroSpellToCast())
|
if (getHeroSpellToCast())
|
||||||
return getHeroSpellToCast();
|
return getHeroSpellToCast();
|
||||||
return getStackSpellToCast();
|
return getStackSpellToCast(hoveredHex);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CStack * BattleActionsController::getStackForHex(BattleHex hoveredHex)
|
const CStack * BattleActionsController::getStackForHex(BattleHex hoveredHex)
|
||||||
@@ -253,7 +261,7 @@ const CStack * BattleActionsController::getStackForHex(BattleHex hoveredHex)
|
|||||||
|
|
||||||
void BattleActionsController::actionSetCursor(PossiblePlayerBattleAction action, BattleHex targetHex)
|
void BattleActionsController::actionSetCursor(PossiblePlayerBattleAction action, BattleHex targetHex)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action.get())
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
||||||
CCS->curh->set(Cursor::Combat::POINTER);
|
CCS->curh->set(Cursor::Combat::POINTER);
|
||||||
@@ -316,7 +324,7 @@ void BattleActionsController::actionSetCursor(PossiblePlayerBattleAction action,
|
|||||||
|
|
||||||
void BattleActionsController::actionSetCursorBlocked(PossiblePlayerBattleAction action, BattleHex targetHex)
|
void BattleActionsController::actionSetCursorBlocked(PossiblePlayerBattleAction action, BattleHex targetHex)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action.get())
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
||||||
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
||||||
@@ -339,7 +347,7 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
|||||||
{
|
{
|
||||||
const CStack * targetStack = getStackForHex(targetHex);
|
const CStack * targetStack = getStackForHex(targetHex);
|
||||||
|
|
||||||
switch (action) //display console message, realize selected action
|
switch (action.get()) //display console message, realize selected action
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
||||||
return (boost::format(CGI->generaltexth->allTexts[481]) % targetStack->getName()).str(); //Select %s
|
return (boost::format(CGI->generaltexth->allTexts[481]) % targetStack->getName()).str(); //Select %s
|
||||||
@@ -372,10 +380,10 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
|||||||
}
|
}
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
||||||
return boost::str(boost::format(CGI->generaltexth->allTexts[27]) % getCurrentSpell()->getNameTranslated() % targetStack->getName()); //Cast %s on %s
|
return boost::str(boost::format(CGI->generaltexth->allTexts[27]) % action.spell().toSpell()->getNameTranslated() % targetStack->getName()); //Cast %s on %s
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::ANY_LOCATION:
|
case PossiblePlayerBattleAction::ANY_LOCATION:
|
||||||
return boost::str(boost::format(CGI->generaltexth->allTexts[26]) % getCurrentSpell()->getNameTranslated()); //Cast %s
|
return boost::str(boost::format(CGI->generaltexth->allTexts[26]) % action.spell().toSpell()->getNameTranslated()); //Cast %s
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL: //we assume that teleport / sacrifice will never be available as random spell
|
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL: //we assume that teleport / sacrifice will never be available as random spell
|
||||||
return boost::str(boost::format(CGI->generaltexth->allTexts[301]) % targetStack->getName()); //Cast a spell on %
|
return boost::str(boost::format(CGI->generaltexth->allTexts[301]) % targetStack->getName()); //Cast a spell on %
|
||||||
@@ -390,7 +398,7 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
|||||||
return (boost::format(CGI->generaltexth->allTexts[549]) % targetStack->getName()).str(); //sacrifice the %s
|
return (boost::format(CGI->generaltexth->allTexts[549]) % targetStack->getName()).str(); //sacrifice the %s
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::FREE_LOCATION:
|
case PossiblePlayerBattleAction::FREE_LOCATION:
|
||||||
return boost::str(boost::format(CGI->generaltexth->allTexts[26]) % getCurrentSpell()->getNameTranslated()); //Cast %s
|
return boost::str(boost::format(CGI->generaltexth->allTexts[26]) % action.spell().toSpell()->getNameTranslated()); //Cast %s
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::HEAL:
|
case PossiblePlayerBattleAction::HEAL:
|
||||||
return (boost::format(CGI->generaltexth->allTexts[419]) % targetStack->getName()).str(); //Apply first aid to the %s
|
return (boost::format(CGI->generaltexth->allTexts[419]) % targetStack->getName()).str(); //Apply first aid to the %s
|
||||||
@@ -410,7 +418,7 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
|||||||
|
|
||||||
std::string BattleActionsController::actionGetStatusMessageBlocked(PossiblePlayerBattleAction action, BattleHex targetHex)
|
std::string BattleActionsController::actionGetStatusMessageBlocked(PossiblePlayerBattleAction action, BattleHex targetHex)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action.get())
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
||||||
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
||||||
@@ -423,7 +431,7 @@ std::string BattleActionsController::actionGetStatusMessageBlocked(PossiblePlaye
|
|||||||
return CGI->generaltexth->allTexts[543]; //choose army to sacrifice
|
return CGI->generaltexth->allTexts[543]; //choose army to sacrifice
|
||||||
break;
|
break;
|
||||||
case PossiblePlayerBattleAction::FREE_LOCATION:
|
case PossiblePlayerBattleAction::FREE_LOCATION:
|
||||||
return boost::str(boost::format(CGI->generaltexth->allTexts[181]) % getCurrentSpell()->getNameTranslated()); //No room to place %s here
|
return boost::str(boost::format(CGI->generaltexth->allTexts[181]) % action.spell().toSpell()->getNameTranslated()); //No room to place %s here
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
@@ -435,7 +443,7 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
|
|||||||
const CStack * targetStack = getStackForHex(targetHex);
|
const CStack * targetStack = getStackForHex(targetHex);
|
||||||
bool targetStackOwned = targetStack && targetStack->owner == owner.curInt->playerID;
|
bool targetStackOwned = targetStack && targetStack->owner == owner.curInt->playerID;
|
||||||
|
|
||||||
switch (action)
|
switch (action.get())
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
||||||
case PossiblePlayerBattleAction::CREATURE_INFO:
|
case PossiblePlayerBattleAction::CREATURE_INFO:
|
||||||
@@ -473,10 +481,10 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
|
|||||||
return owner.curInt->cb->battleCanShoot(owner.stacksController->getActiveStack(), targetHex);
|
return owner.curInt->cb->battleCanShoot(owner.stacksController->getActiveStack(), targetHex);
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::ANY_LOCATION:
|
case PossiblePlayerBattleAction::ANY_LOCATION:
|
||||||
return isCastingPossibleHere(owner.stacksController->getActiveStack(), targetStack, targetHex);
|
return isCastingPossibleHere(action.spell().toSpell(), owner.stacksController->getActiveStack(), targetStack, targetHex);
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
|
||||||
return targetStack && isCastingPossibleHere(owner.stacksController->getActiveStack(), targetStack, targetHex);
|
return targetStack && isCastingPossibleHere(action.spell().toSpell(), owner.stacksController->getActiveStack(), targetStack, targetHex);
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
||||||
if(targetStack && targetStackOwned && targetStack != owner.stacksController->getActiveStack() && targetStack->alive()) //only positive spells for other allied creatures
|
if(targetStack && targetStackOwned && targetStack != owner.stacksController->getActiveStack() && targetStack->alive()) //only positive spells for other allied creatures
|
||||||
@@ -497,8 +505,8 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
|
|||||||
|
|
||||||
case PossiblePlayerBattleAction::OBSTACLE:
|
case PossiblePlayerBattleAction::OBSTACLE:
|
||||||
case PossiblePlayerBattleAction::FREE_LOCATION:
|
case PossiblePlayerBattleAction::FREE_LOCATION:
|
||||||
return isCastingPossibleHere(owner.stacksController->getActiveStack(), targetStack, targetHex);
|
return isCastingPossibleHere(action.spell().toSpell(), owner.stacksController->getActiveStack(), targetStack, targetHex);
|
||||||
return isCastingPossibleHere(owner.stacksController->getActiveStack(), targetStack, targetHex);
|
return isCastingPossibleHere(action.spell().toSpell(), owner.stacksController->getActiveStack(), targetStack, targetHex);
|
||||||
|
|
||||||
case PossiblePlayerBattleAction::CATAPULT:
|
case PossiblePlayerBattleAction::CATAPULT:
|
||||||
return owner.siegeController && owner.siegeController->isAttackableByCatapult(targetHex);
|
return owner.siegeController && owner.siegeController->isAttackableByCatapult(targetHex);
|
||||||
@@ -515,7 +523,7 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
|
|||||||
{
|
{
|
||||||
const CStack * targetStack = getStackForHex(targetHex);
|
const CStack * targetStack = getStackForHex(targetHex);
|
||||||
|
|
||||||
switch (action) //display console message, realize selected action
|
switch (action.get()) //display console message, realize selected action
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
||||||
{
|
{
|
||||||
@@ -546,7 +554,7 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
|
|||||||
case PossiblePlayerBattleAction::WALK_AND_ATTACK:
|
case PossiblePlayerBattleAction::WALK_AND_ATTACK:
|
||||||
case PossiblePlayerBattleAction::ATTACK_AND_RETURN: //TODO: allow to disable return
|
case PossiblePlayerBattleAction::ATTACK_AND_RETURN: //TODO: allow to disable return
|
||||||
{
|
{
|
||||||
bool returnAfterAttack = action == PossiblePlayerBattleAction::ATTACK_AND_RETURN;
|
bool returnAfterAttack = action.get() == PossiblePlayerBattleAction::ATTACK_AND_RETURN;
|
||||||
BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex);
|
BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex);
|
||||||
if(attackFromHex.isValid()) //we can be in this line when unreachable creature is L - clicked (as of revision 1308)
|
if(attackFromHex.isValid()) //we can be in this line when unreachable creature is L - clicked (as of revision 1308)
|
||||||
{
|
{
|
||||||
@@ -599,16 +607,16 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
|
|||||||
case PossiblePlayerBattleAction::SACRIFICE:
|
case PossiblePlayerBattleAction::SACRIFICE:
|
||||||
case PossiblePlayerBattleAction::FREE_LOCATION:
|
case PossiblePlayerBattleAction::FREE_LOCATION:
|
||||||
{
|
{
|
||||||
if (action == PossiblePlayerBattleAction::AIMED_SPELL_CREATURE )
|
if (action.get() == PossiblePlayerBattleAction::AIMED_SPELL_CREATURE )
|
||||||
{
|
{
|
||||||
if (getCurrentSpell()->id == SpellID::SACRIFICE)
|
if (action.spell() == SpellID::SACRIFICE)
|
||||||
{
|
{
|
||||||
heroSpellToCast->aimToHex(targetHex);
|
heroSpellToCast->aimToHex(targetHex);
|
||||||
possibleActions.push_back(PossiblePlayerBattleAction::SACRIFICE);
|
possibleActions.push_back(PossiblePlayerBattleAction::SACRIFICE);
|
||||||
owner.stacksController->setSelectedStack(targetStack);
|
owner.stacksController->setSelectedStack(targetStack);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (getCurrentSpell()->id == SpellID::TELEPORT)
|
if (action.spell() == SpellID::TELEPORT)
|
||||||
{
|
{
|
||||||
heroSpellToCast->aimToUnit(targetStack);
|
heroSpellToCast->aimToUnit(targetStack);
|
||||||
possibleActions.push_back(PossiblePlayerBattleAction::TELEPORT);
|
possibleActions.push_back(PossiblePlayerBattleAction::TELEPORT);
|
||||||
@@ -619,9 +627,9 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
|
|||||||
|
|
||||||
if (!spellcastingModeActive())
|
if (!spellcastingModeActive())
|
||||||
{
|
{
|
||||||
if (getStackSpellToCast())
|
if (action.spell().toSpell())
|
||||||
{
|
{
|
||||||
owner.giveCommand(EActionType::MONSTER_SPELL, targetHex, getStackSpellToCast()->getId());
|
owner.giveCommand(EActionType::MONSTER_SPELL, targetHex, action.spell());
|
||||||
}
|
}
|
||||||
else //unknown random spell
|
else //unknown random spell
|
||||||
{
|
{
|
||||||
@@ -750,12 +758,25 @@ void BattleActionsController::onHexLeftClicked(BattleHex clickedHex)
|
|||||||
|
|
||||||
void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterStack)
|
void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterStack)
|
||||||
{
|
{
|
||||||
const auto spellcaster = casterStack->getBonusLocalFirst(Selector::type()(Bonus::SPELLCASTER));
|
creatureSpells.clear();
|
||||||
|
|
||||||
|
bool spellcaster = casterStack->hasBonusOfType(Bonus::SPELLCASTER);
|
||||||
if(casterStack->canCast() && spellcaster)
|
if(casterStack->canCast() && spellcaster)
|
||||||
{
|
{
|
||||||
// faerie dragon can cast only one, randomly selected spell until their next move
|
// faerie dragon can cast only one, randomly selected spell until their next move
|
||||||
//TODO: faerie dragon type spell should be selected by server
|
//TODO: faerie dragon type spell should be selected by server
|
||||||
creatureSpellToCast = owner.curInt->cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), casterStack, CBattleInfoCallback::RANDOM_AIMED).toSpell();
|
const auto * spellToCast = owner.curInt->cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), casterStack, CBattleInfoCallback::RANDOM_AIMED).toSpell();
|
||||||
|
|
||||||
|
if (spellToCast)
|
||||||
|
creatureSpells.push_back(spellToCast);
|
||||||
|
}
|
||||||
|
|
||||||
|
TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(Bonus::SPELLCASTER));
|
||||||
|
|
||||||
|
for (auto const & bonus : *bl)
|
||||||
|
{
|
||||||
|
if (bonus->additionalInfo[0] <= 0)
|
||||||
|
creatureSpells.push_back(SpellID(bonus->subtype).toSpell());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,11 +797,9 @@ spells::Mode BattleActionsController::getCurrentCastMode() const
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BattleActionsController::isCastingPossibleHere(const CStack *casterStack, const CStack *targetStack, BattleHex targetHex)
|
bool BattleActionsController::isCastingPossibleHere(const CSpell * currentSpell, const CStack *casterStack, const CStack *targetStack, BattleHex targetHex)
|
||||||
{
|
{
|
||||||
auto currentSpell = getCurrentSpell();
|
|
||||||
assert(currentSpell);
|
assert(currentSpell);
|
||||||
|
|
||||||
if (!currentSpell)
|
if (!currentSpell)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -823,7 +842,7 @@ void BattleActionsController::activateStack()
|
|||||||
std::list<PossiblePlayerBattleAction> actionsToSelect;
|
std::list<PossiblePlayerBattleAction> actionsToSelect;
|
||||||
if(!possibleActions.empty())
|
if(!possibleActions.empty())
|
||||||
{
|
{
|
||||||
switch(possibleActions.front())
|
switch(possibleActions.front().get())
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::SHOOT:
|
case PossiblePlayerBattleAction::SHOOT:
|
||||||
actionsToSelect.push_back(possibleActions.front());
|
actionsToSelect.push_back(possibleActions.front());
|
||||||
@@ -873,12 +892,7 @@ bool BattleActionsController::currentActionSpellcasting(BattleHex hoveredHex)
|
|||||||
|
|
||||||
auto action = selectAction(hoveredHex);
|
auto action = selectAction(hoveredHex);
|
||||||
|
|
||||||
return
|
return action.spellcast();
|
||||||
action == PossiblePlayerBattleAction::ANY_LOCATION ||
|
|
||||||
action == PossiblePlayerBattleAction::NO_LOCATION ||
|
|
||||||
action == PossiblePlayerBattleAction::FREE_LOCATION ||
|
|
||||||
action == PossiblePlayerBattleAction::AIMED_SPELL_CREATURE ||
|
|
||||||
action == PossiblePlayerBattleAction::OBSTACLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<PossiblePlayerBattleAction> & BattleActionsController::getPossibleActions() const
|
const std::vector<PossiblePlayerBattleAction> & BattleActionsController::getPossibleActions() const
|
||||||
|
@@ -45,9 +45,9 @@ class BattleActionsController
|
|||||||
std::string currentConsoleMsg;
|
std::string currentConsoleMsg;
|
||||||
|
|
||||||
/// if true, active stack could possibly cast some target spell
|
/// if true, active stack could possibly cast some target spell
|
||||||
const CSpell * creatureSpellToCast;
|
std::vector<const CSpell *> creatureSpells;
|
||||||
|
|
||||||
bool isCastingPossibleHere (const CStack *sactive, const CStack *shere, BattleHex myNumber);
|
bool isCastingPossibleHere (const CSpell * spell, const CStack *sactive, const CStack *shere, BattleHex myNumber);
|
||||||
bool canStackMoveHere (const CStack *sactive, BattleHex MyNumber) const; //TODO: move to BattleState / callback
|
bool canStackMoveHere (const CStack *sactive, BattleHex MyNumber) const; //TODO: move to BattleState / callback
|
||||||
std::vector<PossiblePlayerBattleAction> getPossibleActionsForStack (const CStack *stack) const; //called when stack gets its turn
|
std::vector<PossiblePlayerBattleAction> getPossibleActionsForStack (const CStack *stack) const; //called when stack gets its turn
|
||||||
void reorderPossibleActionsPriority(const CStack * stack, MouseHoveredHexContext context);
|
void reorderPossibleActionsPriority(const CStack * stack, MouseHoveredHexContext context);
|
||||||
@@ -74,7 +74,7 @@ class BattleActionsController
|
|||||||
const CSpell * getHeroSpellToCast() const;
|
const CSpell * getHeroSpellToCast() const;
|
||||||
|
|
||||||
/// if current stack is spellcaster, returns spell being cast, or null othervice
|
/// if current stack is spellcaster, returns spell being cast, or null othervice
|
||||||
const CSpell * getStackSpellToCast( ) const;
|
const CSpell * getStackSpellToCast(BattleHex hoveredHex);
|
||||||
|
|
||||||
/// returns true if current stack is a spellcaster
|
/// returns true if current stack is a spellcaster
|
||||||
bool isActiveStackSpellcaster() const;
|
bool isActiveStackSpellcaster() const;
|
||||||
@@ -116,7 +116,7 @@ public:
|
|||||||
void onHexRightClicked(BattleHex clickedHex);
|
void onHexRightClicked(BattleHex clickedHex);
|
||||||
|
|
||||||
const spells::Caster * getCurrentSpellcaster() const;
|
const spells::Caster * getCurrentSpellcaster() const;
|
||||||
const CSpell * getCurrentSpell() const;
|
const CSpell * getCurrentSpell(BattleHex hoveredHex);
|
||||||
spells::Mode getCurrentCastMode() const;
|
spells::Mode getCurrentCastMode() const;
|
||||||
|
|
||||||
/// methods to work with array of possible actions, needed to control special creatures abilities
|
/// methods to work with array of possible actions, needed to control special creatures abilities
|
||||||
|
@@ -263,7 +263,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesSpellRange()
|
|||||||
const CSpell *spell = nullptr;
|
const CSpell *spell = nullptr;
|
||||||
|
|
||||||
spells::Mode mode = owner.actionsController->getCurrentCastMode();
|
spells::Mode mode = owner.actionsController->getCurrentCastMode();
|
||||||
spell = owner.actionsController->getCurrentSpell();
|
spell = owner.actionsController->getCurrentSpell(hoveredHex);
|
||||||
caster = owner.actionsController->getCurrentSpellcaster();
|
caster = owner.actionsController->getCurrentSpellcaster();
|
||||||
|
|
||||||
if(caster && spell) //when casting spell
|
if(caster && spell) //when casting spell
|
||||||
|
@@ -840,7 +840,7 @@ std::vector<const CStack *> BattleStacksController::selectHoveredStacks()
|
|||||||
const CSpell *spell = nullptr;
|
const CSpell *spell = nullptr;
|
||||||
|
|
||||||
spells::Mode mode = owner.actionsController->getCurrentCastMode();
|
spells::Mode mode = owner.actionsController->getCurrentCastMode();
|
||||||
spell = owner.actionsController->getCurrentSpell();
|
spell = owner.actionsController->getCurrentSpell(hoveredHex);
|
||||||
caster = owner.actionsController->getCurrentSpellcaster();
|
caster = owner.actionsController->getCurrentSpellcaster();
|
||||||
|
|
||||||
if(caster && spell && owner.actionsController->currentActionSpellcasting(hoveredHex) ) //when casting spell
|
if(caster && spell && owner.actionsController->currentActionSpellcasting(hoveredHex) ) //when casting spell
|
||||||
|
@@ -38,7 +38,8 @@
|
|||||||
#include "../windows/settings/SettingsMainWindow.h"
|
#include "../windows/settings/SettingsMainWindow.h"
|
||||||
|
|
||||||
BattleWindow::BattleWindow(BattleInterface & owner):
|
BattleWindow::BattleWindow(BattleInterface & owner):
|
||||||
owner(owner)
|
owner(owner),
|
||||||
|
defaultAction(PossiblePlayerBattleAction::INVALID)
|
||||||
{
|
{
|
||||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||||
pos.w = 800;
|
pos.w = 800;
|
||||||
@@ -326,7 +327,7 @@ void BattleWindow::showAlternativeActionIcon(PossiblePlayerBattleAction action)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
std::string iconName = variables["actionIconDefault"].String();
|
std::string iconName = variables["actionIconDefault"].String();
|
||||||
switch(action)
|
switch(action.get())
|
||||||
{
|
{
|
||||||
case PossiblePlayerBattleAction::ATTACK:
|
case PossiblePlayerBattleAction::ATTACK:
|
||||||
iconName = variables["actionIconAttack"].String();
|
iconName = variables["actionIconAttack"].String();
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
#include "../gui/CIntObject.h"
|
#include "../gui/CIntObject.h"
|
||||||
#include "../gui/InterfaceObjectConfigurable.h"
|
#include "../gui/InterfaceObjectConfigurable.h"
|
||||||
#include "../../lib/battle/CBattleInfoCallback.h"
|
#include "../../lib/battle/CBattleInfoCallback.h"
|
||||||
|
#include "../../lib/battle/PossiblePlayerBattleAction.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
class CStack;
|
class CStack;
|
||||||
|
@@ -258,6 +258,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
|||||||
${MAIN_LIB_DIR}/battle/IBattleInfoCallback.h
|
${MAIN_LIB_DIR}/battle/IBattleInfoCallback.h
|
||||||
${MAIN_LIB_DIR}/battle/IBattleState.h
|
${MAIN_LIB_DIR}/battle/IBattleState.h
|
||||||
${MAIN_LIB_DIR}/battle/IUnitInfo.h
|
${MAIN_LIB_DIR}/battle/IUnitInfo.h
|
||||||
|
${MAIN_LIB_DIR}/battle/PossiblePlayerBattleAction.h
|
||||||
${MAIN_LIB_DIR}/battle/ReachabilityInfo.h
|
${MAIN_LIB_DIR}/battle/ReachabilityInfo.h
|
||||||
${MAIN_LIB_DIR}/battle/SideInBattle.h
|
${MAIN_LIB_DIR}/battle/SideInBattle.h
|
||||||
${MAIN_LIB_DIR}/battle/SiegeInfo.h
|
${MAIN_LIB_DIR}/battle/SiegeInfo.h
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include "../CStack.h"
|
#include "../CStack.h"
|
||||||
#include "BattleInfo.h"
|
#include "BattleInfo.h"
|
||||||
#include "DamageCalculator.h"
|
#include "DamageCalculator.h"
|
||||||
|
#include "PossiblePlayerBattleAction.h"
|
||||||
#include "../NetPacks.h"
|
#include "../NetPacks.h"
|
||||||
#include "../spells/CSpellHandler.h"
|
#include "../spells/CSpellHandler.h"
|
||||||
#include "../mapObjects/CGTownInstance.h"
|
#include "../mapObjects/CGTownInstance.h"
|
||||||
@@ -218,12 +219,15 @@ std::vector<PossiblePlayerBattleAction> CBattleInfoCallback::getClientActionsFor
|
|||||||
{
|
{
|
||||||
if(stack->canCast()) //TODO: check for battlefield effects that prevent casting?
|
if(stack->canCast()) //TODO: check for battlefield effects that prevent casting?
|
||||||
{
|
{
|
||||||
if(stack->hasBonusOfType(Bonus::SPELLCASTER) && data.creatureSpellToCast != -1)
|
if(stack->hasBonusOfType(Bonus::SPELLCASTER))
|
||||||
{
|
{
|
||||||
const CSpell *spell = SpellID(data.creatureSpellToCast).toSpell();
|
for (auto const & spellID : data.creatureSpellsToCast)
|
||||||
|
{
|
||||||
|
const CSpell *spell = spellID.toSpell();
|
||||||
PossiblePlayerBattleAction act = getCasterAction(spell, stack, spells::Mode::CREATURE_ACTIVE);
|
PossiblePlayerBattleAction act = getCasterAction(spell, stack, spells::Mode::CREATURE_ACTIVE);
|
||||||
allowedActionList.push_back(act);
|
allowedActionList.push_back(act);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -251,7 +255,7 @@ std::vector<PossiblePlayerBattleAction> CBattleInfoCallback::getClientActionsFor
|
|||||||
PossiblePlayerBattleAction CBattleInfoCallback::getCasterAction(const CSpell * spell, const spells::Caster * caster, spells::Mode mode) const
|
PossiblePlayerBattleAction CBattleInfoCallback::getCasterAction(const CSpell * spell, const spells::Caster * caster, spells::Mode mode) const
|
||||||
{
|
{
|
||||||
RETURN_IF_NOT_BATTLE(PossiblePlayerBattleAction::INVALID);
|
RETURN_IF_NOT_BATTLE(PossiblePlayerBattleAction::INVALID);
|
||||||
PossiblePlayerBattleAction spellSelMode = PossiblePlayerBattleAction::ANY_LOCATION;
|
auto spellSelMode = PossiblePlayerBattleAction::ANY_LOCATION;
|
||||||
|
|
||||||
const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell), mode);
|
const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell), mode);
|
||||||
|
|
||||||
@@ -264,7 +268,7 @@ PossiblePlayerBattleAction CBattleInfoCallback::getCasterAction(const CSpell * s
|
|||||||
else if(ti.type == spells::AimType::OBSTACLE)
|
else if(ti.type == spells::AimType::OBSTACLE)
|
||||||
spellSelMode = PossiblePlayerBattleAction::OBSTACLE;
|
spellSelMode = PossiblePlayerBattleAction::OBSTACLE;
|
||||||
|
|
||||||
return spellSelMode;
|
return PossiblePlayerBattleAction(spellSelMode, spell->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<BattleHex> CBattleInfoCallback::battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const
|
std::set<BattleHex> CBattleInfoCallback::battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const
|
||||||
@@ -1646,12 +1650,16 @@ SpellID CBattleInfoCallback::getRandomCastedSpell(CRandomGenerator & rand,const
|
|||||||
int totalWeight = 0;
|
int totalWeight = 0;
|
||||||
for(const auto & b : *bl)
|
for(const auto & b : *bl)
|
||||||
{
|
{
|
||||||
totalWeight += std::max(b->additionalInfo[0], 1); //minimal chance to cast is 1
|
totalWeight += std::max(b->additionalInfo[0], 0); //spells with 0 weight are non-random, exclude them
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (totalWeight == 0)
|
||||||
|
return SpellID::NONE;
|
||||||
|
|
||||||
int randomPos = rand.nextInt(totalWeight - 1);
|
int randomPos = rand.nextInt(totalWeight - 1);
|
||||||
for(const auto & b : *bl)
|
for(const auto & b : *bl)
|
||||||
{
|
{
|
||||||
randomPos -= std::max(b->additionalInfo[0], 1);
|
randomPos -= std::max(b->additionalInfo[0], 0);
|
||||||
if(randomPos < 0)
|
if(randomPos < 0)
|
||||||
{
|
{
|
||||||
return SpellID(b->subtype);
|
return SpellID(b->subtype);
|
||||||
|
@@ -24,6 +24,7 @@ class CSpell;
|
|||||||
struct CObstacleInstance;
|
struct CObstacleInstance;
|
||||||
class IBonusBearer;
|
class IBonusBearer;
|
||||||
class CRandomGenerator;
|
class CRandomGenerator;
|
||||||
|
class PossiblePlayerBattleAction;
|
||||||
|
|
||||||
namespace spells
|
namespace spells
|
||||||
{
|
{
|
||||||
@@ -42,35 +43,9 @@ struct DLL_LINKAGE AttackableTiles
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class PossiblePlayerBattleAction // actions performed at l-click
|
|
||||||
{
|
|
||||||
INVALID = -1,
|
|
||||||
CREATURE_INFO,
|
|
||||||
HERO_INFO,
|
|
||||||
MOVE_TACTICS,
|
|
||||||
CHOOSE_TACTICS_STACK,
|
|
||||||
|
|
||||||
MOVE_STACK,
|
|
||||||
ATTACK,
|
|
||||||
WALK_AND_ATTACK,
|
|
||||||
ATTACK_AND_RETURN,
|
|
||||||
SHOOT,
|
|
||||||
CATAPULT,
|
|
||||||
HEAL,
|
|
||||||
|
|
||||||
NO_LOCATION, // massive spells that affect every possible target, automatic casts
|
|
||||||
ANY_LOCATION,
|
|
||||||
OBSTACLE,
|
|
||||||
TELEPORT,
|
|
||||||
SACRIFICE,
|
|
||||||
RANDOM_GENIE_SPELL, // random spell on a friendly creature
|
|
||||||
FREE_LOCATION, // used with Force Field and Fire Wall - all tiles affected by spell must be free
|
|
||||||
AIMED_SPELL_CREATURE, // spell targeted at creature
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DLL_LINKAGE BattleClientInterfaceData
|
struct DLL_LINKAGE BattleClientInterfaceData
|
||||||
{
|
{
|
||||||
si32 creatureSpellToCast;
|
std::vector<SpellID> creatureSpellsToCast;
|
||||||
ui8 tacticsMode;
|
ui8 tacticsMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
74
lib/battle/PossiblePlayerBattleAction.h
Normal file
74
lib/battle/PossiblePlayerBattleAction.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* CBattleInfoCallback.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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../GameConstants.h"
|
||||||
|
|
||||||
|
class PossiblePlayerBattleAction // actions performed at l-click
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Actions {
|
||||||
|
INVALID = -1,
|
||||||
|
CREATURE_INFO,
|
||||||
|
HERO_INFO,
|
||||||
|
MOVE_TACTICS,
|
||||||
|
CHOOSE_TACTICS_STACK,
|
||||||
|
|
||||||
|
MOVE_STACK,
|
||||||
|
ATTACK,
|
||||||
|
WALK_AND_ATTACK,
|
||||||
|
ATTACK_AND_RETURN,
|
||||||
|
SHOOT,
|
||||||
|
CATAPULT,
|
||||||
|
HEAL,
|
||||||
|
|
||||||
|
RANDOM_GENIE_SPELL, // random spell on a friendly creature
|
||||||
|
|
||||||
|
NO_LOCATION, // massive spells that affect every possible target, automatic casts
|
||||||
|
ANY_LOCATION,
|
||||||
|
OBSTACLE,
|
||||||
|
TELEPORT,
|
||||||
|
SACRIFICE,
|
||||||
|
FREE_LOCATION, // used with Force Field and Fire Wall - all tiles affected by spell must be free
|
||||||
|
AIMED_SPELL_CREATURE, // spell targeted at creature
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Actions action;
|
||||||
|
SpellID spellToCast;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool spellcast() const
|
||||||
|
{
|
||||||
|
return action == ANY_LOCATION || action == NO_LOCATION || action == OBSTACLE || action == TELEPORT ||
|
||||||
|
action == SACRIFICE || action == FREE_LOCATION || action == AIMED_SPELL_CREATURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Actions get() const
|
||||||
|
{
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpellID spell() const
|
||||||
|
{
|
||||||
|
return spellToCast;
|
||||||
|
}
|
||||||
|
|
||||||
|
PossiblePlayerBattleAction(Actions action, SpellID spellToCast = SpellID::NONE):
|
||||||
|
action(static_cast<Actions>(action)),
|
||||||
|
spellToCast(spellToCast)
|
||||||
|
{
|
||||||
|
assert((spellToCast != SpellID::NONE) == spellcast());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const PossiblePlayerBattleAction & other) const
|
||||||
|
{
|
||||||
|
return action == other.action && spellToCast == other.spellToCast;
|
||||||
|
}
|
||||||
|
};
|
@@ -4910,8 +4910,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
const CStack * stack = gs->curB->battleGetStackByID(ba.stackNumber);
|
const CStack * stack = gs->curB->battleGetStackByID(ba.stackNumber);
|
||||||
SpellID spellID = SpellID(ba.actionSubtype);
|
SpellID spellID = SpellID(ba.actionSubtype);
|
||||||
|
|
||||||
std::shared_ptr<const Bonus> randSpellcaster = stack->getBonusLocalFirst(Selector::type()(Bonus::RANDOM_SPELLCASTER));
|
std::shared_ptr<const Bonus> randSpellcaster = stack->getBonus(Selector::type()(Bonus::RANDOM_SPELLCASTER));
|
||||||
std::shared_ptr<const Bonus> spellcaster = stack->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPELLCASTER, spellID));
|
std::shared_ptr<const Bonus> spellcaster = stack->getBonus(Selector::typeSubtype(Bonus::SPELLCASTER, spellID));
|
||||||
|
|
||||||
//TODO special bonus for genies ability
|
//TODO special bonus for genies ability
|
||||||
if (randSpellcaster && battleGetRandomStackSpell(getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) < 0)
|
if (randSpellcaster && battleGetRandomStackSpell(getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) < 0)
|
||||||
|
Reference in New Issue
Block a user