Merge remote-tracking branch 'scrawl/master'

deque
Marc Zinnschlag 11 years ago
commit f7c89015f9

@ -57,7 +57,7 @@ add_openmw_dir (mwworld
cells localscripts customdata weather inventorystore ptr actionopen actionread
actionequip timestamp actionalchemy cellstore actionapply actioneat
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader omwloader actiontrap cellreflist
contentloader esmloader omwloader actiontrap cellreflist projectilemanager
)
add_openmw_dir (mwclass

@ -66,9 +66,13 @@ void OMW::Engine::executeLocalScripts()
bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt)
{
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused);
MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame);
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused);
MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame);
}
return true;
}
@ -110,8 +114,12 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
// update actors
MWBase::Environment::get().getMechanicsManager()->update(frametime,
paused);
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
MWBase::Environment::get().getMechanicsManager()->update(frametime,
paused);
}
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
@ -122,16 +130,24 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
}
// update world
MWBase::Environment::get().getWorld()->update(frametime, paused);
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
MWBase::Environment::get().getWorld()->update(frametime, paused);
}
// update GUI
Ogre::RenderWindow* window = mOgre->getWindow();
unsigned int tri, batch;
MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch);
MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch);
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
Ogre::RenderWindow* window = mOgre->getWindow();
unsigned int tri, batch;
MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch);
MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch);
MWBase::Environment::get().getWindowManager()->onFrame(frametime);
MWBase::Environment::get().getWindowManager()->update();
MWBase::Environment::get().getWindowManager()->onFrame(frametime);
MWBase::Environment::get().getWindowManager()->update();
}
}
catch (const std::exception& e)
{
@ -393,10 +409,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setJournal (new MWDialogue::Journal);
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage));
mEnvironment.getWorld()->renderPlayer();
mechanics->buildPlayer();
window->updatePlayer();
mOgre->getRoot()->addFrameListener (this);
// scripts

@ -110,18 +110,25 @@ namespace MWBase
///< Play a sound, independently of 3D-position
///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId,
float volume, float pitch, PlayType type=Play_TypeSfx,
PlayMode mode=Play_Normal, float offset=0) = 0;
///< Play a sound from an object
virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId,
float volume, float pitch, PlayType type=Play_TypeSfx,
PlayMode mode=Play_Normal, float offset=0) = 0;
///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified.
///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
virtual MWBase::SoundPtr playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId,
float volume, float pitch, PlayType type, PlayMode mode, float offset=0) = 0;
///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition.
virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0;
///< Stop the given object from playing the given sound,
virtual void stopSound3D(const MWWorld::Ptr &reference) = 0;
///< Stop the given object from playing all sounds.
virtual void stopSound(MWBase::SoundPtr sound) = 0;
///< Stop the given sound handle
virtual void stopSound(const MWWorld::CellStore *cell) = 0;
///< Stop all sounds for the given cell.

@ -193,9 +193,9 @@ namespace MWBase
virtual void setDragDrop(bool dragDrop) = 0;
virtual bool getWorldMouseOver() = 0;
virtual void toggleFogOfWar() = 0;
virtual bool toggleFogOfWar() = 0;
virtual void toggleFullHelp() = 0;
virtual bool toggleFullHelp() = 0;
///< show extra info in item tooltips (owner, script)
virtual bool getFullHelp() const = 0;

@ -126,7 +126,7 @@ namespace MWBase
virtual void setWaterHeight(const float height) = 0;
virtual void toggleWater() = 0;
virtual bool toggleWater() = 0;
virtual void adjustSky() = 0;
@ -255,7 +255,8 @@ namespace MWBase
virtual void changeToExteriorCell (const ESM::Position& position) = 0;
///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0;
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true) = 0;
///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes
virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
@ -354,15 +355,14 @@ namespace MWBase
virtual void update (float duration, bool paused) = 0;
virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0;
virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0;
///< copy and place an object into the gameworld at the specified cursor position
/// @param object
/// @param cursor X (relative 0-1)
/// @param cursor Y (relative 0-1)
/// @param number of objects to place
/// @return true if the object was placed, or false if it was rejected because the position is too far away
virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0;
virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0;
///< copy and place an object into the gameworld at the given actor's position
/// @param actor giving the dropped object position
/// @param object
@ -468,7 +468,8 @@ namespace MWBase
virtual void castSpell (const MWWorld::Ptr& actor) = 0;
virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;
@ -514,7 +515,7 @@ namespace MWBase
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects,
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0;
};
}

@ -60,6 +60,16 @@ namespace MWClass
}
}
void Container::respawn(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Container> *ref =
ptr.get<ESM::Container>();
if (ref->mBase->mFlags & ESM::Container::Respawn)
{
ptr.getRefData().setCustomData(NULL);
}
}
void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
const std::string model = getModel(ptr);

@ -64,6 +64,8 @@ namespace MWClass
static void registerSelf();
virtual void respawn (const MWWorld::Ptr& ptr) const;
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
};
}

@ -822,6 +822,26 @@ namespace MWClass
return ptr.get<ESM::Creature>()->mBase->mData.mGold;
}
void Creature::respawn(const MWWorld::Ptr &ptr) const
{
if (ptr.get<ESM::Creature>()->mBase->mFlags & ESM::Creature::Respawn)
{
// Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell.
// This also means we cannot respawn dynamically placed references with no content file connection.
if (ptr.getCellRef().mRefNum.mContentFile != -1)
{
if (ptr.getRefData().getCount() == 0)
ptr.getRefData().setCount(1);
// Reset to original position
ESM::Position& pos = ptr.getRefData().getPosition();
pos = ptr.getCellRef().mPos;
ptr.getRefData().setCustomData(NULL);
}
}
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect;

@ -143,6 +143,8 @@ namespace MWClass
///< Write additional state from \a ptr into \a state.
virtual int getBaseGold(const MWWorld::Ptr& ptr) const;
virtual void respawn (const MWWorld::Ptr& ptr) const;
};
}

@ -14,6 +14,7 @@ namespace
{
// actorId of the creature we spawned
int mSpawnActorId;
bool mSpawn; // Should a new creature be spawned?
virtual MWWorld::CustomData *clone() const;
};
@ -31,6 +32,14 @@ namespace MWClass
return "";
}
void CreatureLevList::respawn(const MWWorld::Ptr &ptr) const
{
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
customData.mSpawn = true;
}
void CreatureLevList::registerSelf()
{
boost::shared_ptr<Class> instance (new CreatureLevList);
@ -43,9 +52,8 @@ namespace MWClass
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
if (customData.mSpawnActorId != -1)
return; // TODO: handle respawning
if (!customData.mSpawn)
return;
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
ptr.get<ESM::CreatureLevList>();
@ -54,11 +62,21 @@ namespace MWClass
if (!id.empty())
{
// Delete the previous creature
if (customData.mSpawnActorId != -1)
{
MWWorld::Ptr creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
if (!creature.isEmpty())
MWBase::Environment::get().getWorld()->deleteObject(creature);
customData.mSpawnActorId = -1;
}
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();
customData.mSpawn = false;
}
}
@ -68,6 +86,7 @@ namespace MWClass
{
std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
data->mSpawnActorId = -1;
data->mSpawn = true;
ptr.getRefData().setCustomData(data.release());
}
@ -81,6 +100,7 @@ namespace MWClass
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
customData.mSpawnActorId = state2.mSpawnActorId;
customData.mSpawn = state2.mSpawn;
}
void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
@ -91,5 +111,6 @@ namespace MWClass
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
state2.mSpawnActorId = customData.mSpawnActorId;
state2.mSpawn = customData.mSpawn;
}
}

@ -27,6 +27,8 @@ namespace MWClass
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
virtual void respawn (const MWWorld::Ptr& ptr) const;
};
}

@ -621,7 +621,8 @@ namespace MWClass
// Attacking peaceful NPCs is a crime
// anything below 80 is considered peaceful (see Actors::updateActor)
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80)
if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() &&
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);
@ -1309,6 +1310,26 @@ namespace MWClass
return Misc::StringUtils::ciEqual(ptr.get<ESM::NPC>()->mBase->mClass, className);
}
void Npc::respawn(const MWWorld::Ptr &ptr) const
{
if (ptr.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Respawn)
{
// Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell.
// This also means we cannot respawn dynamically placed references with no content file connection.
if (ptr.getCellRef().mRefNum.mContentFile != -1)
{
if (ptr.getRefData().getCount() == 0)
ptr.getRefData().setCount(1);
// Reset to original position
ESM::Position& pos = ptr.getRefData().getPosition();
pos = ptr.getCellRef().mPos;
ptr.getRefData().setCustomData(NULL);
}
}
}
const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;

@ -178,6 +178,8 @@ namespace MWClass
virtual bool canWalk (const MWWorld::Ptr &ptr) const {
return true;
}
virtual void respawn (const MWWorld::Ptr& ptr) const;
};
}

@ -91,7 +91,8 @@ namespace MWGui
mSourceModel->update();
finish();
targetView->update();
if (targetView)
targetView->update();
// We need to update the view since an other item could be auto-equipped.
mSourceView->update();

@ -17,9 +17,47 @@
#include "itemmodel.hpp"
#include "container.hpp"
#include "itemmodel.hpp"
namespace MWGui
{
/**
* Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world.
*/
class WorldItemModel : public ItemModel
{
public:
WorldItemModel(float left, float top) : mLeft(left), mTop(top) {}
virtual ~WorldItemModel() {}
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::Ptr dropped;
if (world->canPlaceObject(mLeft, mTop))
dropped = world->placeObject(item.mBase, mLeft, mTop, count);
else
dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count);
if (setNewOwner)
dropped.getCellRef().mOwner = "";
return dropped;
}
virtual void removeItem (const ItemStack& item, size_t count) { throw std::runtime_error("removeItem not implemented"); }
virtual ModelIndex getIndex (ItemStack item) { throw std::runtime_error("getIndex not implemented"); }
virtual void update() {}
virtual size_t getItemCount() { return 0; }
virtual ItemStack getItem (ModelIndex index) { throw std::runtime_error("getItem not implemented"); }
private:
// Where to drop the item
float mLeft;
float mTop;
};
HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop)
: Layout("openmw_hud.layout")
, mHealth(NULL)
@ -229,10 +267,6 @@ namespace MWGui
if (mDragAndDrop->mIsOnDragAndDrop)
{
// drop item into the gameworld
MWWorld::Ptr object = mDragAndDrop->mItem.mBase;
MWBase::World* world = MWBase::Environment::get().getWorld();
MWBase::Environment::get().getWorld()->breakInvisibility(
MWBase::Environment::get().getWorld()->getPlayerPtr());
@ -241,20 +275,10 @@ namespace MWGui
float mouseX = cursorPosition.left / float(viewSize.width);
float mouseY = cursorPosition.top / float(viewSize.height);
if (world->canPlaceObject(mouseX, mouseY))
world->placeObject(object, mouseX, mouseY, mDragAndDrop->mDraggedCount);
else
world->dropObjectOnGround(world->getPlayerPtr(), object, mDragAndDrop->mDraggedCount);
WorldItemModel drop (mouseX, mouseY);
mDragAndDrop->drop(&drop, NULL);
MWBase::Environment::get().getWindowManager()->changePointer("arrow");
std::string sound = MWWorld::Class::get(object).getDownSoundId(object);
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
// remove object from the container it was coming from
mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount);
mDragAndDrop->finish();
mDragAndDrop->mSourceModel->update();
}
else
{

@ -89,10 +89,11 @@ namespace MWGui
mChanged = true;
}
void LocalMapBase::toggleFogOfWar()
bool LocalMapBase::toggleFogOfWar()
{
mFogOfWar = !mFogOfWar;
applyFogOfWar();
return mFogOfWar;
}
void LocalMapBase::applyFogOfWar()

@ -35,7 +35,7 @@ namespace MWGui
void setPlayerDir(const float x, const float y);
void setPlayerPos(const float x, const float y);
void toggleFogOfWar();
bool toggleFogOfWar();
struct MarkerPosition
{

@ -547,9 +547,10 @@ namespace MWGui
return " (" + boost::lexical_cast<std::string>(value) + ")";
}
void ToolTips::toggleFullHelp()
bool ToolTips::toggleFullHelp()
{
mFullHelp = !mFullHelp;
return mFullHelp;
}
bool ToolTips::getFullHelp() const

@ -45,7 +45,7 @@ namespace MWGui
void setEnabled(bool enabled);
void toggleFullHelp(); ///< show extra info in item tooltips (owner, script)
bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script)
bool getFullHelp() const;
void setDelay(float delay);

@ -814,10 +814,10 @@ namespace MWGui
mHud->setMinimapVisible (visible);
}
void WindowManager::toggleFogOfWar()
bool WindowManager::toggleFogOfWar()
{
mMap->toggleFogOfWar();
mHud->toggleFogOfWar();
return mHud->toggleFogOfWar();
}
void WindowManager::setFocusObject(const MWWorld::Ptr& focus)
@ -830,9 +830,9 @@ namespace MWGui
mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y);
}
void WindowManager::toggleFullHelp()
bool WindowManager::toggleFullHelp()
{
mToolTips->toggleFullHelp();
return mToolTips->toggleFullHelp();
}
bool WindowManager::getFullHelp() const

@ -188,8 +188,8 @@ namespace MWGui
virtual void setDragDrop(bool dragDrop);
virtual bool getWorldMouseOver();
virtual void toggleFogOfWar();
virtual void toggleFullHelp(); ///< show extra info in item tooltips (owner, script)
virtual bool toggleFogOfWar();
virtual bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script)
virtual bool getFullHelp() const;
virtual void setActiveMap(int x, int y, bool interior);

@ -540,7 +540,7 @@ namespace MWMechanics
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
// Make the summoned creature follow its master and help in fights
AiFollow package(ptr);
AiFollow package(ptr.getRefData().getHandle());
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
int creatureActorId = summonedCreatureStats.getActorId();
@ -785,7 +785,7 @@ namespace MWMechanics
// Update witness crime id
npcStats.setCrimeId(-1);
}
else if (!creatureStats.isHostile())
else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue)
{
if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stack(AiPursue(player), ptr);

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

@ -11,26 +11,23 @@
#include "steering.hpp"
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()
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()
{
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
}
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()
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()
{
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
}
MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mCellId(""), AiPackage()
MWMechanics::AiFollow::AiFollow(const std::string &actorId)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), 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()->searchPtrViaActorId(mActorId); //The target to follow
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); //The target to follow
if(target == MWWorld::Ptr()) return true; //Target doesn't exist
@ -78,8 +75,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
std::string MWMechanics::AiFollow::getFollowedActor()
{
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow
return target.getCellRef().mRefID;
return mActorId;
}
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 MWWorld::Ptr& actor,float duration, float X, float Y, float Z);
AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z);
/// Follow Actor for duration or until you arrive at a position in a cell
AiFollow(const MWWorld::Ptr& actor,const std::string &CellId,float duration, float X, float Y, float Z);
AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z);
/// Follow Actor indefinitively
AiFollow(const MWWorld::Ptr& actor);
AiFollow(const std::string &ActorId);
virtual AiFollow *clone() const;
@ -38,8 +38,8 @@ namespace MWMechanics
float mX;
float mY;
float mZ;
int mActorId; // The actor we should follow
std::string mCellId;
};
}
#endif
std::string mActorId;
std::string mCellId;
};
}
#endif

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

@ -21,6 +21,41 @@
#include "magiceffects.hpp"
#include "npcstats.hpp"
namespace
{
/// Get projectile properties (model, sound and speed) for a spell with the given effects
/// If \a model is empty, the spell has no ranged effects and should not spawn a projectile.
void getProjectileInfo (const ESM::EffectList& effects, std::string& model, std::string& sound, float& speed)
{
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
iter!=effects.mList.end(); ++iter)
{
if (iter->mRange != ESM::RT_Target)
continue;
const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
iter->mEffectID);
model = magicEffect->mBolt;
if (model.empty())
model = "VFX_DefaultBolt";
static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
};
if (!magicEffect->mBoltSound.empty())
sound = magicEffect->mBoltSound;
else
sound = schools[magicEffect->mData.mSchool] + " bolt";
speed = magicEffect->mData.mSpeed;
break;
}
}
}
namespace MWMechanics
{
@ -409,7 +444,7 @@ namespace MWMechanics
}
if (!exploded)
MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, mTarget, effects, caster, mId, mSourceName);
MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, mId, mSourceName);
if (!reflectedEffects.mList.empty())
inflict(caster, target, reflectedEffects, range, true);
@ -603,7 +638,13 @@ namespace MWMechanics
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
}
MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, enchantment->mEffects, mCaster, mSourceName);
std::string projectileModel;
std::string sound;
float speed = 0;
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
if (!projectileModel.empty())
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
false, enchantment->mEffects, mCaster, mSourceName);
return true;
}
@ -682,7 +723,15 @@ namespace MWMechanics
}
}
MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, spell->mEffects, mCaster, mSourceName);
std::string projectileModel;
std::string sound;
float speed = 0;
getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
if (!projectileModel.empty())
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
false, spell->mEffects, mCaster, mSourceName);
return true;
}

@ -11,18 +11,6 @@
namespace MWRender
{
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
float mTime;
public:
EffectAnimationTime() : mTime(0) { }
void addTime(float time) { mTime += time; }
virtual Ogre::Real getValue() const { return mTime; }
virtual void setValue(Ogre::Real value) {}
};
EffectManager::EffectManager(Ogre::SceneManager *sceneMgr)
: mSceneMgr(sceneMgr)
{

@ -5,6 +5,20 @@
namespace MWRender
{
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
float mTime;
public:
EffectAnimationTime() : mTime(0) { }
void addTime(float time) { mTime += time; }
virtual Ogre::Real getValue() const { return mTime; }
virtual void setValue(Ogre::Real value) {}
};
// Note: effects attached to another object should be managed by MWRender::Animation::addEffect.
// This class manages "free" effects, i.e. attached to a dedicated scene node in the world.
class EffectManager

@ -234,9 +234,9 @@ void RenderingManager::removeWater ()
mWater->setActive(false);
}
void RenderingManager::toggleWater()
bool RenderingManager::toggleWater()
{
mWater->toggle();
return mWater->toggle();
}
void RenderingManager::cellAdded (MWWorld::CellStore *store)
@ -694,15 +694,8 @@ Shadows* RenderingManager::getShadows()
return mShadows;
}
void RenderingManager::switchToInterior()
void RenderingManager::notifyWorldSpaceChanged()
{
// TODO: also do this when switching worldspace
mEffectManager->clear();
}
void RenderingManager::switchToExterior()
{
// TODO: also do this when switching worldspace
mEffectManager->clear();
}
@ -1061,6 +1054,7 @@ void RenderingManager::spawnEffect(const std::string &model, const std::string &
void RenderingManager::clear()
{
mLocalMap->clear();
notifyWorldSpaceChanged();
}
} // namespace

@ -127,7 +127,7 @@ public:
void rotateObject (const MWWorld::Ptr& ptr);
void setWaterHeight(const float height);
void toggleWater();
bool toggleWater();
/// Updates object rendering after cell change
/// \param old Object reference in previous cell
@ -163,8 +163,7 @@ public:
Shadows* getShadows();
void switchToInterior();
void switchToExterior();
void notifyWorldSpaceChanged();
void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches);

@ -326,10 +326,11 @@ void Water::setHeight(const float height)
sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(height)));
}
void Water::toggle()
bool Water::toggle()
{
mToggled = !mToggled;
updateVisible();
return mToggled;
}
void

@ -150,7 +150,7 @@ namespace MWRender {
void setActive(bool active);
void toggle();
bool toggle();
void update(float dt, Ogre::Vector3 player);
void frameStarted(float dt);

@ -91,7 +91,6 @@ 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();
@ -108,7 +107,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(actor, duration, x, y, z);
MWMechanics::AiEscort escortPackage(actorID, duration, x, y, z);
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration
@ -127,7 +126,6 @@ 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();
@ -147,7 +145,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(actor, cellID, duration, x, y, z);
MWMechanics::AiEscort escortPackage(actorID, cellID, duration, x, y, z);
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration
@ -283,7 +281,6 @@ 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();
@ -300,7 +297,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(actor, duration, x, y ,z);
MWMechanics::AiFollow followPackage(actorID, duration, x, y ,z);
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration
@ -319,7 +316,6 @@ 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();
@ -339,8 +335,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(actor, cellID, duration, x, y ,z);
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y ,z);
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration
<< std::endl;
}

@ -92,7 +92,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
MWBase::Environment::get().getWindowManager()->toggleFogOfWar();
runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFogOfWar() ? "Fog of war -> On"
: "Fog of war -> Off");
}
};
@ -102,7 +103,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
MWBase::Environment::get().getWindowManager()->toggleFullHelp();
runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFullHelp() ? "Full help -> On"
: "Full help -> Off");
}
};

@ -275,7 +275,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
MWBase::Environment::get().getWorld()->toggleWater();
runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWater() ? "Water -> On"
: "Water -> Off");
}
};

@ -356,6 +356,44 @@ namespace MWSound
return sound;
}
MWBase::SoundPtr SoundManager::playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId,
float volume, float pitch, PlayType type, PlayMode mode, float offset)
{
MWBase::SoundPtr sound;
if(!mOutput->isInitialized())
return sound;
try
{
// Look up the sound in the ESM data
float basevol = volumeFromType(type);
float min, max;
std::string file = lookup(soundId, volume, min, max);
sound = mOutput->playSound3D(file, initialPos, volume, basevol, pitch, min, max, mode|type, offset);
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
}
catch(std::exception &e)
{
//std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
return sound;
}
void SoundManager::stopSound (MWBase::SoundPtr sound)
{
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
if(snditer->first == sound)
{
snditer->first->stop();
mActiveSounds.erase(snditer++);
}
else
++snditer;
}
}
void SoundManager::stopSound3D(const MWWorld::Ptr &ptr, const std::string& soundId)
{
SoundMap::iterator snditer = mActiveSounds.begin();

@ -115,6 +115,13 @@ namespace MWSound
virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId,
float volume, float pitch, PlayType type=Play_TypeSfx,
PlayMode mode=Play_Normal, float offset=0);
///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified.
///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
virtual MWBase::SoundPtr playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId,
float volume, float pitch, PlayType type, PlayMode mode, float offset=0);
///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition.
///< Play a sound from an object
///< @param offset value from [0,1], when to start playback. 0 is beginning, 1 is end.
@ -124,6 +131,9 @@ namespace MWSound
virtual void stopSound3D(const MWWorld::Ptr &reference);
///< Stop the given object from playing all sounds.
virtual void stopSound(MWBase::SoundPtr sound);
///< Stop the given sound handle
virtual void stopSound(const MWWorld::CellStore *cell);
///< Stop all sounds for the given cell.

@ -319,6 +319,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
case ESM::REC_WTHR:
case ESM::REC_DYNA:
case ESM::REC_ACTC:
case ESM::REC_PROJ:
case ESM::REC_MPRJ:
MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap);
break;
@ -361,7 +363,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
ESM::CellId cellId = ptr.getCell()->getCell()->getCellId();
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition());
// Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false);
// Do not trigger erroneous cellChanged events
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
}
catch (const std::exception& e)
{

@ -169,7 +169,7 @@ namespace MWWorld
}
CellStore::CellStore (const ESM::Cell *cell)
: mCell (cell), mState (State_Unloaded), mHasState (false)
: mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0)
{
mWaterLevel = cell->mWater;
}
@ -555,6 +555,7 @@ namespace MWWorld
mWaterLevel = state.mWaterLevel;
mWaterLevel = state.mWaterLevel;
mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn);
}
void CellStore::saveState (ESM::CellState& state) const
@ -566,6 +567,7 @@ namespace MWWorld
state.mWaterLevel = mWaterLevel;
state.mHasFogOfWar = (mFogState.get() ? 1 : 0);
state.mLastRespawn = mLastRespawn.toEsm();
}
void CellStore::writeFog(ESM::ESMWriter &writer) const
@ -754,4 +756,36 @@ namespace MWWorld
{
return mFogState.get();
}
void CellStore::respawn()
{
if (mState == State_Loaded)
{
static int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iMonthsToRespawn")->getInt();
if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn)
{
mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp();
for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
}
}
}
}

@ -13,6 +13,8 @@
#include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld
#include "timestamp.hpp"
namespace ESM
{
struct CellState;
@ -48,6 +50,8 @@ namespace MWWorld
std::vector<std::string> mIds;
float mWaterLevel;
MWWorld::TimeStamp mLastRespawn;
CellRefList<ESM::Activator> mActivators;
CellRefList<ESM::Potion> mPotions;
CellRefList<ESM::Apparatus> mAppas;
@ -161,6 +165,9 @@ namespace MWWorld
void readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap);
void respawn ();
///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded.
template <class T>
CellRefList<T>& get() {
throw std::runtime_error ("Storage for this type not exist in cells");

@ -341,6 +341,8 @@ namespace MWWorld
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 respawn (const MWWorld::Ptr& ptr) const {}
};
}

@ -0,0 +1,400 @@
#include "projectilemanager.hpp"
#include <OgreSceneManager.h>
#include <libs/openengine/bullet/physic.hpp>
#include <components/esm/projectilestate.hpp>
#include "../mwworld/manualref.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwmechanics/combat.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwrender/effectmanager.hpp"
#include "../mwsound/sound.hpp"
namespace MWWorld
{
ProjectileManager::ProjectileManager(Ogre::SceneManager* sceneMgr, OEngine::Physic::PhysicEngine &engine)
: mPhysEngine(engine)
, mSceneMgr(sceneMgr)
{
}
void ProjectileManager::createModel(State &state, const std::string &model)
{
state.mObject = NifOgre::Loader::createObjects(state.mNode, model);
for(size_t i = 0;i < state.mObject->mControllers.size();i++)
{
if(state.mObject->mControllers[i].getSource().isNull())
state.mObject->mControllers[i].setSource(Ogre::SharedPtr<MWRender::EffectAnimationTime> (new MWRender::EffectAnimationTime()));
}
}
void ProjectileManager::update(NifOgre::ObjectScenePtr object, float duration)
{
for(size_t i = 0; i < object->mControllers.size() ;i++)
{
MWRender::EffectAnimationTime* value = dynamic_cast<MWRender::EffectAnimationTime*>(object->mControllers[i].getSource().get());
if (value)
value->addTime(duration);
object->mControllers[i].update();
}
}
void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound,
const std::string &spellId, float speed, bool stack,
const ESM::EffectList &effects, const Ptr &actor, const std::string &sourceName)
{
// Spawn at 0.75 * ActorHeight
float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75;
Ogre::Vector3 pos(actor.getRefData().getPosition().pos);
pos.z += height;
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
MagicBoltState state;
state.mSourceName = sourceName;
state.mId = model;
state.mSpellId = spellId;
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mSpeed = speed;
state.mStack = stack;
state.mSoundId = sound;
// Only interested in "on target" effects
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
iter!=effects.mList.end(); ++iter)
{
if (iter->mRange == ESM::RT_Target)
state.mEffects.mList.push_back(*iter);
}
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), model);
MWWorld::Ptr ptr = ref.getPtr();
state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(pos, orient);
createModel(state, ptr.getClass().getModel(ptr));
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
state.mSound = sndMgr->playManualSound3D(pos, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
mMagicBolts.push_back(state);
}
void ProjectileManager::launchProjectile(Ptr actor, Ptr projectile, const Ogre::Vector3 &pos,
const Ogre::Quaternion &orient, Ptr bow, float speed)
{
ProjectileState state;
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mBowId = bow.getCellRef().mRefID;
state.mVelocity = orient.yAxis() * speed;
state.mId = projectile.getCellRef().mRefID;
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().mRefID);
MWWorld::Ptr ptr = ref.getPtr();
state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(pos, orient);
createModel(state, ptr.getClass().getModel(ptr));
mProjectiles.push_back(state);
}
void ProjectileManager::update(float dt)
{
moveProjectiles(dt);
moveMagicBolts(dt);
}
void ProjectileManager::moveMagicBolts(float duration)
{
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
{
Ogre::Quaternion orient = it->mNode->getOrientation();
static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fTargetSpellMaxSpeed")->getFloat();
float speed = fTargetSpellMaxSpeed * it->mSpeed;
Ogre::Vector3 direction = orient.yAxis();
direction.normalise();
Ogre::Vector3 pos(it->mNode->getPosition());
Ogre::Vector3 newPos = pos + direction * duration * speed;
it->mSound->setPosition(newPos);
it->mNode->setPosition(newPos);
update(it->mObject, duration);
// Check for impact
// TODO: use a proper btRigidBody / btGhostObject?
btVector3 from(pos.x, pos.y, pos.z);
btVector3 to(newPos.x, newPos.y, newPos.z);
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to);
bool hit=false;
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
{
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
if (caster.isEmpty())
caster = obstacle;
if (obstacle.isEmpty())
{
// Terrain
}
else
{
MWMechanics::CastSpell cast(caster, obstacle);
cast.mHitPosition = pos;
cast.mId = it->mSpellId;
cast.mSourceName = it->mSourceName;
cast.mStack = it->mStack;
cast.inflict(obstacle, caster, it->mEffects, ESM::RT_Target, false, true);
}
hit = true;
}
if (hit)
{
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mSpellId, it->mSourceName);
MWBase::Environment::get().getSoundManager()->stopSound(it->mSound);
mSceneMgr->destroySceneNode(it->mNode);
it = mMagicBolts.erase(it);
continue;
}
else
++it;
}
}
void ProjectileManager::moveProjectiles(float duration)
{
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end();)
{
// gravity constant - must be way lower than the gravity affecting actors, since we're not
// simulating aerodynamics at all
it->mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration;
Ogre::Vector3 pos(it->mNode->getPosition());
Ogre::Vector3 newPos = pos + it->mVelocity * duration;
Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->mVelocity);
it->mNode->setOrientation(orient);
it->mNode->setPosition(newPos);
update(it->mObject, duration);
// Check for impact
// TODO: use a proper btRigidBody / btGhostObject?
btVector3 from(pos.x, pos.y, pos.z);
btVector3 to(newPos.x, newPos.y, newPos.z);
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to);
bool hit=false;
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
{
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
// Arrow intersects with player immediately after shooting :/
if (obstacle == caster)
continue;
if (obstacle.isEmpty())
{
// Terrain
}
else if (obstacle.getClass().isActor())
{
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId);
// Try to get a Ptr to the bow that was used. It might no longer exist.
MWWorld::Ptr bow = projectileRef.getPtr();
if (!caster.isEmpty())
{
MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);
MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().mRefID, it->mBowId))
bow = *invIt;
}
if (caster.isEmpty())
caster = obstacle;
MWMechanics::projectileHit(caster, obstacle, bow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first);
}
hit = true;
}
if (hit)
{
mSceneMgr->destroySceneNode(it->mNode);
it = mProjectiles.erase(it);
continue;
}
else
++it;
}
}
void ProjectileManager::clear()
{
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
{
mSceneMgr->destroySceneNode(it->mNode);
}
mProjectiles.clear();
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
{
MWBase::Environment::get().getSoundManager()->stopSound(it->mSound);
mSceneMgr->destroySceneNode(it->mNode);
}
mMagicBolts.clear();
}
void ProjectileManager::write(ESM::ESMWriter &writer, Loading::Listener &progress) const
{
for (std::vector<ProjectileState>::const_iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
{
writer.startRecord(ESM::REC_PROJ);
ESM::ProjectileState state;
state.mId = it->mId;
state.mPosition = it->mNode->getPosition();
state.mOrientation = it->mNode->getOrientation();
state.mActorId = it->mActorId;
state.mBowId = it->mBowId;
state.mVelocity = it->mVelocity;
state.save(writer);
writer.endRecord(ESM::REC_PROJ);
progress.increaseProgress();
}
for (std::vector<MagicBoltState>::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
{
writer.startRecord(ESM::REC_MPRJ);
ESM::MagicBoltState state;
state.mId = it->mId;
state.mPosition = it->mNode->getPosition();
state.mOrientation = it->mNode->getOrientation();
state.mActorId = it->mActorId;
state.mSpellId = it->mSpellId;
state.mEffects = it->mEffects;
state.mSound = it->mSoundId;
state.mSourceName = it->mSourceName;
state.mSpeed = it->mSpeed;
state.mStack = it->mStack;
state.save(writer);
writer.endRecord(ESM::REC_MPRJ);
progress.increaseProgress();
}
}
bool ProjectileManager::readRecord(ESM::ESMReader &reader, int32_t type)
{
if (type == ESM::REC_PROJ)
{
ESM::ProjectileState esm;
esm.load(reader);
ProjectileState state;
state.mActorId = esm.mActorId;
state.mBowId = esm.mBowId;
state.mVelocity = esm.mVelocity;
state.mId = esm.mId;
std::string model;
try
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr);
}
catch(...)
{
return true;
}
state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(esm.mPosition, esm.mOrientation);
createModel(state, model);
mProjectiles.push_back(state);
return true;
}
else if (type == ESM::REC_MPRJ)
{
ESM::MagicBoltState esm;
esm.load(reader);
MagicBoltState state;
state.mSourceName = esm.mSourceName;
state.mId = esm.mId;
state.mSpellId = esm.mSpellId;
state.mActorId = esm.mActorId;
state.mSpeed = esm.mSpeed;
state.mStack = esm.mStack;
state.mEffects = esm.mEffects;
std::string model;
try
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr);
}
catch(...)
{
return true;
}
state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(esm.mPosition, esm.mOrientation);
createModel(state, model);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
state.mSound = sndMgr->playManualSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f,
MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
mMagicBolts.push_back(state);
return true;
}
return false;
}
int ProjectileManager::countSavedGameRecords() const
{
return mMagicBolts.size() + mProjectiles.size();
}
}

@ -0,0 +1,111 @@
#ifndef OPENMW_MWWORLD_PROJECTILEMANAGER_H
#define OPENMW_MWWORLD_PROJECTILEMANAGER_H
#include <string>
#include <OgreVector3.h>
#include <components/esm/effectlist.hpp>
#include <components/nifogre/ogrenifloader.hpp>
#include "../mwbase/soundmanager.hpp"
#include "ptr.hpp"
namespace OEngine
{
namespace Physic
{
class PhysicEngine;
}
}
namespace Loading
{
class Listener;
}
namespace Ogre
{
class SceneManager;
}
namespace MWWorld
{
class ProjectileManager
{
public:
ProjectileManager (Ogre::SceneManager* sceneMgr,
OEngine::Physic::PhysicEngine& engine);
void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName);
void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
void update(float dt);
/// Removes all current projectiles. Should be called when switching to a new worldspace.
void clear();
void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
bool readRecord (ESM::ESMReader& reader, int32_t type);
int countSavedGameRecords() const;
private:
OEngine::Physic::PhysicEngine& mPhysEngine;
Ogre::SceneManager* mSceneMgr;
struct State
{
NifOgre::ObjectScenePtr mObject;
Ogre::SceneNode* mNode;
// Actor who shot this projectile
int mActorId;
// MW-id of this projectile
std::string mId;
};
struct MagicBoltState : public State
{
std::string mSpellId;
// Name of item to display as effect source in magic menu (in case we casted an enchantment)
std::string mSourceName;
ESM::EffectList mEffects;
float mSpeed;
bool mStack;
MWBase::SoundPtr mSound;
std::string mSoundId;
};
struct ProjectileState : public State
{
// RefID of the bow or crossbow the actor was using when this projectile was fired (may be empty)
std::string mBowId;
Ogre::Vector3 mVelocity;
};
std::vector<MagicBoltState> mMagicBolts;
std::vector<ProjectileState> mProjectiles;
void moveProjectiles(float dt);
void moveMagicBolts(float dt);
void createModel (State& state, const std::string& model);
void update (NifOgre::ObjectScenePtr object, float duration);
};
}
#endif

@ -165,6 +165,8 @@ namespace MWWorld
}
}
cell->respawn();
// ... then references. This is important for adjustPosition to work correctly.
/// \todo rescale depending on the state of a new GMST
insertCell (*cell, true, loadingListener);
@ -344,8 +346,6 @@ namespace MWWorld
// Sky system
MWBase::Environment::get().getWorld()->adjustSky();
mRendering.switchToExterior();
mCellChanged = true;
loadingListener->removeWallpaper();
@ -439,7 +439,6 @@ namespace MWWorld
mCurrentCell = cell;
// adjust fog
mRendering.switchToInterior();
mRendering.configureFog(*mCurrentCell);
// adjust player

@ -43,6 +43,7 @@
#include "containerstore.hpp"
#include "inventorystore.hpp"
#include "actionteleport.hpp"
#include "projectilemanager.hpp"
#include "contentloader.hpp"
#include "esmloader.hpp"
@ -136,6 +137,8 @@ namespace MWWorld
mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine();
mProjectileManager.reset(new ProjectileManager(renderer.getScene(), *mPhysEngine));
mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine,&mFallback);
mPhysEngine->setSceneManager(renderer.getScene());
@ -234,6 +237,8 @@ namespace MWWorld
{
mRendering->clear();
mProjectileManager->clear();
mLocalScripts.clear();
mPlayer->clear();
@ -255,8 +260,6 @@ namespace MWWorld
mCells.clear();
mMagicBolts.clear();
mProjectiles.clear();
mDoorStates.clear();
mGodMode = false;
@ -274,6 +277,7 @@ namespace MWWorld
mCells.countSavedGameRecords()
+mStore.countSavedGameRecords()
+mGlobalVariables.countSavedGameRecords()
+mProjectileManager->countSavedGameRecords()
+1 // player record
+1 // weather record
+1; // actorId counter
@ -297,6 +301,7 @@ namespace MWWorld
mGlobalVariables.write (writer, progress);
mPlayer->write (writer, progress);
mWeatherManager->write (writer, progress);
mProjectileManager->write (writer, progress);
}
void World::readRecord (ESM::ESMReader& reader, int32_t type,
@ -312,7 +317,8 @@ namespace MWWorld
!mGlobalVariables.readRecord (reader, type) &&
!mPlayer->readRecord (reader, type) &&
!mWeatherManager->readRecord (reader, type) &&
!mCells.readRecord (reader, type, contentFileMap))
!mCells.readRecord (reader, type, contentFileMap) &&
!mProjectileManager->readRecord (reader, type))
{
throw std::runtime_error ("unknown record in saved game");
}
@ -807,6 +813,14 @@ namespace MWWorld
void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
{
if (mCurrentWorldSpace != cellName)
{
// changed worldspace
mProjectileManager->clear();
mRendering->notifyWorldSpaceChanged();
mCurrentWorldSpace = cellName;
}
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToInteriorCell(cellName, position);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
@ -814,13 +828,22 @@ namespace MWWorld
void World::changeToExteriorCell (const ESM::Position& position)
{
if (mCurrentWorldSpace != "sys::default") // FIXME
{
// changed worldspace
mProjectileManager->clear();
mRendering->notifyWorldSpaceChanged();
}
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToExteriorCell(position);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
}
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position)
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange)
{
if (!detectWorldSpaceChange)
mCurrentWorldSpace = cellId.mWorldspace;
if (cellId.mPaged)
changeToExteriorCell (position);
else
@ -949,8 +972,6 @@ namespace MWWorld
MWWorld::Ptr newPtr = MWWorld::Class::get(ptr)
.copyToCell(ptr, *newCell);
newPtr.getRefData().setBaseNode(0);
objectLeftActiveCell(ptr, newPtr);
}
else
{
@ -1172,8 +1193,7 @@ namespace MWWorld
{
processDoors(duration);
moveMagicBolts(duration);
moveProjectiles(duration);
mProjectileManager->update(duration);
const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration);
PtrVelocityList::const_iterator player(results.end());
@ -1553,9 +1573,9 @@ namespace MWWorld
mRendering->setWaterHeight(height);
}
void World::toggleWater()
bool World::toggleWater()
{
mRendering->toggleWater();
return mRendering->toggleWater();
}
void World::PCDropped (const Ptr& item)
@ -1567,12 +1587,12 @@ namespace MWWorld
item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1);
}
bool World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount)
MWWorld::Ptr World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount)
{
std::pair<bool, Ogre::Vector3> result = mPhysics->castRay(cursorX, cursorY);
if (!result.first)
return false;
return MWWorld::Ptr();
CellStore* cell = getPlayerPtr().getCell();
@ -1593,7 +1613,7 @@ namespace MWWorld
// only the player place items in the world, so no need to check actor
PCDropped(dropped);
return true;
return dropped;
}
bool World::canPlaceObject(float cursorX, float cursorY)
@ -1644,7 +1664,7 @@ namespace MWWorld
return dropped;
}
void World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount)
MWWorld::Ptr World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount)
{
MWWorld::CellStore* cell = actor.getCell();
@ -1673,6 +1693,7 @@ namespace MWWorld
if(actor == mPlayer->getPlayer()) // Only call if dropped by player
PCDropped(dropped);
return dropped;
}
void World::processChangedSettings(const Settings::CategorySettingVector& settings)
@ -2253,306 +2274,14 @@ namespace MWWorld
void World::launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed)
{
ProjectileState state;
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mBow = bow;
state.mVelocity = orient.yAxis() * speed;
MWWorld::ManualRef ref(getStore(), projectile.getCellRef().mRefID);
ESM::Position pos;
pos.pos[0] = worldPos.x;
pos.pos[1] = worldPos.y;
pos.pos[2] = worldPos.z;
// Do NOT copy actor rotation! actors use a different rotation order, and this will not produce the same facing direction.
Ogre::Matrix3 mat;
orient.ToRotationMatrix(mat);
Ogre::Radian xr,yr,zr;
mat.ToEulerAnglesXYZ(xr, yr, zr);
pos.rot[0] = -xr.valueRadians();
pos.rot[1] = -yr.valueRadians();
pos.rot[2] = -zr.valueRadians();
MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), actor.getCell(), pos, false);
ptr.getRefData().setCount(1);
mProjectiles[ptr] = state;
mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed);
}
void World::launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName)
{
std::string projectileModel;
std::string sound;
float speed = 0;
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
iter!=effects.mList.end(); ++iter)
{
if (iter->mRange != ESM::RT_Target)
continue;
const ESM::MagicEffect *magicEffect = getStore().get<ESM::MagicEffect>().find (
iter->mEffectID);
projectileModel = magicEffect->mBolt;
if (projectileModel.empty())
projectileModel = "VFX_DefaultBolt";
static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
};
if (!magicEffect->mBoltSound.empty())
sound = magicEffect->mBoltSound;
else
sound = schools[magicEffect->mData.mSchool] + " bolt";
speed = magicEffect->mData.mSpeed;
break;
}
if (projectileModel.empty())
return;
// Spawn at 0.75 * ActorHeight
float height = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75;
MWWorld::ManualRef ref(getStore(), projectileModel);
ESM::Position pos;
pos.pos[0] = actor.getRefData().getPosition().pos[0];
pos.pos[1] = actor.getRefData().getPosition().pos[1];
pos.pos[2] = actor.getRefData().getPosition().pos[2] + height;
// Do NOT copy rotation directly! actors use a different rotation order, and this will not produce the same facing direction.
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
Ogre::Matrix3 mat;
orient.ToRotationMatrix(mat);
Ogre::Radian xr,yr,zr;
mat.ToEulerAnglesXYZ(xr, yr, zr);
pos.rot[0] = -xr.valueRadians();
pos.rot[1] = -yr.valueRadians();
pos.rot[2] = -zr.valueRadians();
ref.getPtr().getCellRef().mPos = pos;
CellStore* cell = actor.getCell();
MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), cell, pos);
MagicBoltState state;
state.mSourceName = sourceName;
state.mId = id;
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mSpeed = speed;
state.mStack = stack;
// Only interested in "on target" effects
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
iter!=effects.mList.end(); ++iter)
{
if (iter->mRange == ESM::RT_Target)
state.mEffects.mList.push_back(*iter);
}
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
mMagicBolts[ptr] = state;
}
void World::moveProjectiles(float duration)
{
std::map<std::string, ProjectileState> moved;
for (std::map<MWWorld::Ptr, ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end();)
{
if (!mWorldScene->isCellActive(*it->first.getCell()))
{
deleteObject(it->first);
mProjectiles.erase(it++);
continue;
}
MWWorld::Ptr ptr = it->first;
// gravity constant - must be way lower than the gravity affecting actors, since we're not
// simulating aerodynamics at all
it->second.mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration;
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
Ogre::Vector3 newPos = pos + it->second.mVelocity * duration;
Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->second.mVelocity);
Ogre::Matrix3 mat;
orient.ToRotationMatrix(mat);
Ogre::Radian xr,yr,zr;
mat.ToEulerAnglesXYZ(xr, yr, zr);
rotateObject(ptr, -xr.valueDegrees(), -yr.valueDegrees(), -zr.valueDegrees());
// Check for impact
btVector3 from(pos.x, pos.y, pos.z);
btVector3 to(newPos.x, newPos.y, newPos.z);
std::vector<std::pair<float, std::string> > collisions = mPhysEngine->rayTest2(from, to);
bool hit=false;
// HACK: query against the shape as well, since the ray does not take the volume into account
// really, this should be a convex cast, but the whole physics system needs a rewrite
std::vector<std::string> col2 = mPhysEngine->getCollisions(ptr.getRefData().getHandle());
for (std::vector<std::string>::iterator ci = col2.begin(); ci != col2.end(); ++ci)
collisions.push_back(std::make_pair(0.f,*ci));
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
{
MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second);
if (obstacle == ptr)
continue;
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
// Arrow intersects with player immediately after shooting :/
if (obstacle == caster)
continue;
if (caster.isEmpty())
caster = obstacle;
if (obstacle.isEmpty())
{
// Terrain
}
else if (obstacle.getClass().isActor())
{
MWMechanics::projectileHit(caster, obstacle, it->second.mBow, ptr, pos + (newPos - pos) * cIt->first);
}
hit = true;
}
if (hit)
{
deleteObject(ptr);
mProjectiles.erase(it++);
continue;
}
std::string handle = ptr.getRefData().getHandle();
moveObject(ptr, newPos.x, newPos.y, newPos.z);
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
if (!ptr.getRefData().getCount())
{
moved[handle] = it->second;
mProjectiles.erase(it++);
}
else
++it;
}
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
for (std::map<std::string, ProjectileState>::iterator it = moved.begin(); it != moved.end(); ++it)
{
MWWorld::Ptr newPtr = searchPtrViaHandle(it->first);
if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted
continue;
mProjectiles[getPtrViaHandle(it->first)] = it->second;
}
}
void World::moveMagicBolts(float duration)
{
std::map<std::string, MagicBoltState> moved;
for (std::map<MWWorld::Ptr, MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
{
if (!mWorldScene->isCellActive(*it->first.getCell()))
{
deleteObject(it->first);
mMagicBolts.erase(it++);
continue;
}
MWWorld::Ptr ptr = it->first;
Ogre::Vector3 rot(ptr.getRefData().getPosition().rot);
Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation();
static float fTargetSpellMaxSpeed = getStore().get<ESM::GameSetting>().find("fTargetSpellMaxSpeed")->getFloat();
float speed = fTargetSpellMaxSpeed * it->second.mSpeed;
Ogre::Vector3 direction = orient.yAxis();
direction.normalise();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
Ogre::Vector3 newPos = pos + direction * duration * speed;
// Check for impact
btVector3 from(pos.x, pos.y, pos.z);
btVector3 to(newPos.x, newPos.y, newPos.z);
std::vector<std::pair<float, std::string> > collisions = mPhysEngine->rayTest2(from, to);
bool explode = false;
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !explode; ++cIt)
{
MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second);
if (obstacle == ptr)
continue;
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
if (caster.isEmpty())
caster = obstacle;
if (obstacle.isEmpty())
{
// Terrain
}
else
{
MWMechanics::CastSpell cast(caster, obstacle);
cast.mHitPosition = pos;
cast.mId = it->second.mId;
cast.mSourceName = it->second.mSourceName;
cast.mStack = it->second.mStack;
cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false, true);
}
explode = true;
}
if (explode)
{
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);
mMagicBolts.erase(it++);
continue;
}
std::string handle = ptr.getRefData().getHandle();
moveObject(ptr, newPos.x, newPos.y, newPos.z);
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
if (!ptr.getRefData().getCount())
{
moved[handle] = it->second;
mMagicBolts.erase(it++);
}
else
++it;
}
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
for (std::map<std::string, MagicBoltState>::iterator it = moved.begin(); it != moved.end(); ++it)
{
MWWorld::Ptr newPtr = searchPtrViaHandle(it->first);
if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted
continue;
mMagicBolts[getPtrViaHandle(it->first)] = it->second;
}
}
void World::objectLeftActiveCell(Ptr object, Ptr movedPtr)
{
// For now, projectiles moved to an inactive cell are just deleted, because there's no reliable way to hold on to the meta information
if (mMagicBolts.find(object) != mMagicBolts.end())
deleteObject(movedPtr);
if (mProjectiles.find(object) != mProjectiles.end())
deleteObject(movedPtr);
mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName);
}
const std::vector<std::string>& World::getContentFiles() const
@ -2934,7 +2663,7 @@ namespace MWWorld
mRendering->spawnEffect(model, texture, worldPosition);
}
void World::explodeSpell(const Vector3 &origin, const MWWorld::Ptr& object, const ESM::EffectList &effects, const Ptr &caster,
void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster,
const std::string& id, const std::string& sourceName)
{
std::map<MWWorld::Ptr, std::vector<ESM::ENAMstruct> > toApply;
@ -2959,12 +2688,13 @@ namespace MWWorld
static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
};
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(!effect->mAreaSound.empty())
sndMgr->playSound3D(object, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack);
else
sndMgr->playSound3D(object, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack);
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(!effect->mAreaSound.empty())
sndMgr->playManualSound3D(origin, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack);
else
sndMgr->playManualSound3D(origin, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack);
}
// Get the actors in range of the effect
std::vector<MWWorld::Ptr> objects;
MWBase::Environment::get().getMechanicsManager()->getObjectsInRange(

@ -3,6 +3,8 @@
#include "../mwrender/debugging.hpp"
#include <boost/shared_ptr.hpp>
#include "ptr.hpp"
#include "scene.hpp"
#include "esmstore.hpp"
@ -51,6 +53,7 @@ namespace MWWorld
{
class WeatherManager;
class Player;
class ProjectileManager;
/// \brief The game world and its visual representation
@ -72,8 +75,12 @@ namespace MWWorld
Cells mCells;
std::string mCurrentWorldSpace;
OEngine::Physic::PhysicEngine* mPhysEngine;
boost::shared_ptr<ProjectileManager> mProjectileManager;
bool mGodMode;
std::vector<std::string> mContentFiles;
@ -90,37 +97,6 @@ namespace MWWorld
std::map<MWWorld::Ptr, int> mDoorStates;
///< only holds doors that are currently moving. 1 = opening, 2 = closing
struct MagicBoltState
{
// Id of spell or enchantment to apply when it hits
std::string mId;
// Actor who casted this projectile
int mActorId;
// Name of item to display as effect source in magic menu (in case we casted an enchantment)
std::string mSourceName;
ESM::EffectList mEffects;
float mSpeed;
bool mStack;
};
struct ProjectileState
{
// Actor who shot this projectile
int mActorId;
MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from
Ogre::Vector3 mVelocity;
};
std::map<MWWorld::Ptr, MagicBoltState> mMagicBolts;
std::map<MWWorld::Ptr, ProjectileState> mProjectiles;
std::string mStartCell;
void updateWeather(float duration);
@ -148,9 +124,6 @@ namespace MWWorld
void processDoors(float duration);
///< Run physics simulation and modify \a world accordingly.
void moveMagicBolts(float duration);
void moveProjectiles(float duration);
void doPhysics(float duration);
///< Run physics simulation and modify \a world accordingly.
@ -171,9 +144,6 @@ namespace MWWorld
bool mLevitationEnabled;
bool mGoToJail;
/// Called when \a object is moved to an inactive cell
void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr);
float feetToGameUnits(float feet);
public:
@ -213,7 +183,7 @@ namespace MWWorld
virtual void setWaterHeight(const float height);
virtual void toggleWater();
virtual bool toggleWater();
virtual void adjustSky();
@ -343,7 +313,8 @@ namespace MWWorld
virtual void changeToExteriorCell (const ESM::Position& position);
///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position);
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true);
///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes
virtual const ESM::Cell *getExterior (const std::string& cellName) const;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
@ -439,15 +410,14 @@ namespace MWWorld
virtual void update (float duration, bool paused);
virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount);
virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount);
///< copy and place an object into the gameworld at the specified cursor position
/// @param object
/// @param cursor X (relative 0-1)
/// @param cursor Y (relative 0-1)
/// @param number of objects to place
/// @return true if the object was placed, or false if it was rejected because the position is too far away
virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount);
virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount);
///< copy and place an object into the gameworld at the given actor's position
/// @param actor giving the dropped object position
/// @param object
@ -573,7 +543,8 @@ namespace MWWorld
*/
virtual void castSpell (const MWWorld::Ptr& actor);
virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName);
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
@ -613,7 +584,7 @@ namespace MWWorld
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition);
virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects,
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName);
};
}

@ -12,7 +12,6 @@ if (GTEST_FOUND AND GMOCK_FOUND)
file(GLOB UNITTEST_SRC_FILES
components/misc/test_*.cpp
components/file_finder/test_*.cpp
)
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})

@ -1,66 +0,0 @@
#include <gtest/gtest.h>
#include <fstream>
#include "components/file_finder/file_finder.hpp"
struct FileFinderTest : public ::testing::Test
{
protected:
FileFinderTest()
: mTestDir("./filefinder_test_dir/")
, mTestFile("test.txt")
, mTestFileUppercase("TEST.TXT")
, mTestFileNotFound("foobarbaz.txt")
{
}
virtual void SetUp()
{
boost::filesystem::create_directory(boost::filesystem::path(mTestDir));
std::ofstream ofs(std::string(mTestDir + mTestFile).c_str(), std::ofstream::out);
ofs << std::endl;
ofs.close();
}
virtual void TearDown()
{
boost::filesystem::remove_all(boost::filesystem::path(mTestDir));
}
std::string mTestDir;
std::string mTestFile;
std::string mTestFileUppercase;
std::string mTestFileNotFound;
};
TEST_F(FileFinderTest, FileFinder_has_file)
{
FileFinder::FileFinder fileFinder(mTestDir);
ASSERT_TRUE(fileFinder.has(mTestFile));
ASSERT_TRUE(fileFinder.has(mTestFileUppercase));
ASSERT_TRUE(fileFinder.lookup(mTestFile) == std::string(mTestDir + mTestFile));
ASSERT_TRUE(fileFinder.lookup(mTestFileUppercase) == std::string(mTestDir + mTestFile));
}
TEST_F(FileFinderTest, FileFinder_does_not_have_file)
{
FileFinder::FileFinder fileFinder(mTestDir);
ASSERT_FALSE(fileFinder.has(mTestFileNotFound));
ASSERT_TRUE(fileFinder.lookup(mTestFileNotFound).empty());
}
TEST_F(FileFinderTest, FileFinderStrict_has_file)
{
FileFinder::FileFinderStrict fileFinder(mTestDir);
ASSERT_TRUE(fileFinder.has(mTestFile));
ASSERT_FALSE(fileFinder.has(mTestFileUppercase));
ASSERT_TRUE(fileFinder.lookup(mTestFile) == std::string(mTestDir + mTestFile));
ASSERT_FALSE(fileFinder.lookup(mTestFileUppercase) == std::string(mTestDir + mTestFile));
}
TEST_F(FileFinderTest, FileFinderStrict_does_not_have_file)
{
FileFinder::FileFinderStrict fileFinder(mTestDir);
ASSERT_FALSE(fileFinder.has(mTestFileNotFound));
ASSERT_TRUE(fileFinder.lookup(mTestFileNotFound).empty());
}

@ -1,74 +0,0 @@
#include <gtest/gtest.h>
#include <boost/filesystem.hpp>
#include <fstream>
#include "components/file_finder/search.hpp"
struct SearchTest : public ::testing::Test
{
protected:
SearchTest()
: mTestDir("./search_test_dir/")
{
}
virtual void SetUp()
{
boost::filesystem::create_directory(boost::filesystem::path(mTestDir));
std::ofstream ofs(std::string(mTestDir + "test2.txt").c_str(), std::ofstream::out);
ofs << std::endl;
ofs.close();
}
virtual void TearDown()
{
boost::filesystem::remove_all(boost::filesystem::path(mTestDir));
}
std::string mTestDir;
};
TEST_F(SearchTest, file_not_found)
{
struct Result : public FileFinder::ReturnPath
{
Result(const boost::filesystem::path& expectedPath)
: mExpectedPath(expectedPath)
{
}
void add(const boost::filesystem::path& p)
{
ASSERT_FALSE(p == mExpectedPath);
}
private:
boost::filesystem::path mExpectedPath;
} r(boost::filesystem::path(mTestDir + "test.txt"));
FileFinder::find(mTestDir, r, false);
}
TEST_F(SearchTest, file_found)
{
struct Result : public FileFinder::ReturnPath
{
Result(const boost::filesystem::path& expectedPath)
: mExpectedPath(expectedPath)
{
}
void add(const boost::filesystem::path& p)
{
ASSERT_TRUE(p == mExpectedPath);
}
private:
boost::filesystem::path mExpectedPath;
} r(boost::filesystem::path(mTestDir + "test2.txt"));
FileFinder::find(mTestDir, r, false);
}

@ -34,10 +34,6 @@ add_component_dir (to_utf8
to_utf8
)
add_component_dir (file_finder
file_finder filename_less search
)
add_component_dir (esm
attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell
loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst
@ -45,7 +41,7 @@ add_component_dir (esm
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate
)
add_component_dir (misc

@ -11,6 +11,10 @@ void ESM::CellState::load (ESMReader &esm)
mHasFogOfWar = false;
esm.getHNOT (mHasFogOfWar, "HFOW");
mLastRespawn.mDay = 0;
mLastRespawn.mHour = 0;
esm.getHNOT (mLastRespawn, "RESP");
}
void ESM::CellState::save (ESMWriter &esm) const
@ -18,5 +22,7 @@ void ESM::CellState::save (ESMWriter &esm) const
if (!mId.mPaged)
esm.writeHNT ("WLVL", mWaterLevel);
esm.writeHNT("HFOW", mHasFogOfWar);
esm.writeHNT ("HFOW", mHasFogOfWar);
esm.writeHNT ("RESP", mLastRespawn);
}

@ -3,6 +3,8 @@
#include "cellid.hpp"
#include "defs.hpp"
namespace ESM
{
class ESMReader;
@ -19,6 +21,8 @@ namespace ESM
int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp)
ESM::TimeStamp mLastRespawn;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};

@ -12,6 +12,9 @@ namespace ESM
mSpawnActorId = -1;
esm.getHNOT (mSpawnActorId, "SPAW");
mSpawn = false;
esm.getHNOT (mSpawn, "RESP");
}
void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const
@ -20,6 +23,9 @@ namespace ESM
if (mSpawnActorId != -1)
esm.writeHNT ("SPAW", mSpawnActorId);
if (mSpawn)
esm.writeHNT ("RESP", mSpawn);
}
}

@ -10,6 +10,7 @@ namespace ESM
struct CreatureLevListState : public ObjectState
{
int mSpawnActorId;
bool mSpawn;
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;

@ -109,6 +109,8 @@ enum RecNameInts
REC_DYNA = FourCC<'D','Y','N','A'>::value,
REC_ASPL = FourCC<'A','S','P','L'>::value,
REC_ACTC = FourCC<'A','C','T','C'>::value,
REC_MPRJ = FourCC<'M','P','R','J'>::value,
REC_PROJ = FourCC<'P','R','O','J'>::value,
// format 1
REC_FILT = 0x544C4946

@ -0,0 +1,65 @@
#include "projectilestate.hpp"
#include "esmwriter.hpp"
#include "esmreader.hpp"
namespace ESM
{
void BaseProjectileState::save(ESMWriter &esm) const
{
esm.writeHNString ("ID__", mId);
esm.writeHNT ("VEC3", mPosition);
esm.writeHNT ("QUAT", mOrientation);
esm.writeHNT ("ACTO", mActorId);
}
void BaseProjectileState::load(ESMReader &esm)
{
mId = esm.getHNString("ID__");
esm.getHNT (mPosition, "VEC3");
esm.getHNT (mOrientation, "QUAT");
esm.getHNT (mActorId, "ACTO");
}
void MagicBoltState::save(ESMWriter &esm) const
{
BaseProjectileState::save(esm);
esm.writeHNString ("SPEL", mSpellId);
esm.writeHNString ("SRCN", mSourceName);
mEffects.save(esm);
esm.writeHNT ("SPED", mSpeed);
esm.writeHNT ("STCK", mStack);
esm.writeHNString ("SOUN", mSound);
}
void MagicBoltState::load(ESMReader &esm)
{
BaseProjectileState::load(esm);
mSpellId = esm.getHNString("SPEL");
mSourceName = esm.getHNString ("SRCN");
mEffects.load(esm);
esm.getHNT (mSpeed, "SPED");
esm.getHNT (mStack, "STCK");
mSound = esm.getHNString ("SOUN");
}
void ProjectileState::save(ESMWriter &esm) const
{
BaseProjectileState::save(esm);
esm.writeHNString ("BOW_", mBowId);
esm.writeHNT ("VEL_", mVelocity);
}
void ProjectileState::load(ESMReader &esm)
{
BaseProjectileState::load(esm);
mBowId = esm.getHNString ("BOW_");
esm.getHNT (mVelocity, "VEL_");
}
}

@ -0,0 +1,90 @@
#ifndef OPENMW_ESM_PROJECTILESTATE_H
#define OPENMW_ESM_PROJECTILESTATE_H
#include <string>
#include <OgreVector3.h>
#include <OgreQuaternion.h>
#include "effectlist.hpp"
namespace ESM
{
// format 0, savegames only
struct Quaternion
{
float mValues[4];
Quaternion() {}
Quaternion (Ogre::Quaternion q)
{
mValues[0] = q.w;
mValues[1] = q.x;
mValues[2] = q.y;
mValues[3] = q.z;
}
operator Ogre::Quaternion () const
{
return Ogre::Quaternion(mValues[0], mValues[1], mValues[2], mValues[3]);
}
};
struct Vector3
{
float mValues[3];
Vector3() {}
Vector3 (Ogre::Vector3 v)
{
mValues[0] = v.x;
mValues[1] = v.y;
mValues[2] = v.z;
}
operator Ogre::Vector3 () const
{
return Ogre::Vector3(&mValues[0]);
}
};
struct BaseProjectileState
{
std::string mId;
Vector3 mPosition;
Quaternion mOrientation;
int mActorId;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
struct MagicBoltState : public BaseProjectileState
{
std::string mSpellId;
std::string mSourceName;
ESM::EffectList mEffects;
float mSpeed;
bool mStack;
std::string mSound;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
struct ProjectileState : public BaseProjectileState
{
std::string mBowId;
Vector3 mVelocity;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

@ -1,142 +0,0 @@
#ifndef FILE_FINDER_MAIN_H
#define FILE_FINDER_MAIN_H
#include <map>
#include "search.hpp"
#include "filename_less.hpp"
#include <components/files/multidircollection.hpp>
namespace FileFinder
{
template <typename LESS>
class FileFinderT
{
typedef std::map<std::string, std::string, LESS> TableContainer;
TableContainer table;
struct Inserter : ReturnPath
{
FileFinderT<LESS> *owner;
int cut;
void add(const boost::filesystem::path &pth)
{
std::string file = pth.string();
std::string key = file.substr(cut);
owner->table[key] = file;
}
};
Inserter inserter;
public:
FileFinderT(const boost::filesystem::path &path, bool recurse=true)
{
inserter.owner = this;
// Remember the original path length, so we can cut it away from
// the relative paths used as keys
const std::string& pstring = path.string();
inserter.cut = pstring.size();
// If the path does not end in a slash, then boost will add one
// later, which means one more character we have to remove.
char last = *pstring.rbegin();
if(last != '\\' && last != '/')
inserter.cut++;
// Fill the map
find(path, inserter, recurse);
}
bool has(const std::string& file) const
{
return table.find(file) != table.end();
}
// Find the full path from a relative path.
const std::string &lookup(const std::string& file) const
{
static std::string empty;
typename TableContainer::const_iterator it = table.find(file);
return (it != table.end()) ? it->second : empty;
}
};
template
<
class LESS
>
struct TreeFileFinder
{
typedef TreeFileFinder<LESS> finder_t;
TreeFileFinder(const Files::PathContainer& paths, bool recurse = true)
{
struct : ReturnPath
{
finder_t *owner;
int cut;
void add(const boost::filesystem::path &pth)
{
std::string file = pth.string();
std::string key = file.substr(cut);
owner->mTable[key] = file;
}
} inserter;
inserter.owner = this;
for (Files::PathContainer::const_iterator it = paths.begin(); it != paths.end(); ++it)
{
// Remember the original path length, so we can cut it away from
// the relative paths used as keys
const std::string& pstring = it->string();
inserter.cut = pstring.size();
// If the path does not end in a slash, then boost will add one
// later, which means one more character we have to remove.
char last = *pstring.rbegin();
if (last != '\\' && last != '/')
{
inserter.cut++;
}
// Fill the map
find(*it, inserter, recurse);
}
}
bool has(const std::string& file) const
{
return mTable.find(file) != mTable.end();
}
const std::string& lookup(const std::string& file) const
{
static std::string empty;
typename TableContainer::const_iterator it = mTable.find(file);
return (it != mTable.end()) ? it->second : empty;
}
private:
typedef std::map<std::string, std::string, LESS> TableContainer;
TableContainer mTable;
// Inserter inserter;
};
// The default is to use path_less for equality checks
typedef FileFinderT<path_less> FileFinder;
typedef FileFinderT<path_slash> FileFinderStrict;
typedef TreeFileFinder<path_less> LessTreeFileFinder;
typedef TreeFileFinder<path_slash> StrictTreeFileFinder;
} /* namespace FileFinder */
#endif /* FILE_FINDER_MAIN_H */

@ -1,84 +0,0 @@
#ifndef FILE_FINDER_LESS_H
#define FILE_FINDER_LESS_H
#include <libs/platform/strings.h>
#include <string>
namespace FileFinder{
// Used for maps of file paths. Compares file paths, but ignores case
// AND treats \ and / as the same character.
struct path_less
{
int compareChar(char a, char b) const
{
if(a>b) return 1;
else if(a<b) return -1;
return 0;
}
int comparePathChar(char a, char b) const
{
if(a >= 'a' && a <= 'z') a += 'A'-'a';
else if(a == '\\') a = '/';
if(b >= 'a' && b <= 'z') b += 'A'-'a';
else if(b == '\\') b = '/';
return compareChar(a,b);
}
int compareString(const char *a, const char *b) const
{
while(*a && *b)
{
int i = comparePathChar(*a,*b);
if(i != 0) return i;
a++; b++;
}
// At this point, one or both of the chars is a null terminator.
// Normal char comparison will get the correct final result here.
return compareChar(*a,*b);
}
bool operator() (const std::string& a, const std::string& b) const
{
return compareString(a.c_str(), b.c_str()) < 0;
}
};
struct path_slash
{
int compareChar(char a, char b) const
{
if(a>b) return 1;
else if(a<b) return -1;
return 0;
}
int comparePathChar(char a, char b) const
{
if(a == '\\') a = '/';
if(b == '\\') b = '/';
return compareChar(a,b);
}
int compareString(const char *a, const char *b) const
{
while(*a && *b)
{
int i = comparePathChar(*a,*b);
if(i != 0) return i;
a++; b++;
}
// At this point, one or both of the chars is a null terminator.
// Normal char comparison will get the correct final result here.
return compareChar(*a,*b);
}
bool operator() (const std::string& a, const std::string& b) const
{
return compareString(a.c_str(), b.c_str()) < 0;
}
};
}
#endif

@ -1,36 +0,0 @@
#include "search.hpp"
#include <iostream>
void FileFinder::find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse)
{
if (boost::filesystem::exists(dir_path))
{
if (!recurse)
{
boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr)
{
if (!boost::filesystem::is_directory( *itr ))
{
ret.add(*itr);
}
}
}
else
{
boost::filesystem::recursive_directory_iterator end_itr; // default construction yields past-the-end
for (boost::filesystem::recursive_directory_iterator itr(dir_path); itr != end_itr; ++itr)
{
if (!boost::filesystem::is_directory(*itr))
{
ret.add(*itr);
}
}
}
}
else
{
std::cout << "Path " << dir_path << " not found" << std::endl;
}
}

@ -1,20 +0,0 @@
#ifndef FILE_FINDER_SEARCH_H
#define FILE_FINDER_SEARCH_H
#include <boost/filesystem.hpp>
#include <string>
namespace FileFinder
{
struct ReturnPath
{
virtual void add(const boost::filesystem::path &pth) = 0;
};
/** Search the given path and return all file paths through 'ret'. If
recurse==true, all files in subdirectories are returned as well.
*/
void find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse=true);
}
#endif

@ -33,6 +33,7 @@
</Widget>
<Widget type="MWScrollBar" skin="MW_HScroll" position="122 20 210 13" name="MagnitudeMinSlider">
<Property key="Range" value="100"/>
<Property key="Page" value="1"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="122 32 210 20" name="MagnitudeMaxValue">
@ -41,6 +42,7 @@
</Widget>
<Widget type="MWScrollBar" skin="MW_HScroll" position="122 52 210 13" name="MagnitudeMaxSlider">
<Property key="Range" value="100"/>
<Property key="Page" value="1"/>
</Widget>
</Widget>
@ -58,6 +60,7 @@
</Widget>
<Widget type="MWScrollBar" skin="MW_HScroll" position="122 20 210 13" name="DurationSlider">
<Property key="Range" value="1440"/>
<Property key="Page" value="1"/>
</Widget>
</Widget>
@ -74,6 +77,7 @@
</Widget>
<Widget type="MWScrollBar" skin="MW_HScroll" position="122 20 210 13" name="AreaSlider">
<Property key="Range" value="51"/>
<Property key="Page" value="1"/>
</Widget>
</Widget>

Loading…
Cancel
Save