Merge remote-tracking branch 'scrawl/master'

deque
Marc Zinnschlag 11 years ago
commit 3b2ba4f6cd

@ -200,6 +200,9 @@ namespace MWBase
virtual MWWorld::Ptr searchPtrViaHandle (const std::string& handle) = 0; virtual MWWorld::Ptr searchPtrViaHandle (const std::string& handle) = 0;
///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found ///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found
virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0;
///< Search is limited to the active cells.
/// \todo enable reference in the OGRE scene /// \todo enable reference in the OGRE scene
virtual void enable (const MWWorld::Ptr& ptr) = 0; virtual void enable (const MWWorld::Ptr& ptr) = 0;
@ -390,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

@ -164,7 +164,11 @@ namespace MWClass
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);
if(!model.empty()) if(!model.empty())
{
physics.addActor(ptr); physics.addActor(ptr);
if (getCreatureStats(ptr).isDead())
MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false);
}
MWBase::Environment::get().getMechanicsManager()->add(ptr); MWBase::Environment::get().getMechanicsManager()->add(ptr);
} }

@ -2,6 +2,7 @@
#include "creaturelevlist.hpp" #include "creaturelevlist.hpp"
#include <components/esm/loadlevlist.hpp> #include <components/esm/loadlevlist.hpp>
#include <components/esm/creaturelevliststate.hpp>
#include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/levelledlist.hpp"
@ -11,7 +12,9 @@ namespace
{ {
struct CreatureLevListCustomData : public MWWorld::CustomData struct CreatureLevListCustomData : public MWWorld::CustomData
{ {
// TODO: save the creature we spawned here // actorId of the creature we spawned
int mSpawnActorId;
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
}; };
@ -38,6 +41,25 @@ namespace MWClass
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const
{ {
ensureCustomData(ptr); ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
if (customData.mSpawnActorId != -1)
return; // TODO: handle respawning
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
ptr.get<ESM::CreatureLevList>();
std::string id = MWMechanics::getLevelledItem(ref->mBase, true);
if (!id.empty())
{
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();
}
} }
void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const
@ -45,22 +67,29 @@ namespace MWClass
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData); std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
data->mSpawnActorId = -1;
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = ptr.getRefData().setCustomData(data.release());
ptr.get<ESM::CreatureLevList>(); }
}
std::string id = MWMechanics::getLevelledItem(ref->mBase, true); void CreatureLevList::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::CreatureLevListState& state2 = dynamic_cast<const ESM::CreatureLevListState&> (state);
if (!id.empty()) ensureCustomData(ptr);
{ CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); customData.mSpawnActorId = state2.mSpawnActorId;
MWWorld::ManualRef ref(store, id); }
ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos;
// TODO: hold on to this for respawn purposes later
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos);
}
ptr.getRefData().setCustomData(data.release()); void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
} const
{
ESM::CreatureLevListState& state2 = dynamic_cast<ESM::CreatureLevListState&> (state);
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
state2.mSpawnActorId = customData.mSpawnActorId;
} }
} }

@ -19,6 +19,14 @@ namespace MWClass
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering
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.
}; };
} }

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

@ -388,6 +388,8 @@ namespace MWClass
{ {
physics.addActor(ptr); physics.addActor(ptr);
MWBase::Environment::get().getMechanicsManager()->add(ptr); MWBase::Environment::get().getMechanicsManager()->add(ptr);
if (getCreatureStats(ptr).isDead())
MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false);
} }
bool Npc::isPersistent(const MWWorld::Ptr &actor) const bool Npc::isPersistent(const MWWorld::Ptr &actor) const
@ -618,7 +620,8 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty. // NOTE: 'object' and/or 'attacker' may be empty.
// Attacking peaceful NPCs is a crime // Attacking peaceful NPCs is a crime
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) // anything below 80 is considered peaceful (see Actors::updateActor)
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80)
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
getCreatureStats(ptr).setAttacked(true); getCreatureStats(ptr).setAttacked(true);

@ -10,23 +10,23 @@ namespace MWGui
{ {
} }
void CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
{ {
if (mActor.getClass().isNpc()) if (mActor.getClass().isNpc())
{ {
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); stats.modifyProfit(item.mBase.getClass().getValue(item.mBase) * count);
} }
InventoryItemModel::copyItem(item, count, setNewOwner); return InventoryItemModel::copyItem(item, count, setNewOwner);
} }
void CompanionItemModel::removeItem (const ItemStack& item, size_t count) void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
{ {
if (mActor.getClass().isNpc()) if (mActor.getClass().isNpc())
{ {
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); stats.modifyProfit(-item.mBase.getClass().getValue(item.mBase) * count);
} }
InventoryItemModel::removeItem(item, count); InventoryItemModel::removeItem(item, count);

@ -13,7 +13,7 @@ namespace MWGui
public: public:
CompanionItemModel (const MWWorld::Ptr& actor); CompanionItemModel (const MWWorld::Ptr& actor);
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
}; };

@ -71,12 +71,12 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
return -1; return -1;
} }
void ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
{ {
const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
throw std::runtime_error("Item to copy needs to be from a different container!"); throw std::runtime_error("Item to copy needs to be from a different container!");
source.getClass().getContainerStore(source).add(item.mBase, count, source); return *source.getClass().getContainerStore(source).add(item.mBase, count, source);
} }
void ContainerItemModel::removeItem (const ItemStack& item, size_t count) void ContainerItemModel::removeItem (const ItemStack& item, size_t count)

@ -21,7 +21,7 @@ namespace MWGui
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);
virtual size_t getItemCount(); virtual size_t getItemCount();
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
virtual void update(); virtual void update();

@ -40,11 +40,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)
return -1; return -1;
} }
void InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
{ {
if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor))
throw std::runtime_error("Item to copy needs to be from a different container!"); throw std::runtime_error("Item to copy needs to be from a different container!");
mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner); return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner);
} }
@ -59,16 +59,20 @@ void InventoryItemModel::removeItem (const ItemStack& item, size_t count)
throw std::runtime_error("Not enough items in the stack to remove"); throw std::runtime_error("Not enough items in the stack to remove");
} }
void InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel) MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
{ {
bool setNewOwner = false; bool setNewOwner = false;
// Are you dead? Then you wont need that anymore // Are you dead? Then you wont need that anymore
if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead()) if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead()
// Make sure that the item is actually owned by the dead actor
// Prevents a potential exploit for resetting the owner of any item, by placing the item in a corpse
&& Misc::StringUtils::ciEqual(item.mBase.getCellRef().mOwner, mActor.getCellRef().mRefID))
setNewOwner = true; setNewOwner = true;
otherModel->copyItem(item, count, setNewOwner); MWWorld::Ptr ret = otherModel->copyItem(item, count, setNewOwner);
removeItem(item, count); removeItem(item, count);
return ret;
} }
void InventoryItemModel::update() void InventoryItemModel::update()

@ -15,11 +15,11 @@ namespace MWGui
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);
virtual size_t getItemCount(); virtual size_t getItemCount();
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
/// Move items from this model to \a otherModel. /// Move items from this model to \a otherModel.
virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
virtual void update(); virtual void update();

@ -408,14 +408,8 @@ namespace MWGui
if (mDragAndDrop->mSourceModel != mTradeModel) if (mDragAndDrop->mSourceModel != mTradeModel)
{ {
// add item to the player's inventory // Move item to the player's inventory
MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); ptr = mDragAndDrop->mSourceModel->moveItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount, mTradeModel);
MWWorld::ContainerStoreIterator it = invStore.begin();
it = invStore.add(ptr, mDragAndDrop->mDraggedCount, mPtr);
mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount);
ptr = *it;
} }
useItem(ptr); useItem(ptr);
} }

@ -71,10 +71,11 @@ namespace MWGui
{ {
} }
void ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel) MWWorld::Ptr ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
{ {
otherModel->copyItem(item, count); MWWorld::Ptr ret = otherModel->copyItem(item, count);
removeItem(item, count); removeItem(item, count);
return ret;
} }
@ -83,10 +84,9 @@ namespace MWGui
delete mSourceModel; delete mSourceModel;
} }
void ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
{ {
// no need to use mapToSource since itemIndex refers to an index in the sourceModel return mSourceModel->copyItem (item, count, setNewOwner);
mSourceModel->copyItem (item, count, setNewOwner);
} }
void ProxyItemModel::removeItem (const ItemStack& item, size_t count) void ProxyItemModel::removeItem (const ItemStack& item, size_t count)

@ -56,10 +56,10 @@ namespace MWGui
virtual void update() = 0; virtual void update() = 0;
/// Move items from this model to \a otherModel. /// Move items from this model to \a otherModel.
virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
/// @param setNewOwner Set the copied item's owner to the actor we are copying to, or keep the original owner? /// @param setNewOwner Set the copied item's owner to the actor we are copying to, or keep the original owner?
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
virtual void removeItem (const ItemStack& item, size_t count) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0;
private: private:
@ -73,7 +73,7 @@ namespace MWGui
{ {
public: public:
virtual ~ProxyItemModel(); virtual ~ProxyItemModel();
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);

@ -21,7 +21,7 @@ namespace MWGui
{ {
void EffectSourceVisitor::visit (MWMechanics::EffectKey key, void EffectSourceVisitor::visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& casterHandle, const std::string& sourceName, int casterActorId,
float magnitude, float remainingTime) float magnitude, float remainingTime)
{ {
MagicEffectInfo newEffectSource; MagicEffectInfo newEffectSource;

@ -45,7 +45,7 @@ namespace MWGui
virtual ~EffectSourceVisitor() {} virtual ~EffectSourceVisitor() {}
virtual void visit (MWMechanics::EffectKey key, virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& casterHandle, const std::string& sourceName, int casterActorId,
float magnitude, float remainingTime = -1); float magnitude, float remainingTime = -1);
}; };

@ -53,9 +53,9 @@ namespace MWMechanics
{ {
const MWWorld::TimeStamp& start = iter->second.mTimeStamp; const MWWorld::TimeStamp& start = iter->second.mTimeStamp;
const std::vector<Effect>& effects = iter->second.mEffects; const std::vector<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<Effect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) for (std::vector<ActiveEffect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{ {
int duration = effectIt->mDuration; int duration = effectIt->mDuration;
MWWorld::TimeStamp end = start; MWWorld::TimeStamp end = start;
@ -63,7 +63,7 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end>now) if (end>now)
mEffects.add(effectIt->mKey, MWMechanics::EffectParam(effectIt->mMagnitude)); mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude));
} }
} }
} }
@ -91,11 +91,11 @@ namespace MWMechanics
double ActiveSpells::timeToExpire (const TIterator& iterator) const double ActiveSpells::timeToExpire (const TIterator& iterator) const
{ {
const std::vector<Effect>& effects = iterator->second.mEffects; const std::vector<ActiveEffect>& effects = iterator->second.mEffects;
int duration = 0; int duration = 0;
for (std::vector<Effect>::const_iterator iter (effects.begin()); for (std::vector<ActiveEffect>::const_iterator iter (effects.begin());
iter!=effects.end(); ++iter) iter!=effects.end(); ++iter)
{ {
if (iter->mDuration > duration) if (iter->mDuration > duration)
@ -132,8 +132,8 @@ namespace MWMechanics
return mSpells; return mSpells;
} }
void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<Effect> effects, void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<ActiveEffect> effects,
const std::string &displayName, const std::string& casterHandle) const std::string &displayName, int casterActorId)
{ {
bool exists = false; bool exists = false;
for (TContainer::const_iterator it = begin(); it != end(); ++it) for (TContainer::const_iterator it = begin(); it != end(); ++it)
@ -146,7 +146,7 @@ namespace MWMechanics
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
params.mEffects = effects; params.mEffects = effects;
params.mDisplayName = displayName; params.mDisplayName = displayName;
params.mCasterHandle = casterHandle; params.mCasterActorId = casterActorId;
if (!exists || stack) if (!exists || stack)
mSpells.insert (std::make_pair(id, params)); mSpells.insert (std::make_pair(id, params));
@ -168,7 +168,7 @@ namespace MWMechanics
{ {
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
for (std::vector<Effect>::const_iterator effectIt = it->second.mEffects.begin(); for (std::vector<ActiveEffect>::const_iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end(); ++effectIt) effectIt != it->second.mEffects.end(); ++effectIt)
{ {
std::string name = it->second.mDisplayName; std::string name = it->second.mDisplayName;
@ -178,7 +178,7 @@ namespace MWMechanics
float magnitude = effectIt->mMagnitude; float magnitude = effectIt->mMagnitude;
if (magnitude) if (magnitude)
visitor.visit(effectIt->mKey, name, it->second.mCasterHandle, magnitude, remainingTime); visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime);
} }
} }
} }
@ -200,10 +200,10 @@ namespace MWMechanics
{ {
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{ {
for (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin(); for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end();) effectIt != it->second.mEffects.end();)
{ {
if (effectIt->mKey.mId == effectId) if (effectIt->mEffectId == effectId)
effectIt = it->second.mEffects.erase(effectIt); effectIt = it->second.mEffects.erase(effectIt);
else else
++effectIt; ++effectIt;
@ -212,16 +212,16 @@ namespace MWMechanics
mSpellsChanged = true; mSpellsChanged = true;
} }
void ActiveSpells::purge(const std::string &actorHandle) void ActiveSpells::purge(int casterActorId)
{ {
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{ {
for (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin(); for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end();) effectIt != it->second.mEffects.end();)
{ {
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mKey.mId); const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectId);
if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked
&& it->second.mCasterHandle == actorHandle) && it->second.mCasterActorId == casterActorId)
effectIt = it->second.mEffects.erase(effectIt); effectIt = it->second.mEffects.erase(effectIt);
else else
++effectIt; ++effectIt;
@ -229,4 +229,41 @@ namespace MWMechanics
} }
mSpellsChanged = true; mSpellsChanged = true;
} }
void ActiveSpells::clear()
{
mSpells.clear();
mSpellsChanged = true;
}
void ActiveSpells::writeState(ESM::ActiveSpells &state) const
{
for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
// Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp
ESM::ActiveSpells::ActiveSpellParams params;
params.mEffects = it->second.mEffects;
params.mCasterActorId = it->second.mCasterActorId;
params.mDisplayName = it->second.mDisplayName;
params.mTimeStamp = it->second.mTimeStamp.toEsm();
state.mSpells.insert (std::make_pair(it->first, params));
}
}
void ActiveSpells::readState(const ESM::ActiveSpells &state)
{
for (ESM::ActiveSpells::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it)
{
// Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp
ActiveSpellParams params;
params.mEffects = it->second.mEffects;
params.mCasterActorId = it->second.mCasterActorId;
params.mDisplayName = it->second.mDisplayName;
params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp);
mSpells.insert (std::make_pair(it->first, params));
mSpellsChanged = true;
}
}
} }

@ -10,6 +10,7 @@
#include "magiceffects.hpp" #include "magiceffects.hpp"
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/activespells.hpp>
namespace MWMechanics namespace MWMechanics
{ {
@ -21,29 +22,24 @@ namespace MWMechanics
{ {
public: public:
// Parameters of an effect concerning lasting effects. typedef ESM::ActiveEffect ActiveEffect;
// Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc.
// It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster.
struct Effect
{
float mMagnitude;
EffectKey mKey;
float mDuration;
};
struct ActiveSpellParams struct ActiveSpellParams
{ {
std::vector<Effect> mEffects; std::vector<ActiveEffect> mEffects;
MWWorld::TimeStamp mTimeStamp; MWWorld::TimeStamp mTimeStamp;
std::string mDisplayName; std::string mDisplayName;
// Handle to the caster that that inflicted this spell on us // The caster that inflicted this spell on us
std::string mCasterHandle; int mCasterActorId;
}; };
typedef std::multimap<std::string, ActiveSpellParams > TContainer; typedef std::multimap<std::string, ActiveSpellParams > TContainer;
typedef TContainer::const_iterator TIterator; typedef TContainer::const_iterator TIterator;
void readState (const ESM::ActiveSpells& state);
void writeState (ESM::ActiveSpells& state) const;
private: private:
mutable TContainer mSpells; mutable TContainer mSpells;
@ -76,10 +72,9 @@ namespace MWMechanics
/// \param stack If false, the spell is not added if one with the same ID exists already. /// \param stack If false, the spell is not added if one with the same ID exists already.
/// \param effects /// \param effects
/// \param displayName Name for display in magic menu. /// \param displayName Name for display in magic menu.
/// \param casterHandle
/// ///
void addSpell (const std::string& id, bool stack, std::vector<Effect> effects, void addSpell (const std::string& id, bool stack, std::vector<ActiveEffect> effects,
const std::string& displayName, const std::string& casterHandle); const std::string& displayName, int casterActorId);
/// Removes the active effects from this spell/potion/.. with \a id /// Removes the active effects from this spell/potion/.. with \a id
void removeEffects (const std::string& id); void removeEffects (const std::string& id);
@ -90,8 +85,11 @@ namespace MWMechanics
/// Remove all active effects, if roll succeeds (for each effect) /// Remove all active effects, if roll succeeds (for each effect)
void purgeAll (float chance); void purgeAll (float chance);
/// Remove all effects with CASTER_LINKED flag that were cast by \a actorHandle /// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId
void purge (const std::string& actorHandle); void purge (int casterActorId);
/// Remove all spells
void clear();
bool isSpellActive (std::string id) const; bool isSpellActive (std::string id) const;
///< case insensitive ///< case insensitive

@ -38,9 +38,12 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a
{ {
if (bound) if (bound)
{ {
MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor); if (actor.getClass().getContainerStore(actor).count(item) == 0)
MWWorld::ActionEquip action(newPtr); {
action.execute(actor); MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor);
MWWorld::ActionEquip action(newPtr);
action.execute(actor);
}
} }
else else
{ {
@ -116,7 +119,7 @@ namespace MWMechanics
: mCreature(trappedCreature) {} : mCreature(trappedCreature) {}
virtual void visit (MWMechanics::EffectKey key, virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& casterHandle, const std::string& sourceName, int casterActorId,
float magnitude, float remainingTime = -1) float magnitude, float remainingTime = -1)
{ {
if (key.mId != ESM::MagicEffect::Soultrap) if (key.mId != ESM::MagicEffect::Soultrap)
@ -126,7 +129,7 @@ namespace MWMechanics
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::Ptr caster = world->searchPtrViaHandle(casterHandle); MWWorld::Ptr caster = world->searchPtrViaActorId(casterActorId);
if (caster.isEmpty() || !caster.getClass().isActor()) if (caster.isEmpty() || !caster.getClass().isActor())
return; return;
@ -534,28 +537,54 @@ namespace MWMechanics
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
ref.getPtr().getCellRef().mPos = ipos; ref.getPtr().getCellRef().mPos = ipos;
// TODO: Add AI to follow player and fight for him MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
AiFollow package(ptr.getRefData().getHandle());
MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package, ptr); // Make the summoned creature follow its master and help in fights
AiFollow package(ptr);
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
int creatureActorId = summonedCreatureStats.getActorId();
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
// TODO: VFX_SummonStart, VFX_SummonEnd // TODO: VFX_SummonStart, VFX_SummonEnd
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureActorId));
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle()));
} }
} }
else else
{ {
std::string handle = creatureStats.mSummonedCreatures[it->first]; // Summon lifetime has expired. Try to delete the creature.
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation int actorId = creatureStats.mSummonedCreatures[it->first];
// plays though, which is a rather lame exploit in vanilla. creatureStats.mSummonedCreatures.erase(it->first);
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle);
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId);
if (!ptr.isEmpty()) if (!ptr.isEmpty())
{ {
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
// plays though, which is a rather lame exploit in vanilla.
MWBase::Environment::get().getWorld()->deleteObject(ptr); MWBase::Environment::get().getWorld()->deleteObject(ptr);
creatureStats.mSummonedCreatures.erase(it->first); creatureStats.mSummonedCreatures.erase(it->first);
} }
else
{
// We didn't find the creature. It's probably in an inactive cell.
// Add to graveyard so we can delete it when the cell becomes active.
creatureStats.mSummonGraveyard.push_back(actorId);
}
} }
} }
} }
for (std::vector<int>::iterator it = creatureStats.mSummonGraveyard.begin(); it != creatureStats.mSummonGraveyard.end(); )
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it);
if (!ptr.isEmpty())
{
it = creatureStats.mSummonGraveyard.erase(it);
MWBase::Environment::get().getWorld()->deleteObject(ptr);
}
else
++it;
}
} }
void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr) void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr)
@ -730,7 +759,7 @@ namespace MWMechanics
&& MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getWorld()->getLOS(ptr, player)
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))
{ {
creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); creatureStats.getAiSequence().stack(AiPursue(player), ptr);
creatureStats.setAlarmed(true); creatureStats.setAlarmed(true);
npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId()); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId());
} }
@ -759,7 +788,7 @@ namespace MWMechanics
else if (!creatureStats.isHostile()) else if (!creatureStats.isHostile())
{ {
if (ptr.getClass().isClass(ptr, "Guard")) if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); creatureStats.getAiSequence().stack(AiPursue(player), ptr);
else else
{ {
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
@ -781,7 +810,7 @@ namespace MWMechanics
} }
} }
void Actors::addActor (const MWWorld::Ptr& ptr) void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately)
{ {
// erase previous death events since we are currently only tracking them while in an active cell // erase previous death events since we are currently only tracking them while in an active cell
MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied(); MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied();
@ -790,6 +819,8 @@ namespace MWMechanics
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim)));
if (updateImmediately)
mActors[ptr]->update(0);
} }
void Actors::removeActor (const MWWorld::Ptr& ptr) void Actors::removeActor (const MWWorld::Ptr& ptr)
@ -912,18 +943,17 @@ namespace MWMechanics
continue; continue;
} }
// Make sure spell effects with CasterLinked flag are removed
// TODO: would be nice not to do this all the time...
for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
{
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(iter->first.getRefData().getHandle());
}
if (iter->second->kill()) if (iter->second->kill())
{ {
++mDeathCount[cls.getId(iter->first)]; ++mDeathCount[cls.getId(iter->first)];
// Make sure spell effects with CasterLinked flag are removed
for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
{
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(stats.getActorId());
}
// Apply soultrap // Apply soultrap
if (iter->first.getTypeName() == typeid(ESM::Creature).name()) if (iter->first.getTypeName() == typeid(ESM::Creature).name())
{ {
@ -934,6 +964,7 @@ namespace MWMechanics
// Reset magic effects and recalculate derived effects // Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death // One case where we need this is to make sure bound items are removed upon death
stats.setMagicEffects(MWMechanics::MagicEffects()); stats.setMagicEffects(MWMechanics::MagicEffects());
stats.getActiveSpells().clear();
calculateCreatureStatModifiers(iter->first, 0); calculateCreatureStatModifiers(iter->first, 0);
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);

@ -58,7 +58,7 @@ namespace MWMechanics
/// paused we may want to do it manually (after equipping permanent enchantment) /// paused we may want to do it manually (after equipping permanent enchantment)
void updateMagicEffects (const MWWorld::Ptr& ptr) { adjustMagicEffects(ptr); } void updateMagicEffects (const MWWorld::Ptr& ptr) { adjustMagicEffects(ptr); }
void addActor (const MWWorld::Ptr& ptr); void addActor (const MWWorld::Ptr& ptr, bool updateImmediately=false);
///< Register an actor for stats management ///< Register an actor for stats management
/// ///
/// \note Dead actors are ignored. /// \note Dead actors are ignored.

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

@ -11,9 +11,9 @@ namespace MWMechanics
{ {
/// \brief AiPackage to have an actor avoid an opening door /// \brief AiPackage to have an actor avoid an opening door
/** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it
**/ **/
class AiAvoidDoor : public AiPackage class AiAvoidDoor : public AiPackage
{ {
public: public:
/// Avoid door until the door is fully open /// Avoid door until the door is fully open
AiAvoidDoor(const MWWorld::Ptr& doorPtr); AiAvoidDoor(const MWWorld::Ptr& doorPtr);
@ -29,10 +29,10 @@ namespace MWMechanics
private: private:
float mDuration; float mDuration;
const MWWorld::Ptr& mDoorPtr; MWWorld::Ptr mDoorPtr;
ESM::Position mLastPos; ESM::Position mLastPos;
float mAdjAngle; float mAdjAngle;
}; };
} }
#endif #endif

@ -81,7 +81,7 @@ namespace MWMechanics
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp
AiCombat::AiCombat(const MWWorld::Ptr& actor) : AiCombat::AiCombat(const MWWorld::Ptr& actor) :
mTarget(actor), mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()),
mTimerAttack(0), mTimerAttack(0),
mTimerReact(0), mTimerReact(0),
mTimerCombatMove(0), mTimerCombatMove(0),
@ -153,7 +153,9 @@ namespace MWMechanics
|| actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) || actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0)
return true; return true;
if(mTarget.getClass().getCreatureStats(mTarget).isDead()) MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
if(target.getClass().getCreatureStats(target).isDead())
return true; return true;
//Update every frame //Update every frame
@ -325,7 +327,7 @@ namespace MWMechanics
ESM::Position pos = actor.getRefData().getPosition(); ESM::Position pos = actor.getRefData().getPosition();
Ogre::Vector3 vActorPos(pos.pos); Ogre::Vector3 vActorPos(pos.pos);
Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos); Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos);
Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos; Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
bool isStuck = false; bool isStuck = false;
@ -396,7 +398,7 @@ namespace MWMechanics
else // remote pathfinding else // remote pathfinding
{ {
bool preferShortcut = false; bool preferShortcut = false;
bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target);
if(mReadyToAttack) isStuck = false; if(mReadyToAttack) isStuck = false;
@ -432,7 +434,7 @@ namespace MWMechanics
mFollowTarget = false; mFollowTarget = false;
buildNewPath(actor); //may fail to build a path, check before use buildNewPath(actor, target); //may fail to build a path, check before use
//delete visited path node //delete visited path node
mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]); mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]);
@ -476,9 +478,9 @@ namespace MWMechanics
//less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing)
//then start attacking //then start attacking
float speed1 = actorCls.getSpeed(actor); float speed1 = actorCls.getSpeed(actor);
float speed2 = mTarget.getClass().getSpeed(mTarget); float speed2 = target.getClass().getSpeed(target);
if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 if(target.getClass().getMovementSettings(target).mPosition[0] == 0
&& mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) && target.getClass().getMovementSettings(target).mPosition[1] == 0)
speed2 = 0; speed2 = 0;
float s1 = distToTarget - weapRange; float s1 = distToTarget - weapRange;
@ -570,9 +572,9 @@ namespace MWMechanics
return false; return false;
} }
void AiCombat::buildNewPath(const MWWorld::Ptr& actor) void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target)
{ {
Ogre::Vector3 newPathTarget = Ogre::Vector3(mTarget.getRefData().getPosition().pos); Ogre::Vector3 newPathTarget = Ogre::Vector3(target.getRefData().getPosition().pos);
float dist; float dist;
@ -627,9 +629,12 @@ namespace MWMechanics
return 1; return 1;
} }
const std::string &AiCombat::getTargetId() const std::string AiCombat::getTargetId() const
{ {
return mTarget.getRefData().getHandle(); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
if (target.isEmpty())
return "";
return target.getRefData().getHandle();
} }

@ -31,7 +31,7 @@ namespace MWMechanics
virtual unsigned int getPriority() const; virtual unsigned int getPriority() const;
///Returns target ID ///Returns target ID
const std::string &getTargetId() const; std::string getTargetId() const;
private: private:
PathFinder mPathFinder; PathFinder mPathFinder;
@ -53,7 +53,7 @@ namespace MWMechanics
ESM::Position mLastPos; ESM::Position mLastPos;
MWMechanics::Movement mMovement; MWMechanics::Movement mMovement;
MWWorld::Ptr mTarget; int mTargetActorId;
const MWWorld::CellStore* mCell; const MWWorld::CellStore* mCell;
ObstacleCheck mObstacleCheck; ObstacleCheck mObstacleCheck;
@ -63,7 +63,7 @@ namespace MWMechanics
MWWorld::CellRefList<ESM::Door>::List::iterator mDoorIter; MWWorld::CellRefList<ESM::Door>::List::iterator mDoorIter;
MWWorld::CellRefList<ESM::Door>& mDoors; MWWorld::CellRefList<ESM::Door>& mDoors;
void buildNewPath(const MWWorld::Ptr& actor); void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
}; };
} }

@ -8,6 +8,8 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/timestamp.hpp" #include "../mwworld/timestamp.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
@ -19,8 +21,8 @@
namespace MWMechanics namespace MWMechanics
{ {
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) AiEscort::AiEscort(const MWWorld::Ptr& actor, int duration, float x, float y, float z)
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) : mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mX(x), mY(y), mZ(z), mDuration(duration)
, mCellX(std::numeric_limits<int>::max()) , mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max()) , mCellY(std::numeric_limits<int>::max())
{ {
@ -38,8 +40,8 @@ namespace MWMechanics
} }
} }
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) AiEscort::AiEscort(const MWWorld::Ptr& actor, const std::string &cellId,int duration, float x, float y, float z)
: mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) : mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
, mCellX(std::numeric_limits<int>::max()) , mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max()) , mCellY(std::numeric_limits<int>::max())
{ {
@ -75,7 +77,14 @@ namespace MWMechanics
return true; return true;
} }
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); 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 float* const leaderPos = actor.getRefData().getPosition().pos; const float* const leaderPos = actor.getRefData().getPosition().pos;
const float* const followerPos = follower.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos;
double differenceBetween[3]; double differenceBetween[3];
@ -89,7 +98,7 @@ namespace MWMechanics
if(distanceBetweenResult <= mMaxDist * mMaxDist) 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; return true;
mMaxDist = 470; mMaxDist = 470;
} }

@ -15,11 +15,11 @@ namespace MWMechanics
/// Implementation of AiEscort /// 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 /** 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 **/ \implement AiEscort **/
AiEscort(const std::string &actorId,int duration, float x, float y, float z); AiEscort(const MWWorld::Ptr& actor,int duration, float x, float y, float z);
/// Implementation of AiEscortCell /// 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 /** 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 **/ \implement AiEscortCell **/
AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); AiEscort(const MWWorld::Ptr& actor,const std::string &cellId,int duration, float x, float y, float z);
virtual AiEscort *clone() const; virtual AiEscort *clone() const;
@ -28,7 +28,7 @@ namespace MWMechanics
virtual int getTypeId() const; virtual int getTypeId() const;
private: private:
std::string mActorId; int mActorId;
std::string mCellId; std::string mCellId;
float mX; float mX;
float mY; float mY;

@ -11,23 +11,26 @@
#include "steering.hpp" #include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) 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), mActorId(actorId), mCellId(""), AiPackage() : mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(""), AiPackage()
{ {
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
} }
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) 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), mActorId(actorId), mCellId(cellId), AiPackage() : mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(cellId), AiPackage()
{ {
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
} }
MWMechanics::AiFollow::AiFollow(const std::string &actorId) MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), AiPackage() : mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mCellId(""), AiPackage()
{ {
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
} }
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
{ {
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); //The target to follow const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow
if(target == MWWorld::Ptr()) return true; //Target doesn't exist if(target == MWWorld::Ptr()) return true; //Target doesn't exist
@ -75,7 +78,8 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
std::string MWMechanics::AiFollow::getFollowedActor() std::string MWMechanics::AiFollow::getFollowedActor()
{ {
return mActorId; const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow
return target.getCellRef().mRefID;
} }
MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const

@ -10,16 +10,16 @@ namespace MWMechanics
{ {
/// \brief AiPackage for an actor to follow another actor/the PC /// \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 /** 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: public:
/// Follow Actor for duration or until you arrive at a world position /// Follow Actor for duration or until you arrive at a world position
AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); AiFollow(const MWWorld::Ptr& actor,float duration, float X, float Y, float Z);
/// Follow Actor for duration or until you arrive at a position in a cell /// Follow Actor for duration or until you arrive at a position in a cell
AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); AiFollow(const MWWorld::Ptr& actor,const std::string &CellId,float duration, float X, float Y, float Z);
/// Follow Actor indefinitively /// Follow Actor indefinitively
AiFollow(const std::string &ActorId); AiFollow(const MWWorld::Ptr& actor);
virtual AiFollow *clone() const; virtual AiFollow *clone() const;
@ -38,8 +38,8 @@ namespace MWMechanics
float mX; float mX;
float mY; float mY;
float mZ; float mZ;
std::string mActorId; int mActorId; // The actor we should follow
std::string mCellId; std::string mCellId;
}; };
} }
#endif #endif

@ -7,12 +7,14 @@
#include "../mwworld/action.hpp" #include "../mwworld/action.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
MWMechanics::AiPursue::AiPursue(const std::string &objectId) MWMechanics::AiPursue::AiPursue(const MWWorld::Ptr& actor)
: mObjectId(objectId) : mActorId(actor.getClass().getCreatureStats(actor).getActorId())
{ {
} }
MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const
@ -23,7 +25,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration)
{ {
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow
if(target == MWWorld::Ptr()) if(target == MWWorld::Ptr())
return true; //Target doesn't exist return true; //Target doesn't exist
@ -33,8 +35,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration)
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); target.getClass().activate(target,actor).get()->execute(actor); //Arrest player
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player
return true; return true;
} }
else { else {

@ -16,14 +16,14 @@ namespace MWMechanics
{ {
public: public:
///Constructor ///Constructor
/** \param objectId Actor to pursue **/ /** \param actor Actor to pursue **/
AiPursue(const std::string &objectId); AiPursue(const MWWorld::Ptr& actor);
virtual AiPursue *clone() const; virtual AiPursue *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
virtual int getTypeId() const; virtual int getTypeId() const;
private: private:
std::string mObjectId; int mActorId; // The actor to pursue
int mCellX; int mCellX;
int mCellY; int mCellY;
}; };

@ -175,7 +175,8 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
else if (it->mType == ESM::AI_Escort) else if (it->mType == ESM::AI_Escort)
{ {
ESM::AITarget data = it->mTarget; ESM::AITarget data = it->mTarget;
package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); 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);
} }
else if (it->mType == ESM::AI_Travel) else if (it->mType == ESM::AI_Travel)
{ {
@ -190,7 +191,8 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
else //if (it->mType == ESM::AI_Follow) else //if (it->mType == ESM::AI_Follow)
{ {
ESM::AITarget data = it->mTarget; ESM::AITarget data = it->mTarget;
package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); 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);
} }
mPackages.push_back(package); mPackages.push_back(package);
} }

@ -431,6 +431,11 @@ void CharacterController::playRandomDeath(float startpoint)
mDeathState = static_cast<CharacterState>(CharState_Death1 + (selected-1)); mDeathState = static_cast<CharacterState>(CharState_Death1 + (selected-1));
} }
// For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually.
mMovementState = CharState_None;
mAnimation->disable(mCurrentMovement);
mCurrentMovement = "";
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", startpoint, 0); false, 1.0f, "start", "stop", startpoint, 0);
} }
@ -1367,9 +1372,9 @@ bool CharacterController::kill()
{ {
if( isDead() ) if( isDead() )
{ {
//player's death animation is over
if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) ) if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) )
{ {
//player's death animation is over
MWBase::Environment::get().getStateManager()->askLoadRecent(); MWBase::Environment::get().getStateManager()->askLoadRecent();
} }
return false; return false;

@ -202,7 +202,9 @@ public:
void skipAnim(); void skipAnim();
bool isAnimPlaying(const std::string &groupName); bool isAnimPlaying(const std::string &groupName);
/// @return false if the character has already been killed before
bool kill(); bool kill();
void resurrect(); void resurrect();
bool isDead() const bool isDead() const
{ return mDeathState != CharState_None; } { return mDeathState != CharState_None; }

@ -11,6 +11,8 @@
namespace MWMechanics namespace MWMechanics
{ {
int CreatureStats::sActorId = 0;
CreatureStats::CreatureStats() CreatureStats::CreatureStats()
: mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
mTalkedTo (false), mAlarmed (false), mTalkedTo (false), mAlarmed (false),
@ -20,7 +22,7 @@ namespace MWMechanics
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false),
mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f),
mTradeTime(0,0), mGoldPool(0) mTradeTime(0,0), mGoldPool(0), mActorId(-1)
{ {
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
mAiSettings[i] = 0; mAiSettings[i] = 0;
@ -494,13 +496,15 @@ namespace MWMechanics
state.mBlock = mBlock; state.mBlock = mBlock;
state.mMovementFlags = mMovementFlags; state.mMovementFlags = mMovementFlags;
state.mAttackStrength = mAttackStrength; state.mAttackStrength = mAttackStrength;
state.mFallHeight = mFallHeight; state.mFallHeight = mFallHeight; // TODO: vertical velocity (move from PhysicActor to CreatureStats?)
state.mLastHitObject = mLastHitObject; state.mLastHitObject = mLastHitObject;
state.mRecalcDynamicStats = mRecalcDynamicStats; state.mRecalcDynamicStats = mRecalcDynamicStats;
state.mDrawState = mDrawState; state.mDrawState = mDrawState;
state.mLevel = mLevel; state.mLevel = mLevel;
state.mActorId = mActorId;
mSpells.writeState(state.mSpells); mSpells.writeState(state.mSpells);
mActiveSpells.writeState(state.mActiveSpells);
} }
void CreatureStats::readState (const ESM::CreatureStats& state) void CreatureStats::readState (const ESM::CreatureStats& state)
@ -536,8 +540,10 @@ namespace MWMechanics
mRecalcDynamicStats = state.mRecalcDynamicStats; mRecalcDynamicStats = state.mRecalcDynamicStats;
mDrawState = DrawState_(state.mDrawState); mDrawState = DrawState_(state.mDrawState);
mLevel = state.mLevel; mLevel = state.mLevel;
mActorId = state.mActorId;
mSpells.readState(state.mSpells); mSpells.readState(state.mSpells);
mActiveSpells.readState(state.mActiveSpells);
} }
// Relates to NPC gold reset delay // Relates to NPC gold reset delay
@ -559,4 +565,34 @@ namespace MWMechanics
{ {
return mGoldPool; return mGoldPool;
} }
int CreatureStats::getActorId()
{
if (mActorId==-1)
mActorId = sActorId++;
return mActorId;
}
bool CreatureStats::matchesActorId (int id) const
{
return mActorId!=-1 && id==mActorId;
}
void CreatureStats::cleanup()
{
sActorId = 0;
}
void CreatureStats::writeActorIdCounter (ESM::ESMWriter& esm)
{
esm.startRecord(ESM::REC_ACTC);
esm.writeHNT("COUN", sActorId);
esm.endRecord(ESM::REC_ACTC);
}
void CreatureStats::readActorIdCounter (ESM::ESMReader& esm)
{
esm.getHNT(sActorId, "COUN");
}
} }

@ -24,6 +24,7 @@ namespace MWMechanics
/// ///
class CreatureStats class CreatureStats
{ {
static int sActorId;
DrawState_ mDrawState; DrawState_ mDrawState;
AttributeValue mAttributes[8]; AttributeValue mAttributes[8];
DynamicStat<float> mDynamic[3]; // health, magicka, fatigue DynamicStat<float> mDynamic[3]; // health, magicka, fatigue
@ -58,6 +59,7 @@ namespace MWMechanics
MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay
int mGoldPool; // the pool of merchant gold not in inventory int mGoldPool; // the pool of merchant gold not in inventory
int mActorId;
protected: protected:
// These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods. // These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods.
@ -221,21 +223,39 @@ namespace MWMechanics
void setLastHitObject(const std::string &objectid); void setLastHitObject(const std::string &objectid);
const std::string &getLastHitObject() const; const std::string &getLastHitObject() const;
// Note, this is just a cache to avoid checking the whole container store every frame TODO: Put it somewhere else? // Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves.
// TODO: Put it somewhere else?
std::set<int> mBoundItems; std::set<int> mBoundItems;
// Same as above
std::map<int, std::string> mSummonedCreatures; // TODO: store in savegame
// TODO: encapsulate?
// <ESM::MagicEffect index, actor index>
std::map<int, int> mSummonedCreatures;
// Contains summoned creatures with an expired lifetime that have not been deleted yet.
std::vector<int> mSummonGraveyard;
void writeState (ESM::CreatureStats& state) const; void writeState (ESM::CreatureStats& state) const;
void readState (const ESM::CreatureStats& state); void readState (const ESM::CreatureStats& state);
static void writeActorIdCounter (ESM::ESMWriter& esm);
static void readActorIdCounter (ESM::ESMReader& esm);
// Relates to NPC gold reset delay // Relates to NPC gold reset delay
void setTradeTime(MWWorld::TimeStamp tradeTime); void setTradeTime(MWWorld::TimeStamp tradeTime);
MWWorld::TimeStamp getTradeTime() const; MWWorld::TimeStamp getTradeTime() const;
void setGoldPool(int pool); void setGoldPool(int pool);
int getGoldPool() const; int getGoldPool() const;
int getActorId();
///< Will generate an actor ID, if the actor does not have one yet.
bool matchesActorId (int id) const;
///< Check if \a id matches the actor ID of *this (if the actor does not have an ID
/// assigned this function will return false).
static void cleanup();
}; };
} }

@ -56,7 +56,7 @@ namespace MWMechanics
struct EffectSourceVisitor struct EffectSourceVisitor
{ {
virtual void visit (MWMechanics::EffectKey key, virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& casterHandle, const std::string& sourceName, int casterActorId,
float magnitude, float remainingTime = -1) = 0; float magnitude, float remainingTime = -1) = 0;
}; };

@ -25,11 +25,11 @@ namespace
bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim)
{ {
const std::string& owner = item.getCellRef().mOwner; const std::string& owner = item.getCellRef().mOwner;
bool isOwned = !owner.empty(); bool isOwned = !owner.empty() && owner != "player";
const std::string& faction = item.getCellRef().mFaction; const std::string& faction = item.getCellRef().mFaction;
bool isFactionOwned = false; bool isFactionOwned = false;
if (!faction.empty()) if (!faction.empty() && ptr.getClass().isNpc())
{ {
const std::map<std::string, int>& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); const std::map<std::string, int>& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks();
if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end())
@ -385,7 +385,7 @@ namespace MWMechanics
// have been made for them. Make sure they're properly updated. // have been made for them. Make sure they're properly updated.
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();
mActors.removeActor(ptr); mActors.removeActor(ptr);
mActors.addActor(ptr); mActors.addActor(ptr, true);
} }
mActors.update(duration, paused); mActors.update(duration, paused);

@ -140,7 +140,7 @@ namespace MWMechanics
float x = (willpower + 0.1 * luck) * stats.getFatigueTerm(); float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa // This makes spells that are easy to cast harder to resist and vice versa
if (spell != NULL && caster.getClass().isActor()) if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor())
{ {
float castChance = getSpellSuccessChance(spell, caster); float castChance = getSpellSuccessChance(spell, caster);
if (castChance > 0) if (castChance > 0)
@ -189,6 +189,9 @@ namespace MWMechanics
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
{ {
if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())
return;
// If none of the effects need to apply, we can early-out // If none of the effects need to apply, we can early-out
bool found = false; bool found = false;
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
@ -219,10 +222,12 @@ namespace MWMechanics
} }
ESM::EffectList reflectedEffects; ESM::EffectList reflectedEffects;
std::vector<ActiveSpells::Effect> appliedLastingEffects; std::vector<ActiveSpells::ActiveEffect> appliedLastingEffects;
bool firstAppliedEffect = true; bool firstAppliedEffect = true;
bool anyHarmfulEffect = false; bool anyHarmfulEffect = false;
bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player");
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
effectIt!=effects.mList.end(); ++effectIt) effectIt!=effects.mList.end(); ++effectIt)
{ {
@ -235,7 +240,7 @@ namespace MWMechanics
if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate) if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate)
{ {
if (caster.getRefData().getHandle() == "player") if (castByPlayer)
MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
continue; continue;
} }
@ -246,13 +251,13 @@ namespace MWMechanics
effectIt->mEffectID == ESM::MagicEffect::Mark || effectIt->mEffectID == ESM::MagicEffect::Mark ||
effectIt->mEffectID == ESM::MagicEffect::Recall)) effectIt->mEffectID == ESM::MagicEffect::Recall))
{ {
if (caster.getRefData().getHandle() == "player") if (castByPlayer)
MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}");
continue; continue;
} }
// If player is healing someone, show the target's HP bar // If player is healing someone, show the target's HP bar
if (caster.getRefData().getHandle() == "player" && target != caster if (castByPlayer && target != caster
&& effectIt->mEffectID == ESM::MagicEffect::RestoreHealth && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth
&& target.getClass().isActor()) && target.getClass().isActor())
MWBase::Environment::get().getWindowManager()->setEnemy(target); MWBase::Environment::get().getWindowManager()->setEnemy(target);
@ -263,7 +268,7 @@ namespace MWMechanics
anyHarmfulEffect = true; anyHarmfulEffect = true;
// If player is attempting to cast a harmful spell, show the target's HP bar // If player is attempting to cast a harmful spell, show the target's HP bar
if (caster.getRefData().getHandle() == "player" && target != caster) if (castByPlayer && target != caster)
MWBase::Environment::get().getWindowManager()->setEnemy(target); MWBase::Environment::get().getWindowManager()->setEnemy(target);
// Try absorbing if it's a spell // Try absorbing if it's a spell
@ -329,8 +334,9 @@ namespace MWMechanics
bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);
if (target.getClass().isActor() && hasDuration) if (target.getClass().isActor() && hasDuration)
{ {
ActiveSpells::Effect effect; ActiveSpells::ActiveEffect effect;
effect.mKey = MWMechanics::EffectKey(*effectIt); effect.mEffectId = effectIt->mEffectID;
effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;
effect.mDuration = effectIt->mDuration; effect.mDuration = effectIt->mDuration;
effect.mMagnitude = magnitude; effect.mMagnitude = magnitude;
@ -338,17 +344,20 @@ namespace MWMechanics
// For absorb effects, also apply the effect to the caster - but with a negative // For absorb effects, also apply the effect to the caster - but with a negative
// magnitude, since we're transfering stats from the target to the caster // magnitude, since we're transfering stats from the target to the caster
for (int i=0; i<5; ++i) if (!caster.isEmpty() && caster.getClass().isActor())
{ {
if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) for (int i=0; i<5; ++i)
{ {
std::vector<ActiveSpells::Effect> effects; if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i)
ActiveSpells::Effect effect_ = effect; {
effect_.mMagnitude *= -1; std::vector<ActiveSpells::ActiveEffect> effects;
effects.push_back(effect_); ActiveSpells::ActiveEffect effect_ = effect;
// Also make sure to set casterHandle = target, so that the effect on the caster gets purged when the target dies effect_.mMagnitude *= -1;
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, effects.push_back(effect_);
effects, mSourceName, target.getRefData().getHandle()); // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
effects, mSourceName, target.getClass().getCreatureStats(target).getActorId());
}
} }
} }
} }
@ -389,6 +398,7 @@ namespace MWMechanics
else else
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit"); castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");
// TODO: VFX are no longer active after saving/reloading the game
bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx; bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
// Note: in case of non actor, a free effect should be fine as well // Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
@ -405,12 +415,17 @@ namespace MWMechanics
inflict(caster, target, reflectedEffects, range, true); inflict(caster, target, reflectedEffects, range, true);
if (!appliedLastingEffects.empty()) if (!appliedLastingEffects.empty())
{
int casterActorId = -1;
if (caster.getClass().isActor())
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
mSourceName, caster.getRefData().getHandle()); mSourceName, casterActorId);
}
if (anyHarmfulEffect && target.getClass().isActor() && target != caster // Notify the target actor they've been hit
&& target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor())
MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault); target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
} }
void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude)
@ -431,7 +446,8 @@ namespace MWMechanics
if (target.getCellRef().mLockLevel > 0) if (target.getCellRef().mLockLevel > 0)
{ {
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f);
MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); if (!caster.isEmpty() && caster.getClass().isActor())
MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target);
} }
target.getCellRef().mLockLevel = -abs(target.getCellRef().mLockLevel); //unlocks the door target.getCellRef().mLockLevel = -abs(target.getCellRef().mLockLevel); //unlocks the door
} }

@ -61,9 +61,12 @@ namespace MWMechanics
/// @note Auto detects if spell, ingredient or potion /// @note Auto detects if spell, ingredient or potion
bool cast (const std::string& id); bool cast (const std::string& id);
/// @note \a target can be any type of object, not just actors.
/// @note \a caster can be any type of object, or even an empty object.
void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false); const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false);
/// @note \a caster can be any type of object, or even an empty object.
void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude);
}; };

@ -211,7 +211,7 @@ namespace MWMechanics
random = it->second.at(i); random = it->second.at(i);
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude); visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, -1, magnitude);
} }
} }
} }

@ -91,6 +91,7 @@ namespace MWScript
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
Interpreter::Type_Float duration = runtime[0].mFloat; Interpreter::Type_Float duration = runtime[0].mFloat;
runtime.pop(); runtime.pop();
@ -107,7 +108,7 @@ namespace MWScript
// discard additional arguments (reset), because we have no idea what they mean. // discard additional arguments (reset), because we have no idea what they mean.
for (unsigned int i=0; i<arg0; ++i) runtime.pop(); for (unsigned int i=0; i<arg0; ++i) runtime.pop();
MWMechanics::AiEscort escortPackage(actorID, duration, x, y, z); MWMechanics::AiEscort escortPackage(actor, duration, x, y, z);
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration
@ -126,6 +127,7 @@ namespace MWScript
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
@ -145,7 +147,7 @@ namespace MWScript
// discard additional arguments (reset), because we have no idea what they mean. // discard additional arguments (reset), because we have no idea what they mean.
for (unsigned int i=0; i<arg0; ++i) runtime.pop(); for (unsigned int i=0; i<arg0; ++i) runtime.pop();
MWMechanics::AiEscort escortPackage(actorID, cellID, duration, x, y, z); MWMechanics::AiEscort escortPackage(actor, cellID, duration, x, y, z);
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration
@ -281,6 +283,7 @@ namespace MWScript
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
Interpreter::Type_Float duration = runtime[0].mFloat; Interpreter::Type_Float duration = runtime[0].mFloat;
runtime.pop(); runtime.pop();
@ -297,7 +300,7 @@ namespace MWScript
// discard additional arguments (reset), because we have no idea what they mean. // discard additional arguments (reset), because we have no idea what they mean.
for (unsigned int i=0; i<arg0; ++i) runtime.pop(); for (unsigned int i=0; i<arg0; ++i) runtime.pop();
MWMechanics::AiFollow followPackage(actorID, duration, x, y ,z); MWMechanics::AiFollow followPackage(actor, duration, x, y ,z);
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr); MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration
@ -316,6 +319,7 @@ namespace MWScript
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
@ -335,8 +339,8 @@ namespace MWScript
// discard additional arguments (reset), because we have no idea what they mean. // discard additional arguments (reset), because we have no idea what they mean.
for (unsigned int i=0; i<arg0; ++i) runtime.pop(); for (unsigned int i=0; i<arg0; ++i) runtime.pop();
MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y ,z); MWMechanics::AiFollow followPackage(actor, cellID, duration, x, y ,z);
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration
<< std::endl; << std::endl;
} }

@ -28,6 +28,7 @@
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwscript/globalscripts.hpp" #include "../mwscript/globalscripts.hpp"
@ -46,6 +47,8 @@ void MWState::StateManager::cleanup (bool force)
mState = State_NoGame; mState = State_NoGame;
mCharacterManager.clearCurrentCharacter(); mCharacterManager.clearCurrentCharacter();
mTimePlayed = 0; mTimePlayed = 0;
MWMechanics::CreatureStats::cleanup();
} }
} }
@ -315,6 +318,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
case ESM::REC_CSTA: case ESM::REC_CSTA:
case ESM::REC_WTHR: case ESM::REC_WTHR:
case ESM::REC_DYNA: case ESM::REC_DYNA:
case ESM::REC_ACTC:
MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap);
break; break;

@ -22,7 +22,6 @@ namespace MWWorld
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it) for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
{ {
std::cout << "teleporting someone!" << (*it).getCellRef().mRefID;
executeImp(*it); executeImp(*it);
} }

@ -12,10 +12,14 @@
#include <components/esm/npcstate.hpp> #include <components/esm/npcstate.hpp>
#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/doorstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "ptr.hpp" #include "ptr.hpp"
#include "esmstore.hpp" #include "esmstore.hpp"
#include "class.hpp" #include "class.hpp"
@ -41,6 +45,22 @@ namespace
return MWWorld::Ptr(); return MWWorld::Ptr();
} }
template<typename T>
MWWorld::Ptr searchViaActorId (MWWorld::CellRefList<T>& actorList, int actorId,
MWWorld::CellStore *cell)
{
for (typename MWWorld::CellRefList<T>::List::iterator iter (actorList.mList.begin());
iter!=actorList.mList.end(); ++iter)
{
MWWorld::Ptr actor (&*iter, cell);
if (actor.getClass().getCreatureStats (actor).matchesActorId (actorId) && actor.getRefData().getCount() > 0)
return actor;
}
return MWWorld::Ptr();
}
template<typename RecordType, typename T> template<typename RecordType, typename T>
void writeReferenceCollection (ESM::ESMWriter& writer, void writeReferenceCollection (ESM::ESMWriter& writer,
const MWWorld::CellRefList<T>& collection) const MWWorld::CellRefList<T>& collection)
@ -323,6 +343,17 @@ namespace MWWorld
return Ptr(); return Ptr();
} }
Ptr CellStore::searchViaActorId (int id)
{
if (Ptr ptr = ::searchViaActorId (mNpcs, id, this))
return ptr;
if (Ptr ptr = ::searchViaActorId (mCreatures, id, this))
return ptr;
return Ptr();
}
float CellStore::getWaterLevel() const float CellStore::getWaterLevel() const
{ {
return mWaterLevel; return mWaterLevel;
@ -561,9 +592,9 @@ 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::ObjectState> (writer, mCreatureLists); writeReferenceCollection<ESM::CreatureLevListState> (writer, mCreatureLists);
writeReferenceCollection<ESM::ObjectState> (writer, mItemLists); writeReferenceCollection<ESM::ObjectState> (writer, mItemLists);
writeReferenceCollection<ESM::LightState> (writer, mLights); writeReferenceCollection<ESM::LightState> (writer, mLights);
writeReferenceCollection<ESM::ObjectState> (writer, mLockpicks); writeReferenceCollection<ESM::ObjectState> (writer, mLockpicks);
@ -629,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:
@ -639,7 +670,7 @@ namespace MWWorld
case ESM::REC_LEVC: case ESM::REC_LEVC:
readReferenceCollection<ESM::ObjectState> (reader, mCreatureLists, contentFileMap); readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, contentFileMap);
break; break;
case ESM::REC_LEVI: case ESM::REC_LEVI:

@ -91,6 +91,9 @@ namespace MWWorld
Ptr searchViaHandle (const std::string& handle); Ptr searchViaHandle (const std::string& handle);
///< Will return an empty Ptr if cell is not loaded. ///< Will return an empty Ptr if cell is not loaded.
Ptr searchViaActorId (int id);
///< Will return an empty Ptr if cell is not loaded.
float getWaterLevel() const; float getWaterLevel() const;
void setWaterLevel (float level); void setWaterLevel (float level);

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

@ -599,7 +599,7 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i]; const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i];
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
magnitude *= params.mMultiplier; magnitude *= params.mMultiplier;
visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), "", magnitude); visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), -1, magnitude);
++i; ++i;
} }

@ -507,4 +507,24 @@ namespace MWWorld
} }
return false; return false;
} }
Ptr Scene::searchPtrViaHandle (const std::string& handle)
{
for (CellStoreCollection::const_iterator iter (mActiveCells.begin());
iter!=mActiveCells.end(); ++iter)
if (Ptr ptr = (*iter)->searchViaHandle (handle))
return ptr;
return Ptr();
}
Ptr Scene::searchPtrViaActorId (int actorId)
{
for (CellStoreCollection::const_iterator iter (mActiveCells.begin());
iter!=mActiveCells.end(); ++iter)
if (Ptr ptr = (*iter)->searchViaActorId (actorId))
return ptr;
return Ptr();
}
} }

@ -104,6 +104,10 @@ namespace MWWorld
///< Remove an object from the scene, but not from the world model. ///< Remove an object from the scene, but not from the world model.
bool isCellActive(const CellStore &cell); bool isCellActive(const CellStore &cell);
Ptr searchPtrViaHandle (const std::string& handle);
Ptr searchPtrViaActorId (int actorId);
}; };
} }

@ -275,7 +275,8 @@ namespace MWWorld
+mStore.countSavedGameRecords() +mStore.countSavedGameRecords()
+mGlobalVariables.countSavedGameRecords() +mGlobalVariables.countSavedGameRecords()
+1 // player record +1 // player record
+1; // weather record +1 // weather record
+1; // actorId counter
} }
void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
@ -288,6 +289,9 @@ namespace MWWorld
mRendering->writeFog(cellstore); mRendering->writeFog(cellstore);
} }
MWMechanics::CreatureStats::writeActorIdCounter(writer);
progress.increaseProgress();
mCells.write (writer, progress); mCells.write (writer, progress);
mStore.write (writer, progress); mStore.write (writer, progress);
mGlobalVariables.write (writer, progress); mGlobalVariables.write (writer, progress);
@ -298,6 +302,12 @@ namespace MWWorld
void World::readRecord (ESM::ESMReader& reader, int32_t type, void World::readRecord (ESM::ESMReader& reader, int32_t type,
const std::map<int, int>& contentFileMap) const std::map<int, int>& contentFileMap)
{ {
if (type == ESM::REC_ACTC)
{
MWMechanics::CreatureStats::readActorIdCounter(reader);
return;
}
if (!mStore.readRecord (reader, type) && if (!mStore.readRecord (reader, type) &&
!mGlobalVariables.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) &&
!mPlayer->readRecord (reader, type) && !mPlayer->readRecord (reader, type) &&
@ -547,17 +557,17 @@ namespace MWWorld
{ {
if (mPlayer->getPlayer().getRefData().getHandle()==handle) if (mPlayer->getPlayer().getRefData().getHandle()==handle)
return mPlayer->getPlayer(); return mPlayer->getPlayer();
for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin());
iter!=mWorldScene->getActiveCells().end(); ++iter)
{
CellStore* cellstore = *iter;
Ptr ptr = cellstore->searchViaHandle (handle);
if (!ptr.isEmpty()) return mWorldScene->searchPtrViaHandle (handle);
return ptr; }
}
return MWWorld::Ptr(); Ptr World::searchPtrViaActorId (int actorId)
{
// The player is not registered in any CellStore so must be checked manually
if (actorId == getPlayerPtr().getClass().getCreatureStats(getPlayerPtr()).getActorId())
return getPlayerPtr();
// Now search cells
return mWorldScene->searchPtrViaActorId (actorId);
} }
void World::addContainerScripts(const Ptr& reference, CellStore * cell) void World::addContainerScripts(const Ptr& reference, CellStore * cell)
@ -1196,36 +1206,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;
} }
@ -1843,31 +1865,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);
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(); 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)
@ -2231,7 +2254,7 @@ namespace MWWorld
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed)
{ {
ProjectileState state; ProjectileState state;
state.mActorHandle = actor.getRefData().getHandle(); state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mBow = bow; state.mBow = bow;
state.mVelocity = orient.yAxis() * speed; state.mVelocity = orient.yAxis() * speed;
@ -2318,7 +2341,7 @@ namespace MWWorld
MagicBoltState state; MagicBoltState state;
state.mSourceName = sourceName; state.mSourceName = sourceName;
state.mId = id; state.mId = id;
state.mActorHandle = actor.getRefData().getHandle(); state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mSpeed = speed; state.mSpeed = speed;
state.mStack = stack; state.mStack = stack;
@ -2382,7 +2405,7 @@ namespace MWWorld
if (obstacle == ptr) if (obstacle == ptr)
continue; continue;
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
// Arrow intersects with player immediately after shooting :/ // Arrow intersects with player immediately after shooting :/
if (obstacle == caster) if (obstacle == caster)
@ -2468,7 +2491,7 @@ namespace MWWorld
if (obstacle == ptr) if (obstacle == ptr)
continue; continue;
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
if (caster.isEmpty()) if (caster.isEmpty())
caster = obstacle; caster = obstacle;
@ -2491,7 +2514,7 @@ namespace MWWorld
if (explode) if (explode)
{ {
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); 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); explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName);
deleteObject(ptr); deleteObject(ptr);

@ -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
{ {
@ -96,7 +96,7 @@ namespace MWWorld
std::string mId; std::string mId;
// Actor who casted this projectile // Actor who casted this projectile
std::string mActorHandle; int mActorId;
// Name of item to display as effect source in magic menu (in case we casted an enchantment) // Name of item to display as effect source in magic menu (in case we casted an enchantment)
std::string mSourceName; std::string mSourceName;
@ -111,7 +111,7 @@ namespace MWWorld
struct ProjectileState struct ProjectileState
{ {
// Actor who shot this projectile // Actor who shot this projectile
std::string mActorHandle; int mActorId;
MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from
@ -287,6 +287,9 @@ namespace MWWorld
virtual Ptr searchPtrViaHandle (const std::string& handle); virtual Ptr searchPtrViaHandle (const std::string& handle);
///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found ///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found
virtual Ptr searchPtrViaActorId (int actorId);
///< Search is limited to the active cells.
virtual void adjustPosition (const Ptr& ptr); virtual void adjustPosition (const Ptr& ptr);
///< Adjust position after load to be on ground. Must be called after model load. ///< Adjust position after load to be on ground. Must be called after model load.
@ -493,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 npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate
) )
add_component_dir (misc add_component_dir (misc

@ -0,0 +1,56 @@
#include "activespells.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
void ActiveSpells::save(ESMWriter &esm) const
{
for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
esm.writeHNString ("ID__", it->first);
const ActiveSpellParams& params = it->second;
esm.writeHNT ("CAST", params.mCasterActorId);
esm.writeHNString ("DISP", params.mDisplayName);
esm.writeHNT ("TIME", params.mTimeStamp);
for (std::vector<ActiveEffect>::const_iterator effectIt = params.mEffects.begin(); effectIt != params.mEffects.end(); ++effectIt)
{
esm.writeHNT ("MGEF", effectIt->mEffectId);
if (effectIt->mArg != -1)
esm.writeHNT ("ARG_", effectIt->mArg);
esm.writeHNT ("MAGN", effectIt->mMagnitude);
esm.writeHNT ("DURA", effectIt->mDuration);
}
}
}
void ActiveSpells::load(ESMReader &esm)
{
while (esm.isNextSub("ID__"))
{
std::string spellId = esm.getHString();
ActiveSpellParams params;
esm.getHNT (params.mCasterActorId, "CAST");
params.mDisplayName = esm.getHNString ("DISP");
esm.getHNT (params.mTimeStamp, "TIME");
while (esm.isNextSub("MGEF"))
{
ActiveEffect effect;
esm.getHT(effect.mEffectId);
effect.mArg = -1;
esm.getHNOT(effect.mArg, "ARG_");
esm.getHNT (effect.mMagnitude, "MAGN");
esm.getHNT (effect.mDuration, "DURA");
params.mEffects.push_back(effect);
}
mSpells.insert(std::make_pair(spellId, params));
}
}
}

@ -0,0 +1,45 @@
#ifndef OPENMW_ESM_ACTIVESPELLS_H
#define OPENMW_ESM_ACTIVESPELLS_H
#include "effectlist.hpp"
#include "defs.hpp"
#include <string>
#include <map>
namespace ESM
{
class ESMReader;
class ESMWriter;
// Parameters of an effect concerning lasting effects.
// Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc.
// It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster.
struct ActiveEffect
{
int mEffectId;
float mMagnitude;
int mArg; // skill or attribute
float mDuration;
};
// format 0, saved games only
struct ActiveSpells
{
struct ActiveSpellParams
{
std::vector<ActiveEffect> mEffects;
ESM::TimeStamp mTimeStamp;
std::string mDisplayName;
int mCasterActorId;
};
typedef std::multimap<std::string, ActiveSpellParams > TContainer;
TContainer mSpells;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

@ -0,0 +1,25 @@
#include "creaturelevliststate.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
void CreatureLevListState::load(ESMReader &esm)
{
ObjectState::load(esm);
mSpawnActorId = -1;
esm.getHNOT (mSpawnActorId, "SPAW");
}
void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const
{
ObjectState::save(esm, inInventory);
if (mSpawnActorId != -1)
esm.writeHNT ("SPAW", mSpawnActorId);
}
}

@ -0,0 +1,19 @@
#ifndef OPENMW_ESM_CREATURELEVLISTSTATE_H
#define OPENMW_ESM_CREATURELEVLISTSTATE_H
#include "objectstate.hpp"
namespace ESM
{
// format 0, saved games only
struct CreatureLevListState : public ObjectState
{
int mSpawnActorId;
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;
};
}
#endif

@ -1,4 +1,3 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
void ESM::CreatureStats::load (ESMReader &esm) void ESM::CreatureStats::load (ESMReader &esm)
@ -72,7 +71,11 @@ void ESM::CreatureStats::load (ESMReader &esm)
mLevel = 1; mLevel = 1;
esm.getHNOT (mLevel, "LEVL"); esm.getHNOT (mLevel, "LEVL");
mActorId = -1;
esm.getHNOT (mActorId, "ACID");
mSpells.load(esm); mSpells.load(esm);
mActiveSpells.load(esm);
} }
void ESM::CreatureStats::save (ESMWriter &esm) const void ESM::CreatureStats::save (ESMWriter &esm) const
@ -146,5 +149,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (mLevel != 1) if (mLevel != 1)
esm.writeHNT ("LEVL", mLevel); esm.writeHNT ("LEVL", mLevel);
if (mActorId != -1)
esm.writeHNT ("ACID", mActorId);
mSpells.save(esm); mSpells.save(esm);
mActiveSpells.save(esm);
} }

@ -10,6 +10,7 @@
#include "defs.hpp" #include "defs.hpp"
#include "spellstate.hpp" #include "spellstate.hpp"
#include "activespells.hpp"
namespace ESM namespace ESM
{ {
@ -24,6 +25,7 @@ namespace ESM
ESM::TimeStamp mTradeTime; ESM::TimeStamp mTradeTime;
int mGoldPool; int mGoldPool;
int mActorId;
bool mDead; bool mDead;
bool mDied; bool mDied;
@ -48,6 +50,7 @@ namespace ESM
int mLevel; int mLevel;
SpellState mSpells; SpellState mSpells;
ActiveSpells mActiveSpells;
void load (ESMReader &esm); void load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;

@ -108,6 +108,7 @@ enum RecNameInts
REC_KEYS = FourCC<'K','E','Y','S'>::value, REC_KEYS = FourCC<'K','E','Y','S'>::value,
REC_DYNA = FourCC<'D','Y','N','A'>::value, REC_DYNA = FourCC<'D','Y','N','A'>::value,
REC_ASPL = FourCC<'A','S','P','L'>::value, REC_ASPL = FourCC<'A','S','P','L'>::value,
REC_ACTC = FourCC<'A','C','T','C'>::value,
// format 1 // format 1
REC_FILT = 0x544C4946 REC_FILT = 0x544C4946

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

@ -74,6 +74,18 @@ class ESMWriter
endRecord(name); endRecord(name);
} }
private:
// Prevent using writeHNT with strings. This already happened by accident and results in
// state being discarded without any error on writing or reading it. :(
// writeHNString and friends must be used instead.
void writeHNT(const std::string &name, std::string data)
{
}
void writeT(const std::string& data)
{
}
public:
template<typename T> template<typename T>
void writeHNT(const std::string& name, const T& data, int size) void writeHNT(const std::string& name, const T& data, int size)
{ {

@ -140,7 +140,7 @@ void ESM::NpcStats::save (ESMWriter &esm) const
for (std::vector<std::string>::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end(); for (std::vector<std::string>::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end();
++iter) ++iter)
esm.writeHNT ("USED", *iter); esm.writeHNString ("USED", *iter);
if (mTimeToStartDrowning) if (mTimeToStartDrowning)
esm.writeHNT ("DRTI", mTimeToStartDrowning); esm.writeHNT ("DRTI", mTimeToStartDrowning);

@ -150,7 +150,7 @@ device =
master volume = 1.0 master volume = 1.0
sfx volume = 1.0 sfx volume = 1.0
music volume = 0.4 music volume = 0.4
footsteps volume = 0.6 footsteps volume = 0.15
voice volume = 1.0 voice volume = 1.0

@ -18,6 +18,7 @@ namespace Physic
PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale) PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale)
: mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0) : mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0)
, mBody(0), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0) , mBody(0), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0)
, mCollisionBody(true)
, mForce(0.0f) , mForce(0.0f)
{ {
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation);
@ -49,8 +50,9 @@ namespace Physic
void PhysicActor::enableCollisionBody(bool collision) void PhysicActor::enableCollisionBody(bool collision)
{ {
assert(mBody); assert(mBody);
if(collision && !mCollisionMode) enableCollisionBody(); if(collision && !mCollisionBody) enableCollisionBody();
if(!collision && mCollisionMode) disableCollisionBody(); if(!collision && mCollisionBody) disableCollisionBody();
mCollisionBody = collision;
} }
void PhysicActor::setPosition(const Ogre::Vector3 &pos) void PhysicActor::setPosition(const Ogre::Vector3 &pos)
@ -108,7 +110,7 @@ namespace Physic
//Create the newly scaled rigid body //Create the newly scaled rigid body
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot); mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot);
mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true); mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true);
mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map mEngine->addRigidBody(mCollisionBody ? mBody : 0, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map
} }
Ogre::Vector3 PhysicActor::getHalfExtents() const Ogre::Vector3 PhysicActor::getHalfExtents() const

@ -175,6 +175,7 @@ namespace Physic
Ogre::Vector3 mForce; Ogre::Vector3 mForce;
bool mOnGround; bool mOnGround;
bool mCollisionMode; bool mCollisionMode;
bool mCollisionBody;
std::string mMesh; std::string mMesh;
std::string mName; std::string mName;

Loading…
Cancel
Save