@ -3,6 +3,7 @@
# include <iomanip>
# include <memory>
# include <optional>
# include <osg/PositionAttitudeTransform>
# include <components/debug/debuglog.hpp>
@ -43,6 +44,7 @@
# include "../mwsound/sound.hpp"
# include "../mwphysics/collisiontype.hpp"
# include "../mwphysics/physicssystem.hpp"
# include "../mwphysics/projectile.hpp"
@ -187,7 +189,7 @@ namespace MWWorld
} ;
void ProjectileManager : : createModel ( State & state , const std : : string & model , const osg : : Vec3f & pos , const osg : : Quat & orient ,
float ProjectileManager : : createModel ( State & state , const std : : string & model , const osg : : Vec3f & pos , const osg : : Quat & orient ,
bool rotate , bool createLight , osg : : Vec4 lightDiffuseColor , std : : string texture )
{
state . mNode = new osg : : PositionAttitudeTransform ;
@ -251,6 +253,7 @@ namespace MWWorld
state . mNode - > accept ( assignVisitor ) ;
MWRender : : overrideFirstRootTexture ( texture , mResourceSystem , projectile ) ;
return projectile - > getBound ( ) . radius ( ) ;
}
void ProjectileManager : : update ( State & state , float duration )
@ -305,7 +308,7 @@ namespace MWWorld
osg : : Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor ( state . mEffects ) ;
createModel ( state , ptr . getClass ( ) . getModel ( ptr ) , pos , orient , true , true , lightDiffuseColor , texture ) ;
const auto radius = createModel ( state , ptr . getClass ( ) . getModel ( ptr ) , pos , orient , true , true , lightDiffuseColor , texture ) ;
MWBase : : SoundManager * sndMgr = MWBase : : Environment : : get ( ) . getSoundManager ( ) ;
for ( const std : : string & soundid : state . mSoundIds )
@ -316,7 +319,7 @@ namespace MWWorld
state . mSounds . push_back ( sound ) ;
}
state . mProjectileId = mPhysics - > addProjectile ( caster , pos );
state . mProjectileId = mPhysics - > addProjectile ( caster , pos , radius , false );
state . mToDelete = false ;
mMagicBolts . push_back ( state ) ;
}
@ -340,7 +343,7 @@ namespace MWWorld
if ( ! ptr . getClass ( ) . getEnchantment ( ptr ) . empty ( ) )
SceneUtil : : addEnchantedGlow ( state . mNode , mResourceSystem , ptr . getClass ( ) . getEnchantmentColor ( ptr ) ) ;
state . mProjectileId = mPhysics - > addProjectile ( actor , pos );
state . mProjectileId = mPhysics - > addProjectile ( actor , pos , 1.f , true );
state . mToDelete = false ;
mProjectiles . push_back ( state ) ;
}
@ -407,15 +410,7 @@ namespace MWWorld
float speed = fTargetSpellMaxSpeed * magicBoltState . mSpeed ;
osg : : Vec3f direction = orient * osg : : Vec3f ( 0 , 1 , 0 ) ;
direction . normalize ( ) ;
osg : : Vec3f pos ( magicBoltState . mNode - > getPosition ( ) ) ;
osg : : Vec3f newPos = pos + direction * duration * speed ;
for ( const auto & sound : magicBoltState . mSounds )
sound - > setPosition ( newPos ) ;
magicBoltState . mNode - > setPosition ( newPos ) ;
mPhysics - > updateProjectile ( magicBoltState . mProjectileId , newPos ) ;
osg : : Vec3f newPos = projectile - > getPosition ( ) + direction * duration * speed ;
update ( magicBoltState , duration ) ;
@ -425,41 +420,7 @@ namespace MWWorld
caster . getClass ( ) . getCreatureStats ( caster ) . getAiSequence ( ) . getCombatTargets ( targetActors ) ;
projectile - > setValidTargets ( targetActors ) ;
// Check for impact
// TODO: use a proper btRigidBody / btGhostObject?
const auto result = mPhysics - > castRay ( pos , newPos , caster , targetActors , 0xff , MWPhysics : : CollisionType_Projectile , magicBoltState . mProjectileId ) ;
bool hit = false ;
if ( result . mHit )
{
hit = true ;
if ( result . mHitObject . isEmpty ( ) )
{
// terrain or projectile
}
else
{
MWMechanics : : CastSpell cast ( caster , result . mHitObject ) ;
cast . mHitPosition = pos ;
cast . mId = magicBoltState . mSpellId ;
cast . mSourceName = magicBoltState . mSourceName ;
cast . mStack = false ;
cast . inflict ( result . mHitObject , caster , magicBoltState . mEffects , ESM : : RT_Target , false , true ) ;
mPhysics - > reportCollision ( Misc : : Convert : : toBullet ( result . mHitPos ) , Misc : : Convert : : toBullet ( result . mHitNormal ) ) ;
}
}
// Explodes when hitting water
if ( MWBase : : Environment : : get ( ) . getWorld ( ) - > isUnderwater ( MWMechanics : : getPlayer ( ) . getCell ( ) , newPos ) )
hit = true ;
if ( hit )
{
MWBase : : Environment : : get ( ) . getWorld ( ) - > explodeSpell ( pos , magicBoltState . mEffects , caster , result . mHitObject ,
ESM : : RT_Target , magicBoltState . mSpellId , magicBoltState . mSourceName ) ;
cleanupMagicBolt ( magicBoltState ) ;
}
mPhysics - > updateProjectile ( magicBoltState . mProjectileId , newPos ) ;
}
}
@ -467,9 +428,6 @@ namespace MWWorld
{
for ( auto & projectileState : mProjectiles )
{
if ( projectileState . mToDelete )
continue ;
auto * projectile = mPhysics - > getProjectile ( projectileState . mProjectileId ) ;
if ( ! projectile - > isActive ( ) )
continue ;
@ -477,8 +435,7 @@ namespace MWWorld
// simulating aerodynamics at all
projectileState . mVelocity - = osg : : Vec3f ( 0 , 0 , Constants : : GravityConst * Constants : : UnitsPerMeter * 0.1f ) * duration ;
osg : : Vec3f pos ( projectileState . mNode - > getPosition ( ) ) ;
osg : : Vec3f newPos = pos + projectileState . mVelocity * duration ;
osg : : Vec3f newPos = projectile - > getPosition ( ) + projectileState . mVelocity * duration ;
// rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction.
if ( ! projectileState . mThrown )
@ -488,10 +445,6 @@ namespace MWWorld
projectileState . mNode - > setAttitude ( orient ) ;
}
projectileState . mNode - > setPosition ( newPos ) ;
mPhysics - > updateProjectile ( projectileState . mProjectileId , newPos ) ;
update ( projectileState , duration ) ;
MWWorld : : Ptr caster = projectileState . getCaster ( ) ;
@ -502,36 +455,7 @@ namespace MWWorld
caster . getClass ( ) . getCreatureStats ( caster ) . getAiSequence ( ) . getCombatTargets ( targetActors ) ;
projectile - > setValidTargets ( targetActors ) ;
// Check for impact
// TODO: use a proper btRigidBody / btGhostObject?
const auto result = mPhysics - > castRay ( pos , newPos , caster , targetActors , 0xff , MWPhysics : : CollisionType_Projectile , projectileState . mProjectileId ) ;
bool underwater = MWBase : : Environment : : get ( ) . getWorld ( ) - > isUnderwater ( MWMechanics : : getPlayer ( ) . getCell ( ) , newPos ) ;
if ( result . mHit | | underwater )
{
// Try to get a Ptr to the bow that was used. It might no longer exist.
MWWorld : : ManualRef projectileRef ( MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) , projectileState . mIdArrow ) ;
MWWorld : : Ptr bow = projectileRef . getPtr ( ) ;
if ( ! caster . isEmpty ( ) & & projectileState . mIdArrow ! = projectileState . mBowId )
{
MWWorld : : InventoryStore & inv = caster . getClass ( ) . getInventoryStore ( caster ) ;
MWWorld : : ContainerStoreIterator invIt = inv . getSlot ( MWWorld : : InventoryStore : : Slot_CarriedRight ) ;
if ( invIt ! = inv . end ( ) & & Misc : : StringUtils : : ciEqual ( invIt - > getCellRef ( ) . getRefId ( ) , projectileState . mBowId ) )
bow = * invIt ;
}
if ( caster . isEmpty ( ) )
caster = result . mHitObject ;
MWMechanics : : projectileHit ( caster , result . mHitObject , bow , projectileRef . getPtr ( ) , result . mHit ? result . mHitPos : newPos , projectileState . mAttackStrength ) ;
mPhysics - > reportCollision ( Misc : : Convert : : toBullet ( result . mHitPos ) , Misc : : Convert : : toBullet ( result . mHitNormal ) ) ;
if ( underwater )
mRendering - > emitWaterRipple ( newPos ) ;
cleanupProjectile ( projectileState ) ;
}
mPhysics - > updateProjectile ( projectileState . mProjectileId , newPos ) ;
}
}
@ -543,17 +467,19 @@ namespace MWWorld
continue ;
auto * projectile = mPhysics - > getProjectile ( projectileState . mProjectileId ) ;
if ( const auto hitWaterPos = projectile - > getWaterHitPosition ( ) )
mRendering - > emitWaterRipple ( Misc : : Convert : : toOsg ( * hitWaterPos ) ) ;
const auto pos = projectile - > getPosition ( ) ;
projectileState . mNode - > setPosition ( pos ) ;
if ( projectile - > isActive ( ) )
continue ;
const auto target = projectile - > getTarget ( ) ;
const auto pos = projectile - > getHitPos ( ) ;
MWWorld : : Ptr caster = projectileState . getCaster ( ) ;
auto caster = projectileState . getCaster ( ) ;
assert ( target ! = caster ) ;
if ( ! projectile - > isValidTarget ( target ) )
{
projectile - > activate ( ) ;
continue ;
}
if ( caster . isEmpty ( ) )
caster = target ;
@ -569,9 +495,8 @@ namespace MWWorld
bow = * invIt ;
}
projectileState . mHitPosition = pos ;
cleanupProjectile ( projectileState ) ;
MWMechanics : : projectileHit ( caster , target , bow , projectileRef . getPtr ( ) , pos , projectileState . mAttackStrength ) ;
cleanupProjectile ( projectileState ) ;
}
for ( auto & magicBoltState : mMagicBolts )
{
@ -579,20 +504,18 @@ namespace MWWorld
continue ;
auto * projectile = mPhysics - > getProjectile ( magicBoltState . mProjectileId ) ;
const auto pos = projectile - > getPosition ( ) ;
magicBoltState . mNode - > setPosition ( pos ) ;
for ( const auto & sound : magicBoltState . mSounds )
sound - > setPosition ( pos ) ;
if ( projectile - > isActive ( ) )
continue ;
const auto target = projectile - > getTarget ( ) ;
const auto pos = projectile - > getHitPos ( ) ;
MWWorld : : Ptr caster = magicBoltState . getCaster ( ) ;
const auto caster = magicBoltState . getCaster ( ) ;
assert ( target ! = caster ) ;
if ( ! projectile - > isValidTarget ( target ) )
{
projectile - > activate ( ) ;
continue ;
}
magicBoltState . mHitPosition = pos ;
cleanupMagicBolt ( magicBoltState ) ;
MWMechanics : : CastSpell cast ( caster , target ) ;
cast . mHitPosition = pos ;
@ -602,6 +525,7 @@ namespace MWWorld
cast . inflict ( target , caster , magicBoltState . mEffects , ESM : : RT_Target , false , true ) ;
MWBase : : Environment : : get ( ) . getWorld ( ) - > explodeSpell ( pos , magicBoltState . mEffects , caster , target , ESM : : RT_Target , magicBoltState . mSpellId , magicBoltState . mSourceName ) ;
cleanupMagicBolt ( magicBoltState ) ;
}
mProjectiles . erase ( std : : remove_if ( mProjectiles . begin ( ) , mProjectiles . end ( ) , [ ] ( const State & state ) { return state . mToDelete ; } ) ,
mProjectiles . end ( ) ) ;
@ -702,7 +626,7 @@ namespace MWWorld
int weaponType = ptr . get < ESM : : Weapon > ( ) - > mBase - > mData . mType ;
state . mThrown = MWMechanics : : getWeaponType ( weaponType ) - > mWeaponClass = = ESM : : WeaponType : : Thrown ;
state . mProjectileId = mPhysics - > addProjectile ( state . getCaster ( ) , osg : : Vec3f ( esm . mPosition ) );
state . mProjectileId = mPhysics - > addProjectile ( state . getCaster ( ) , osg : : Vec3f ( esm . mPosition ) , 1.f , true );
}
catch ( . . . )
{
@ -747,7 +671,6 @@ namespace MWWorld
MWWorld : : ManualRef ref ( MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) , state . mIdMagic . at ( 0 ) ) ;
MWWorld : : Ptr ptr = ref . getPtr ( ) ;
model = ptr . getClass ( ) . getModel ( ptr ) ;
state . mProjectileId = mPhysics - > addProjectile ( state . getCaster ( ) , osg : : Vec3f ( esm . mPosition ) ) ;
}
catch ( . . . )
{
@ -755,7 +678,8 @@ namespace MWWorld
}
osg : : Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor ( state . mEffects ) ;
createModel ( state , model , osg : : Vec3f ( esm . mPosition ) , osg : : Quat ( esm . mOrientation ) , true , true , lightDiffuseColor , texture ) ;
const auto radius = createModel ( state , model , osg : : Vec3f ( esm . mPosition ) , osg : : Quat ( esm . mOrientation ) , true , true , lightDiffuseColor , texture ) ;
state . mProjectileId = mPhysics - > addProjectile ( state . getCaster ( ) , osg : : Vec3f ( esm . mPosition ) , radius , false ) ;
MWBase : : SoundManager * sndMgr = MWBase : : Environment : : get ( ) . getSoundManager ( ) ;
for ( const std : : string & soundid : state . mSoundIds )