1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-03 13:19:40 +00:00

Fix crash for on target spells cast by non-actors (Fixes #1529)

This commit is contained in:
scrawl 2014-06-18 01:41:07 +02:00
parent fe1e6a2719
commit e95483c40f
7 changed files with 57 additions and 21 deletions

View file

@ -470,7 +470,7 @@ namespace MWBase
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
float speed, bool stack, const ESM::EffectList& effects, 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, virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0; const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;

View file

@ -340,7 +340,7 @@ namespace MWMechanics
} }
// Try reflecting // 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 reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).mMagnitude;
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99] int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
@ -465,14 +465,14 @@ namespace MWMechanics
if (!appliedLastingEffects.empty()) if (!appliedLastingEffects.empty())
{ {
int casterActorId = -1; int casterActorId = -1;
if (caster.getClass().isActor()) if (!caster.isEmpty() && caster.getClass().isActor())
casterActorId = caster.getClass().getCreatureStats(caster).getActorId(); casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
mSourceName, casterActorId); mSourceName, casterActorId);
} }
// Notify the target actor they've been hit // 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); target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
} }
@ -657,7 +657,9 @@ namespace MWMechanics
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed); getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
if (!projectileModel.empty()) if (!projectileModel.empty())
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, 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; return true;
} }
@ -742,8 +744,18 @@ namespace MWMechanics
float speed = 0; float speed = 0;
getProjectileInfo(spell->mEffects, projectileModel, sound, speed); getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
if (!projectileModel.empty()) 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, MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
false, spell->mEffects, mCaster, mSourceName); false, spell->mEffects, mCaster, mSourceName, fallbackDirection);
}
return true; return true;
} }

View file

@ -47,8 +47,8 @@ namespace MWMechanics
class CastSpell class CastSpell
{ {
private: private:
MWWorld::Ptr mCaster; MWWorld::Ptr mCaster; // May be empty
MWWorld::Ptr mTarget; MWWorld::Ptr mTarget; // May be empty
public: public:
bool mStack; bool mStack;
std::string mId; // ID of spell, potion, item etc std::string mId; // ID of spell, potion, item etc
@ -59,8 +59,13 @@ namespace MWMechanics
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target); CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
bool cast (const ESM::Spell* spell); bool cast (const ESM::Spell* spell);
/// @note mCaster must be an actor
bool cast (const MWWorld::Ptr& item); bool cast (const MWWorld::Ptr& item);
/// @note mCaster must be an NPC
bool cast (const ESM::Ingredient* ingredient); bool cast (const ESM::Ingredient* ingredient);
bool cast (const ESM::Potion* potion); bool cast (const ESM::Potion* potion);
/// @note Auto detects if spell, ingredient or potion /// @note Auto detects if spell, ingredient or potion

View file

@ -57,22 +57,32 @@ namespace MWWorld
void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound, void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound,
const std::string &spellId, float speed, bool stack, 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 = 0;
float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75; 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; pos.z += height;
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * Ogre::Quaternion orient;
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); 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; MagicBoltState state;
state.mSourceName = sourceName; state.mSourceName = sourceName;
state.mId = model; state.mId = model;
state.mSpellId = spellId; 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.mSpeed = speed;
state.mStack = stack; state.mStack = stack;
state.mSoundId = sound; state.mSoundId = sound;
@ -152,7 +162,9 @@ namespace MWWorld
{ {
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); 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) if (!obstacle.isEmpty() && obstacle == caster)
continue; continue;

View file

@ -39,9 +39,10 @@ namespace MWWorld
ProjectileManager (Ogre::SceneManager* sceneMgr, ProjectileManager (Ogre::SceneManager* sceneMgr,
OEngine::Physic::PhysicEngine& engine); 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, void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
float speed, bool stack, const ESM::EffectList& effects, 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, void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
@ -64,9 +65,15 @@ namespace MWWorld
NifOgre::ObjectScenePtr mObject; NifOgre::ObjectScenePtr mObject;
Ogre::SceneNode* mNode; Ogre::SceneNode* mNode;
// Actor who shot this projectile
int mActorId; 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 // MW-id of this projectile
std::string mId; std::string mId;
}; };

View file

@ -2329,9 +2329,9 @@ namespace MWWorld
void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
float speed, bool stack, const ESM::EffectList& effects, 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 const std::vector<std::string>& World::getContentFiles() const

View file

@ -546,7 +546,7 @@ namespace MWWorld
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
float speed, bool stack, const ESM::EffectList& effects, 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, virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);