From 12de0afb03a382332f2da3d31b073fe9d49bb7ff Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 7 Mar 2014 06:11:00 +0100 Subject: [PATCH] Feature #50: Spawn projectiles Fix a bug in copyObjectToCell. Make actor rotations more consistent. --- apps/openmw/mwbase/world.hpp | 8 +- apps/openmw/mwclass/creaturelevlist.cpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +- apps/openmw/mwmechanics/actors.cpp | 4 +- apps/openmw/mwmechanics/character.cpp | 12 +- apps/openmw/mwmechanics/spellcasting.cpp | 4 +- apps/openmw/mwrender/camera.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 48 +++- apps/openmw/mwrender/renderingmanager.cpp | 9 +- .../mwscript/transformationextensions.cpp | 34 +-- apps/openmw/mwstate/statemanagerimp.cpp | 4 +- apps/openmw/mwworld/actionteleport.cpp | 4 +- apps/openmw/mwworld/physicssystem.cpp | 16 +- apps/openmw/mwworld/worldimp.cpp | 245 +++++++++++++----- apps/openmw/mwworld/worldimp.hpp | 24 +- libs/openengine/bullet/physic.cpp | 2 + 16 files changed, 295 insertions(+), 127 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fd3978943..5c2912a8f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -274,7 +274,7 @@ namespace MWBase virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; virtual void - moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore &newCell, float x, float y, float z) = 0; + moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z) = 0; virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; @@ -282,7 +282,7 @@ namespace MWBase virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; - virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0; + virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0; ///< place an object in a "safe" location (ie not in the void, etc). virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) @@ -464,8 +464,10 @@ namespace MWBase virtual void castSpell (const MWWorld::Ptr& actor) = 0; - virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, + virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) = 0; + virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0; virtual const std::vector& getContentFiles() const = 0; diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index ea586e5b6..caef521af 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -57,7 +57,7 @@ namespace MWClass MWWorld::ManualRef ref(store, id); ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; // TODO: hold on to this for respawn purposes later - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), *ptr.getCell() , ptr.getCellRef().mPos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos); } ptr.getRefData().setCustomData(data.release()); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b5d83427b..30cefe2df 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -578,13 +578,13 @@ namespace MWInput float rot[3]; rot[0] = -y; rot[1] = 0.0f; - rot[2] = x; + rot[2] = -x; // Only actually turn player when we're not in vanity mode if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) { mPlayer->yaw(x); - mPlayer->pitch(-y); + mPlayer->pitch(y); } if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1fb22ce63..92be89f2f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -210,7 +210,7 @@ namespace MWMechanics && LOS ) { - creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer())); + creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr())); creatureStats.setHostile(true); } } @@ -541,7 +541,7 @@ namespace MWMechanics // TODO: Add AI to follow player and fight for him // TODO: VFX_SummonStart, VFX_SummonEnd creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle())); } } else diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d19f32507..04d909b49 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -313,6 +313,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mAnimation->disable(mCurrentMovement); mCurrentMovement = movement; + mMovementAnimVelocity = 0.0f; if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; @@ -320,7 +321,10 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + { + mMovementAnimVelocity = vel; speedmult = mMovementSpeed / vel; + } else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight) speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed else if (mMovementSpeed > 0.0f) @@ -330,10 +334,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f); mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); - - mMovementAnimVelocity = vel; } - else mMovementAnimVelocity = 0.0f; } } @@ -1194,7 +1195,10 @@ void CharacterController::update(float duration) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); } - else if(rot.z != 0.0f && !inwater && !sneak) + // Don't play turning animations during attack. It would break positioning of the arrow bone when releasing a shot. + // Actually, in vanilla the turning animation is not even played when merely having equipped the weapon, + // but I don't think we need to go as far as that. + else if(rot.z != 0.0f && !inwater && !sneak && mUpperBodyState < UpperCharState_StartToMinAttack) { if(rot.z > 0.0f) movestate = CharState_TurnRight; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 9745cdebe..7f5a7fac5 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -587,7 +587,7 @@ namespace MWMechanics inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); } - MWBase::Environment::get().getWorld()->launchProjectile(mId, false, enchantment->mEffects, mCaster, mSourceName); + MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, enchantment->mEffects, mCaster, mSourceName); return true; } @@ -666,7 +666,7 @@ namespace MWMechanics } } - MWBase::Environment::get().getWorld()->launchProjectile(mId, false, spell->mEffects, mCaster, mSourceName); + MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, spell->mEffects, mCaster, mSourceName); return true; } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9ae9c5878..294264951 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -70,7 +70,7 @@ namespace MWRender if (!mVanity.enabled && !mPreviewMode) { mCamera->getParentNode()->setOrientation(xr); } else { - Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z); mCamera->getParentNode()->setOrientation(zr * xr); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 60760b7fb..6169b6ddb 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -532,7 +532,7 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) { float pitch = mPtr.getRefData().getPosition().rot[0]; Ogre::Node *node = baseinst->getBone("Bip01 Neck"); - node->pitch(Ogre::Radian(pitch), Ogre::Node::TS_WORLD); + node->pitch(Ogre::Radian(-pitch), Ogre::Node::TS_WORLD); // This has to be done before this function ends; // updateSkeletonInstance, below, touches the hands. @@ -543,9 +543,9 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) // In third person mode we may still need pitch for ranged weapon targeting float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor; Ogre::Node *node = baseinst->getBone("Bip01 Spine2"); - node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD); + node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); node = baseinst->getBone("Bip01 Spine1"); - node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD); + node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); } mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. @@ -770,12 +770,35 @@ void NpcAnimation::attachArrow() void NpcAnimation::releaseArrow() { - // Thrown weapons get detached now MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (weapon != inv.end() && weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + if (weapon == inv.end()) + return; + + // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise. + Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) { + // Thrown weapons get detached now + NifOgre::ObjectScenePtr objects = mObjectParts[ESM::PRT_Weapon]; + + Ogre::Vector3 launchPos(0,0,0); + if (objects->mSkelBase) + { + launchPos = objects->mSkelBase->getParentNode()->_getDerivedPosition(); + } + else if (objects->mEntities.size()) + { + objects->mEntities[0]->getParentNode()->needUpdate(true); + launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition(); + } + + MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *weapon, launchPos, orient, *weapon, 400); + showWeapons(false); + inv.remove(*weapon, 1, mPtr); } else @@ -784,6 +807,21 @@ void NpcAnimation::releaseArrow() MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo == inv.end()) return; + + Ogre::Vector3 launchPos(0,0,0); + if (mAmmunition->mSkelBase) + { + launchPos = mAmmunition->mSkelBase->getParentNode()->_getDerivedPosition(); + } + else if (mAmmunition->mEntities.size()) + { + mAmmunition->mEntities[0]->getParentNode()->needUpdate(true); + launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition(); + } + + /// \todo speed + MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *ammo, launchPos, orient, *weapon, 400); + inv.remove(*ammo, 1, mPtr); mAmmunition.setNull(); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7c70b17f0..a0d06f068 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -280,13 +280,12 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) if(ptr.getRefData().getHandle() == mCamera->getHandle() && !mCamera->isVanityOrPreviewModeEnabled()) - mCamera->rotateCamera(rot, false); + mCamera->rotateCamera(-rot, false); - Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(rot.z), Ogre::Vector3::NEGATIVE_UNIT_Z); if(!MWWorld::Class::get(ptr).isActor()) - newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) * - Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo; - + newo = Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::NEGATIVE_UNIT_X) * + Ogre::Quaternion(Ogre::Radian(rot.y), Ogre::Vector3::NEGATIVE_UNIT_Y) * newo; ptr.getRefData().getBaseNode()->setOrientation(newo); } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 43a0fedce..9a9175f19 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -82,27 +82,21 @@ namespace MWScript Interpreter::Type_Float angle = runtime[0].mFloat; runtime.pop(); - float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); - float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); - - float *objRot = ptr.getRefData().getPosition().rot; - - float lx = Ogre::Radian(objRot[0]).valueDegrees(); - float ly = Ogre::Radian(objRot[1]).valueDegrees(); - float lz = Ogre::Radian(objRot[2]).valueDegrees(); + float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); if (axis == "x") { - MWBase::Environment::get().getWorld()->localRotateObject(ptr,angle-lx,ay,az); + MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); } else if (axis == "y") { - MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,angle-ly,az); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); } else if (axis == "z") { - MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,angle-lz); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -152,15 +146,15 @@ namespace MWScript if (axis=="x") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees()); } else if (axis=="y") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees()); } else if (axis=="z") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -313,7 +307,7 @@ namespace MWScript } if(store) { - MWBase::Environment::get().getWorld()->moveObject(ptr,*store,x,y,z); + MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity @@ -361,7 +355,7 @@ namespace MWScript int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); MWBase::Environment::get().getWorld()->moveObject(ptr, - *MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z); + MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity @@ -421,7 +415,7 @@ namespace MWScript pos.rot[2] = zRot; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().mPos = pos; - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,pos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); } else { @@ -462,7 +456,7 @@ namespace MWScript pos.rot[2] = zRot; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().mPos = pos; - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,pos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); } else { @@ -530,7 +524,7 @@ namespace MWScript MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, count); ref.getPtr().getCellRef().mPos = ipos; - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); } }; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 3807a67b6..8b01bacdf 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -148,7 +148,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot MWBase::World& world = *MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world.getPlayer().getPlayer(); + MWWorld::Ptr player = world.getPlayerPtr(); profile.mContentFiles = world.getContentFiles(); @@ -300,7 +300,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWindowManager()->updatePlayer(); MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index b4c572ba9..150f0bed2 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -41,11 +41,11 @@ namespace MWWorld int cellX; int cellY; world->positionToIndex(mPosition.pos[0],mPosition.pos[1],cellX,cellY); - world->moveObject(actor,*world->getExterior(cellX,cellY), + world->moveObject(actor,world->getExterior(cellX,cellY), mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); } else - world->moveObject(actor,*world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); + world->moveObject(actor,world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); } } } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 8ef5797ca..1657cf267 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -120,11 +120,9 @@ namespace MWWorld OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); if(!physicActor || !physicActor->getCollisionMode()) { - // FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why? - return position + (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)* - Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * - movement * time; + return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) + * movement * time; } btCollisionObject *colobj = physicActor->getCollisionBody(); @@ -140,14 +138,12 @@ namespace MWWorld Ogre::Vector3 velocity; if(position.z < waterlevel || isFlying) { - velocity = (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)* - Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * - movement; + velocity = (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)* + Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement; } else { - velocity = Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z) * movement; + velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement; if(!physicActor->getOnGround()) { // If falling, add part of the incoming velocity with the current inertia diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f154f3b24..0ef50c582 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -228,6 +228,7 @@ namespace MWWorld mCells.clear(); + mMagicBolts.clear(); mProjectiles.clear(); mDoorStates.clear(); @@ -821,7 +822,7 @@ namespace MWWorld const ESM::Position &posdata = ptr.getRefData().getPosition(); Ogre::Vector3 pos(posdata.pos); Ogre::Quaternion rot = Ogre::Quaternion(Ogre::Radian(posdata.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * - Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::UNIT_X); + Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); MWRender::Animation *anim = mRendering->getAnimation(ptr); if(anim != NULL) @@ -858,7 +859,7 @@ namespace MWWorld } } - void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z) + void World::moveObject(const Ptr &ptr, CellStore* newCell, float x, float y, float z) { ESM::Position &pos = ptr.getRefData().getPosition(); @@ -872,27 +873,27 @@ namespace MWWorld bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell); - if (*currCell != newCell) + if (currCell != newCell) { removeContainerScripts(ptr); if (isPlayer) { - if (!newCell.isExterior()) - changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.getCell()->mName), pos); + if (!newCell->isExterior()) + changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos); else { - int cellX = newCell.getCell()->getGridX(); - int cellY = newCell.getCell()->getGridY(); + int cellX = newCell->getCell()->getGridX(); + int cellY = newCell->getCell()->getGridY(); mWorldScene->changeCell(cellX, cellY, pos, false); } - addContainerScripts (getPlayerPtr(), &newCell); + addContainerScripts (getPlayerPtr(), newCell); } else { if (!mWorldScene->isCellActive(*currCell)) - copyObjectToCell(ptr, newCell, pos); - else if (!mWorldScene->isCellActive(newCell)) + ptr.getClass().copyToCell(ptr, *newCell, pos); + else if (!mWorldScene->isCellActive(*newCell)) { mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); @@ -900,7 +901,7 @@ namespace MWWorld haveToMove = false; MWWorld::Ptr newPtr = MWWorld::Class::get(ptr) - .copyToCell(ptr, newCell); + .copyToCell(ptr, *newCell); newPtr.getRefData().setBaseNode(0); objectLeftActiveCell(ptr, newPtr); @@ -908,7 +909,7 @@ namespace MWWorld else { MWWorld::Ptr copy = - MWWorld::Class::get(ptr).copyToCell(ptr, newCell, pos); + MWWorld::Class::get(ptr).copyToCell(ptr, *newCell, pos); mRendering->updateObjectCell(ptr, copy); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, copy); @@ -923,7 +924,7 @@ namespace MWWorld mLocalScripts.remove(ptr); removeContainerScripts (ptr); mLocalScripts.add(script, copy); - addContainerScripts (copy, &newCell); + addContainerScripts (copy, newCell); } } ptr.getRefData().setCount(0); @@ -947,7 +948,7 @@ namespace MWWorld cell = getExterior(cellX, cellY); } - moveObject(ptr, *cell, x, y, z); + moveObject(ptr, cell, x, y, z); return cell != ptr.getCell(); } @@ -1041,15 +1042,13 @@ namespace MWWorld while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad) ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad; - float *worldRot = ptr.getRefData().getPosition().rot; + Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)* + Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)* + Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)); - Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(-worldRot[0]), Ogre::Vector3::UNIT_X)* - Ogre::Quaternion(Ogre::Radian(-worldRot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian(-worldRot[2]), Ogre::Vector3::UNIT_Z)); - - Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-x).valueRadians()), Ogre::Vector3::UNIT_X)* - Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-y).valueRadians()), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-z).valueRadians()), Ogre::Vector3::UNIT_Z)); + Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Degree(x), Ogre::Vector3::NEGATIVE_UNIT_X)* + Ogre::Quaternion(Ogre::Degree(y), Ogre::Vector3::NEGATIVE_UNIT_Y)* + Ogre::Quaternion(Ogre::Degree(z), Ogre::Vector3::NEGATIVE_UNIT_Z)); ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); mPhysics->rotateObject(ptr); @@ -1080,7 +1079,7 @@ namespace MWWorld pos.z = traced.z; } - moveObject(ptr, *ptr.getCell(), pos.x, pos.y, pos.z); + moveObject(ptr, ptr.getCell(), pos.x, pos.y, pos.z); } void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) @@ -1091,9 +1090,9 @@ namespace MWWorld adjust); } - MWWorld::Ptr World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) + MWWorld::Ptr World::safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos) { - return copyObjectToCell(ptr,Cell,pos,false); + return copyObjectToCell(ptr,cell,pos,false); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const @@ -1127,6 +1126,7 @@ namespace MWWorld { processDoors(duration); + moveMagicBolts(duration); moveProjectiles(duration); const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); @@ -1506,15 +1506,7 @@ namespace MWWorld if (!result.first) return false; - CellStore* cell; - if (isCellExterior()) - { - int cellX, cellY; - positionToIndex(result.second[0], result.second[1], cellX, cellY); - cell = mCells.getExterior(cellX, cellY); - } - else - cell = getPlayerPtr().getCell(); + CellStore* cell = getPlayerPtr().getCell(); ESM::Position pos = getPlayerPtr().getRefData().getPosition(); pos.pos[0] = result.second[0]; @@ -1527,7 +1519,7 @@ namespace MWWorld // copy the object and set its count int origCount = object.getRefData().getCount(); object.getRefData().setCount(amount); - Ptr dropped = copyObjectToCell(object, *cell, pos, true); + Ptr dropped = copyObjectToCell(object, cell, pos, true); object.getRefData().setCount(origCount); // only the player place items in the world, so no need to check actor @@ -1548,7 +1540,7 @@ namespace MWWorld } - Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, ESM::Position pos, bool adjustPos) + Ptr World::copyObjectToCell(const Ptr &object, CellStore* cell, ESM::Position pos, bool adjustPos) { if (object.getClass().isActor() || adjustPos) { @@ -1560,17 +1552,17 @@ namespace MWWorld } } - if (cell.isExterior()) + if (cell->isExterior()) { int cellX, cellY; positionToIndex(pos.pos[0], pos.pos[1], cellX, cellY); - cell = *mCells.getExterior(cellX, cellY); + cell = mCells.getExterior(cellX, cellY); } MWWorld::Ptr dropped = - MWWorld::Class::get(object).copyToCell(object, cell, pos); + MWWorld::Class::get(object).copyToCell(object, *cell, pos); - if (mWorldScene->isCellActive(cell)) { + if (mWorldScene->isCellActive(*cell)) { if (dropped.getRefData().isEnabled()) { mWorldScene->addObjectToScene(dropped); } @@ -1578,7 +1570,7 @@ namespace MWWorld if (!script.empty()) { mLocalScripts.add(script, dropped); } - addContainerScripts(dropped, &cell); + addContainerScripts(dropped, cell); } return dropped; @@ -1608,7 +1600,7 @@ namespace MWWorld // copy the object and set its count int origCount = object.getRefData().getCount(); object.getRefData().setCount(amount); - Ptr dropped = copyObjectToCell(object, *cell, pos); + Ptr dropped = copyObjectToCell(object, cell, pos); object.getRefData().setCount(origCount); if(actor == mPlayer->getPlayer()) // Only call if dropped by player @@ -2143,7 +2135,37 @@ namespace MWWorld } } - void World::launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, + void World::launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) + { + ProjectileState state; + state.mActorHandle = actor.getRefData().getHandle(); + state.mBow = bow; + state.mSpeed = speed; + + MWWorld::ManualRef ref(getStore(), projectile.getCellRef().mRefID); + + ESM::Position pos; + pos.pos[0] = worldPos.x; + pos.pos[1] = worldPos.y; + pos.pos[2] = worldPos.z; + + // Do NOT copy actor rotation! actors use a different rotation order, and this will not produce the same facing direction. + Ogre::Matrix3 mat; + orient.ToRotationMatrix(mat); + Ogre::Radian xr,yr,zr; + mat.ToEulerAnglesXYZ(xr, yr, zr); + pos.rot[0] = -xr.valueRadians(); + pos.rot[1] = -yr.valueRadians(); + pos.rot[2] = -zr.valueRadians(); + + MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), actor.getCell(), pos, false); + ptr.getRefData().setCount(1); + + mProjectiles[ptr] = state; + } + + void World::launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) { std::string projectileModel; @@ -2185,13 +2207,23 @@ namespace MWWorld pos.pos[0] = actor.getRefData().getPosition().pos[0]; pos.pos[1] = actor.getRefData().getPosition().pos[1]; pos.pos[2] = actor.getRefData().getPosition().pos[2] + height; - pos.rot[0] = actor.getRefData().getPosition().rot[0]; - pos.rot[1] = actor.getRefData().getPosition().rot[1]; - pos.rot[2] = actor.getRefData().getPosition().rot[2]; - ref.getPtr().getCellRef().mPos = pos; - MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), *actor.getCell(), pos); - ProjectileState state; + // Do NOT copy rotation directly! actors use a different rotation order, and this will not produce the same facing direction. + 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::Matrix3 mat; + orient.ToRotationMatrix(mat); + Ogre::Radian xr,yr,zr; + mat.ToEulerAnglesXYZ(xr, yr, zr); + pos.rot[0] = -xr.valueRadians(); + pos.rot[1] = -yr.valueRadians(); + pos.rot[2] = -zr.valueRadians(); + + ref.getPtr().getCellRef().mPos = pos; + CellStore* cell = actor.getCell(); + MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), cell, pos); + + MagicBoltState state; state.mSourceName = sourceName; state.mId = id; state.mActorHandle = actor.getRefData().getHandle(); @@ -2209,7 +2241,7 @@ namespace MWWorld MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); - mProjectiles[ptr] = state; + mMagicBolts[ptr] = state; } void World::moveProjectiles(float duration) @@ -2226,13 +2258,100 @@ namespace MWWorld MWWorld::Ptr ptr = it->first; + Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation(); + + float speed = it->second.mSpeed; + + Ogre::Vector3 direction = orient.yAxis(); + direction.normalise(); + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + Ogre::Vector3 newPos = pos + direction * duration * speed; + + // Check for impact + btVector3 from(pos.x, pos.y, pos.z); + btVector3 to(newPos.x, newPos.y, newPos.z); + std::vector > collisions = mPhysEngine->rayTest2(from, to); + bool hit=false; + + // HACK: query against the shape as well, since the ray does not take the volume into account + // really, this should be a convex cast, but the whole physics system needs a rewrite + std::vector col2 = mPhysEngine->getCollisions(ptr.getRefData().getHandle()); + for (std::vector::iterator ci = col2.begin(); ci != col2.end(); ++ci) + collisions.push_back(std::make_pair(0.f,*ci)); + + for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) + { + MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second); + if (obstacle == ptr) + continue; + + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + + // Arrow intersects with player immediately after shooting :/ + if (obstacle == caster) + continue; + + if (caster.isEmpty()) + caster = obstacle; + + if (obstacle.isEmpty()) + { + // Terrain + } + else if (obstacle.getClass().isActor()) + { + // Fargoth + obstacle.getClass().getCreatureStats(obstacle).setHealth(0); + } + hit = true; + } + if (hit) + { + mProjectiles.erase(it++); + continue; + } + + std::string handle = ptr.getRefData().getHandle(); + + moveObject(ptr, newPos.x, newPos.y, newPos.z); + + // HACK: Re-fetch Ptrs if necessary, since the cell might have changed + if (!ptr.getRefData().getCount()) + { + moved[handle] = it->second; + mProjectiles.erase(it++); + } + else + ++it; + } + + // HACK: Re-fetch Ptrs if necessary, since the cell might have changed + for (std::map::iterator it = moved.begin(); it != moved.end(); ++it) + { + MWWorld::Ptr newPtr = searchPtrViaHandle(it->first); + if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted + continue; + mProjectiles[getPtrViaHandle(it->first)] = it->second; + } + } + + void World::moveMagicBolts(float duration) + { + std::map moved; + for (std::map::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) + { + if (!mWorldScene->isCellActive(*it->first.getCell())) + { + deleteObject(it->first); + mMagicBolts.erase(it++); + continue; + } + + MWWorld::Ptr ptr = it->first; + Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - // TODO: Why -rot.z, but not -rot.x? (note: same issue in MovementSolver::move) - Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); - orient = orient * Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); - - + Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation(); static float fTargetSpellMaxSpeed = getStore().get().find("fTargetSpellMaxSpeed")->getFloat(); float speed = fTargetSpellMaxSpeed * it->second.mSpeed; @@ -2279,7 +2398,7 @@ namespace MWWorld explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName); deleteObject(ptr); - mProjectiles.erase(it++); + mMagicBolts.erase(it++); continue; } @@ -2291,29 +2410,29 @@ namespace MWWorld if (!ptr.getRefData().getCount()) { moved[handle] = it->second; - mProjectiles.erase(it++); + mMagicBolts.erase(it++); } else ++it; } // HACK: Re-fetch Ptrs if necessary, since the cell might have changed - for (std::map::iterator it = moved.begin(); it != moved.end(); ++it) + for (std::map::iterator it = moved.begin(); it != moved.end(); ++it) { MWWorld::Ptr newPtr = searchPtrViaHandle(it->first); if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted continue; - mProjectiles[getPtrViaHandle(it->first)] = it->second; + mMagicBolts[getPtrViaHandle(it->first)] = it->second; } } void World::objectLeftActiveCell(Ptr object, Ptr movedPtr) { // For now, projectiles moved to an inactive cell are just deleted, because there's no reliable way to hold on to the meta information - if (mProjectiles.find(object) != mProjectiles.end()) - { + if (mMagicBolts.find(object) != mMagicBolts.end()) + deleteObject(movedPtr); + if (mProjectiles.find(object) != mProjectiles.end()) deleteObject(movedPtr); - } } const std::vector& World::getContentFiles() const @@ -2650,7 +2769,7 @@ namespace MWWorld MWWorld::ManualRef ref(getStore(), selectedCreature, 1); ref.getPtr().getCellRef().mPos = ipos; - safePlaceObject(ref.getPtr(),*cell,ipos); + safePlaceObject(ref.getPtr(), cell, ipos); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 036cafe2d..40fe4c96a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -90,7 +90,7 @@ namespace MWWorld std::map mDoorStates; ///< only holds doors that are currently moving. 0 means closing, 1 opening - struct ProjectileState + struct MagicBoltState { // Id of spell or enchantment to apply when it hits std::string mId; @@ -108,6 +108,17 @@ namespace MWWorld bool mStack; }; + struct ProjectileState + { + // Actor who shot this projectile + std::string mActorHandle; + + MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from + + float mSpeed; + }; + + std::map mMagicBolts; std::map mProjectiles; void updateWeather(float duration); int getDaysPerMonth (int month) const; @@ -117,7 +128,7 @@ namespace MWWorld bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed - Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, ESM::Position pos, bool adjustPos=true); + Ptr copyObjectToCell(const Ptr &ptr, CellStore* cell, ESM::Position pos, bool adjustPos=true); void updateWindowManager (); void performUpdateSceneQueries (); @@ -134,6 +145,7 @@ namespace MWWorld void processDoors(float duration); ///< Run physics simulation and modify \a world accordingly. + void moveMagicBolts(float duration); void moveProjectiles(float duration); void doPhysics(float duration); @@ -341,7 +353,7 @@ namespace MWWorld virtual void deleteObject (const Ptr& ptr); virtual void moveObject (const Ptr& ptr, float x, float y, float z); - virtual void moveObject (const Ptr& ptr, CellStore &newCell, float x, float y, float z); + virtual void moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z); virtual void scaleObject (const Ptr& ptr, float scale); @@ -351,7 +363,7 @@ namespace MWWorld virtual void localRotateObject (const Ptr& ptr, float x, float y, float z); - virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos); + virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos); ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) @@ -552,8 +564,10 @@ namespace MWWorld */ virtual void castSpell (const MWWorld::Ptr& actor); - virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, + virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName); + virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); virtual const std::vector& getContentFiles() const; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index f124abb99..4484d9862 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -597,6 +597,8 @@ namespace Physic std::vector PhysicEngine::getCollisions(const std::string& name) { RigidBody* body = getRigidBody(name); + if (!body) // fall back to raycasting body if there is no collision body + body = getRigidBody(name, true); ContactTestResultCallback callback; dynamicsWorld->contactTest(body, callback); return callback.mResult;