diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index aee5cbeacc..e791ab8910 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -57,7 +57,7 @@ add_openmw_dir (mwworld cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor - contentloader esmloader omwloader actiontrap cellreflist + contentloader esmloader omwloader actiontrap cellreflist projectilemanager ) add_openmw_dir (mwclass diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 03361408cf..66c09b6ff7 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -66,9 +66,13 @@ void OMW::Engine::executeLocalScripts() bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt) { - bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); - MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused); - MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame); + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); + MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused); + MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame); + } return true; } @@ -110,8 +114,12 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // update actors - MWBase::Environment::get().getMechanicsManager()->update(frametime, - paused); + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + MWBase::Environment::get().getMechanicsManager()->update(frametime, + paused); + } if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) @@ -122,16 +130,24 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) } // update world - MWBase::Environment::get().getWorld()->update(frametime, paused); + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + MWBase::Environment::get().getWorld()->update(frametime, paused); + } // update GUI - Ogre::RenderWindow* window = mOgre->getWindow(); - unsigned int tri, batch; - MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch); - MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch); + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + Ogre::RenderWindow* window = mOgre->getWindow(); + unsigned int tri, batch; + MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch); + MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch); - MWBase::Environment::get().getWindowManager()->onFrame(frametime); - MWBase::Environment::get().getWindowManager()->update(); + MWBase::Environment::get().getWindowManager()->onFrame(frametime); + MWBase::Environment::get().getWindowManager()->update(); + } } catch (const std::exception& e) { @@ -393,10 +409,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setJournal (new MWDialogue::Journal); mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage)); - mEnvironment.getWorld()->renderPlayer(); - mechanics->buildPlayer(); - window->updatePlayer(); - mOgre->getRoot()->addFrameListener (this); // scripts diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index f3973153a2..15739730ba 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -110,18 +110,25 @@ namespace MWBase ///< Play a sound, independently of 3D-position ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. - virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, - float volume, float pitch, PlayType type=Play_TypeSfx, - PlayMode mode=Play_Normal, float offset=0) = 0; - ///< Play a sound from an object + virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, + float volume, float pitch, PlayType type=Play_TypeSfx, + PlayMode mode=Play_Normal, float offset=0) = 0; + ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified. ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + virtual MWBase::SoundPtr playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId, + float volume, float pitch, PlayType type, PlayMode mode, float offset=0) = 0; + ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition. + virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0; ///< Stop the given object from playing the given sound, virtual void stopSound3D(const MWWorld::Ptr &reference) = 0; ///< Stop the given object from playing all sounds. + virtual void stopSound(MWBase::SoundPtr sound) = 0; + ///< Stop the given sound handle + virtual void stopSound(const MWWorld::CellStore *cell) = 0; ///< Stop all sounds for the given cell. diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 39cfc47eda..44ebed3e98 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -193,9 +193,9 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual bool getWorldMouseOver() = 0; - virtual void toggleFogOfWar() = 0; + virtual bool toggleFogOfWar() = 0; - virtual void toggleFullHelp() = 0; + virtual bool toggleFullHelp() = 0; ///< show extra info in item tooltips (owner, script) virtual bool getFullHelp() const = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 0459d5341b..81bec6fe86 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -126,7 +126,7 @@ namespace MWBase virtual void setWaterHeight(const float height) = 0; - virtual void toggleWater() = 0; + virtual bool toggleWater() = 0; virtual void adjustSky() = 0; @@ -255,7 +255,8 @@ namespace MWBase virtual void changeToExteriorCell (const ESM::Position& position) = 0; ///< Move to exterior cell. - virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0; + virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true) = 0; + ///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. @@ -354,15 +355,14 @@ namespace MWBase virtual void update (float duration, bool paused) = 0; - virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; + virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place - /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0; + virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0; ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object @@ -468,7 +468,8 @@ namespace MWBase virtual void castSpell (const MWWorld::Ptr& actor) = 0; - virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, + 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; virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0; @@ -514,7 +515,7 @@ namespace MWBase /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0; - virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, + virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; }; } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index be76bd0b41..f0c71bf536 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -60,6 +60,16 @@ namespace MWClass } } + void Container::respawn(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + if (ref->mBase->mFlags & ESM::Container::Respawn) + { + ptr.getRefData().setCustomData(NULL); + } + } + void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index f012d675c8..79a8012485 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -64,6 +64,8 @@ namespace MWClass static void registerSelf(); + virtual void respawn (const MWWorld::Ptr& ptr) const; + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index dc23b63f3b..4abc9ea713 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -822,6 +822,26 @@ namespace MWClass return ptr.get()->mBase->mData.mGold; } + void Creature::respawn(const MWWorld::Ptr &ptr) const + { + if (ptr.get()->mBase->mFlags & ESM::Creature::Respawn) + { + // Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell. + // This also means we cannot respawn dynamically placed references with no content file connection. + if (ptr.getCellRef().mRefNum.mContentFile != -1) + { + if (ptr.getRefData().getCount() == 0) + ptr.getRefData().setCount(1); + + // Reset to original position + ESM::Position& pos = ptr.getRefData().getPosition(); + pos = ptr.getCellRef().mPos; + + ptr.getRefData().setCustomData(NULL); + } + } + } + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting *Creature::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 04c010c830..d3ffb321ef 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -143,6 +143,8 @@ namespace MWClass ///< Write additional state from \a ptr into \a state. virtual int getBaseGold(const MWWorld::Ptr& ptr) const; + + virtual void respawn (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index be01b848a3..fea30735c6 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -14,6 +14,7 @@ namespace { // actorId of the creature we spawned int mSpawnActorId; + bool mSpawn; // Should a new creature be spawned? virtual MWWorld::CustomData *clone() const; }; @@ -31,6 +32,14 @@ namespace MWClass return ""; } + void CreatureLevList::respawn(const MWWorld::Ptr &ptr) const + { + ensureCustomData(ptr); + + CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + customData.mSpawn = true; + } + void CreatureLevList::registerSelf() { boost::shared_ptr instance (new CreatureLevList); @@ -43,9 +52,8 @@ namespace MWClass ensureCustomData(ptr); CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); - if (customData.mSpawnActorId != -1) - return; // TODO: handle respawning - + if (!customData.mSpawn) + return; MWWorld::LiveCellRef *ref = ptr.get(); @@ -54,11 +62,21 @@ namespace MWClass if (!id.empty()) { + // Delete the previous creature + if (customData.mSpawnActorId != -1) + { + MWWorld::Ptr creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId); + if (!creature.isEmpty()) + MWBase::Environment::get().getWorld()->deleteObject(creature); + customData.mSpawnActorId = -1; + } + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); MWWorld::ManualRef ref(store, id); ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos); customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); + customData.mSpawn = false; } } @@ -68,6 +86,7 @@ namespace MWClass { std::auto_ptr data (new CreatureLevListCustomData); data->mSpawnActorId = -1; + data->mSpawn = true; ptr.getRefData().setCustomData(data.release()); } @@ -81,6 +100,7 @@ namespace MWClass ensureCustomData(ptr); CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); customData.mSpawnActorId = state2.mSpawnActorId; + customData.mSpawn = state2.mSpawn; } void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -91,5 +111,6 @@ namespace MWClass ensureCustomData(ptr); CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); state2.mSpawnActorId = customData.mSpawnActorId; + state2.mSpawn = customData.mSpawn; } } diff --git a/apps/openmw/mwclass/creaturelevlist.hpp b/apps/openmw/mwclass/creaturelevlist.hpp index b67fb5523f..6c51a31891 100644 --- a/apps/openmw/mwclass/creaturelevlist.hpp +++ b/apps/openmw/mwclass/creaturelevlist.hpp @@ -27,6 +27,8 @@ namespace MWClass virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. + + virtual void respawn (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 845fffbc0d..5b9e26f269 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -621,7 +621,8 @@ namespace MWClass // Attacking peaceful NPCs is a crime // anything below 80 is considered peaceful (see Actors::updateActor) - if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) + if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() && + ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); getCreatureStats(ptr).setAttacked(true); @@ -1309,6 +1310,26 @@ namespace MWClass return Misc::StringUtils::ciEqual(ptr.get()->mBase->mClass, className); } + void Npc::respawn(const MWWorld::Ptr &ptr) const + { + if (ptr.get()->mBase->mFlags & ESM::NPC::Respawn) + { + // Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell. + // This also means we cannot respawn dynamically placed references with no content file connection. + if (ptr.getCellRef().mRefNum.mContentFile != -1) + { + if (ptr.getRefData().getCount() == 0) + ptr.getRefData().setCount(1); + + // Reset to original position + ESM::Position& pos = ptr.getRefData().getPosition(); + pos = ptr.getCellRef().mPos; + + ptr.getRefData().setCustomData(NULL); + } + } + } + const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 4b9c8988ec..03c6e234a1 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -178,6 +178,8 @@ namespace MWClass virtual bool canWalk (const MWWorld::Ptr &ptr) const { return true; } + + virtual void respawn (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 5856473388..02c4b93560 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -91,7 +91,8 @@ namespace MWGui mSourceModel->update(); finish(); - targetView->update(); + if (targetView) + targetView->update(); // We need to update the view since an other item could be auto-equipped. mSourceView->update(); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index ccd4489ba8..dc02f9976e 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -17,9 +17,47 @@ #include "itemmodel.hpp" #include "container.hpp" +#include "itemmodel.hpp" + namespace MWGui { + /** + * Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world. + */ + class WorldItemModel : public ItemModel + { + public: + WorldItemModel(float left, float top) : mLeft(left), mTop(top) {} + virtual ~WorldItemModel() {} + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + + MWWorld::Ptr dropped; + if (world->canPlaceObject(mLeft, mTop)) + dropped = world->placeObject(item.mBase, mLeft, mTop, count); + else + dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count); + if (setNewOwner) + dropped.getCellRef().mOwner = ""; + + return dropped; + } + + virtual void removeItem (const ItemStack& item, size_t count) { throw std::runtime_error("removeItem not implemented"); } + virtual ModelIndex getIndex (ItemStack item) { throw std::runtime_error("getIndex not implemented"); } + virtual void update() {} + virtual size_t getItemCount() { return 0; } + virtual ItemStack getItem (ModelIndex index) { throw std::runtime_error("getItem not implemented"); } + + private: + // Where to drop the item + float mLeft; + float mTop; + }; + + HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) : Layout("openmw_hud.layout") , mHealth(NULL) @@ -229,10 +267,6 @@ namespace MWGui if (mDragAndDrop->mIsOnDragAndDrop) { // drop item into the gameworld - MWWorld::Ptr object = mDragAndDrop->mItem.mBase; - - MWBase::World* world = MWBase::Environment::get().getWorld(); - MWBase::Environment::get().getWorld()->breakInvisibility( MWBase::Environment::get().getWorld()->getPlayerPtr()); @@ -241,20 +275,10 @@ namespace MWGui float mouseX = cursorPosition.left / float(viewSize.width); float mouseY = cursorPosition.top / float(viewSize.height); - if (world->canPlaceObject(mouseX, mouseY)) - world->placeObject(object, mouseX, mouseY, mDragAndDrop->mDraggedCount); - else - world->dropObjectOnGround(world->getPlayerPtr(), object, mDragAndDrop->mDraggedCount); + WorldItemModel drop (mouseX, mouseY); + mDragAndDrop->drop(&drop, NULL); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - // remove object from the container it was coming from - mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); - mDragAndDrop->finish(); - mDragAndDrop->mSourceModel->update(); } else { diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 2bd5e44cbd..29c065f3d5 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -89,10 +89,11 @@ namespace MWGui mChanged = true; } - void LocalMapBase::toggleFogOfWar() + bool LocalMapBase::toggleFogOfWar() { mFogOfWar = !mFogOfWar; applyFogOfWar(); + return mFogOfWar; } void LocalMapBase::applyFogOfWar() diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 5251fac230..d23b0c2285 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -35,7 +35,7 @@ namespace MWGui void setPlayerDir(const float x, const float y); void setPlayerPos(const float x, const float y); - void toggleFogOfWar(); + bool toggleFogOfWar(); struct MarkerPosition { diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index f941c699b4..b931966f07 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -547,9 +547,10 @@ namespace MWGui return " (" + boost::lexical_cast(value) + ")"; } - void ToolTips::toggleFullHelp() + bool ToolTips::toggleFullHelp() { mFullHelp = !mFullHelp; + return mFullHelp; } bool ToolTips::getFullHelp() const diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index be5c631913..4e73cc5551 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -45,7 +45,7 @@ namespace MWGui void setEnabled(bool enabled); - void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) + bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script) bool getFullHelp() const; void setDelay(float delay); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ed6910221b..3cf7379ff9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -814,10 +814,10 @@ namespace MWGui mHud->setMinimapVisible (visible); } - void WindowManager::toggleFogOfWar() + bool WindowManager::toggleFogOfWar() { mMap->toggleFogOfWar(); - mHud->toggleFogOfWar(); + return mHud->toggleFogOfWar(); } void WindowManager::setFocusObject(const MWWorld::Ptr& focus) @@ -830,9 +830,9 @@ namespace MWGui mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); } - void WindowManager::toggleFullHelp() + bool WindowManager::toggleFullHelp() { - mToolTips->toggleFullHelp(); + return mToolTips->toggleFullHelp(); } bool WindowManager::getFullHelp() const diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 7617a4463d..c98c32c524 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -188,8 +188,8 @@ namespace MWGui virtual void setDragDrop(bool dragDrop); virtual bool getWorldMouseOver(); - virtual void toggleFogOfWar(); - virtual void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) + virtual bool toggleFogOfWar(); + virtual bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script) virtual bool getFullHelp() const; virtual void setActiveMap(int x, int y, bool interior); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a0616258c7..a3211892e6 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -540,7 +540,7 @@ namespace MWMechanics MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); // Make the summoned creature follow its master and help in fights - AiFollow package(ptr); + AiFollow package(ptr.getRefData().getHandle()); summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); int creatureActorId = summonedCreatureStats.getActorId(); @@ -785,7 +785,7 @@ namespace MWMechanics // Update witness crime id npcStats.setCrimeId(-1); } - else if (!creatureStats.isHostile()) + else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue) { if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stack(AiPursue(player), ptr); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index f3ce7143a4..0430adf06a 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -8,8 +8,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" -#include "../mwmechanics/creaturestats.hpp" - #include "steering.hpp" #include "movement.hpp" @@ -21,8 +19,8 @@ namespace MWMechanics { - AiEscort::AiEscort(const MWWorld::Ptr& actor, int duration, float x, float y, float z) - : mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mX(x), mY(y), mZ(z), mDuration(duration) + AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) + : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { @@ -40,8 +38,8 @@ namespace MWMechanics } } - AiEscort::AiEscort(const MWWorld::Ptr& actor, const std::string &cellId,int duration, float x, float y, float z) - : mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) + AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) + : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { @@ -77,14 +75,7 @@ namespace MWMechanics return true; } - const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); - if (follower.isEmpty()) - { - // The follower disappeared - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; - } - + const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos; double differenceBetween[3]; @@ -98,7 +89,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { - if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete + if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete return true; mMaxDist = 470; } diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index b8dc9d6e0f..3771417fa2 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -15,11 +15,11 @@ namespace MWMechanics /// Implementation of AiEscort /** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time \implement AiEscort **/ - AiEscort(const MWWorld::Ptr& actor,int duration, float x, float y, float z); + AiEscort(const std::string &actorId,int duration, float x, float y, float z); /// Implementation of AiEscortCell /** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time \implement AiEscortCell **/ - AiEscort(const MWWorld::Ptr& actor,const std::string &cellId,int duration, float x, float y, float z); + AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); virtual AiEscort *clone() const; @@ -28,7 +28,7 @@ namespace MWMechanics virtual int getTypeId() const; private: - int mActorId; + std::string mActorId; std::string mCellId; float mX; float mY; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index d747781a38..f1296a9493 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -11,26 +11,23 @@ #include "steering.hpp" -MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor,float duration, float x, float y, float z) -: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(""), AiPackage() +MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) +: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), AiPackage() { - mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } -MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor,const std::string &cellId,float duration, float x, float y, float z) -: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(cellId), AiPackage() +MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) +: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), AiPackage() { - mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } -MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor) -: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mCellId(""), AiPackage() +MWMechanics::AiFollow::AiFollow(const std::string &actorId) +: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), AiPackage() { - mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); //The target to follow if(target == MWWorld::Ptr()) return true; //Target doesn't exist @@ -78,8 +75,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) std::string MWMechanics::AiFollow::getFollowedActor() { - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow - return target.getCellRef().mRefID; + return mActorId; } MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 91bdbdac2f..10a381410a 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -10,16 +10,16 @@ namespace MWMechanics { /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely - **/ - class AiFollow : public AiPackage - { + **/ + class AiFollow : public AiPackage + { public: /// Follow Actor for duration or until you arrive at a world position - AiFollow(const MWWorld::Ptr& actor,float duration, float X, float Y, float Z); + AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); /// Follow Actor for duration or until you arrive at a position in a cell - AiFollow(const MWWorld::Ptr& actor,const std::string &CellId,float duration, float X, float Y, float Z); + AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); /// Follow Actor indefinitively - AiFollow(const MWWorld::Ptr& actor); + AiFollow(const std::string &ActorId); virtual AiFollow *clone() const; @@ -38,8 +38,8 @@ namespace MWMechanics float mX; float mY; float mZ; - int mActorId; // The actor we should follow - std::string mCellId; - }; -} -#endif + std::string mActorId; + std::string mCellId; + }; +} +#endif diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 8afe8402ff..0b1da180d7 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -175,8 +175,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) else if (it->mType == ESM::AI_Escort) { ESM::AITarget data = it->mTarget; - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(data.mId.toString(), false); - package = new MWMechanics::AiEscort(target, data.mDuration, data.mX, data.mY, data.mZ); + package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Travel) { @@ -191,8 +190,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) else //if (it->mType == ESM::AI_Follow) { ESM::AITarget data = it->mTarget; - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(data.mId.toString(), false); - package = new MWMechanics::AiFollow(target, data.mDuration, data.mX, data.mY, data.mZ); + package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } mPackages.push_back(package); } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0843e3e547..91a81c74be 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -21,6 +21,41 @@ #include "magiceffects.hpp" #include "npcstats.hpp" +namespace +{ + + /// Get projectile properties (model, sound and speed) for a spell with the given effects + /// If \a model is empty, the spell has no ranged effects and should not spawn a projectile. + void getProjectileInfo (const ESM::EffectList& effects, std::string& model, std::string& sound, float& speed) + { + for (std::vector::const_iterator iter (effects.mList.begin()); + iter!=effects.mList.end(); ++iter) + { + if (iter->mRange != ESM::RT_Target) + continue; + + const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( + iter->mEffectID); + + model = magicEffect->mBolt; + if (model.empty()) + model = "VFX_DefaultBolt"; + + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + if (!magicEffect->mBoltSound.empty()) + sound = magicEffect->mBoltSound; + else + sound = schools[magicEffect->mData.mSchool] + " bolt"; + + speed = magicEffect->mData.mSpeed; + break; + } + } + +} + namespace MWMechanics { @@ -409,7 +444,7 @@ namespace MWMechanics } if (!exploded) - MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, mTarget, effects, caster, mId, mSourceName); + MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, mId, mSourceName); if (!reflectedEffects.mList.empty()) inflict(caster, target, reflectedEffects, range, true); @@ -603,7 +638,13 @@ namespace MWMechanics inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); } - MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, enchantment->mEffects, mCaster, mSourceName); + std::string projectileModel; + std::string sound; + float speed = 0; + getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed); + if (!projectileModel.empty()) + MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, + false, enchantment->mEffects, mCaster, mSourceName); return true; } @@ -682,7 +723,15 @@ namespace MWMechanics } } - MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, spell->mEffects, mCaster, mSourceName); + + std::string projectileModel; + std::string sound; + float speed = 0; + getProjectileInfo(spell->mEffects, projectileModel, sound, speed); + if (!projectileModel.empty()) + MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, + false, spell->mEffects, mCaster, mSourceName); + return true; } diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index 1e6119daac..968be0f9e9 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -11,18 +11,6 @@ namespace MWRender { -class EffectAnimationTime : public Ogre::ControllerValue -{ -private: - float mTime; -public: - EffectAnimationTime() : mTime(0) { } - void addTime(float time) { mTime += time; } - - virtual Ogre::Real getValue() const { return mTime; } - virtual void setValue(Ogre::Real value) {} -}; - EffectManager::EffectManager(Ogre::SceneManager *sceneMgr) : mSceneMgr(sceneMgr) { diff --git a/apps/openmw/mwrender/effectmanager.hpp b/apps/openmw/mwrender/effectmanager.hpp index bc9e68d269..eb68636555 100644 --- a/apps/openmw/mwrender/effectmanager.hpp +++ b/apps/openmw/mwrender/effectmanager.hpp @@ -5,6 +5,20 @@ namespace MWRender { + + class EffectAnimationTime : public Ogre::ControllerValue + { + private: + float mTime; + public: + EffectAnimationTime() : mTime(0) { } + void addTime(float time) { mTime += time; } + + virtual Ogre::Real getValue() const { return mTime; } + virtual void setValue(Ogre::Real value) {} + }; + + // Note: effects attached to another object should be managed by MWRender::Animation::addEffect. // This class manages "free" effects, i.e. attached to a dedicated scene node in the world. class EffectManager diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a3c53dc444..991ca690ee 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -234,9 +234,9 @@ void RenderingManager::removeWater () mWater->setActive(false); } -void RenderingManager::toggleWater() +bool RenderingManager::toggleWater() { - mWater->toggle(); + return mWater->toggle(); } void RenderingManager::cellAdded (MWWorld::CellStore *store) @@ -694,15 +694,8 @@ Shadows* RenderingManager::getShadows() return mShadows; } -void RenderingManager::switchToInterior() +void RenderingManager::notifyWorldSpaceChanged() { - // TODO: also do this when switching worldspace - mEffectManager->clear(); -} - -void RenderingManager::switchToExterior() -{ - // TODO: also do this when switching worldspace mEffectManager->clear(); } @@ -1061,6 +1054,7 @@ void RenderingManager::spawnEffect(const std::string &model, const std::string & void RenderingManager::clear() { mLocalMap->clear(); + notifyWorldSpaceChanged(); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index eb68272925..f539f9270f 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -127,7 +127,7 @@ public: void rotateObject (const MWWorld::Ptr& ptr); void setWaterHeight(const float height); - void toggleWater(); + bool toggleWater(); /// Updates object rendering after cell change /// \param old Object reference in previous cell @@ -163,8 +163,7 @@ public: Shadows* getShadows(); - void switchToInterior(); - void switchToExterior(); + void notifyWorldSpaceChanged(); void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 5368cbe687..2cbc4462c3 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -326,10 +326,11 @@ void Water::setHeight(const float height) sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(height))); } -void Water::toggle() +bool Water::toggle() { mToggled = !mToggled; updateVisible(); + return mToggled; } void diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 481a412977..6a7b05a3aa 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -150,7 +150,7 @@ namespace MWRender { void setActive(bool active); - void toggle(); + bool toggle(); void update(float dt, Ogre::Vector3 player); void frameStarted(float dt); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 8ed19925ec..e53b53e58f 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -91,7 +91,6 @@ namespace MWScript std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); @@ -108,7 +107,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -147,7 +145,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); @@ -300,7 +297,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -339,8 +335,8 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; itoggleFogOfWar(); + runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFogOfWar() ? "Fog of war -> On" + : "Fog of war -> Off"); } }; @@ -102,7 +103,8 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWBase::Environment::get().getWindowManager()->toggleFullHelp(); + runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFullHelp() ? "Full help -> On" + : "Full help -> Off"); } }; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 20013493f2..620a0d0c94 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -275,7 +275,8 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWBase::Environment::get().getWorld()->toggleWater(); + runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWater() ? "Water -> On" + : "Water -> Off"); } }; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 6854358f59..4a3093b10d 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -356,6 +356,44 @@ namespace MWSound return sound; } + MWBase::SoundPtr SoundManager::playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId, + float volume, float pitch, PlayType type, PlayMode mode, float offset) + { + MWBase::SoundPtr sound; + if(!mOutput->isInitialized()) + return sound; + try + { + // Look up the sound in the ESM data + float basevol = volumeFromType(type); + float min, max; + std::string file = lookup(soundId, volume, min, max); + + sound = mOutput->playSound3D(file, initialPos, volume, basevol, pitch, min, max, mode|type, offset); + mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); + } + catch(std::exception &e) + { + //std::cout <<"Sound Error: "<first == sound) + { + snditer->first->stop(); + mActiveSounds.erase(snditer++); + } + else + ++snditer; + } + } + void SoundManager::stopSound3D(const MWWorld::Ptr &ptr, const std::string& soundId) { SoundMap::iterator snditer = mActiveSounds.begin(); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 1454240b4a..ab9dcf7345 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -115,6 +115,13 @@ namespace MWSound virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0); + ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified. + ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + + virtual MWBase::SoundPtr playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId, + float volume, float pitch, PlayType type, PlayMode mode, float offset=0); + ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition. + ///< Play a sound from an object ///< @param offset value from [0,1], when to start playback. 0 is beginning, 1 is end. @@ -124,6 +131,9 @@ namespace MWSound virtual void stopSound3D(const MWWorld::Ptr &reference); ///< Stop the given object from playing all sounds. + virtual void stopSound(MWBase::SoundPtr sound); + ///< Stop the given sound handle + virtual void stopSound(const MWWorld::CellStore *cell); ///< Stop all sounds for the given cell. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f2cda5a018..304228010a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -319,6 +319,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_WTHR: case ESM::REC_DYNA: case ESM::REC_ACTC: + case ESM::REC_PROJ: + case ESM::REC_MPRJ: MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; @@ -361,7 +363,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); - MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false); + + // Do not trigger erroneous cellChanged events + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); } catch (const std::exception& e) { diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 5d2d8a5175..aa247f0b90 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -169,7 +169,7 @@ namespace MWWorld } CellStore::CellStore (const ESM::Cell *cell) - : mCell (cell), mState (State_Unloaded), mHasState (false) + : mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) { mWaterLevel = cell->mWater; } @@ -555,6 +555,7 @@ namespace MWWorld mWaterLevel = state.mWaterLevel; mWaterLevel = state.mWaterLevel; + mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn); } void CellStore::saveState (ESM::CellState& state) const @@ -566,6 +567,7 @@ namespace MWWorld state.mWaterLevel = mWaterLevel; state.mHasFogOfWar = (mFogState.get() ? 1 : 0); + state.mLastRespawn = mLastRespawn.toEsm(); } void CellStore::writeFog(ESM::ESMWriter &writer) const @@ -754,4 +756,36 @@ namespace MWWorld { return mFogState.get(); } + + void CellStore::respawn() + { + if (mState == State_Loaded) + { + static int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get().find("iMonthsToRespawn")->getInt(); + if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn) + { + mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); + for (CellRefList::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) + { + Ptr ptr (&*it, this); + ptr.getClass().respawn(ptr); + } + for (CellRefList::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) + { + Ptr ptr (&*it, this); + ptr.getClass().respawn(ptr); + } + for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) + { + Ptr ptr (&*it, this); + ptr.getClass().respawn(ptr); + } + for (CellRefList::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it) + { + Ptr ptr (&*it, this); + ptr.getClass().respawn(ptr); + } + } + } + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 91c536b430..ba6fad7baa 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -13,6 +13,8 @@ #include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld +#include "timestamp.hpp" + namespace ESM { struct CellState; @@ -48,6 +50,8 @@ namespace MWWorld std::vector mIds; float mWaterLevel; + MWWorld::TimeStamp mLastRespawn; + CellRefList mActivators; CellRefList mPotions; CellRefList mAppas; @@ -161,6 +165,9 @@ namespace MWWorld void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + void respawn (); + ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. + template CellRefList& get() { throw std::runtime_error ("Storage for this type not exist in cells"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 057bc906ef..f47b8ace32 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -341,6 +341,8 @@ namespace MWWorld virtual int getDoorState (const MWWorld::Ptr &ptr) const; /// This does not actually cause the door to move. Use World::activateDoor instead. virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; + + virtual void respawn (const MWWorld::Ptr& ptr) const {} }; } diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp new file mode 100644 index 0000000000..266b566f70 --- /dev/null +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -0,0 +1,400 @@ +#include "projectilemanager.hpp" + +#include + +#include + +#include + +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" + +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwmechanics/combat.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellcasting.hpp" + +#include "../mwrender/effectmanager.hpp" + +#include "../mwsound/sound.hpp" + +namespace MWWorld +{ + + ProjectileManager::ProjectileManager(Ogre::SceneManager* sceneMgr, OEngine::Physic::PhysicEngine &engine) + : mPhysEngine(engine) + , mSceneMgr(sceneMgr) + { + + } + + void ProjectileManager::createModel(State &state, const std::string &model) + { + state.mObject = NifOgre::Loader::createObjects(state.mNode, model); + for(size_t i = 0;i < state.mObject->mControllers.size();i++) + { + if(state.mObject->mControllers[i].getSource().isNull()) + state.mObject->mControllers[i].setSource(Ogre::SharedPtr (new MWRender::EffectAnimationTime())); + } + } + + void ProjectileManager::update(NifOgre::ObjectScenePtr object, float duration) + { + for(size_t i = 0; i < object->mControllers.size() ;i++) + { + MWRender::EffectAnimationTime* value = dynamic_cast(object->mControllers[i].getSource().get()); + if (value) + value->addTime(duration); + + object->mControllers[i].update(); + } + } + + 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) + { + // Spawn at 0.75 * ActorHeight + float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75; + + Ogre::Vector3 pos(actor.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); + + MagicBoltState state; + state.mSourceName = sourceName; + state.mId = model; + state.mSpellId = spellId; + state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); + state.mSpeed = speed; + state.mStack = stack; + state.mSoundId = sound; + + // Only interested in "on target" effects + for (std::vector::const_iterator iter (effects.mList.begin()); + iter!=effects.mList.end(); ++iter) + { + if (iter->mRange == ESM::RT_Target) + state.mEffects.mList.push_back(*iter); + } + + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), model); + MWWorld::Ptr ptr = ref.getPtr(); + + state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(pos, orient); + createModel(state, ptr.getClass().getModel(ptr)); + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + state.mSound = sndMgr->playManualSound3D(pos, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + + mMagicBolts.push_back(state); + } + + void ProjectileManager::launchProjectile(Ptr actor, Ptr projectile, const Ogre::Vector3 &pos, + const Ogre::Quaternion &orient, Ptr bow, float speed) + { + ProjectileState state; + state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); + state.mBowId = bow.getCellRef().mRefID; + state.mVelocity = orient.yAxis() * speed; + state.mId = projectile.getCellRef().mRefID; + + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().mRefID); + MWWorld::Ptr ptr = ref.getPtr(); + + state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(pos, orient); + createModel(state, ptr.getClass().getModel(ptr)); + + mProjectiles.push_back(state); + } + + void ProjectileManager::update(float dt) + { + moveProjectiles(dt); + moveMagicBolts(dt); + } + + void ProjectileManager::moveMagicBolts(float duration) + { + for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) + { + Ogre::Quaternion orient = it->mNode->getOrientation(); + static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() + .find("fTargetSpellMaxSpeed")->getFloat(); + float speed = fTargetSpellMaxSpeed * it->mSpeed; + + Ogre::Vector3 direction = orient.yAxis(); + direction.normalise(); + Ogre::Vector3 pos(it->mNode->getPosition()); + Ogre::Vector3 newPos = pos + direction * duration * speed; + + it->mSound->setPosition(newPos); + + it->mNode->setPosition(newPos); + + update(it->mObject, duration); + + // Check for impact + // TODO: use a proper btRigidBody / btGhostObject? + 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; + + for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) + { + MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); + + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); + if (caster.isEmpty()) + caster = obstacle; + + if (obstacle.isEmpty()) + { + // Terrain + } + else + { + MWMechanics::CastSpell cast(caster, obstacle); + cast.mHitPosition = pos; + cast.mId = it->mSpellId; + cast.mSourceName = it->mSourceName; + cast.mStack = it->mStack; + cast.inflict(obstacle, caster, it->mEffects, ESM::RT_Target, false, true); + } + + hit = true; + } + if (hit) + { + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); + MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mSpellId, it->mSourceName); + + MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); + + mSceneMgr->destroySceneNode(it->mNode); + + it = mMagicBolts.erase(it); + continue; + } + else + ++it; + } + } + + void ProjectileManager::moveProjectiles(float duration) + { + for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end();) + { + // gravity constant - must be way lower than the gravity affecting actors, since we're not + // simulating aerodynamics at all + it->mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration; + + Ogre::Vector3 pos(it->mNode->getPosition()); + Ogre::Vector3 newPos = pos + it->mVelocity * duration; + + Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->mVelocity); + it->mNode->setOrientation(orient); + it->mNode->setPosition(newPos); + + update(it->mObject, duration); + + // Check for impact + // TODO: use a proper btRigidBody / btGhostObject? + 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; + + for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) + { + MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); + + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); + + // Arrow intersects with player immediately after shooting :/ + if (obstacle == caster) + continue; + + if (obstacle.isEmpty()) + { + // Terrain + } + else if (obstacle.getClass().isActor()) + { + MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId); + + // Try to get a Ptr to the bow that was used. It might no longer exist. + MWWorld::Ptr bow = projectileRef.getPtr(); + if (!caster.isEmpty()) + { + 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().mRefID, it->mBowId)) + bow = *invIt; + } + + if (caster.isEmpty()) + caster = obstacle; + + MWMechanics::projectileHit(caster, obstacle, bow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first); + } + hit = true; + } + if (hit) + { + mSceneMgr->destroySceneNode(it->mNode); + + it = mProjectiles.erase(it); + continue; + } + else + ++it; + } + } + + void ProjectileManager::clear() + { + for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) + { + mSceneMgr->destroySceneNode(it->mNode); + } + mProjectiles.clear(); + for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) + { + MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); + mSceneMgr->destroySceneNode(it->mNode); + } + mMagicBolts.clear(); + } + + void ProjectileManager::write(ESM::ESMWriter &writer, Loading::Listener &progress) const + { + for (std::vector::const_iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) + { + writer.startRecord(ESM::REC_PROJ); + + ESM::ProjectileState state; + state.mId = it->mId; + state.mPosition = it->mNode->getPosition(); + state.mOrientation = it->mNode->getOrientation(); + state.mActorId = it->mActorId; + + state.mBowId = it->mBowId; + state.mVelocity = it->mVelocity; + + state.save(writer); + + writer.endRecord(ESM::REC_PROJ); + + progress.increaseProgress(); + } + + for (std::vector::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) + { + writer.startRecord(ESM::REC_MPRJ); + + ESM::MagicBoltState state; + state.mId = it->mId; + state.mPosition = it->mNode->getPosition(); + state.mOrientation = it->mNode->getOrientation(); + state.mActorId = it->mActorId; + + state.mSpellId = it->mSpellId; + state.mEffects = it->mEffects; + state.mSound = it->mSoundId; + state.mSourceName = it->mSourceName; + state.mSpeed = it->mSpeed; + state.mStack = it->mStack; + + state.save(writer); + + writer.endRecord(ESM::REC_MPRJ); + + progress.increaseProgress(); + } + } + + bool ProjectileManager::readRecord(ESM::ESMReader &reader, int32_t type) + { + if (type == ESM::REC_PROJ) + { + ESM::ProjectileState esm; + esm.load(reader); + + ProjectileState state; + state.mActorId = esm.mActorId; + state.mBowId = esm.mBowId; + state.mVelocity = esm.mVelocity; + state.mId = esm.mId; + + std::string model; + try + { + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId); + MWWorld::Ptr ptr = ref.getPtr(); + model = ptr.getClass().getModel(ptr); + } + catch(...) + { + return true; + } + + state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(esm.mPosition, esm.mOrientation); + createModel(state, model); + + mProjectiles.push_back(state); + return true; + } + else if (type == ESM::REC_MPRJ) + { + ESM::MagicBoltState esm; + esm.load(reader); + + MagicBoltState state; + state.mSourceName = esm.mSourceName; + state.mId = esm.mId; + state.mSpellId = esm.mSpellId; + state.mActorId = esm.mActorId; + state.mSpeed = esm.mSpeed; + state.mStack = esm.mStack; + state.mEffects = esm.mEffects; + + std::string model; + try + { + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId); + MWWorld::Ptr ptr = ref.getPtr(); + model = ptr.getClass().getModel(ptr); + } + catch(...) + { + return true; + } + + state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(esm.mPosition, esm.mOrientation); + createModel(state, model); + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + state.mSound = sndMgr->playManualSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f, + MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + + mMagicBolts.push_back(state); + return true; + } + + return false; + } + + int ProjectileManager::countSavedGameRecords() const + { + return mMagicBolts.size() + mProjectiles.size(); + } + +} diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp new file mode 100644 index 0000000000..da965a4cff --- /dev/null +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -0,0 +1,111 @@ +#ifndef OPENMW_MWWORLD_PROJECTILEMANAGER_H +#define OPENMW_MWWORLD_PROJECTILEMANAGER_H + +#include + +#include + +#include +#include + +#include "../mwbase/soundmanager.hpp" + +#include "ptr.hpp" + +namespace OEngine +{ +namespace Physic +{ + class PhysicEngine; +} +} + +namespace Loading +{ + class Listener; +} + +namespace Ogre +{ + class SceneManager; +} + +namespace MWWorld +{ + + class ProjectileManager + { + public: + ProjectileManager (Ogre::SceneManager* sceneMgr, + OEngine::Physic::PhysicEngine& engine); + + 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); + + void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); + + void update(float dt); + + /// Removes all current projectiles. Should be called when switching to a new worldspace. + void clear(); + + void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; + bool readRecord (ESM::ESMReader& reader, int32_t type); + int countSavedGameRecords() const; + + private: + OEngine::Physic::PhysicEngine& mPhysEngine; + Ogre::SceneManager* mSceneMgr; + + struct State + { + NifOgre::ObjectScenePtr mObject; + Ogre::SceneNode* mNode; + + // Actor who shot this projectile + int mActorId; + + // MW-id of this projectile + std::string mId; + }; + + struct MagicBoltState : public State + { + std::string mSpellId; + + // Name of item to display as effect source in magic menu (in case we casted an enchantment) + std::string mSourceName; + + ESM::EffectList mEffects; + + float mSpeed; + + bool mStack; + + MWBase::SoundPtr mSound; + std::string mSoundId; + }; + + struct ProjectileState : public State + { + // RefID of the bow or crossbow the actor was using when this projectile was fired (may be empty) + std::string mBowId; + + Ogre::Vector3 mVelocity; + }; + + std::vector mMagicBolts; + std::vector mProjectiles; + + void moveProjectiles(float dt); + void moveMagicBolts(float dt); + + void createModel (State& state, const std::string& model); + void update (NifOgre::ObjectScenePtr object, float duration); + }; + +} + +#endif diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 4b92a920bd..25ee0c2e83 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -165,6 +165,8 @@ namespace MWWorld } } + cell->respawn(); + // ... then references. This is important for adjustPosition to work correctly. /// \todo rescale depending on the state of a new GMST insertCell (*cell, true, loadingListener); @@ -344,8 +346,6 @@ namespace MWWorld // Sky system MWBase::Environment::get().getWorld()->adjustSky(); - mRendering.switchToExterior(); - mCellChanged = true; loadingListener->removeWallpaper(); @@ -439,7 +439,6 @@ namespace MWWorld mCurrentCell = cell; // adjust fog - mRendering.switchToInterior(); mRendering.configureFog(*mCurrentCell); // adjust player diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2b1cbe4c72..1f645fd860 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -43,6 +43,7 @@ #include "containerstore.hpp" #include "inventorystore.hpp" #include "actionteleport.hpp" +#include "projectilemanager.hpp" #include "contentloader.hpp" #include "esmloader.hpp" @@ -136,6 +137,8 @@ namespace MWWorld mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); + mProjectileManager.reset(new ProjectileManager(renderer.getScene(), *mPhysEngine)); + mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine,&mFallback); mPhysEngine->setSceneManager(renderer.getScene()); @@ -234,6 +237,8 @@ namespace MWWorld { mRendering->clear(); + mProjectileManager->clear(); + mLocalScripts.clear(); mPlayer->clear(); @@ -255,8 +260,6 @@ namespace MWWorld mCells.clear(); - mMagicBolts.clear(); - mProjectiles.clear(); mDoorStates.clear(); mGodMode = false; @@ -274,6 +277,7 @@ namespace MWWorld mCells.countSavedGameRecords() +mStore.countSavedGameRecords() +mGlobalVariables.countSavedGameRecords() + +mProjectileManager->countSavedGameRecords() +1 // player record +1 // weather record +1; // actorId counter @@ -297,6 +301,7 @@ namespace MWWorld mGlobalVariables.write (writer, progress); mPlayer->write (writer, progress); mWeatherManager->write (writer, progress); + mProjectileManager->write (writer, progress); } void World::readRecord (ESM::ESMReader& reader, int32_t type, @@ -312,7 +317,8 @@ namespace MWWorld !mGlobalVariables.readRecord (reader, type) && !mPlayer->readRecord (reader, type) && !mWeatherManager->readRecord (reader, type) && - !mCells.readRecord (reader, type, contentFileMap)) + !mCells.readRecord (reader, type, contentFileMap) && + !mProjectileManager->readRecord (reader, type)) { throw std::runtime_error ("unknown record in saved game"); } @@ -807,6 +813,14 @@ namespace MWWorld void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + if (mCurrentWorldSpace != cellName) + { + // changed worldspace + mProjectileManager->clear(); + mRendering->notifyWorldSpaceChanged(); + mCurrentWorldSpace = cellName; + } + removeContainerScripts(getPlayerPtr()); mWorldScene->changeToInteriorCell(cellName, position); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); @@ -814,13 +828,22 @@ namespace MWWorld void World::changeToExteriorCell (const ESM::Position& position) { + if (mCurrentWorldSpace != "sys::default") // FIXME + { + // changed worldspace + mProjectileManager->clear(); + mRendering->notifyWorldSpaceChanged(); + } removeContainerScripts(getPlayerPtr()); mWorldScene->changeToExteriorCell(position); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } - void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position) + void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange) { + if (!detectWorldSpaceChange) + mCurrentWorldSpace = cellId.mWorldspace; + if (cellId.mPaged) changeToExteriorCell (position); else @@ -949,8 +972,6 @@ namespace MWWorld MWWorld::Ptr newPtr = MWWorld::Class::get(ptr) .copyToCell(ptr, *newCell); newPtr.getRefData().setBaseNode(0); - - objectLeftActiveCell(ptr, newPtr); } else { @@ -1172,8 +1193,7 @@ namespace MWWorld { processDoors(duration); - moveMagicBolts(duration); - moveProjectiles(duration); + mProjectileManager->update(duration); const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); PtrVelocityList::const_iterator player(results.end()); @@ -1553,9 +1573,9 @@ namespace MWWorld mRendering->setWaterHeight(height); } - void World::toggleWater() + bool World::toggleWater() { - mRendering->toggleWater(); + return mRendering->toggleWater(); } void World::PCDropped (const Ptr& item) @@ -1567,12 +1587,12 @@ namespace MWWorld item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1); } - bool World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) + MWWorld::Ptr World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) { std::pair result = mPhysics->castRay(cursorX, cursorY); if (!result.first) - return false; + return MWWorld::Ptr(); CellStore* cell = getPlayerPtr().getCell(); @@ -1593,7 +1613,7 @@ namespace MWWorld // only the player place items in the world, so no need to check actor PCDropped(dropped); - return true; + return dropped; } bool World::canPlaceObject(float cursorX, float cursorY) @@ -1644,7 +1664,7 @@ namespace MWWorld return dropped; } - void World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) + MWWorld::Ptr World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) { MWWorld::CellStore* cell = actor.getCell(); @@ -1673,6 +1693,7 @@ namespace MWWorld if(actor == mPlayer->getPlayer()) // Only call if dropped by player PCDropped(dropped); + return dropped; } void World::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -2253,306 +2274,14 @@ namespace MWWorld 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.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); - state.mBow = bow; - state.mVelocity = orient.yAxis() * 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; + mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed); } - void World::launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, + 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) { - std::string projectileModel; - std::string sound; - float speed = 0; - for (std::vector::const_iterator iter (effects.mList.begin()); - iter!=effects.mList.end(); ++iter) - { - if (iter->mRange != ESM::RT_Target) - continue; - - const ESM::MagicEffect *magicEffect = getStore().get().find ( - iter->mEffectID); - - projectileModel = magicEffect->mBolt; - if (projectileModel.empty()) - projectileModel = "VFX_DefaultBolt"; - - static const std::string schools[] = { - "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" - }; - - if (!magicEffect->mBoltSound.empty()) - sound = magicEffect->mBoltSound; - else - sound = schools[magicEffect->mData.mSchool] + " bolt"; - - speed = magicEffect->mData.mSpeed; - break; - } - if (projectileModel.empty()) - return; - - // Spawn at 0.75 * ActorHeight - float height = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75; - - MWWorld::ManualRef ref(getStore(), projectileModel); - ESM::Position pos; - 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; - - // 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.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); - state.mSpeed = speed; - state.mStack = stack; - - // Only interested in "on target" effects - for (std::vector::const_iterator iter (effects.mList.begin()); - iter!=effects.mList.end(); ++iter) - { - if (iter->mRange == ESM::RT_Target) - state.mEffects.mList.push_back(*iter); - } - - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); - - mMagicBolts[ptr] = state; - } - - void World::moveProjectiles(float duration) - { - std::map moved; - for (std::map::iterator it = mProjectiles.begin(); it != mProjectiles.end();) - { - if (!mWorldScene->isCellActive(*it->first.getCell())) - { - deleteObject(it->first); - mProjectiles.erase(it++); - continue; - } - - MWWorld::Ptr ptr = it->first; - - // gravity constant - must be way lower than the gravity affecting actors, since we're not - // simulating aerodynamics at all - it->second.mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration; - - Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); - Ogre::Vector3 newPos = pos + it->second.mVelocity * duration; - - Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->second.mVelocity); - Ogre::Matrix3 mat; - orient.ToRotationMatrix(mat); - Ogre::Radian xr,yr,zr; - mat.ToEulerAnglesXYZ(xr, yr, zr); - rotateObject(ptr, -xr.valueDegrees(), -yr.valueDegrees(), -zr.valueDegrees()); - - // 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 = searchPtrViaActorId(it->second.mActorId); - - // 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()) - { - MWMechanics::projectileHit(caster, obstacle, it->second.mBow, ptr, pos + (newPos - pos) * cIt->first); - } - hit = true; - } - if (hit) - { - deleteObject(ptr); - 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); - - Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation(); - static float fTargetSpellMaxSpeed = getStore().get().find("fTargetSpellMaxSpeed")->getFloat(); - float speed = fTargetSpellMaxSpeed * 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 explode = false; - for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !explode; ++cIt) - { - MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second); - if (obstacle == ptr) - continue; - - MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId); - if (caster.isEmpty()) - caster = obstacle; - - if (obstacle.isEmpty()) - { - // Terrain - } - else - { - MWMechanics::CastSpell cast(caster, obstacle); - cast.mHitPosition = pos; - cast.mId = it->second.mId; - cast.mSourceName = it->second.mSourceName; - cast.mStack = it->second.mStack; - cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false, true); - } - - explode = true; - } - - if (explode) - { - MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId); - explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName); - - deleteObject(ptr); - mMagicBolts.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; - 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) - { - MWWorld::Ptr newPtr = searchPtrViaHandle(it->first); - if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted - continue; - 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 (mMagicBolts.find(object) != mMagicBolts.end()) - deleteObject(movedPtr); - if (mProjectiles.find(object) != mProjectiles.end()) - deleteObject(movedPtr); + mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName); } const std::vector& World::getContentFiles() const @@ -2934,7 +2663,7 @@ namespace MWWorld mRendering->spawnEffect(model, texture, worldPosition); } - void World::explodeSpell(const Vector3 &origin, const MWWorld::Ptr& object, const ESM::EffectList &effects, const Ptr &caster, + void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, const std::string& id, const std::string& sourceName) { std::map > toApply; @@ -2959,12 +2688,13 @@ namespace MWWorld static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(!effect->mAreaSound.empty()) - sndMgr->playSound3D(object, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); - else - sndMgr->playSound3D(object, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); - + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mAreaSound.empty()) + sndMgr->playManualSound3D(origin, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + else + sndMgr->playManualSound3D(origin, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + } // Get the actors in range of the effect std::vector objects; MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7b85c3ed87..285d5fef6d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -3,6 +3,8 @@ #include "../mwrender/debugging.hpp" +#include + #include "ptr.hpp" #include "scene.hpp" #include "esmstore.hpp" @@ -51,6 +53,7 @@ namespace MWWorld { class WeatherManager; class Player; + class ProjectileManager; /// \brief The game world and its visual representation @@ -72,8 +75,12 @@ namespace MWWorld Cells mCells; + std::string mCurrentWorldSpace; + OEngine::Physic::PhysicEngine* mPhysEngine; + boost::shared_ptr mProjectileManager; + bool mGodMode; std::vector mContentFiles; @@ -90,37 +97,6 @@ namespace MWWorld std::map mDoorStates; ///< only holds doors that are currently moving. 1 = opening, 2 = closing - struct MagicBoltState - { - // Id of spell or enchantment to apply when it hits - std::string mId; - - // Actor who casted this projectile - int mActorId; - - // Name of item to display as effect source in magic menu (in case we casted an enchantment) - std::string mSourceName; - - ESM::EffectList mEffects; - - float mSpeed; - - bool mStack; - }; - - struct ProjectileState - { - // Actor who shot this projectile - int mActorId; - - MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from - - Ogre::Vector3 mVelocity; - }; - - std::map mMagicBolts; - std::map mProjectiles; - std::string mStartCell; void updateWeather(float duration); @@ -148,9 +124,6 @@ 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); ///< Run physics simulation and modify \a world accordingly. @@ -171,9 +144,6 @@ namespace MWWorld bool mLevitationEnabled; bool mGoToJail; - /// Called when \a object is moved to an inactive cell - void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr); - float feetToGameUnits(float feet); public: @@ -213,7 +183,7 @@ namespace MWWorld virtual void setWaterHeight(const float height); - virtual void toggleWater(); + virtual bool toggleWater(); virtual void adjustSky(); @@ -343,7 +313,8 @@ namespace MWWorld virtual void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. - virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position); + virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true); + ///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes virtual const ESM::Cell *getExterior (const std::string& cellName) const; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. @@ -439,15 +410,14 @@ namespace MWWorld virtual void update (float duration, bool paused); - virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); + virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place - /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount); + virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount); ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object @@ -573,7 +543,8 @@ namespace MWWorld */ virtual void castSpell (const MWWorld::Ptr& actor); - virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, + 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); virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); @@ -613,7 +584,7 @@ namespace MWWorld /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition); - virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, + virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); }; } diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 6820103402..9fe7890acc 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -12,7 +12,6 @@ if (GTEST_FOUND AND GMOCK_FOUND) file(GLOB UNITTEST_SRC_FILES components/misc/test_*.cpp - components/file_finder/test_*.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp b/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp deleted file mode 100644 index 2d151988b9..0000000000 --- a/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include "components/file_finder/file_finder.hpp" - -struct FileFinderTest : public ::testing::Test -{ - protected: - FileFinderTest() - : mTestDir("./filefinder_test_dir/") - , mTestFile("test.txt") - , mTestFileUppercase("TEST.TXT") - , mTestFileNotFound("foobarbaz.txt") - { - } - - virtual void SetUp() - { - boost::filesystem::create_directory(boost::filesystem::path(mTestDir)); - - std::ofstream ofs(std::string(mTestDir + mTestFile).c_str(), std::ofstream::out); - ofs << std::endl; - ofs.close(); - } - - virtual void TearDown() - { - boost::filesystem::remove_all(boost::filesystem::path(mTestDir)); - } - - std::string mTestDir; - std::string mTestFile; - std::string mTestFileUppercase; - std::string mTestFileNotFound; -}; - -TEST_F(FileFinderTest, FileFinder_has_file) -{ - FileFinder::FileFinder fileFinder(mTestDir); - ASSERT_TRUE(fileFinder.has(mTestFile)); - ASSERT_TRUE(fileFinder.has(mTestFileUppercase)); - ASSERT_TRUE(fileFinder.lookup(mTestFile) == std::string(mTestDir + mTestFile)); - ASSERT_TRUE(fileFinder.lookup(mTestFileUppercase) == std::string(mTestDir + mTestFile)); -} - -TEST_F(FileFinderTest, FileFinder_does_not_have_file) -{ - FileFinder::FileFinder fileFinder(mTestDir); - ASSERT_FALSE(fileFinder.has(mTestFileNotFound)); - ASSERT_TRUE(fileFinder.lookup(mTestFileNotFound).empty()); -} - -TEST_F(FileFinderTest, FileFinderStrict_has_file) -{ - FileFinder::FileFinderStrict fileFinder(mTestDir); - ASSERT_TRUE(fileFinder.has(mTestFile)); - ASSERT_FALSE(fileFinder.has(mTestFileUppercase)); - ASSERT_TRUE(fileFinder.lookup(mTestFile) == std::string(mTestDir + mTestFile)); - ASSERT_FALSE(fileFinder.lookup(mTestFileUppercase) == std::string(mTestDir + mTestFile)); -} - -TEST_F(FileFinderTest, FileFinderStrict_does_not_have_file) -{ - FileFinder::FileFinderStrict fileFinder(mTestDir); - ASSERT_FALSE(fileFinder.has(mTestFileNotFound)); - ASSERT_TRUE(fileFinder.lookup(mTestFileNotFound).empty()); -} diff --git a/apps/openmw_test_suite/components/file_finder/test_search.cpp b/apps/openmw_test_suite/components/file_finder/test_search.cpp deleted file mode 100644 index 63745b6256..0000000000 --- a/apps/openmw_test_suite/components/file_finder/test_search.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include - -#include "components/file_finder/search.hpp" - -struct SearchTest : public ::testing::Test -{ - protected: - SearchTest() - : mTestDir("./search_test_dir/") - { - } - - virtual void SetUp() - { - boost::filesystem::create_directory(boost::filesystem::path(mTestDir)); - - std::ofstream ofs(std::string(mTestDir + "test2.txt").c_str(), std::ofstream::out); - ofs << std::endl; - ofs.close(); - } - - virtual void TearDown() - { - boost::filesystem::remove_all(boost::filesystem::path(mTestDir)); - } - - std::string mTestDir; -}; - -TEST_F(SearchTest, file_not_found) -{ - struct Result : public FileFinder::ReturnPath - { - Result(const boost::filesystem::path& expectedPath) - : mExpectedPath(expectedPath) - { - } - - void add(const boost::filesystem::path& p) - { - ASSERT_FALSE(p == mExpectedPath); - } - - private: - boost::filesystem::path mExpectedPath; - - } r(boost::filesystem::path(mTestDir + "test.txt")); - - FileFinder::find(mTestDir, r, false); -} - -TEST_F(SearchTest, file_found) -{ - struct Result : public FileFinder::ReturnPath - { - Result(const boost::filesystem::path& expectedPath) - : mExpectedPath(expectedPath) - { - } - - void add(const boost::filesystem::path& p) - { - ASSERT_TRUE(p == mExpectedPath); - } - - private: - boost::filesystem::path mExpectedPath; - - } r(boost::filesystem::path(mTestDir + "test2.txt")); - - FileFinder::find(mTestDir, r, false); -} diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d7bdaf36cd..e0166138e4 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -34,10 +34,6 @@ add_component_dir (to_utf8 to_utf8 ) -add_component_dir (file_finder - file_finder filename_less search - ) - add_component_dir (esm attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst @@ -45,7 +41,7 @@ add_component_dir (esm loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate - npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate + npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate ) add_component_dir (misc diff --git a/components/esm/cellstate.cpp b/components/esm/cellstate.cpp index 541a359c6a..4df04d0e52 100644 --- a/components/esm/cellstate.cpp +++ b/components/esm/cellstate.cpp @@ -11,6 +11,10 @@ void ESM::CellState::load (ESMReader &esm) mHasFogOfWar = false; esm.getHNOT (mHasFogOfWar, "HFOW"); + + mLastRespawn.mDay = 0; + mLastRespawn.mHour = 0; + esm.getHNOT (mLastRespawn, "RESP"); } void ESM::CellState::save (ESMWriter &esm) const @@ -18,5 +22,7 @@ void ESM::CellState::save (ESMWriter &esm) const if (!mId.mPaged) esm.writeHNT ("WLVL", mWaterLevel); - esm.writeHNT("HFOW", mHasFogOfWar); + esm.writeHNT ("HFOW", mHasFogOfWar); + + esm.writeHNT ("RESP", mLastRespawn); } diff --git a/components/esm/cellstate.hpp b/components/esm/cellstate.hpp index 88918a3abf..55c1e51550 100644 --- a/components/esm/cellstate.hpp +++ b/components/esm/cellstate.hpp @@ -3,6 +3,8 @@ #include "cellid.hpp" +#include "defs.hpp" + namespace ESM { class ESMReader; @@ -19,6 +21,8 @@ namespace ESM int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp) + ESM::TimeStamp mLastRespawn; + void load (ESMReader &esm); void save (ESMWriter &esm) const; }; diff --git a/components/esm/creaturelevliststate.cpp b/components/esm/creaturelevliststate.cpp index 164dae96e9..21cc73b89c 100644 --- a/components/esm/creaturelevliststate.cpp +++ b/components/esm/creaturelevliststate.cpp @@ -12,6 +12,9 @@ namespace ESM mSpawnActorId = -1; esm.getHNOT (mSpawnActorId, "SPAW"); + + mSpawn = false; + esm.getHNOT (mSpawn, "RESP"); } void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const @@ -20,6 +23,9 @@ namespace ESM if (mSpawnActorId != -1) esm.writeHNT ("SPAW", mSpawnActorId); + + if (mSpawn) + esm.writeHNT ("RESP", mSpawn); } } diff --git a/components/esm/creaturelevliststate.hpp b/components/esm/creaturelevliststate.hpp index 99b5a7fa2a..da64cd7c2b 100644 --- a/components/esm/creaturelevliststate.hpp +++ b/components/esm/creaturelevliststate.hpp @@ -10,6 +10,7 @@ namespace ESM struct CreatureLevListState : public ObjectState { int mSpawnActorId; + bool mSpawn; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 6c388de08c..bdeb95291f 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -109,6 +109,8 @@ enum RecNameInts REC_DYNA = FourCC<'D','Y','N','A'>::value, REC_ASPL = FourCC<'A','S','P','L'>::value, REC_ACTC = FourCC<'A','C','T','C'>::value, + REC_MPRJ = FourCC<'M','P','R','J'>::value, + REC_PROJ = FourCC<'P','R','O','J'>::value, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/projectilestate.cpp b/components/esm/projectilestate.cpp new file mode 100644 index 0000000000..85c00025b6 --- /dev/null +++ b/components/esm/projectilestate.cpp @@ -0,0 +1,65 @@ +#include "projectilestate.hpp" + +#include "esmwriter.hpp" +#include "esmreader.hpp" + +namespace ESM +{ + + void BaseProjectileState::save(ESMWriter &esm) const + { + esm.writeHNString ("ID__", mId); + esm.writeHNT ("VEC3", mPosition); + esm.writeHNT ("QUAT", mOrientation); + esm.writeHNT ("ACTO", mActorId); + } + + void BaseProjectileState::load(ESMReader &esm) + { + mId = esm.getHNString("ID__"); + esm.getHNT (mPosition, "VEC3"); + esm.getHNT (mOrientation, "QUAT"); + esm.getHNT (mActorId, "ACTO"); + } + + void MagicBoltState::save(ESMWriter &esm) const + { + BaseProjectileState::save(esm); + + esm.writeHNString ("SPEL", mSpellId); + esm.writeHNString ("SRCN", mSourceName); + mEffects.save(esm); + esm.writeHNT ("SPED", mSpeed); + esm.writeHNT ("STCK", mStack); + esm.writeHNString ("SOUN", mSound); + } + + void MagicBoltState::load(ESMReader &esm) + { + BaseProjectileState::load(esm); + + mSpellId = esm.getHNString("SPEL"); + mSourceName = esm.getHNString ("SRCN"); + mEffects.load(esm); + esm.getHNT (mSpeed, "SPED"); + esm.getHNT (mStack, "STCK"); + mSound = esm.getHNString ("SOUN"); + } + + void ProjectileState::save(ESMWriter &esm) const + { + BaseProjectileState::save(esm); + + esm.writeHNString ("BOW_", mBowId); + esm.writeHNT ("VEL_", mVelocity); + } + + void ProjectileState::load(ESMReader &esm) + { + BaseProjectileState::load(esm); + + mBowId = esm.getHNString ("BOW_"); + esm.getHNT (mVelocity, "VEL_"); + } + +} diff --git a/components/esm/projectilestate.hpp b/components/esm/projectilestate.hpp new file mode 100644 index 0000000000..6e36efb5b4 --- /dev/null +++ b/components/esm/projectilestate.hpp @@ -0,0 +1,90 @@ +#ifndef OPENMW_ESM_PROJECTILESTATE_H +#define OPENMW_ESM_PROJECTILESTATE_H + +#include + +#include +#include + +#include "effectlist.hpp" + +namespace ESM +{ + + // format 0, savegames only + + struct Quaternion + { + float mValues[4]; + + Quaternion() {} + Quaternion (Ogre::Quaternion q) + { + mValues[0] = q.w; + mValues[1] = q.x; + mValues[2] = q.y; + mValues[3] = q.z; + } + + operator Ogre::Quaternion () const + { + return Ogre::Quaternion(mValues[0], mValues[1], mValues[2], mValues[3]); + } + }; + + struct Vector3 + { + float mValues[3]; + + Vector3() {} + Vector3 (Ogre::Vector3 v) + { + mValues[0] = v.x; + mValues[1] = v.y; + mValues[2] = v.z; + } + + operator Ogre::Vector3 () const + { + return Ogre::Vector3(&mValues[0]); + } + }; + + struct BaseProjectileState + { + std::string mId; + + Vector3 mPosition; + Quaternion mOrientation; + + int mActorId; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + + struct MagicBoltState : public BaseProjectileState + { + std::string mSpellId; + std::string mSourceName; + ESM::EffectList mEffects; + float mSpeed; + bool mStack; + std::string mSound; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + + struct ProjectileState : public BaseProjectileState + { + std::string mBowId; + Vector3 mVelocity; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + +} + +#endif diff --git a/components/file_finder/file_finder.hpp b/components/file_finder/file_finder.hpp deleted file mode 100644 index 8a15af73af..0000000000 --- a/components/file_finder/file_finder.hpp +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef FILE_FINDER_MAIN_H -#define FILE_FINDER_MAIN_H - -#include - -#include "search.hpp" -#include "filename_less.hpp" -#include - -namespace FileFinder -{ - -template -class FileFinderT -{ - typedef std::map TableContainer; - TableContainer table; - - struct Inserter : ReturnPath - { - FileFinderT *owner; - int cut; - - void add(const boost::filesystem::path &pth) - { - std::string file = pth.string(); - std::string key = file.substr(cut); - owner->table[key] = file; - } - }; - - Inserter inserter; - -public: - FileFinderT(const boost::filesystem::path &path, bool recurse=true) - { - inserter.owner = this; - - // Remember the original path length, so we can cut it away from - // the relative paths used as keys - const std::string& pstring = path.string(); - inserter.cut = pstring.size(); - - // If the path does not end in a slash, then boost will add one - // later, which means one more character we have to remove. - char last = *pstring.rbegin(); - if(last != '\\' && last != '/') - inserter.cut++; - - // Fill the map - find(path, inserter, recurse); - } - - bool has(const std::string& file) const - { - return table.find(file) != table.end(); - } - - // Find the full path from a relative path. - const std::string &lookup(const std::string& file) const - { - static std::string empty; - typename TableContainer::const_iterator it = table.find(file); - return (it != table.end()) ? it->second : empty; - } -}; - -template -< - class LESS -> -struct TreeFileFinder -{ - typedef TreeFileFinder finder_t; - - TreeFileFinder(const Files::PathContainer& paths, bool recurse = true) - { - struct : ReturnPath - { - finder_t *owner; - int cut; - - void add(const boost::filesystem::path &pth) - { - std::string file = pth.string(); - std::string key = file.substr(cut); - owner->mTable[key] = file; - } - } inserter; - - inserter.owner = this; - - for (Files::PathContainer::const_iterator it = paths.begin(); it != paths.end(); ++it) - { - - // Remember the original path length, so we can cut it away from - // the relative paths used as keys - const std::string& pstring = it->string(); - inserter.cut = pstring.size(); - - // If the path does not end in a slash, then boost will add one - // later, which means one more character we have to remove. - char last = *pstring.rbegin(); - if (last != '\\' && last != '/') - { - inserter.cut++; - } - - // Fill the map - find(*it, inserter, recurse); - } - } - - bool has(const std::string& file) const - { - return mTable.find(file) != mTable.end(); - } - - const std::string& lookup(const std::string& file) const - { - static std::string empty; - typename TableContainer::const_iterator it = mTable.find(file); - return (it != mTable.end()) ? it->second : empty; - } - - private: - typedef std::map TableContainer; - TableContainer mTable; - -// Inserter inserter; -}; - - -// The default is to use path_less for equality checks -typedef FileFinderT FileFinder; -typedef FileFinderT FileFinderStrict; - -typedef TreeFileFinder LessTreeFileFinder; -typedef TreeFileFinder StrictTreeFileFinder; - -} /* namespace FileFinder */ -#endif /* FILE_FINDER_MAIN_H */ diff --git a/components/file_finder/filename_less.hpp b/components/file_finder/filename_less.hpp deleted file mode 100644 index bc3186ce98..0000000000 --- a/components/file_finder/filename_less.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef FILE_FINDER_LESS_H -#define FILE_FINDER_LESS_H - -#include -#include - -namespace FileFinder{ - -// Used for maps of file paths. Compares file paths, but ignores case -// AND treats \ and / as the same character. -struct path_less -{ - int compareChar(char a, char b) const - { - if(a>b) return 1; - else if(a= 'a' && a <= 'z') a += 'A'-'a'; - else if(a == '\\') a = '/'; - if(b >= 'a' && b <= 'z') b += 'A'-'a'; - else if(b == '\\') b = '/'; - return compareChar(a,b); - } - - int compareString(const char *a, const char *b) const - { - while(*a && *b) - { - int i = comparePathChar(*a,*b); - if(i != 0) return i; - a++; b++; - } - // At this point, one or both of the chars is a null terminator. - // Normal char comparison will get the correct final result here. - return compareChar(*a,*b); - } - - bool operator() (const std::string& a, const std::string& b) const - { - return compareString(a.c_str(), b.c_str()) < 0; - } -}; - -struct path_slash -{ - int compareChar(char a, char b) const - { - if(a>b) return 1; - else if(a - -void FileFinder::find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse) -{ - if (boost::filesystem::exists(dir_path)) - { - if (!recurse) - { - boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end - for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) - { - if (!boost::filesystem::is_directory( *itr )) - { - ret.add(*itr); - } - } - } - else - { - boost::filesystem::recursive_directory_iterator end_itr; // default construction yields past-the-end - for (boost::filesystem::recursive_directory_iterator itr(dir_path); itr != end_itr; ++itr) - { - if (!boost::filesystem::is_directory(*itr)) - { - ret.add(*itr); - } - } - } - } - else - { - std::cout << "Path " << dir_path << " not found" << std::endl; - } -} diff --git a/components/file_finder/search.hpp b/components/file_finder/search.hpp deleted file mode 100644 index 4e16fb64a0..0000000000 --- a/components/file_finder/search.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef FILE_FINDER_SEARCH_H -#define FILE_FINDER_SEARCH_H - -#include -#include - -namespace FileFinder -{ - struct ReturnPath - { - virtual void add(const boost::filesystem::path &pth) = 0; - }; - - /** Search the given path and return all file paths through 'ret'. If - recurse==true, all files in subdirectories are returned as well. - */ - void find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse=true); -} - -#endif diff --git a/files/mygui/openmw_edit_effect.layout b/files/mygui/openmw_edit_effect.layout index fa1e58b9d2..5dc53e5057 100644 --- a/files/mygui/openmw_edit_effect.layout +++ b/files/mygui/openmw_edit_effect.layout @@ -33,6 +33,7 @@ + @@ -41,6 +42,7 @@ + @@ -58,6 +60,7 @@ + @@ -74,6 +77,7 @@ +