mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 20:26:48 +00:00 
			
		
		
		
	Savegame: store door movement state (Closes #747)
This commit is contained in:
		
							parent
							
								
									37b9d2fb0c
								
							
						
					
					
						commit
						a76e391ad0
					
				
					 12 changed files with 199 additions and 41 deletions
				
			
		|  | @ -393,14 +393,10 @@ namespace MWBase | |||
|             virtual void setupPlayer() = 0; | ||||
|             virtual void renderPlayer() = 0; | ||||
| 
 | ||||
|             /// if activated, should this door be opened or closed?
 | ||||
|             virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0; | ||||
| 
 | ||||
|             /// activate (open or close) an non-teleport door
 | ||||
|             /// open or close a non-teleport door (depending on current state)
 | ||||
|             virtual void activateDoor(const MWWorld::Ptr& door) = 0; | ||||
| 
 | ||||
|             /// Is door currently opening/closing?
 | ||||
|             virtual bool getIsMovingDoor(const MWWorld::Ptr& door) = 0; | ||||
|             /// open or close a non-teleport door as specified
 | ||||
|             virtual void activateDoor(const MWWorld::Ptr& door, bool open) = 0; | ||||
| 
 | ||||
|             virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object
 | ||||
|             virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| #include "door.hpp" | ||||
| 
 | ||||
| #include <components/esm/loaddoor.hpp> | ||||
| #include <components/esm/doorstate.hpp> | ||||
| 
 | ||||
| #include "../mwbase/environment.hpp" | ||||
| #include "../mwbase/world.hpp" | ||||
|  | @ -17,12 +18,28 @@ | |||
| #include "../mwworld/physicssystem.hpp" | ||||
| #include "../mwworld/inventorystore.hpp" | ||||
| #include "../mwworld/actiontrap.hpp" | ||||
| #include "../mwworld/customdata.hpp" | ||||
| 
 | ||||
| #include "../mwgui/tooltips.hpp" | ||||
| 
 | ||||
| #include "../mwrender/objects.hpp" | ||||
| #include "../mwrender/renderinginterface.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|     struct DoorCustomData : public MWWorld::CustomData | ||||
|     { | ||||
|         int mDoorState; // 0 = nothing, 1 = opening, 2 = closing
 | ||||
| 
 | ||||
|         virtual MWWorld::CustomData *clone() const; | ||||
|     }; | ||||
| 
 | ||||
|     MWWorld::CustomData *DoorCustomData::clone() const | ||||
|     { | ||||
|         return new DoorCustomData (*this); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| namespace MWClass | ||||
| { | ||||
|     void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const | ||||
|  | @ -38,6 +55,14 @@ namespace MWClass | |||
|         const std::string model = getModel(ptr); | ||||
|         if(!model.empty()) | ||||
|             physics.addObject(ptr); | ||||
| 
 | ||||
|         // Resume the door's opening/closing animation if it wasn't finished
 | ||||
|         ensureCustomData(ptr); | ||||
|         const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData()); | ||||
|         if (customData.mDoorState > 0) | ||||
|         { | ||||
|             MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::string Door::getModel(const MWWorld::Ptr &ptr) const | ||||
|  | @ -125,7 +150,14 @@ namespace MWClass | |||
|             { | ||||
|                 // animated door
 | ||||
|                 boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr)); | ||||
|                 if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr)) | ||||
|                 int doorstate = getDoorState(ptr); | ||||
|                 bool opening = true; | ||||
|                 if (doorstate == 1) | ||||
|                     opening = false; | ||||
|                 if (doorstate == 0 && ptr.getRefData().getLocalRotation().rot[2] != 0) | ||||
|                     opening = false; | ||||
| 
 | ||||
|                 if (opening) | ||||
|                 { | ||||
|                     MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, | ||||
|                             closeSound, 0.5); | ||||
|  | @ -262,4 +294,48 @@ namespace MWClass | |||
| 
 | ||||
|         return MWWorld::Ptr(&cell.get<ESM::Door>().insert(*ref), &cell); | ||||
|     } | ||||
| 
 | ||||
|     void Door::ensureCustomData(const MWWorld::Ptr &ptr) const | ||||
|     { | ||||
|         if (!ptr.getRefData().getCustomData()) | ||||
|         { | ||||
|             std::auto_ptr<DoorCustomData> data(new DoorCustomData); | ||||
| 
 | ||||
|             data->mDoorState = 0; | ||||
|             ptr.getRefData().setCustomData(data.release()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     int Door::getDoorState (const MWWorld::Ptr &ptr) const | ||||
|     { | ||||
|         ensureCustomData(ptr); | ||||
|         const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData()); | ||||
|         return customData.mDoorState; | ||||
|     } | ||||
| 
 | ||||
|     void Door::setDoorState (const MWWorld::Ptr &ptr, int state) const | ||||
|     { | ||||
|         ensureCustomData(ptr); | ||||
|         DoorCustomData& customData = dynamic_cast<DoorCustomData&>(*ptr.getRefData().getCustomData()); | ||||
|         customData.mDoorState = state; | ||||
|     } | ||||
| 
 | ||||
|     void Door::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const | ||||
|     { | ||||
|         ensureCustomData(ptr); | ||||
|         DoorCustomData& customData = dynamic_cast<DoorCustomData&>(*ptr.getRefData().getCustomData()); | ||||
| 
 | ||||
|         const ESM::DoorState& state2 = dynamic_cast<const ESM::DoorState&>(state); | ||||
|         customData.mDoorState = state2.mDoorState; | ||||
|     } | ||||
| 
 | ||||
|     void Door::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const | ||||
|     { | ||||
|         ensureCustomData(ptr); | ||||
|         const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData()); | ||||
| 
 | ||||
|         ESM::DoorState& state2 = dynamic_cast<ESM::DoorState&>(state); | ||||
|         state2.mDoorState = customData.mDoorState; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -9,6 +9,8 @@ namespace MWClass | |||
| { | ||||
|     class Door : public MWWorld::Class | ||||
|     { | ||||
|             void ensureCustomData (const MWWorld::Ptr& ptr) const; | ||||
| 
 | ||||
|             virtual MWWorld::Ptr | ||||
|             copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; | ||||
| 
 | ||||
|  | @ -48,7 +50,20 @@ namespace MWClass | |||
|             static void registerSelf(); | ||||
| 
 | ||||
|             virtual std::string getModel(const MWWorld::Ptr &ptr) const; | ||||
|         private: | ||||
| 
 | ||||
|             /// 0 = nothing, 1 = opening, 2 = closing
 | ||||
|             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 readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) | ||||
|                 const; | ||||
|             ///< Read additional state from \a state into \a ptr.
 | ||||
| 
 | ||||
|             virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) | ||||
|                 const; | ||||
|             ///< Write additional state from \a ptr into \a state.
 | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor,float duration | |||
|             return true; // We have tried backing up for more than one second, we've probably cleared it
 | ||||
|     } | ||||
| 
 | ||||
|     if(!MWBase::Environment::get().getWorld()->getIsMovingDoor(mDoorPtr)) | ||||
|     if (!mDoorPtr.getClass().getDoorState(mDoorPtr)) | ||||
|         return true; //Door is no longer opening
 | ||||
| 
 | ||||
|     ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
 | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include <components/esm/creaturestate.hpp> | ||||
| #include <components/esm/fogstate.hpp> | ||||
| #include <components/esm/creaturelevliststate.hpp> | ||||
| #include <components/esm/doorstate.hpp> | ||||
| 
 | ||||
| #include "../mwbase/environment.hpp" | ||||
| #include "../mwbase/world.hpp" | ||||
|  | @ -591,7 +592,7 @@ namespace MWWorld | |||
|         writeReferenceCollection<ESM::ObjectState> (writer, mClothes); | ||||
|         writeReferenceCollection<ESM::ContainerState> (writer, mContainers); | ||||
|         writeReferenceCollection<ESM::CreatureState> (writer, mCreatures); | ||||
|         writeReferenceCollection<ESM::ObjectState> (writer, mDoors); | ||||
|         writeReferenceCollection<ESM::DoorState> (writer, mDoors); | ||||
|         writeReferenceCollection<ESM::ObjectState> (writer, mIngreds); | ||||
|         writeReferenceCollection<ESM::CreatureLevListState> (writer, mCreatureLists); | ||||
|         writeReferenceCollection<ESM::ObjectState> (writer, mItemLists); | ||||
|  | @ -659,7 +660,7 @@ namespace MWWorld | |||
| 
 | ||||
|                 case ESM::REC_DOOR: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mDoors, contentFileMap); | ||||
|                     readReferenceCollection<ESM::DoorState> (reader, mDoors, contentFileMap); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_INGR: | ||||
|  |  | |||
|  | @ -406,4 +406,14 @@ namespace MWWorld | |||
|     { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     int Class::getDoorState (const MWWorld::Ptr &ptr) const | ||||
|     { | ||||
|         throw std::runtime_error("this is not a door"); | ||||
|     } | ||||
| 
 | ||||
|     void Class::setDoorState (const MWWorld::Ptr &ptr, int state) const | ||||
|     { | ||||
|         throw std::runtime_error("this is not a door"); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -336,6 +336,11 @@ namespace MWWorld | |||
|             virtual int getBaseGold(const MWWorld::Ptr& ptr) const; | ||||
| 
 | ||||
|             virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const; | ||||
| 
 | ||||
|             /// 0 = nothing, 1 = opening, 2 = closing
 | ||||
|             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; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1202,36 +1202,48 @@ namespace MWWorld | |||
|         while (it != mDoorStates.end()) | ||||
|         { | ||||
|             if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode()) | ||||
|             { | ||||
|                 // The door is no longer in an active cell, or it was disabled.
 | ||||
|                 // Erase from mDoorStates, since we no longer need to move it.
 | ||||
|                 // Once we load the door's cell again (or re-enable the door), Door::insertObject will reinsert to mDoorStates.
 | ||||
|                 mDoorStates.erase(it++); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees(); | ||||
|                 float diff = duration * 90; | ||||
|                 float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second ? 1 : -1)), 90.f); | ||||
|                 float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second == 1 ? 1 : -1)), 90.f); | ||||
|                 localRotateObject(it->first, 0, 0, targetRot); | ||||
| 
 | ||||
|                 bool reached = (targetRot == 90.f && it->second) || targetRot == 0.f; | ||||
| 
 | ||||
|                 /// \todo should use convexSweepTest here
 | ||||
|                 std::vector<std::string> collisions = mPhysics->getCollisions(it->first); | ||||
|                 for (std::vector<std::string>::iterator cit = collisions.begin(); cit != collisions.end(); ++cit) | ||||
|                 { | ||||
|                     MWWorld::Ptr ptr = getPtrViaHandle(*cit); | ||||
|                     if (MWWorld::Class::get(ptr).isActor()) | ||||
|                     if (ptr.getClass().isActor()) | ||||
|                     { | ||||
|                         // Collided with actor, ask actor to try to avoid door
 | ||||
|                         if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr() ) { | ||||
|                             MWMechanics::AiSequence& seq = MWWorld::Class::get(ptr).getCreatureStats(ptr).getAiSequence(); | ||||
|                             MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); | ||||
|                             if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
 | ||||
|                                 seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr); | ||||
|                         } | ||||
| 
 | ||||
|                         // we need to undo the rotation
 | ||||
|                         localRotateObject(it->first, 0, 0, oldRot); | ||||
|                         reached = false; | ||||
|                         //break; //Removed in case multiple actors are touching
 | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if ((targetRot == 90.f && it->second) || targetRot == 0.f) | ||||
|                 if (reached) | ||||
|                 { | ||||
|                     // Mark as non-moving
 | ||||
|                     it->first.getClass().setDoorState(it->first, 0); | ||||
|                     mDoorStates.erase(it++); | ||||
|                 } | ||||
|                 else | ||||
|                     ++it; | ||||
|             } | ||||
|  | @ -1849,31 +1861,32 @@ namespace MWWorld | |||
| 
 | ||||
|     void World::activateDoor(const MWWorld::Ptr& door) | ||||
|     { | ||||
|         if (mDoorStates.find(door) != mDoorStates.end()) | ||||
|         { | ||||
|             // if currently opening, then close, if closing, then open
 | ||||
|             mDoorStates[door] = !mDoorStates[door]; | ||||
|         } | ||||
|         else | ||||
|         int state = door.getClass().getDoorState(door); | ||||
|         switch (state) | ||||
|         { | ||||
|         case 0: | ||||
|             if (door.getRefData().getLocalRotation().rot[2] == 0) | ||||
|                 mDoorStates[door] = 1; // open
 | ||||
|                 state = 1; // if closed, then open
 | ||||
|             else | ||||
|                 mDoorStates[door] = 0; // close
 | ||||
|                 state = 2; // if open, then close
 | ||||
|             break; | ||||
|         case 2: | ||||
|             state = 1; // if closing, then open
 | ||||
|             break; | ||||
|         case 1: | ||||
|         default: | ||||
|             state = 2; // if opening, then close
 | ||||
|             break; | ||||
|         } | ||||
|         door.getClass().setDoorState(door, state); | ||||
|         mDoorStates[door] = state; | ||||
|     } | ||||
| 
 | ||||
|     bool World::getOpenOrCloseDoor(const Ptr &door) | ||||
|     void World::activateDoor(const Ptr &door, bool open) | ||||
|     { | ||||
|         if (mDoorStates.find(door) != mDoorStates.end()) | ||||
|             return !mDoorStates[door]; // if currently opening or closing, then do the opposite
 | ||||
|         return door.getRefData().getLocalRotation().rot[2] == 0; | ||||
|     } | ||||
| 
 | ||||
|     bool World::getIsMovingDoor(const Ptr& door) | ||||
|     { | ||||
|         bool result = mDoorStates.find(door) != mDoorStates.end(); | ||||
|         return result; | ||||
|         int state = open ? 1 : 2; | ||||
|         door.getClass().setDoorState(door, state); | ||||
|         mDoorStates[door] = state; | ||||
|     } | ||||
| 
 | ||||
|     bool World::getPlayerStandingOn (const MWWorld::Ptr& object) | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ namespace MWWorld | |||
|             float mFacedDistance; | ||||
| 
 | ||||
|             std::map<MWWorld::Ptr, int> mDoorStates; | ||||
|             ///< only holds doors that are currently moving. 0 means closing, 1 opening
 | ||||
|             ///< only holds doors that are currently moving. 1 = opening, 2 = closing
 | ||||
| 
 | ||||
|             struct MagicBoltState | ||||
|             { | ||||
|  | @ -496,13 +496,11 @@ namespace MWWorld | |||
|             virtual void setupPlayer(); | ||||
|             virtual void renderPlayer(); | ||||
| 
 | ||||
|             /// if activated, should this door be opened or closed?
 | ||||
|             virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door); | ||||
| 
 | ||||
|             /// activate (open or close) an non-teleport door
 | ||||
|             /// open or close a non-teleport door (depending on current state)
 | ||||
|             virtual void activateDoor(const MWWorld::Ptr& door); | ||||
| 
 | ||||
|             virtual bool getIsMovingDoor(const MWWorld::Ptr& door); | ||||
|             /// open or close a non-teleport door as specified
 | ||||
|             virtual void activateDoor(const MWWorld::Ptr& door, bool open); | ||||
| 
 | ||||
|             virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object
 | ||||
|             virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object
 | ||||
|  |  | |||
|  | @ -45,7 +45,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 | ||||
|     npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate | ||||
|     ) | ||||
| 
 | ||||
| add_component_dir (misc | ||||
|  |  | |||
							
								
								
									
										25
									
								
								components/esm/doorstate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								components/esm/doorstate.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| #include "doorstate.hpp" | ||||
| 
 | ||||
| #include "esmreader.hpp" | ||||
| #include "esmwriter.hpp" | ||||
| 
 | ||||
| namespace ESM | ||||
| { | ||||
| 
 | ||||
|     void DoorState::load(ESMReader &esm) | ||||
|     { | ||||
|         ObjectState::load(esm); | ||||
| 
 | ||||
|         mDoorState = 0; | ||||
|         esm.getHNOT (mDoorState, "ANIM"); | ||||
|     } | ||||
| 
 | ||||
|     void DoorState::save(ESMWriter &esm, bool inInventory) const | ||||
|     { | ||||
|         ObjectState::save(esm, inInventory); | ||||
| 
 | ||||
|         if (mDoorState != 0) | ||||
|             esm.writeHNT ("ANIM", mDoorState); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										19
									
								
								components/esm/doorstate.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								components/esm/doorstate.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| #ifndef OPENMW_ESM_DOORSTATE_H | ||||
| #define OPENMW_ESM_DOORSTATE_H | ||||
| 
 | ||||
| #include "objectstate.hpp" | ||||
| 
 | ||||
| namespace ESM | ||||
| { | ||||
|     // format 0, saved games only
 | ||||
| 
 | ||||
|     struct DoorState : public ObjectState | ||||
|     { | ||||
|         int mDoorState; | ||||
| 
 | ||||
|         virtual void load (ESMReader &esm); | ||||
|         virtual void save (ESMWriter &esm, bool inInventory = false) const; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
		Reference in a new issue