forked from mirror/openmw-tes3mp
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