Bring attack voice lines in line with research

Only play them when starting combat when not in combat or not in combat
with one of the target's allies.
Don't play them when casting spells whose first effect isn't ranged.
ini_importer_tests
Evil Eye 4 months ago
parent 4dfe6078c8
commit bdc6119b31

@ -24,6 +24,7 @@
Bug #5129: Stuttering animation on Centurion Archer
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
Bug #5413: Enemies do a battlecry everytime the player summons a creature
Bug #5714: Touch spells cast using ExplodeSpell don't always explode
Bug #5849: Paralysis breaks landing
Bug #5870: Disposing of actors who were selected in the console doesn't deselect them like vanilla

@ -109,7 +109,9 @@ namespace MWBase
virtual bool awarenessCheck(const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0;
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
virtual void startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
virtual void startCombat(
const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, const std::set<MWWorld::Ptr>* targetAllies)
= 0;
/// Removes an actor and its allies from combat with the actor's targets.
virtual void stopCombat(const MWWorld::Ptr& ptr) = 0;

@ -620,7 +620,7 @@ namespace MWMechanics
if (creatureStats2.matchesActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId()))
{
mechanicsManager->startCombat(actor1, actor2);
mechanicsManager->startCombat(actor1, actor2, nullptr);
// Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat
// if the player gets out of reach, while the ally would continue combat with the player
creatureStats1.setHitAttemptActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId());
@ -655,11 +655,11 @@ namespace MWMechanics
{
if (ally2 != actor2 && ally2.getClass().getCreatureStats(ally2).getAiSequence().isInCombat(actor1))
{
mechanicsManager->startCombat(actor1, actor2);
mechanicsManager->startCombat(actor1, actor2, &allies2);
// Also have actor1's allies start combat
for (const MWWorld::Ptr& ally1 : allies1)
if (ally1 != player)
mechanicsManager->startCombat(ally1, actor2);
mechanicsManager->startCombat(ally1, actor2, &allies2);
return;
}
}
@ -747,7 +747,7 @@ namespace MWMechanics
bool LOS = world->getLOS(actor1, actor2) && mechanicsManager->awarenessCheck(actor2, actor1);
if (LOS)
mechanicsManager->startCombat(actor1, actor2);
mechanicsManager->startCombat(actor1, actor2, nullptr);
}
}
@ -1133,7 +1133,7 @@ namespace MWMechanics
= esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->mValue.getInteger();
if (playerStats.getBounty() >= cutoff * iCrimeThresholdMultiplier)
{
mechanicsManager->startCombat(ptr, player);
mechanicsManager->startCombat(ptr, player, nullptr);
creatureStats.setHitAttemptActorId(
playerClass.getCreatureStats(player)
.getActorId()); // Stops the guard from quitting combat if player is unreachable

@ -270,7 +270,15 @@ namespace MWMechanics
{
storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target);
// start new attack
storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat);
bool canShout = true;
ESM::RefId spellId = storage.mCurrentAction->getSpell();
if (!spellId.empty())
{
const ESM::Spell* spell = MWBase::Environment::get().getESMStore()->get<ESM::Spell>().find(spellId);
if (spell->mEffects.mList.empty() || spell->mEffects.mList[0].mRange != ESM::RT_Target)
canShout = false;
}
storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat, canShout);
}
// If actor uses custom destination it has to try to rebuild path because environment can change
@ -635,7 +643,7 @@ namespace MWMechanics
}
void AiCombatStorage::startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
const ESM::Weapon* weapon, bool distantCombat)
const ESM::Weapon* weapon, bool distantCombat, bool canShout)
{
if (mReadyToAttack && characterController.readyToStartAttack())
{
@ -658,12 +666,15 @@ namespace MWMechanics
baseDelay = store.get<ESM::GameSetting>().find("fCombatDelayNPC")->mValue.getFloat();
}
// Say a provoking combat phrase
const int iVoiceAttackOdds
= store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->mValue.getInteger();
if (Misc::Rng::roll0to99(prng) < iVoiceAttackOdds)
if (canShout)
{
MWBase::Environment::get().getDialogueManager()->say(actor, ESM::RefId::stringRefId("attack"));
// Say a provoking combat phrase
const int iVoiceAttackOdds
= store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->mValue.getInteger();
if (Misc::Rng::roll0to99(prng) < iVoiceAttackOdds)
{
MWBase::Environment::get().getDialogueManager()->say(actor, ESM::RefId::stringRefId("attack"));
}
}
mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(prng), baseDelay + 0.9);
}

@ -64,7 +64,7 @@ namespace MWMechanics
void updateCombatMove(float duration);
void stopCombatMove();
void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
const ESM::Weapon* weapon, bool distantCombat);
const ESM::Weapon* weapon, bool distantCombat, bool canShout);
void updateAttack(const MWWorld::Ptr& actor, CharacterController& characterController);
void stopAttack();

@ -16,6 +16,7 @@ namespace MWMechanics
virtual float getCombatRange(bool& isRanged) const = 0;
virtual float getActionCooldown() const { return 0.f; }
virtual const ESM::Weapon* getWeapon() const { return nullptr; }
virtual ESM::RefId getSpell() const { return {}; }
virtual bool isAttackingOrSpell() const { return true; }
virtual bool isFleeing() const { return false; }
};
@ -43,6 +44,7 @@ namespace MWMechanics
void prepare(const MWWorld::Ptr& actor) override;
float getCombatRange(bool& isRanged) const override;
ESM::RefId getSpell() const override { return mSpellId; }
};
class ActionEnchantedItem : public Action

@ -132,9 +132,6 @@ namespace MWMechanics
/// Are we in combat with this particular actor?
bool isInCombat(const MWWorld::Ptr& actor) const;
bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const;
///< Function assumes that actor can have only 1 target apart player
/// Removes all combat packages until first non-combat or stack empty.
void stopCombat();

@ -1169,9 +1169,9 @@ namespace MWMechanics
&& !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue))
reported = reportCrime(player, victim, type, ESM::RefId(), arg);
// TODO: combat should be started with an "unaware" flag, which makes the victim flee?
if (!reported)
startCombat(victim,
player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee?
startCombat(victim, player, &playerFollowers);
}
return crimeSeen;
}
@ -1412,7 +1412,7 @@ namespace MWMechanics
observerStats.modCrimeDispositionModifier(dispositionModifier);
}
startCombat(actor, player);
startCombat(actor, player, &playerFollowers);
// Apply aggression value to the base Fight rating, so that the actor can continue fighting
// after a Calm spell wears off
@ -1463,7 +1463,7 @@ namespace MWMechanics
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
// Note: accidental or collateral damage attacks are ignored.
if (!victim.getClass().getCreatureStats(victim).getAiSequence().isInPursuit())
startCombat(victim, player);
startCombat(victim, player, &playerFollowers);
// Set the crime ID, which we will use to calm down participants
// once the bounty has been paid.
@ -1508,14 +1508,14 @@ namespace MWMechanics
if (!peaceful)
{
startCombat(target, attacker);
startCombat(target, attacker, nullptr);
// Force friendly actors into combat to prevent infighting between followers
std::set<MWWorld::Ptr> followersTarget;
getActorsSidingWith(target, followersTarget);
for (const auto& follower : followersTarget)
{
if (follower != attacker && follower != player)
startCombat(follower, attacker);
startCombat(follower, attacker, nullptr);
}
}
}
@ -1664,7 +1664,8 @@ namespace MWMechanics
}
}
void MechanicsManager::startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target)
void MechanicsManager::startCombat(
const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, const std::set<MWWorld::Ptr>* targetAllies)
{
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
@ -1682,6 +1683,30 @@ namespace MWMechanics
}
const bool inCombat = stats.getAiSequence().isInCombat();
bool shout = !inCombat;
if (inCombat)
{
const auto isInCombatWithOneOf = [&](const auto& allies) {
for (const MWWorld::Ptr& ally : allies)
{
if (stats.getAiSequence().isInCombat(ally))
return true;
}
return false;
};
if (targetAllies)
shout = !isInCombatWithOneOf(*targetAllies);
else
{
shout = stats.getAiSequence().isInCombat(target);
if (!shout)
{
std::set<MWWorld::Ptr> sidingActors;
getActorsSidingWith(target, sidingActors);
shout = !isInCombatWithOneOf(sidingActors);
}
}
}
stats.getAiSequence().stack(MWMechanics::AiCombat(target), ptr);
if (target == getPlayer())
{
@ -1716,7 +1741,7 @@ namespace MWMechanics
}
// Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly
if (!inCombat)
if (shout)
MWBase::Environment::get().getDialogueManager()->say(ptr, ESM::RefId::stringRefId("attack"));
}

@ -107,7 +107,8 @@ namespace MWMechanics
bool awarenessCheck(const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) override;
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
void startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) override;
void startCombat(
const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, const std::set<MWWorld::Ptr>* targetAllies) override;
void stopCombat(const MWWorld::Ptr& ptr) override;

@ -508,7 +508,7 @@ namespace MWScript
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(targetID, true, false);
if (!target.isEmpty() && !target.getClass().getCreatureStats(target).isDead())
MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target);
MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target, nullptr);
}
};

Loading…
Cancel
Save