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;
///< 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
virtual void enable (const MWWorld::Ptr& ptr) = 0;
@ -390,14 +393,10 @@ namespace MWBase
virtual void setupPlayer() = 0;
virtual void renderPlayer() = 0;
/// if activated, should this door be opened or closed?
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0;
/// activate (open or close) an non-teleport door
/// open or close a non-teleport door (depending on current state)
virtual void activateDoor(const MWWorld::Ptr& door) = 0;
/// Is door currently opening/closing?
virtual bool getIsMovingDoor(const MWWorld::Ptr& door) = 0;
/// open or close a non-teleport door as specified
virtual void activateDoor(const MWWorld::Ptr& door, bool open) = 0;
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object

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

@ -2,6 +2,7 @@
#include "creaturelevlist.hpp"
#include <components/esm/loadlevlist.hpp>
#include <components/esm/creaturelevliststate.hpp>
#include "../mwmechanics/levelledlist.hpp"
@ -11,7 +12,9 @@ namespace
{
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;
};
@ -38,6 +41,25 @@ namespace MWClass
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const
{
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
@ -45,22 +67,29 @@ namespace MWClass
if (!ptr.getRefData().getCustomData())
{
std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
data->mSpawnActorId = -1;
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
ptr.get<ESM::CreatureLevList>();
ptr.getRefData().setCustomData(data.release());
}
}
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())
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
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);
}
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
customData.mSpawnActorId = state2.mSpawnActorId;
}
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;
///< 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 <components/esm/loaddoor.hpp>
#include <components/esm/doorstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -17,12 +18,28 @@
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/actiontrap.hpp"
#include "../mwworld/customdata.hpp"
#include "../mwgui/tooltips.hpp"
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
namespace
{
struct DoorCustomData : public MWWorld::CustomData
{
int mDoorState; // 0 = nothing, 1 = opening, 2 = closing
virtual MWWorld::CustomData *clone() const;
};
MWWorld::CustomData *DoorCustomData::clone() const
{
return new DoorCustomData (*this);
}
}
namespace MWClass
{
void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -38,6 +55,14 @@ namespace MWClass
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
// Resume the door's opening/closing animation if it wasn't finished
ensureCustomData(ptr);
const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
if (customData.mDoorState > 0)
{
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false);
}
}
std::string Door::getModel(const MWWorld::Ptr &ptr) const
@ -125,7 +150,14 @@ namespace MWClass
{
// animated door
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));
if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr))
int doorstate = getDoorState(ptr);
bool opening = true;
if (doorstate == 1)
opening = false;
if (doorstate == 0 && ptr.getRefData().getLocalRotation().rot[2] != 0)
opening = false;
if (opening)
{
MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr,
closeSound, 0.5);
@ -262,4 +294,48 @@ namespace MWClass
return MWWorld::Ptr(&cell.get<ESM::Door>().insert(*ref), &cell);
}
void Door::ensureCustomData(const MWWorld::Ptr &ptr) const
{
if (!ptr.getRefData().getCustomData())
{
std::auto_ptr<DoorCustomData> data(new DoorCustomData);
data->mDoorState = 0;
ptr.getRefData().setCustomData(data.release());
}
}
int Door::getDoorState (const MWWorld::Ptr &ptr) const
{
ensureCustomData(ptr);
const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
return customData.mDoorState;
}
void Door::setDoorState (const MWWorld::Ptr &ptr, int state) const
{
ensureCustomData(ptr);
DoorCustomData& customData = dynamic_cast<DoorCustomData&>(*ptr.getRefData().getCustomData());
customData.mDoorState = state;
}
void Door::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const
{
ensureCustomData(ptr);
DoorCustomData& customData = dynamic_cast<DoorCustomData&>(*ptr.getRefData().getCustomData());
const ESM::DoorState& state2 = dynamic_cast<const ESM::DoorState&>(state);
customData.mDoorState = state2.mDoorState;
}
void Door::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const
{
ensureCustomData(ptr);
const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
ESM::DoorState& state2 = dynamic_cast<ESM::DoorState&>(state);
state2.mDoorState = customData.mDoorState;
}
}

@ -9,6 +9,8 @@ namespace MWClass
{
class Door : public MWWorld::Class
{
void ensureCustomData (const MWWorld::Ptr& ptr) const;
virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
@ -48,7 +50,20 @@ namespace MWClass
static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
private:
/// 0 = nothing, 1 = opening, 2 = closing
virtual int getDoorState (const MWWorld::Ptr &ptr) const;
/// This does not actually cause the door to move. Use World::activateDoor instead.
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
};
}

@ -388,6 +388,8 @@ namespace MWClass
{
physics.addActor(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
@ -618,7 +620,8 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty.
// 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);
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())
{
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
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)
{
if (mActor.getClass().isNpc())
{
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
stats.modifyProfit(-item.mBase.getClass().getValue(item.mBase) * count);
}
InventoryItemModel::removeItem(item, count);

@ -13,7 +13,7 @@ namespace MWGui
public:
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);
};

@ -71,12 +71,12 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
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];
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
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)

@ -21,7 +21,7 @@ namespace MWGui
virtual ModelIndex getIndex (ItemStack item);
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 update();

@ -40,11 +40,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)
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))
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");
}
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;
// 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;
otherModel->copyItem(item, count, setNewOwner);
MWWorld::Ptr ret = otherModel->copyItem(item, count, setNewOwner);
removeItem(item, count);
return ret;
}
void InventoryItemModel::update()

@ -15,11 +15,11 @@ namespace MWGui
virtual ModelIndex getIndex (ItemStack item);
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);
/// 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();

@ -408,14 +408,8 @@ namespace MWGui
if (mDragAndDrop->mSourceModel != mTradeModel)
{
// add item to the player's inventory
MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr);
MWWorld::ContainerStoreIterator it = invStore.begin();
it = invStore.add(ptr, mDragAndDrop->mDraggedCount, mPtr);
mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount);
ptr = *it;
// Move item to the player's inventory
ptr = mDragAndDrop->mSourceModel->moveItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount, mTradeModel);
}
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);
return ret;
}
@ -83,10 +84,9 @@ namespace MWGui
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
mSourceModel->copyItem (item, count, setNewOwner);
return mSourceModel->copyItem (item, count, setNewOwner);
}
void ProxyItemModel::removeItem (const ItemStack& item, size_t count)

@ -56,10 +56,10 @@ namespace MWGui
virtual void update() = 0;
/// 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?
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;
private:
@ -73,7 +73,7 @@ namespace MWGui
{
public:
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 ModelIndex getIndex (ItemStack item);

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

@ -45,7 +45,7 @@ namespace MWGui
virtual ~EffectSourceVisitor() {}
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);
};

@ -53,9 +53,9 @@ namespace MWMechanics
{
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;
MWWorld::TimeStamp end = start;
@ -63,7 +63,7 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
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
{
const std::vector<Effect>& effects = iterator->second.mEffects;
const std::vector<ActiveEffect>& effects = iterator->second.mEffects;
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)
{
if (iter->mDuration > duration)
@ -132,8 +132,8 @@ namespace MWMechanics
return mSpells;
}
void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<Effect> effects,
const std::string &displayName, const std::string& casterHandle)
void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<ActiveEffect> effects,
const std::string &displayName, int casterActorId)
{
bool exists = false;
for (TContainer::const_iterator it = begin(); it != end(); ++it)
@ -146,7 +146,7 @@ namespace MWMechanics
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
params.mEffects = effects;
params.mDisplayName = displayName;
params.mCasterHandle = casterHandle;
params.mCasterActorId = casterActorId;
if (!exists || stack)
mSpells.insert (std::make_pair(id, params));
@ -168,7 +168,7 @@ namespace MWMechanics
{
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)
{
std::string name = it->second.mDisplayName;
@ -178,7 +178,7 @@ namespace MWMechanics
float magnitude = effectIt->mMagnitude;
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 (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin();
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end();)
{
if (effectIt->mKey.mId == effectId)
if (effectIt->mEffectId == effectId)
effectIt = it->second.mEffects.erase(effectIt);
else
++effectIt;
@ -212,16 +212,16 @@ namespace MWMechanics
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 (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin();
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
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
&& it->second.mCasterHandle == actorHandle)
&& it->second.mCasterActorId == casterActorId)
effectIt = it->second.mEffects.erase(effectIt);
else
++effectIt;
@ -229,4 +229,41 @@ namespace MWMechanics
}
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 <components/esm/defs.hpp>
#include <components/esm/activespells.hpp>
namespace MWMechanics
{
@ -21,29 +22,24 @@ namespace MWMechanics
{
public:
// 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 Effect
{
float mMagnitude;
EffectKey mKey;
float mDuration;
};
typedef ESM::ActiveEffect ActiveEffect;
struct ActiveSpellParams
{
std::vector<Effect> mEffects;
std::vector<ActiveEffect> mEffects;
MWWorld::TimeStamp mTimeStamp;
std::string mDisplayName;
// Handle to the caster that that inflicted this spell on us
std::string mCasterHandle;
// The caster that inflicted this spell on us
int mCasterActorId;
};
typedef std::multimap<std::string, ActiveSpellParams > TContainer;
typedef TContainer::const_iterator TIterator;
void readState (const ESM::ActiveSpells& state);
void writeState (ESM::ActiveSpells& state) const;
private:
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 effects
/// \param displayName Name for display in magic menu.
/// \param casterHandle
///
void addSpell (const std::string& id, bool stack, std::vector<Effect> effects,
const std::string& displayName, const std::string& casterHandle);
void addSpell (const std::string& id, bool stack, std::vector<ActiveEffect> effects,
const std::string& displayName, int casterActorId);
/// Removes the active effects from this spell/potion/.. with \a id
void removeEffects (const std::string& id);
@ -90,8 +85,11 @@ namespace MWMechanics
/// Remove all active effects, if roll succeeds (for each effect)
void purgeAll (float chance);
/// Remove all effects with CASTER_LINKED flag that were cast by \a actorHandle
void purge (const std::string& actorHandle);
/// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId
void purge (int casterActorId);
/// Remove all spells
void clear();
bool isSpellActive (std::string id) const;
///< case insensitive

@ -38,9 +38,12 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a
{
if (bound)
{
MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor);
MWWorld::ActionEquip action(newPtr);
action.execute(actor);
if (actor.getClass().getContainerStore(actor).count(item) == 0)
{
MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor);
MWWorld::ActionEquip action(newPtr);
action.execute(actor);
}
}
else
{
@ -116,7 +119,7 @@ namespace MWMechanics
: mCreature(trappedCreature) {}
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)
{
if (key.mId != ESM::MagicEffect::Soultrap)
@ -126,7 +129,7 @@ namespace MWMechanics
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())
return;
@ -534,28 +537,54 @@ namespace MWMechanics
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
ref.getPtr().getCellRef().mPos = ipos;
// TODO: Add AI to follow player and fight for him
AiFollow package(ptr.getRefData().getHandle());
MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package, ptr);
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
// 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
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle()));
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureActorId));
}
}
else
{
std::string handle = creatureStats.mSummonedCreatures[it->first];
// 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.
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle);
// Summon lifetime has expired. Try to delete the creature.
int actorId = creatureStats.mSummonedCreatures[it->first];
creatureStats.mSummonedCreatures.erase(it->first);
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId);
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);
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)
@ -730,7 +759,7 @@ namespace MWMechanics
&& MWBase::Environment::get().getWorld()->getLOS(ptr, player)
&& 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);
npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId());
}
@ -759,7 +788,7 @@ namespace MWMechanics
else if (!creatureStats.isHostile())
{
if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr);
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
else
{
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
MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied();
@ -790,6 +819,8 @@ namespace MWMechanics
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim)));
if (updateImmediately)
mActors[ptr]->update(0);
}
void Actors::removeActor (const MWWorld::Ptr& ptr)
@ -912,18 +943,17 @@ namespace MWMechanics
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())
{
++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
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
{
@ -934,6 +964,7 @@ namespace MWMechanics
// Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death
stats.setMagicEffects(MWMechanics::MagicEffects());
stats.getActiveSpells().clear();
calculateCreatureStatModifiers(iter->first, 0);
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)
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
///
/// \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
}
if(!MWBase::Environment::get().getWorld()->getIsMovingDoor(mDoorPtr))
if (!mDoorPtr.getClass().getDoorState(mDoorPtr))
return true; //Door is no longer opening
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door

@ -11,9 +11,9 @@ namespace MWMechanics
{
/// \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
**/
class AiAvoidDoor : public AiPackage
{
**/
class AiAvoidDoor : public AiPackage
{
public:
/// Avoid door until the door is fully open
AiAvoidDoor(const MWWorld::Ptr& doorPtr);
@ -29,10 +29,10 @@ namespace MWMechanics
private:
float mDuration;
const MWWorld::Ptr& mDoorPtr;
MWWorld::Ptr mDoorPtr;
ESM::Position mLastPos;
float mAdjAngle;
};
}
#endif
float mAdjAngle;
};
}
#endif

@ -81,7 +81,7 @@ namespace MWMechanics
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp
AiCombat::AiCombat(const MWWorld::Ptr& actor) :
mTarget(actor),
mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()),
mTimerAttack(0),
mTimerReact(0),
mTimerCombatMove(0),
@ -153,7 +153,9 @@ namespace MWMechanics
|| actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0)
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;
//Update every frame
@ -325,7 +327,7 @@ namespace MWMechanics
ESM::Position pos = actor.getRefData().getPosition();
Ogre::Vector3 vActorPos(pos.pos);
Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos);
Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos);
Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
bool isStuck = false;
@ -396,7 +398,7 @@ namespace MWMechanics
else // remote pathfinding
{
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;
@ -432,7 +434,7 @@ namespace MWMechanics
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
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)
//then start attacking
float speed1 = actorCls.getSpeed(actor);
float speed2 = mTarget.getClass().getSpeed(mTarget);
if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0
&& mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0)
float speed2 = target.getClass().getSpeed(target);
if(target.getClass().getMovementSettings(target).mPosition[0] == 0
&& target.getClass().getMovementSettings(target).mPosition[1] == 0)
speed2 = 0;
float s1 = distToTarget - weapRange;
@ -570,9 +572,9 @@ namespace MWMechanics
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;
@ -627,9 +629,12 @@ namespace MWMechanics
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;
///Returns target ID
const std::string &getTargetId() const;
std::string getTargetId() const;
private:
PathFinder mPathFinder;
@ -53,7 +53,7 @@ namespace MWMechanics
ESM::Position mLastPos;
MWMechanics::Movement mMovement;
MWWorld::Ptr mTarget;
int mTargetActorId;
const MWWorld::CellStore* mCell;
ObstacleCheck mObstacleCheck;
@ -63,7 +63,7 @@ namespace MWMechanics
MWWorld::CellRefList<ESM::Door>::List::iterator mDoorIter;
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/timestamp.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "steering.hpp"
#include "movement.hpp"
@ -19,8 +21,8 @@
namespace MWMechanics
{
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
AiEscort::AiEscort(const MWWorld::Ptr& actor, int duration, float x, float y, float z)
: mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mX(x), mY(y), mZ(z), mDuration(duration)
, mCellX(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)
: mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
AiEscort::AiEscort(const MWWorld::Ptr& actor, const std::string &cellId,int duration, float x, float y, float z)
: mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
, mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max())
{
@ -75,7 +77,14 @@ namespace MWMechanics
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 followerPos = follower.getRefData().getPosition().pos;
double differenceBetween[3];
@ -89,7 +98,7 @@ namespace MWMechanics
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;
mMaxDist = 470;
}

@ -15,11 +15,11 @@ namespace MWMechanics
/// 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
\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
/** 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 **/
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;
@ -28,7 +28,7 @@ namespace MWMechanics
virtual int getTypeId() const;
private:
std::string mActorId;
int mActorId;
std::string mCellId;
float mX;
float mY;

@ -11,23 +11,26 @@
#include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), AiPackage()
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), 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)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), AiPackage()
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), mCellId(cellId), AiPackage()
{
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), AiPackage()
MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor)
: 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)
{
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
@ -75,7 +78,8 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
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

@ -10,16 +10,16 @@ namespace MWMechanics
{
/// \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
**/
class AiFollow : public AiPackage
{
**/
class AiFollow : public AiPackage
{
public:
/// 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
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
AiFollow(const std::string &ActorId);
AiFollow(const MWWorld::Ptr& actor);
virtual AiFollow *clone() const;
@ -38,8 +38,8 @@ namespace MWMechanics
float mX;
float mY;
float mZ;
std::string mActorId;
std::string mCellId;
};
}
#endif
int mActorId; // The actor we should follow
std::string mCellId;
};
}
#endif

@ -7,12 +7,14 @@
#include "../mwworld/action.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "steering.hpp"
#include "movement.hpp"
#include "creaturestats.hpp"
MWMechanics::AiPursue::AiPursue(const std::string &objectId)
: mObjectId(objectId)
MWMechanics::AiPursue::AiPursue(const MWWorld::Ptr& actor)
: mActorId(actor.getClass().getCreatureStats(actor).getActorId())
{
}
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
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())
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
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player
return true;
}
else {

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

@ -175,7 +175,8 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
else if (it->mType == ESM::AI_Escort)
{
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)
{
@ -190,7 +191,8 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
else //if (it->mType == ESM::AI_Follow)
{
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);
}

@ -431,6 +431,11 @@ void CharacterController::playRandomDeath(float startpoint)
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,
false, 1.0f, "start", "stop", startpoint, 0);
}
@ -1367,9 +1372,9 @@ bool CharacterController::kill()
{
if( isDead() )
{
//player's death animation is over
if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) )
{
//player's death animation is over
MWBase::Environment::get().getStateManager()->askLoadRecent();
}
return false;

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

@ -11,6 +11,8 @@
namespace MWMechanics
{
int CreatureStats::sActorId = 0;
CreatureStats::CreatureStats()
: mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
mTalkedTo (false), mAlarmed (false),
@ -20,7 +22,7 @@ namespace MWMechanics
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false),
mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
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)
mAiSettings[i] = 0;
@ -494,13 +496,15 @@ namespace MWMechanics
state.mBlock = mBlock;
state.mMovementFlags = mMovementFlags;
state.mAttackStrength = mAttackStrength;
state.mFallHeight = mFallHeight;
state.mFallHeight = mFallHeight; // TODO: vertical velocity (move from PhysicActor to CreatureStats?)
state.mLastHitObject = mLastHitObject;
state.mRecalcDynamicStats = mRecalcDynamicStats;
state.mDrawState = mDrawState;
state.mLevel = mLevel;
state.mActorId = mActorId;
mSpells.writeState(state.mSpells);
mActiveSpells.writeState(state.mActiveSpells);
}
void CreatureStats::readState (const ESM::CreatureStats& state)
@ -536,8 +540,10 @@ namespace MWMechanics
mRecalcDynamicStats = state.mRecalcDynamicStats;
mDrawState = DrawState_(state.mDrawState);
mLevel = state.mLevel;
mActorId = state.mActorId;
mSpells.readState(state.mSpells);
mActiveSpells.readState(state.mActiveSpells);
}
// Relates to NPC gold reset delay
@ -559,4 +565,34 @@ namespace MWMechanics
{
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
{
static int sActorId;
DrawState_ mDrawState;
AttributeValue mAttributes[8];
DynamicStat<float> mDynamic[3]; // health, magicka, fatigue
@ -58,6 +59,7 @@ namespace MWMechanics
MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay
int mGoldPool; // the pool of merchant gold not in inventory
int mActorId;
protected:
// 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);
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;
// 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 readState (const ESM::CreatureStats& state);
static void writeActorIdCounter (ESM::ESMWriter& esm);
static void readActorIdCounter (ESM::ESMReader& esm);
// Relates to NPC gold reset delay
void setTradeTime(MWWorld::TimeStamp tradeTime);
MWWorld::TimeStamp getTradeTime() const;
void setGoldPool(int pool);
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
{
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;
};

@ -25,11 +25,11 @@ namespace
bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim)
{
const std::string& owner = item.getCellRef().mOwner;
bool isOwned = !owner.empty();
bool isOwned = !owner.empty() && owner != "player";
const std::string& faction = item.getCellRef().mFaction;
bool isFactionOwned = false;
if (!faction.empty())
if (!faction.empty() && ptr.getClass().isNpc())
{
const std::map<std::string, int>& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks();
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.
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();
mActors.removeActor(ptr);
mActors.addActor(ptr);
mActors.addActor(ptr, true);
}
mActors.update(duration, paused);

@ -140,7 +140,7 @@ namespace MWMechanics
float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
// 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);
if (castChance > 0)
@ -189,6 +189,9 @@ namespace MWMechanics
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
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
bool found = false;
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
@ -219,10 +222,12 @@ namespace MWMechanics
}
ESM::EffectList reflectedEffects;
std::vector<ActiveSpells::Effect> appliedLastingEffects;
std::vector<ActiveSpells::ActiveEffect> appliedLastingEffects;
bool firstAppliedEffect = true;
bool anyHarmfulEffect = false;
bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player");
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
effectIt!=effects.mList.end(); ++effectIt)
{
@ -235,7 +240,7 @@ namespace MWMechanics
if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate)
{
if (caster.getRefData().getHandle() == "player")
if (castByPlayer)
MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
continue;
}
@ -246,13 +251,13 @@ namespace MWMechanics
effectIt->mEffectID == ESM::MagicEffect::Mark ||
effectIt->mEffectID == ESM::MagicEffect::Recall))
{
if (caster.getRefData().getHandle() == "player")
if (castByPlayer)
MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}");
continue;
}
// 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
&& target.getClass().isActor())
MWBase::Environment::get().getWindowManager()->setEnemy(target);
@ -263,7 +268,7 @@ namespace MWMechanics
anyHarmfulEffect = true;
// 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);
// Try absorbing if it's a spell
@ -329,8 +334,9 @@ namespace MWMechanics
bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);
if (target.getClass().isActor() && hasDuration)
{
ActiveSpells::Effect effect;
effect.mKey = MWMechanics::EffectKey(*effectIt);
ActiveSpells::ActiveEffect effect;
effect.mEffectId = effectIt->mEffectID;
effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;
effect.mDuration = effectIt->mDuration;
effect.mMagnitude = magnitude;
@ -338,17 +344,20 @@ namespace MWMechanics
// 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
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;
ActiveSpells::Effect effect_ = effect;
effect_.mMagnitude *= -1;
effects.push_back(effect_);
// Also make sure to set casterHandle = target, so that the effect on the caster gets purged when the target dies
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
effects, mSourceName, target.getRefData().getHandle());
if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i)
{
std::vector<ActiveSpells::ActiveEffect> effects;
ActiveSpells::ActiveEffect effect_ = effect;
effect_.mMagnitude *= -1;
effects.push_back(effect_);
// 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
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;
// Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
@ -405,12 +415,17 @@ namespace MWMechanics
inflict(caster, target, reflectedEffects, range, true);
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,
mSourceName, caster.getRefData().getHandle());
mSourceName, casterActorId);
}
if (anyHarmfulEffect && target.getClass().isActor() && target != caster
&& target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault);
// Notify the target actor they've been hit
if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor())
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)
@ -431,7 +446,8 @@ namespace MWMechanics
if (target.getCellRef().mLockLevel > 0)
{
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
}

@ -61,9 +61,12 @@ namespace MWMechanics
/// @note Auto detects if spell, ingredient or potion
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,
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);
};

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

@ -28,6 +28,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwscript/globalscripts.hpp"
@ -46,6 +47,8 @@ void MWState::StateManager::cleanup (bool force)
mState = State_NoGame;
mCharacterManager.clearCurrentCharacter();
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_WTHR:
case ESM::REC_DYNA:
case ESM::REC_ACTC:
MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap);
break;

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

@ -12,10 +12,14 @@
#include <components/esm/npcstate.hpp>
#include <components/esm/creaturestate.hpp>
#include <components/esm/fogstate.hpp>
#include <components/esm/creaturelevliststate.hpp>
#include <components/esm/doorstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "ptr.hpp"
#include "esmstore.hpp"
#include "class.hpp"
@ -41,6 +45,22 @@ namespace
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>
void writeReferenceCollection (ESM::ESMWriter& writer,
const MWWorld::CellRefList<T>& collection)
@ -323,6 +343,17 @@ namespace MWWorld
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
{
return mWaterLevel;
@ -561,9 +592,9 @@ namespace MWWorld
writeReferenceCollection<ESM::ObjectState> (writer, mClothes);
writeReferenceCollection<ESM::ContainerState> (writer, mContainers);
writeReferenceCollection<ESM::CreatureState> (writer, mCreatures);
writeReferenceCollection<ESM::ObjectState> (writer, mDoors);
writeReferenceCollection<ESM::DoorState> (writer, mDoors);
writeReferenceCollection<ESM::ObjectState> (writer, mIngreds);
writeReferenceCollection<ESM::ObjectState> (writer, mCreatureLists);
writeReferenceCollection<ESM::CreatureLevListState> (writer, mCreatureLists);
writeReferenceCollection<ESM::ObjectState> (writer, mItemLists);
writeReferenceCollection<ESM::LightState> (writer, mLights);
writeReferenceCollection<ESM::ObjectState> (writer, mLockpicks);
@ -629,7 +660,7 @@ namespace MWWorld
case ESM::REC_DOOR:
readReferenceCollection<ESM::ObjectState> (reader, mDoors, contentFileMap);
readReferenceCollection<ESM::DoorState> (reader, mDoors, contentFileMap);
break;
case ESM::REC_INGR:
@ -639,7 +670,7 @@ namespace MWWorld
case ESM::REC_LEVC:
readReferenceCollection<ESM::ObjectState> (reader, mCreatureLists, contentFileMap);
readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, contentFileMap);
break;
case ESM::REC_LEVI:

@ -91,6 +91,9 @@ namespace MWWorld
Ptr searchViaHandle (const std::string& handle);
///< 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;
void setWaterLevel (float level);

@ -406,4 +406,14 @@ namespace MWWorld
{
return false;
}
int Class::getDoorState (const MWWorld::Ptr &ptr) const
{
throw std::runtime_error("this is not a door");
}
void Class::setDoorState (const MWWorld::Ptr &ptr, int state) const
{
throw std::runtime_error("this is not a door");
}
}

@ -336,6 +336,11 @@ namespace MWWorld
virtual int getBaseGold(const MWWorld::Ptr& ptr) const;
virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const;
/// 0 = nothing, 1 = opening, 2 = closing
virtual int getDoorState (const MWWorld::Ptr &ptr) const;
/// This does not actually cause the door to move. Use World::activateDoor instead.
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
};
}

@ -599,7 +599,7 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i];
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
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;
}

@ -507,4 +507,24 @@ namespace MWWorld
}
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.
bool isCellActive(const CellStore &cell);
Ptr searchPtrViaHandle (const std::string& handle);
Ptr searchPtrViaActorId (int actorId);
};
}

@ -275,7 +275,8 @@ namespace MWWorld
+mStore.countSavedGameRecords()
+mGlobalVariables.countSavedGameRecords()
+1 // player record
+1; // weather record
+1 // weather record
+1; // actorId counter
}
void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
@ -288,6 +289,9 @@ namespace MWWorld
mRendering->writeFog(cellstore);
}
MWMechanics::CreatureStats::writeActorIdCounter(writer);
progress.increaseProgress();
mCells.write (writer, progress);
mStore.write (writer, progress);
mGlobalVariables.write (writer, progress);
@ -298,6 +302,12 @@ namespace MWWorld
void World::readRecord (ESM::ESMReader& reader, int32_t type,
const std::map<int, int>& contentFileMap)
{
if (type == ESM::REC_ACTC)
{
MWMechanics::CreatureStats::readActorIdCounter(reader);
return;
}
if (!mStore.readRecord (reader, type) &&
!mGlobalVariables.readRecord (reader, type) &&
!mPlayer->readRecord (reader, type) &&
@ -547,17 +557,17 @@ namespace MWWorld
{
if (mPlayer->getPlayer().getRefData().getHandle()==handle)
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 ptr;
}
return mWorldScene->searchPtrViaHandle (handle);
}
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)
@ -1196,36 +1206,48 @@ namespace MWWorld
while (it != mDoorStates.end())
{
if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode())
{
// The door is no longer in an active cell, or it was disabled.
// Erase from mDoorStates, since we no longer need to move it.
// Once we load the door's cell again (or re-enable the door), Door::insertObject will reinsert to mDoorStates.
mDoorStates.erase(it++);
}
else
{
float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees();
float diff = duration * 90;
float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second ? 1 : -1)), 90.f);
float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second == 1 ? 1 : -1)), 90.f);
localRotateObject(it->first, 0, 0, targetRot);
bool reached = (targetRot == 90.f && it->second) || targetRot == 0.f;
/// \todo should use convexSweepTest here
std::vector<std::string> collisions = mPhysics->getCollisions(it->first);
for (std::vector<std::string>::iterator cit = collisions.begin(); cit != collisions.end(); ++cit)
{
MWWorld::Ptr ptr = getPtrViaHandle(*cit);
if (MWWorld::Class::get(ptr).isActor())
if (ptr.getClass().isActor())
{
// Collided with actor, ask actor to try to avoid door
if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr() ) {
MWMechanics::AiSequence& seq = MWWorld::Class::get(ptr).getCreatureStats(ptr).getAiSequence();
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr);
}
// we need to undo the rotation
localRotateObject(it->first, 0, 0, oldRot);
reached = false;
//break; //Removed in case multiple actors are touching
}
}
if ((targetRot == 90.f && it->second) || targetRot == 0.f)
if (reached)
{
// Mark as non-moving
it->first.getClass().setDoorState(it->first, 0);
mDoorStates.erase(it++);
}
else
++it;
}
@ -1843,31 +1865,32 @@ namespace MWWorld
void World::activateDoor(const MWWorld::Ptr& door)
{
if (mDoorStates.find(door) != mDoorStates.end())
{
// if currently opening, then close, if closing, then open
mDoorStates[door] = !mDoorStates[door];
}
else
int state = door.getClass().getDoorState(door);
switch (state)
{
case 0:
if (door.getRefData().getLocalRotation().rot[2] == 0)
mDoorStates[door] = 1; // open
state = 1; // if closed, then open
else
mDoorStates[door] = 0; // close
state = 2; // if open, then close
break;
case 2:
state = 1; // if closing, then open
break;
case 1:
default:
state = 2; // if opening, then close
break;
}
door.getClass().setDoorState(door, state);
mDoorStates[door] = state;
}
bool World::getOpenOrCloseDoor(const Ptr &door)
{
if (mDoorStates.find(door) != mDoorStates.end())
return !mDoorStates[door]; // if currently opening or closing, then do the opposite
return door.getRefData().getLocalRotation().rot[2] == 0;
}
bool World::getIsMovingDoor(const Ptr& door)
void World::activateDoor(const Ptr &door, bool open)
{
bool result = mDoorStates.find(door) != mDoorStates.end();
return result;
int state = open ? 1 : 2;
door.getClass().setDoorState(door, state);
mDoorStates[door] = state;
}
bool World::getPlayerStandingOn (const MWWorld::Ptr& object)
@ -2231,7 +2254,7 @@ namespace MWWorld
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed)
{
ProjectileState state;
state.mActorHandle = actor.getRefData().getHandle();
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mBow = bow;
state.mVelocity = orient.yAxis() * speed;
@ -2318,7 +2341,7 @@ namespace MWWorld
MagicBoltState state;
state.mSourceName = sourceName;
state.mId = id;
state.mActorHandle = actor.getRefData().getHandle();
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mSpeed = speed;
state.mStack = stack;
@ -2382,7 +2405,7 @@ namespace MWWorld
if (obstacle == ptr)
continue;
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle);
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
// Arrow intersects with player immediately after shooting :/
if (obstacle == caster)
@ -2468,7 +2491,7 @@ namespace MWWorld
if (obstacle == ptr)
continue;
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle);
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
if (caster.isEmpty())
caster = obstacle;
@ -2491,7 +2514,7 @@ namespace MWWorld
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);
deleteObject(ptr);

@ -88,7 +88,7 @@ namespace MWWorld
float mFacedDistance;
std::map<MWWorld::Ptr, int> mDoorStates;
///< only holds doors that are currently moving. 0 means closing, 1 opening
///< only holds doors that are currently moving. 1 = opening, 2 = closing
struct MagicBoltState
{
@ -96,7 +96,7 @@ namespace MWWorld
std::string mId;
// 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)
std::string mSourceName;
@ -111,7 +111,7 @@ namespace MWWorld
struct ProjectileState
{
// Actor who shot this projectile
std::string mActorHandle;
int mActorId;
MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from
@ -287,6 +287,9 @@ namespace MWWorld
virtual Ptr searchPtrViaHandle (const std::string& handle);
///< 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);
///< 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 renderPlayer();
/// if activated, should this door be opened or closed?
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door);
/// activate (open or close) an non-teleport door
/// open or close a non-teleport door (depending on current state)
virtual void activateDoor(const MWWorld::Ptr& door);
virtual bool getIsMovingDoor(const MWWorld::Ptr& door);
/// open or close a non-teleport door as specified
virtual void activateDoor(const MWWorld::Ptr& door, bool open);
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object

@ -45,7 +45,7 @@ add_component_dir (esm
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate
)
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"
void ESM::CreatureStats::load (ESMReader &esm)
@ -72,7 +71,11 @@ void ESM::CreatureStats::load (ESMReader &esm)
mLevel = 1;
esm.getHNOT (mLevel, "LEVL");
mActorId = -1;
esm.getHNOT (mActorId, "ACID");
mSpells.load(esm);
mActiveSpells.load(esm);
}
void ESM::CreatureStats::save (ESMWriter &esm) const
@ -146,5 +149,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (mLevel != 1)
esm.writeHNT ("LEVL", mLevel);
if (mActorId != -1)
esm.writeHNT ("ACID", mActorId);
mSpells.save(esm);
mActiveSpells.save(esm);
}

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

@ -108,6 +108,7 @@ enum RecNameInts
REC_KEYS = FourCC<'K','E','Y','S'>::value,
REC_DYNA = FourCC<'D','Y','N','A'>::value,
REC_ASPL = FourCC<'A','S','P','L'>::value,
REC_ACTC = FourCC<'A','C','T','C'>::value,
// format 1
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);
}
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>
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();
++iter)
esm.writeHNT ("USED", *iter);
esm.writeHNString ("USED", *iter);
if (mTimeToStartDrowning)
esm.writeHNT ("DRTI", mTimeToStartDrowning);

@ -150,7 +150,7 @@ device =
master volume = 1.0
sfx volume = 1.0
music volume = 0.4
footsteps volume = 0.6
footsteps volume = 0.15
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)
: 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)
, mCollisionBody(true)
, mForce(0.0f)
{
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation);
@ -49,8 +50,9 @@ namespace Physic
void PhysicActor::enableCollisionBody(bool collision)
{
assert(mBody);
if(collision && !mCollisionMode) enableCollisionBody();
if(!collision && mCollisionMode) disableCollisionBody();
if(collision && !mCollisionBody) enableCollisionBody();
if(!collision && mCollisionBody) disableCollisionBody();
mCollisionBody = collision;
}
void PhysicActor::setPosition(const Ogre::Vector3 &pos)
@ -108,7 +110,7 @@ namespace Physic
//Create the newly scaled rigid body
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot);
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

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

Loading…
Cancel
Save