Savegame: store door movement state (Closes #747)

deque
scrawl 10 years ago
parent 37b9d2fb0c
commit a76e391ad0

@ -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)
{
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();
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

@ -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