Bläddra i källkod

Implemented configurable FEAR ability

Ivan Savenko 4 månader sedan
förälder
incheckning
ae22de3ccf

+ 1 - 1
client/CPlayerInterface.cpp

@@ -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);

+ 2 - 2
client/battle/BattleEffectsController.cpp

@@ -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:

+ 11 - 4
config/creatures/neutral.json

@@ -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" :
 			{

+ 1 - 8
lib/BasicTypes.cpp

@@ -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);
 }
 
 

+ 11 - 2
lib/CCreatureHandler.cpp

@@ -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':

+ 2 - 2
lib/bonuses/BonusEnum.h

@@ -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)									\

+ 1 - 0
lib/bonuses/Limiters.cpp

@@ -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>()},

+ 3 - 3
lib/gameState/GameStatePackVisitor.cpp

@@ -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));
 	}
 }
 

+ 1 - 1
lib/networkPacks/PacksForClientBattle.h

@@ -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;
 

+ 10 - 17
server/battles/BattleFlowProcessor.cpp

@@ -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 (battle.battleMatchOwner(st, stack) && stack->alive() && stack->hasBonusOfType(BonusType::FEAR))
-				{
-					fearsomeCreature = true;
-					break;
-				}
-			}
-			if (fearsomeCreature && gameHandler->randomizer->rollCombatAbility(opponentArmyID, 10)) //fixed 10%
+
+			if (gameHandler->randomizer->rollCombatAbility(opponentArmyID, chance))
 			{
-				bte.effect = vstd::to_underlying(BonusType::FEAR);
+				bte.effect = BonusType::FEARFUL;
 				gameHandler->sendAndApply(bte);
 			}
 		}