forked from teamnwah/openmw-tes3coop
Merge remote-tracking branch 'scrawl/master'
commit
f7c89015f9
@ -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
|
@ -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);
|
||||
}
|
@ -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
|
Loading…
Reference in New Issue