mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-23 06:41:08 +00:00
Fix crash for on target spells cast by non-actors (Fixes #1529)
This commit is contained in:
parent
fe1e6a2719
commit
e95483c40f
@ -470,7 +470,7 @@ namespace MWBase
|
||||
|
||||
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
|
||||
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection) = 0;
|
||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;
|
||||
|
||||
|
@ -340,7 +340,7 @@ namespace MWMechanics
|
||||
}
|
||||
|
||||
// Try reflecting
|
||||
if (!reflected && magnitudeMult > 0 && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
|
||||
if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
|
||||
{
|
||||
int reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).mMagnitude;
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
@ -465,14 +465,14 @@ namespace MWMechanics
|
||||
if (!appliedLastingEffects.empty())
|
||||
{
|
||||
int casterActorId = -1;
|
||||
if (caster.getClass().isActor())
|
||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
||||
mSourceName, casterActorId);
|
||||
}
|
||||
|
||||
// Notify the target actor they've been hit
|
||||
if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor())
|
||||
if (anyHarmfulEffect && target.getClass().isActor() && target != caster && !caster.isEmpty() && caster.getClass().isActor())
|
||||
target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
|
||||
}
|
||||
|
||||
@ -657,7 +657,9 @@ namespace MWMechanics
|
||||
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
|
||||
if (!projectileModel.empty())
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
||||
false, enchantment->mEffects, mCaster, mSourceName);
|
||||
false, enchantment->mEffects, mCaster, mSourceName,
|
||||
// Not needed, enchantments can only be cast by actors
|
||||
Ogre::Vector3(1,0,0));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -742,8 +744,18 @@ namespace MWMechanics
|
||||
float speed = 0;
|
||||
getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
|
||||
if (!projectileModel.empty())
|
||||
{
|
||||
Ogre::Vector3 fallbackDirection (0,1,0);
|
||||
// Fall back to a "caster to target" direction if we have no other means of determining it
|
||||
// (e.g. when cast by a non-actor)
|
||||
if (!mTarget.isEmpty())
|
||||
fallbackDirection =
|
||||
Ogre::Vector3(mTarget.getRefData().getPosition().pos)-
|
||||
Ogre::Vector3(mCaster.getRefData().getPosition().pos);
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
||||
false, spell->mEffects, mCaster, mSourceName);
|
||||
false, spell->mEffects, mCaster, mSourceName, fallbackDirection);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ namespace MWMechanics
|
||||
class CastSpell
|
||||
{
|
||||
private:
|
||||
MWWorld::Ptr mCaster;
|
||||
MWWorld::Ptr mTarget;
|
||||
MWWorld::Ptr mCaster; // May be empty
|
||||
MWWorld::Ptr mTarget; // May be empty
|
||||
public:
|
||||
bool mStack;
|
||||
std::string mId; // ID of spell, potion, item etc
|
||||
@ -59,8 +59,13 @@ namespace MWMechanics
|
||||
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
|
||||
|
||||
bool cast (const ESM::Spell* spell);
|
||||
|
||||
/// @note mCaster must be an actor
|
||||
bool cast (const MWWorld::Ptr& item);
|
||||
|
||||
/// @note mCaster must be an NPC
|
||||
bool cast (const ESM::Ingredient* ingredient);
|
||||
|
||||
bool cast (const ESM::Potion* potion);
|
||||
|
||||
/// @note Auto detects if spell, ingredient or potion
|
||||
|
@ -57,22 +57,32 @@ namespace MWWorld
|
||||
|
||||
void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound,
|
||||
const std::string &spellId, float speed, bool stack,
|
||||
const ESM::EffectList &effects, const Ptr &actor, const std::string &sourceName)
|
||||
const ESM::EffectList &effects, const Ptr &caster, const std::string &sourceName,
|
||||
const Ogre::Vector3& fallbackDirection)
|
||||
{
|
||||
// Spawn at 0.75 * ActorHeight
|
||||
float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75;
|
||||
float height = 0;
|
||||
if (OEngine::Physic::PhysicActor* actor = mPhysEngine.getCharacter(caster.getRefData().getHandle()))
|
||||
height = actor->getHalfExtents().z * 2 * 0.75; // Spawn at 0.75 * ActorHeight
|
||||
|
||||
Ogre::Vector3 pos(actor.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 pos(caster.getRefData().getPosition().pos);
|
||||
pos.z += height;
|
||||
|
||||
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
Ogre::Quaternion orient;
|
||||
if (caster.getClass().isActor())
|
||||
orient = Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
else
|
||||
orient = Ogre::Vector3::UNIT_Y.getRotationTo(fallbackDirection);
|
||||
|
||||
MagicBoltState state;
|
||||
state.mSourceName = sourceName;
|
||||
state.mId = model;
|
||||
state.mSpellId = spellId;
|
||||
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
state.mCaster = caster;
|
||||
if (caster.getClass().isActor())
|
||||
state.mActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
else
|
||||
state.mActorId = -1;
|
||||
state.mSpeed = speed;
|
||||
state.mStack = stack;
|
||||
state.mSoundId = sound;
|
||||
@ -152,7 +162,9 @@ namespace MWWorld
|
||||
{
|
||||
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
|
||||
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
|
||||
MWWorld::Ptr caster = it->mCaster;
|
||||
if (caster.isEmpty())
|
||||
caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
|
||||
|
||||
if (!obstacle.isEmpty() && obstacle == caster)
|
||||
continue;
|
||||
|
@ -39,9 +39,10 @@ namespace MWWorld
|
||||
ProjectileManager (Ogre::SceneManager* sceneMgr,
|
||||
OEngine::Physic::PhysicEngine& engine);
|
||||
|
||||
/// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used.
|
||||
void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName);
|
||||
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection);
|
||||
|
||||
void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
||||
@ -64,9 +65,15 @@ namespace MWWorld
|
||||
NifOgre::ObjectScenePtr mObject;
|
||||
Ogre::SceneNode* mNode;
|
||||
|
||||
// Actor who shot this projectile
|
||||
int mActorId;
|
||||
|
||||
// actorId doesn't work for non-actors, so we also keep track of the Ptr.
|
||||
// For non-actors, the caster ptr is mainly needed to prevent the projectile
|
||||
// from colliding with its caster.
|
||||
// TODO: this will break when the game is saved and reloaded, since there is currently
|
||||
// no way to write identifiers for non-actors to a savegame.
|
||||
MWWorld::Ptr mCaster;
|
||||
|
||||
// MW-id of this projectile
|
||||
std::string mId;
|
||||
};
|
||||
|
@ -2329,9 +2329,9 @@ namespace MWWorld
|
||||
|
||||
void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName)
|
||||
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection)
|
||||
{
|
||||
mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName);
|
||||
mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, caster, sourceName, fallbackDirection);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& World::getContentFiles() const
|
||||
|
@ -546,7 +546,7 @@ namespace MWWorld
|
||||
|
||||
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName);
|
||||
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection);
|
||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user