mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-22 21:40:42 +00:00
Merge branch 'friendlyhit' into 'master'
Correct friendly fire reaction for different damage sources (bug #7646) Closes #7646 See merge request OpenMW/openmw!3694
This commit is contained in:
commit
7647af6e4e
@ -103,6 +103,7 @@
|
|||||||
Bug #7641: loopgroup loops the animation one time too many for actors
|
Bug #7641: loopgroup loops the animation one time too many for actors
|
||||||
Bug #7642: Items in repair and recharge menus aren't sorted alphabetically
|
Bug #7642: Items in repair and recharge menus aren't sorted alphabetically
|
||||||
Bug #7643: Can't enchant items with constant effect on self magic effects for non-player character
|
Bug #7643: Can't enchant items with constant effect on self magic effects for non-player character
|
||||||
|
Bug #7646: Follower voices pain sounds when attacked with magic
|
||||||
Bug #7647: NPC walk cycle bugs after greeting player
|
Bug #7647: NPC walk cycle bugs after greeting player
|
||||||
Bug #7654: Tooltips for enchantments with invalid effects cause crashes
|
Bug #7654: Tooltips for enchantments with invalid effects cause crashes
|
||||||
Bug #7660: Some inconsistencies regarding Invisibility breaking
|
Bug #7660: Some inconsistencies regarding Invisibility breaking
|
||||||
|
@ -69,7 +69,8 @@ namespace MWBase
|
|||||||
|
|
||||||
// TODO: notify LuaManager about other events
|
// TODO: notify LuaManager about other events
|
||||||
// virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object,
|
// virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object,
|
||||||
// const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) = 0;
|
// const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful,
|
||||||
|
// DamageSourceType sourceType) = 0;
|
||||||
|
|
||||||
struct InputEvent
|
struct InputEvent
|
||||||
{
|
{
|
||||||
|
@ -284,7 +284,8 @@ namespace MWClass
|
|||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false);
|
victim.getClass().onHit(
|
||||||
|
victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false, MWMechanics::DamageSourceType::Melee);
|
||||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -345,24 +346,35 @@ namespace MWClass
|
|||||||
|
|
||||||
MWMechanics::diseaseContact(victim, ptr);
|
MWMechanics::diseaseContact(victim, ptr);
|
||||||
|
|
||||||
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
victim.getClass().onHit(
|
||||||
|
victim, damage, healthdmg, weapon, ptr, hitPosition, true, MWMechanics::DamageSourceType::Melee);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
void Creature::onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const
|
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||||
|
const MWMechanics::DamageSourceType sourceType) const
|
||||||
{
|
{
|
||||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
|
|
||||||
|
// Self defense
|
||||||
|
bool setOnPcHitMe = true;
|
||||||
|
|
||||||
// NOTE: 'object' and/or 'attacker' may be empty.
|
// NOTE: 'object' and/or 'attacker' may be empty.
|
||||||
if (!attacker.isEmpty() && attacker.getClass().isActor() && !stats.getAiSequence().isInCombat(attacker))
|
if (!attacker.isEmpty() && attacker.getClass().isActor() && !stats.getAiSequence().isInCombat(attacker))
|
||||||
|
{
|
||||||
stats.setAttacked(true);
|
stats.setAttacked(true);
|
||||||
|
|
||||||
// Self defense
|
// No retaliation for totally static creatures (they have no movement or attacks anyway)
|
||||||
bool setOnPcHitMe = true; // Note OnPcHitMe is not set for friendly hits.
|
if (isMobile(ptr))
|
||||||
|
{
|
||||||
// No retaliation for totally static creatures (they have no movement or attacks anyway)
|
bool complain = sourceType == MWMechanics::DamageSourceType::Melee;
|
||||||
if (isMobile(ptr) && !attacker.isEmpty())
|
bool supportFriendlyFire = sourceType != MWMechanics::DamageSourceType::Ranged;
|
||||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
if (supportFriendlyFire && MWMechanics::friendlyHit(attacker, ptr, complain))
|
||||||
|
setOnPcHitMe = false;
|
||||||
|
else
|
||||||
|
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
||||||
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
||||||
|
@ -67,7 +67,8 @@ namespace MWClass
|
|||||||
const osg::Vec3f& hitPosition, bool success) const override;
|
const osg::Vec3f& hitPosition, bool success) const override;
|
||||||
|
|
||||||
void onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
void onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const override;
|
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||||
|
const MWMechanics::DamageSourceType sourceType) const override;
|
||||||
|
|
||||||
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
|
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
|
||||||
///< Generate action for activation
|
///< Generate action for activation
|
||||||
|
@ -597,7 +597,8 @@ namespace MWClass
|
|||||||
float damage = 0.0f;
|
float damage = 0.0f;
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
othercls.onHit(victim, damage, false, weapon, ptr, osg::Vec3f(), false);
|
othercls.onHit(
|
||||||
|
victim, damage, false, weapon, ptr, osg::Vec3f(), false, MWMechanics::DamageSourceType::Melee);
|
||||||
MWMechanics::reduceWeaponCondition(damage, false, weapon, ptr);
|
MWMechanics::reduceWeaponCondition(damage, false, weapon, ptr);
|
||||||
MWMechanics::resistNormalWeapon(victim, ptr, weapon, damage);
|
MWMechanics::resistNormalWeapon(victim, ptr, weapon, damage);
|
||||||
return;
|
return;
|
||||||
@ -670,24 +671,29 @@ namespace MWClass
|
|||||||
|
|
||||||
MWMechanics::diseaseContact(victim, ptr);
|
MWMechanics::diseaseContact(victim, ptr);
|
||||||
|
|
||||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true, MWMechanics::DamageSourceType::Melee);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Npc::onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
void Npc::onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const
|
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||||
|
const MWMechanics::DamageSourceType sourceType) const
|
||||||
{
|
{
|
||||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
bool wasDead = stats.isDead();
|
bool wasDead = stats.isDead();
|
||||||
|
|
||||||
// Note OnPcHitMe is not set for friendly hits.
|
|
||||||
bool setOnPcHitMe = true;
|
bool setOnPcHitMe = true;
|
||||||
|
|
||||||
// NOTE: 'object' and/or 'attacker' may be empty.
|
// NOTE: 'object' and/or 'attacker' may be empty.
|
||||||
if (!attacker.isEmpty() && attacker.getClass().isActor() && !stats.getAiSequence().isInCombat(attacker))
|
if (!attacker.isEmpty() && attacker.getClass().isActor() && !stats.getAiSequence().isInCombat(attacker))
|
||||||
{
|
{
|
||||||
stats.setAttacked(true);
|
stats.setAttacked(true);
|
||||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
bool complain = sourceType == MWMechanics::DamageSourceType::Melee;
|
||||||
|
bool supportFriendlyFire = sourceType != MWMechanics::DamageSourceType::Ranged;
|
||||||
|
if (supportFriendlyFire && MWMechanics::friendlyHit(attacker, ptr, complain))
|
||||||
|
setOnPcHitMe = false;
|
||||||
|
else
|
||||||
|
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
||||||
|
@ -82,7 +82,8 @@ namespace MWClass
|
|||||||
const osg::Vec3f& hitPosition, bool success) const override;
|
const osg::Vec3f& hitPosition, bool success) const override;
|
||||||
|
|
||||||
void onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
void onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const override;
|
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||||
|
const MWMechanics::DamageSourceType sourceType) const override;
|
||||||
|
|
||||||
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
||||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <components/esm3/loadmgef.hpp>
|
#include <components/esm3/loadmgef.hpp>
|
||||||
#include <components/esm3/loadsoun.hpp>
|
#include <components/esm3/loadsoun.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/dialoguemanager.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
@ -230,7 +231,8 @@ namespace MWMechanics
|
|||||||
|
|
||||||
if (Misc::Rng::roll0to99(world->getPrng()) >= getHitChance(attacker, victim, skillValue))
|
if (Misc::Rng::roll0to99(world->getPrng()) >= getHitChance(attacker, victim, skillValue))
|
||||||
{
|
{
|
||||||
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false);
|
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false,
|
||||||
|
MWMechanics::DamageSourceType::Ranged);
|
||||||
MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);
|
MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -286,7 +288,8 @@ namespace MWMechanics
|
|||||||
victim.getClass().getContainerStore(victim).add(projectile, 1);
|
victim.getClass().getContainerStore(victim).add(projectile, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true);
|
victim.getClass().onHit(
|
||||||
|
victim, damage, true, projectile, attacker, hitPosition, true, MWMechanics::DamageSourceType::Ranged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,4 +652,26 @@ namespace MWMechanics
|
|||||||
|
|
||||||
return std::make_pair(result, hitPos);
|
return std::make_pair(result, hitPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool friendlyHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& target, bool complain)
|
||||||
|
{
|
||||||
|
const MWWorld::Ptr& player = getPlayer();
|
||||||
|
if (attacker != player)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::set<MWWorld::Ptr> followersAttacker;
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->getActorsSidingWith(attacker, followersAttacker);
|
||||||
|
if (followersAttacker.find(target) == followersAttacker.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target);
|
||||||
|
statsTarget.friendlyHit();
|
||||||
|
if (statsTarget.getFriendlyHits() >= 4)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (complain)
|
||||||
|
MWBase::Environment::get().getDialogueManager()->say(target, ESM::RefId::stringRefId("hit"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,8 @@ namespace MWMechanics
|
|||||||
|
|
||||||
// Similarly cursed hit target selection
|
// Similarly cursed hit target selection
|
||||||
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::Ptr& actor, float reach);
|
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::Ptr& actor, float reach);
|
||||||
|
|
||||||
|
bool friendlyHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& target, bool complain);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
15
apps/openmw/mwmechanics/damagesourcetype.hpp
Normal file
15
apps/openmw/mwmechanics/damagesourcetype.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef OPENMW_MWMECHANICS_DAMAGESOURCETYPE_H
|
||||||
|
#define OPENMW_MWMECHANICS_DAMAGESOURCETYPE_H
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
enum class DamageSourceType
|
||||||
|
{
|
||||||
|
Unspecified,
|
||||||
|
Melee,
|
||||||
|
Ranged,
|
||||||
|
Magical,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1466,26 +1466,10 @@ namespace MWMechanics
|
|||||||
if (target == player || !attacker.getClass().isActor())
|
if (target == player || !attacker.getClass().isActor())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target);
|
|
||||||
if (attacker == player)
|
|
||||||
{
|
|
||||||
std::set<MWWorld::Ptr> followersAttacker;
|
|
||||||
getActorsSidingWith(attacker, followersAttacker);
|
|
||||||
if (followersAttacker.find(target) != followersAttacker.end())
|
|
||||||
{
|
|
||||||
statsTarget.friendlyHit();
|
|
||||||
|
|
||||||
if (statsTarget.getFriendlyHits() < 4)
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getDialogueManager()->say(target, ESM::RefId::stringRefId("hit"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canCommitCrimeAgainst(target, attacker))
|
if (canCommitCrimeAgainst(target, attacker))
|
||||||
commitCrime(attacker, target, MWBase::MechanicsManager::OT_Assault);
|
commitCrime(attacker, target, MWBase::MechanicsManager::OT_Assault);
|
||||||
|
|
||||||
|
MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target);
|
||||||
AiSequence& seq = statsTarget.getAiSequence();
|
AiSequence& seq = statsTarget.getAiSequence();
|
||||||
|
|
||||||
if (!attacker.isEmpty()
|
if (!attacker.isEmpty()
|
||||||
|
@ -353,7 +353,8 @@ namespace
|
|||||||
// Notify the target actor they've been hit
|
// Notify the target actor they've been hit
|
||||||
bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful;
|
bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful;
|
||||||
if (target.getClass().isActor() && target != caster && !caster.isEmpty() && isHarmful)
|
if (target.getClass().isActor() && target != caster && !caster.isEmpty() && isHarmful)
|
||||||
target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true);
|
target.getClass().onHit(
|
||||||
|
target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true, MWMechanics::DamageSourceType::Magical);
|
||||||
// Apply resistances
|
// Apply resistances
|
||||||
if (!(effect.mFlags & ESM::ActiveEffect::Flag_Ignore_Resistances))
|
if (!(effect.mFlags & ESM::ActiveEffect::Flag_Ignore_Resistances))
|
||||||
{
|
{
|
||||||
|
@ -124,7 +124,7 @@ namespace MWWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker,
|
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker,
|
||||||
const osg::Vec3f& hitPosition, bool successful) const
|
const osg::Vec3f& hitPosition, bool successful, const MWMechanics::DamageSourceType sourceType) const
|
||||||
{
|
{
|
||||||
throw std::runtime_error("class cannot be hit");
|
throw std::runtime_error("class cannot be hit");
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
#include "ptr.hpp"
|
#include "ptr.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/aisetting.hpp"
|
#include "../mwmechanics/aisetting.hpp"
|
||||||
|
#include "../mwmechanics/damagesourcetype.hpp"
|
||||||
|
|
||||||
#include <components/esm/refid.hpp>
|
#include <components/esm/refid.hpp>
|
||||||
#include <components/esm3/loadskil.hpp>
|
#include <components/esm3/loadskil.hpp>
|
||||||
|
|
||||||
@ -142,11 +144,12 @@ namespace MWWorld
|
|||||||
/// (default implementation: throw an exception)
|
/// (default implementation: throw an exception)
|
||||||
|
|
||||||
virtual void onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
virtual void onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const;
|
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||||
|
const MWMechanics::DamageSourceType sourceType) const;
|
||||||
///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is
|
///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is
|
||||||
/// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the
|
/// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the
|
||||||
/// actor responsible for the attack, and \a successful specifies if the hit is
|
/// actor responsible for the attack. \a successful specifies if the hit is
|
||||||
/// successful or not.
|
/// successful or not. \a sourceType classifies the damage source.
|
||||||
|
|
||||||
virtual void block(const Ptr& ptr) const;
|
virtual void block(const Ptr& ptr) const;
|
||||||
///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield
|
///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield
|
||||||
|
Loading…
x
Reference in New Issue
Block a user