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:
parent
fe1e6a2719
commit
e95483c40f
7 changed files with 57 additions and 21 deletions
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue