1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-17 01:32:21 +02:00

Implemented configurable FEAR ability

This commit is contained in:
Ivan Savenko
2025-06-08 19:22:06 +03:00
parent f71db8af07
commit ae22de3ccf
10 changed files with 43 additions and 40 deletions

View File

@ -892,7 +892,7 @@ void CPlayerInterface::battleTriggerEffect(const BattleID & battleID, const Batt
battleInt->effectsController->battleTriggerEffect(bte);
if(bte.effect == vstd::to_underlying(BonusType::MANA_DRAIN))
if(bte.effect == BonusType::MANA_DRAIN)
{
const CGHeroInstance * manaDrainedHero = GAME->interface()->cb->getHero(ObjectInstanceID(bte.additionalInfo));
battleInt->windowObject->heroManaPointsChanged(manaDrainedHero);

View File

@ -66,7 +66,7 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
return;
}
//don't show animation when no HP is regenerated
switch(static_cast<BonusType>(bte.effect))
switch(bte.effect)
{
case BonusType::HP_REGENERATION:
displayEffect(EBattleEffect::REGENERATION, AudioPath::builtin("REGENER"), stack->getPosition(), 0.5);
@ -77,7 +77,7 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
case BonusType::POISON:
displayEffect(EBattleEffect::POISON, AudioPath::builtin("POISON"), stack->getPosition());
break;
case BonusType::FEAR:
case BonusType::FEARFUL:
displayEffect(EBattleEffect::FEAR, AudioPath::builtin("FEAR"), stack->getPosition(), 0.5);
break;
case BonusType::MORALE:

View File

@ -83,13 +83,20 @@
{
"type" : "TWO_HEX_ATTACK_BREATH"
},
"fear" :
"fearful" :
{
"type" : "FEAR"
"type" : "FEARFUL",
"val" : 10,
"propagator": "BATTLE_WIDE",
"propagationUpdater" : "BONUS_OWNER_UPDATER",
"limiters" : [ "OPPOSITE_SIDE", "LIVING" ]
},
"fearless" :
"fearfulImmune" :
{
"type" : "FEARLESS"
"type" : "FEARFUL",
"valueType" : "INDEPENDENT_MAX",
"val" : 0
},
"spellImmunity" :
{

View File

@ -186,14 +186,7 @@ ui32 ACreature::getMovementRange(int turn) const
bool ACreature::isLiving() const //TODO: theoreticaly there exists "LIVING" bonus in stack experience documentation
{
static const std::string cachingStr = "ACreature::isLiving";
static const CSelector selector = Selector::type()(BonusType::UNDEAD)
.Or(Selector::type()(BonusType::NON_LIVING))
.Or(Selector::type()(BonusType::MECHANICAL))
.Or(Selector::type()(BonusType::GARGOYLE))
.Or(Selector::type()(BonusType::SIEGE_WEAPON));
return !getBonusBearer()->hasBonus(selector, cachingStr);
return getBonusBearer()->hasBonusOfType(BonusType::LIVING);
}

View File

@ -936,6 +936,15 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
}
}
static const CSelector livingSelector = Selector::type()(BonusType::UNDEAD)
.Or(Selector::type()(BonusType::NON_LIVING))
.Or(Selector::type()(BonusType::MECHANICAL))
.Or(Selector::type()(BonusType::GARGOYLE))
.Or(Selector::type()(BonusType::SIEGE_WEAPON));
if (!creature->hasBonus(livingSelector))
creature->addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::LIVING, BonusSource::CREATURE_ABILITY, 0, BonusSourceID(creature->getId())));
LIBRARY->identifiers()->requestIdentifier("faction", config["faction"], [=](si32 faction)
{
creature->faction = FactionID(faction);
@ -1076,7 +1085,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
b.subtype = BonusCustomSubtype::deathStareGorgon;
break;
case 'F':
b.type = BonusType::FEAR; break;
b.type = BonusType::FEARFUL; break;
case 'g':
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
b.subtype = BonusSubtypeID(SpellSchool::ANY);
@ -1105,7 +1114,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
case 'D':
b.type = BonusType::ADDITIONAL_ATTACK; break;
case 'f':
b.type = BonusType::FEARLESS; break;
b.type = BonusType::FEARFUL; break;
case 'F':
b.type = BonusType::FLYING; break;
case 'm':

View File

@ -91,8 +91,8 @@ class JsonNode;
BONUS_NAME(DEFENSIVE_STANCE) /* val - bonus to defense while defending */ \
BONUS_NAME(ATTACKS_ALL_ADJACENT) /*eg. hydra*/ \
BONUS_NAME(MORE_DAMAGE_FROM_SPELL) /*value - damage increase in %, subtype - spell id*/ \
BONUS_NAME(FEAR) \
BONUS_NAME(FEARLESS) \
BONUS_NAME(FEARFUL) \
BONUS_NAME(LIVING) \
BONUS_NAME(NO_DISTANCE_PENALTY) \
BONUS_NAME(ENCHANTER)/* for Enchanter spells, val - skill level, subtype - spell id, additionalInfo - cooldown */ \
BONUS_NAME(HEALER) \

View File

@ -33,6 +33,7 @@ const std::map<std::string, TLimiterPtr> bonusLimiterMap =
{"SHOOTER_ONLY", std::make_shared<HasAnotherBonusLimiter>(BonusType::SHOOTER)},
{"DRAGON_NATURE", std::make_shared<HasAnotherBonusLimiter>(BonusType::DRAGON_NATURE)},
{"IS_UNDEAD", std::make_shared<HasAnotherBonusLimiter>(BonusType::UNDEAD)},
{"LIVING", std::make_shared<HasAnotherBonusLimiter>(BonusType::LIVING)},
{"CREATURE_NATIVE_TERRAIN", std::make_shared<CreatureTerrainLimiter>()},
{"CREATURE_FACTION", std::make_shared<AllOfLimiter>(std::initializer_list<TLimiterPtr>{std::make_shared<CreatureLevelLimiter>(), std::make_shared<FactionLimiter>()})},
{"SAME_FACTION", std::make_shared<FactionLimiter>()},

View File

@ -1191,7 +1191,7 @@ void GameStatePackVisitor::visitBattleTriggerEffect(BattleTriggerEffect & pack)
{
CStack * st = gs.getBattle(pack.battleID)->getStack(pack.stackID);
assert(st);
switch(static_cast<BonusType>(pack.effect))
switch(pack.effect)
{
case BonusType::HP_REGENERATION:
{
@ -1218,11 +1218,11 @@ void GameStatePackVisitor::visitBattleTriggerEffect(BattleTriggerEffect & pack)
case BonusType::ENCHANTER:
case BonusType::MORALE:
break;
case BonusType::FEAR:
case BonusType::FEARFUL:
st->fear = true;
break;
default:
logNetwork->error("Unrecognized trigger effect type %d", pack.effect);
logNetwork->error("Unrecognized trigger effect type %d", static_cast<int>(pack.effect));
}
}

View File

@ -488,7 +488,7 @@ struct DLL_LINKAGE BattleTriggerEffect : public CPackForClient
{
BattleID battleID = BattleID::NONE;
int stackID = 0;
int effect = 0; //use corresponding Bonus type
BonusType effect = BonusType::NONE;
int val = 0;
int additionalInfo = 0;

View File

@ -279,7 +279,7 @@ const CStack * BattleFlowProcessor::getNextStack(const CBattleInfoCallback & bat
BattleTriggerEffect bte;
bte.battleID = battle.getBattle()->getBattleID();
bte.stackID = stack->unitId();
bte.effect = vstd::to_underlying(BonusType::HP_REGENERATION);
bte.effect = BonusType::HP_REGENERATION;
const int32_t lostHealth = stack->getMaxHealth() - stack->getFirstHPleft();
if(stack->hasBonusOfType(BonusType::HP_REGENERATION))
@ -529,7 +529,7 @@ bool BattleFlowProcessor::rollGoodMorale(const CBattleInfoCallback & battle, con
BattleTriggerEffect bte;
bte.battleID = battle.getBattle()->getBattleID();
bte.stackID = next->unitId();
bte.effect = vstd::to_underlying(BonusType::MORALE);
bte.effect = BonusType::MORALE;
bte.val = 1;
bte.additionalInfo = 0;
gameHandler->sendAndApply(bte); //play animation
@ -667,7 +667,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
BattleTriggerEffect bte;
bte.battleID = battle.getBattle()->getBattleID();
bte.stackID = st->unitId();
bte.effect = -1;
bte.effect = BonusType::NONE;
bte.val = 0;
bte.additionalInfo = 0;
if (st->alive())
@ -710,7 +710,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
bte.val = std::max (b->val - 10, -(st->valOfBonuses(BonusType::POISON)));
if (bte.val < b->val) //(negative) poison effect increases - update it
{
bte.effect = vstd::to_underlying(BonusType::POISON);
bte.effect = BonusType::POISON;
gameHandler->sendAndApply(bte);
}
}
@ -724,28 +724,21 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
vstd::amin(manaDrained, opponentHero->mana);
if(manaDrained)
{
bte.effect = vstd::to_underlying(BonusType::MANA_DRAIN);
bte.effect = BonusType::MANA_DRAIN;
bte.val = manaDrained;
bte.additionalInfo = opponentHero->id.getNum(); //for sanity
gameHandler->sendAndApply(bte);
}
}
}
if (st->isLiving() && !st->hasBonusOfType(BonusType::FEARLESS))
if (st->hasBonusOfType(BonusType::FEARFUL))
{
int chance = st->valOfBonuses(BonusType::FEARFUL);
ObjectInstanceID opponentArmyID = battle.battleGetArmyObject(battle.otherSide(st->unitSide()))->id;
bool fearsomeCreature = false;
for (const CStack * stack : battle.battleGetAllStacks(true))
if (gameHandler->randomizer->rollCombatAbility(opponentArmyID, chance))
{
if (battle.battleMatchOwner(st, stack) && stack->alive() && stack->hasBonusOfType(BonusType::FEAR))
{
fearsomeCreature = true;
break;
}
}
if (fearsomeCreature && gameHandler->randomizer->rollCombatAbility(opponentArmyID, 10)) //fixed 10%
{
bte.effect = vstd::to_underlying(BonusType::FEAR);
bte.effect = BonusType::FEARFUL;
gameHandler->sendAndApply(bte);
}
}