Savegame: store door movement state (Closes #747)

deque
scrawl 11 years ago
parent 37b9d2fb0c
commit a76e391ad0

@ -393,14 +393,10 @@ namespace MWBase
virtual void setupPlayer() = 0; virtual void setupPlayer() = 0;
virtual void renderPlayer() = 0; virtual void renderPlayer() = 0;
/// if activated, should this door be opened or closed? /// open or close a non-teleport door (depending on current state)
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0;
/// activate (open or close) an non-teleport door
virtual void activateDoor(const MWWorld::Ptr& door) = 0; virtual void activateDoor(const MWWorld::Ptr& door) = 0;
/// open or close a non-teleport door as specified
/// Is door currently opening/closing? virtual void activateDoor(const MWWorld::Ptr& door, bool open) = 0;
virtual bool getIsMovingDoor(const MWWorld::Ptr& door) = 0;
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object 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 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 "door.hpp"
#include <components/esm/loaddoor.hpp> #include <components/esm/loaddoor.hpp>
#include <components/esm/doorstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -17,12 +18,28 @@
#include "../mwworld/physicssystem.hpp" #include "../mwworld/physicssystem.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/actiontrap.hpp" #include "../mwworld/actiontrap.hpp"
#include "../mwworld/customdata.hpp"
#include "../mwgui/tooltips.hpp" #include "../mwgui/tooltips.hpp"
#include "../mwrender/objects.hpp" #include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.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 namespace MWClass
{ {
void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -38,6 +55,14 @@ namespace MWClass
const std::string model = getModel(ptr); const std::string model = getModel(ptr);
if(!model.empty()) if(!model.empty())
physics.addObject(ptr); 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 std::string Door::getModel(const MWWorld::Ptr &ptr) const
@ -125,7 +150,14 @@ namespace MWClass
{ {
// animated door // animated door
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr)); 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, MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr,
closeSound, 0.5); closeSound, 0.5);
@ -262,4 +294,48 @@ namespace MWClass
return MWWorld::Ptr(&cell.get<ESM::Door>().insert(*ref), &cell); 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 class Door : public MWWorld::Class
{ {
void ensureCustomData (const MWWorld::Ptr& ptr) const;
virtual MWWorld::Ptr virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
@ -48,7 +50,20 @@ namespace MWClass
static void registerSelf(); static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const; 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 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 return true; //Door is no longer opening
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door

@ -13,6 +13,7 @@
#include <components/esm/creaturestate.hpp> #include <components/esm/creaturestate.hpp>
#include <components/esm/fogstate.hpp> #include <components/esm/fogstate.hpp>
#include <components/esm/creaturelevliststate.hpp> #include <components/esm/creaturelevliststate.hpp>
#include <components/esm/doorstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -591,7 +592,7 @@ namespace MWWorld
writeReferenceCollection<ESM::ObjectState> (writer, mClothes); writeReferenceCollection<ESM::ObjectState> (writer, mClothes);
writeReferenceCollection<ESM::ContainerState> (writer, mContainers); writeReferenceCollection<ESM::ContainerState> (writer, mContainers);
writeReferenceCollection<ESM::CreatureState> (writer, mCreatures); writeReferenceCollection<ESM::CreatureState> (writer, mCreatures);
writeReferenceCollection<ESM::ObjectState> (writer, mDoors); writeReferenceCollection<ESM::DoorState> (writer, mDoors);
writeReferenceCollection<ESM::ObjectState> (writer, mIngreds); writeReferenceCollection<ESM::ObjectState> (writer, mIngreds);
writeReferenceCollection<ESM::CreatureLevListState> (writer, mCreatureLists); writeReferenceCollection<ESM::CreatureLevListState> (writer, mCreatureLists);
writeReferenceCollection<ESM::ObjectState> (writer, mItemLists); writeReferenceCollection<ESM::ObjectState> (writer, mItemLists);
@ -659,7 +660,7 @@ namespace MWWorld
case ESM::REC_DOOR: case ESM::REC_DOOR:
readReferenceCollection<ESM::ObjectState> (reader, mDoors, contentFileMap); readReferenceCollection<ESM::DoorState> (reader, mDoors, contentFileMap);
break; break;
case ESM::REC_INGR: case ESM::REC_INGR:

@ -406,4 +406,14 @@ namespace MWWorld
{ {
return false; 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 int getBaseGold(const MWWorld::Ptr& ptr) const;
virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) 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()) while (it != mDoorStates.end())
{ {
if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode()) 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++); mDoorStates.erase(it++);
}
else else
{ {
float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees(); float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees();
float diff = duration * 90; 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); localRotateObject(it->first, 0, 0, targetRot);
bool reached = (targetRot == 90.f && it->second) || targetRot == 0.f;
/// \todo should use convexSweepTest here /// \todo should use convexSweepTest here
std::vector<std::string> collisions = mPhysics->getCollisions(it->first); std::vector<std::string> collisions = mPhysics->getCollisions(it->first);
for (std::vector<std::string>::iterator cit = collisions.begin(); cit != collisions.end(); ++cit) for (std::vector<std::string>::iterator cit = collisions.begin(); cit != collisions.end(); ++cit)
{ {
MWWorld::Ptr ptr = getPtrViaHandle(*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 // Collided with actor, ask actor to try to avoid door
if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr() ) { 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 if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr); seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr);
} }
// we need to undo the rotation // we need to undo the rotation
localRotateObject(it->first, 0, 0, oldRot); localRotateObject(it->first, 0, 0, oldRot);
reached = false;
//break; //Removed in case multiple actors are touching //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++); mDoorStates.erase(it++);
}
else else
++it; ++it;
} }
@ -1849,31 +1861,32 @@ namespace MWWorld
void World::activateDoor(const MWWorld::Ptr& door) void World::activateDoor(const MWWorld::Ptr& door)
{ {
if (mDoorStates.find(door) != mDoorStates.end()) int state = door.getClass().getDoorState(door);
{ switch (state)
// if currently opening, then close, if closing, then open
mDoorStates[door] = !mDoorStates[door];
}
else
{ {
case 0:
if (door.getRefData().getLocalRotation().rot[2] == 0) if (door.getRefData().getLocalRotation().rot[2] == 0)
mDoorStates[door] = 1; // open state = 1; // if closed, then open
else 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);
bool World::getOpenOrCloseDoor(const Ptr &door) mDoorStates[door] = state;
{
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) void World::activateDoor(const Ptr &door, bool open)
{ {
bool result = mDoorStates.find(door) != mDoorStates.end(); int state = open ? 1 : 2;
return result; door.getClass().setDoorState(door, state);
mDoorStates[door] = state;
} }
bool World::getPlayerStandingOn (const MWWorld::Ptr& object) bool World::getPlayerStandingOn (const MWWorld::Ptr& object)

@ -88,7 +88,7 @@ namespace MWWorld
float mFacedDistance; float mFacedDistance;
std::map<MWWorld::Ptr, int> mDoorStates; 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 struct MagicBoltState
{ {
@ -496,13 +496,11 @@ namespace MWWorld
virtual void setupPlayer(); virtual void setupPlayer();
virtual void renderPlayer(); virtual void renderPlayer();
/// if activated, should this door be opened or closed? /// open or close a non-teleport door (depending on current state)
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door);
/// activate (open or close) an non-teleport door
virtual void activateDoor(const MWWorld::Ptr& door); 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 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 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 loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter 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 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 add_component_dir (misc

@ -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);
}
}

@ -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…
Cancel
Save