mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-05 14:41:32 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
1ca921d6e2
9 changed files with 87 additions and 40 deletions
|
@ -191,6 +191,14 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player
|
if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player
|
||||||
|
|
||||||
|
// pure water creatures won't try to fight with the target on the ground
|
||||||
|
// except that creature is already hostile
|
||||||
|
if ((againstPlayer || !creatureStats.isHostile())
|
||||||
|
&& ((actor1.getClass().canSwim(actor1) && !actor1.getClass().canWalk(actor1) // pure water creature
|
||||||
|
&& !MWBase::Environment::get().getWorld()->isSwimming(actor2))
|
||||||
|
|| (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target
|
||||||
|
return;
|
||||||
|
|
||||||
float fight;
|
float fight;
|
||||||
|
|
||||||
if (againstPlayer)
|
if (againstPlayer)
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace
|
||||||
|
|
||||||
// cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
|
// cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
|
||||||
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
|
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
|
||||||
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offset)
|
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY)
|
||||||
{
|
{
|
||||||
if((to - from).length() >= PATHFIND_CAUTION_DIST || abs(from.z - to.z) <= PATHFIND_Z_REACH)
|
if((to - from).length() >= PATHFIND_CAUTION_DIST || abs(from.z - to.z) <= PATHFIND_Z_REACH)
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,7 @@ namespace
|
||||||
dir.z = 0;
|
dir.z = 0;
|
||||||
dir.normalise();
|
dir.normalise();
|
||||||
float verticalOffset = 200; // instead of '200' here we want the height of the actor
|
float verticalOffset = 200; // instead of '200' here we want the height of the actor
|
||||||
Ogre::Vector3 _from = from + dir*offset + Ogre::Vector3::UNIT_Z * verticalOffset;
|
Ogre::Vector3 _from = from + dir*offsetXY + Ogre::Vector3::UNIT_Z * verticalOffset;
|
||||||
|
|
||||||
// cast up-down ray and find height in world space of hit
|
// cast up-down ray and find height in world space of hit
|
||||||
float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
|
float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
|
||||||
|
@ -149,13 +149,27 @@ namespace MWMechanics
|
||||||
bool AiCombat::execute (const MWWorld::Ptr& actor,float duration)
|
bool AiCombat::execute (const MWWorld::Ptr& actor,float duration)
|
||||||
{
|
{
|
||||||
//General description
|
//General description
|
||||||
if(actor.getClass().getCreatureStats(actor).isDead()) return true;
|
if(actor.getClass().getCreatureStats(actor).isDead())
|
||||||
|
return true;
|
||||||
|
|
||||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||||
|
|
||||||
if(target.getClass().getCreatureStats(target).isDead())
|
if(target.getClass().getCreatureStats(target).isDead())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
const MWWorld::Class& actorClass = actor.getClass();
|
||||||
|
MWBase::World& world = *MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
|
if ((!actorClass.isNpc() && target == world.getPlayerPtr() &&
|
||||||
|
actorClass.canSwim(actor) && !actorClass.canWalk(actor) // pure water creature
|
||||||
|
&& !world.isSwimming(target)) // Player moved out of water
|
||||||
|
|| (!actorClass.canSwim(actor) && world.isSwimming(target))) // creature can't swim to Player
|
||||||
|
{
|
||||||
|
actorClass.getCreatureStats(actor).setHostile(false);
|
||||||
|
actorClass.getCreatureStats(actor).setAttackingOrSpell(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//Update every frame
|
//Update every frame
|
||||||
if(mCombatMove)
|
if(mCombatMove)
|
||||||
{
|
{
|
||||||
|
@ -327,10 +341,11 @@ namespace MWMechanics
|
||||||
Ogre::Vector3 vActorPos(pos.pos);
|
Ogre::Vector3 vActorPos(pos.pos);
|
||||||
Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos);
|
Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos);
|
||||||
Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
|
Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
|
||||||
|
float distToTarget = vDirToTarget.length();
|
||||||
|
|
||||||
bool isStuck = false;
|
bool isStuck = false;
|
||||||
float speed = 0.0f;
|
float speed = 0.0f;
|
||||||
if(mMovement.mPosition[1] && (Ogre::Vector3(mLastPos.pos) - vActorPos).length() < (speed = actorCls.getSpeed(actor)) / 10.0f)
|
if(mMovement.mPosition[1] && (Ogre::Vector3(mLastPos.pos) - vActorPos).length() < (speed = actorCls.getSpeed(actor)) * tReaction / 2)
|
||||||
isStuck = true;
|
isStuck = true;
|
||||||
|
|
||||||
mLastPos = pos;
|
mLastPos = pos;
|
||||||
|
@ -342,16 +357,15 @@ namespace MWMechanics
|
||||||
// determine vertical angle to target
|
// determine vertical angle to target
|
||||||
// if actor can move along z-axis it will control movement dir
|
// if actor can move along z-axis it will control movement dir
|
||||||
// if can't - it will control correct aiming
|
// if can't - it will control correct aiming
|
||||||
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget);
|
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget);
|
||||||
|
|
||||||
vDirToTarget.z = 0;
|
|
||||||
float distToTarget = vDirToTarget.length();
|
|
||||||
|
|
||||||
// (within strike dist) || (not quite strike dist while following)
|
// (within strike dist) || (not quite strike dist while following)
|
||||||
if(distToTarget < rangeAttack || (distToTarget <= rangeFollow && mFollowTarget && !isStuck) )
|
if(distToTarget < rangeAttack || (distToTarget <= rangeFollow && mFollowTarget && !isStuck) )
|
||||||
{
|
{
|
||||||
//Melee and Close-up combat
|
//Melee and Close-up combat
|
||||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
|
||||||
|
// if we preserve dir.z then horizontal angle can be inaccurate
|
||||||
|
mMovement.mRotation[2] = getZAngleToDir(Ogre::Vector3(vDirToTarget.x, vDirToTarget.y, 0));
|
||||||
|
|
||||||
// (not quite strike dist while following)
|
// (not quite strike dist while following)
|
||||||
if (mFollowTarget && distToTarget > rangeAttack)
|
if (mFollowTarget && distToTarget > rangeAttack)
|
||||||
|
@ -398,18 +412,14 @@ namespace MWMechanics
|
||||||
bool preferShortcut = false;
|
bool preferShortcut = false;
|
||||||
bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target);
|
bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target);
|
||||||
|
|
||||||
if(mReadyToAttack) isStuck = false;
|
|
||||||
|
|
||||||
// check if shortcut is available
|
// check if shortcut is available
|
||||||
if(!isStuck
|
if(inLOS && (!isStuck || mReadyToAttack)
|
||||||
&& (!mForceNoShortcut
|
&& (!mForceNoShortcut || (Ogre::Vector3(mShortcutFailPos.pos) - vActorPos).length() >= PATHFIND_SHORTCUT_RETRY_DIST))
|
||||||
|| (Ogre::Vector3(mShortcutFailPos.pos) - vActorPos).length() >= PATHFIND_SHORTCUT_RETRY_DIST)
|
|
||||||
&& inLOS)
|
|
||||||
{
|
{
|
||||||
if(speed == 0.0f) speed = actorCls.getSpeed(actor);
|
if(speed == 0.0f) speed = actorCls.getSpeed(actor);
|
||||||
// maximum dist before pit/obstacle for actor to avoid them depending on his speed
|
// maximum dist before pit/obstacle for actor to avoid them depending on his speed
|
||||||
float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2; // *2 - for reliability
|
float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2; // *2 - for reliability
|
||||||
preferShortcut = checkWayIsClear(vActorPos, vTargetPos, distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
|
preferShortcut = checkWayIsClear(vActorPos, vTargetPos, Ogre::Vector3(vDirToTarget.x, vDirToTarget.y, 0).length() > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't use pathgrid when actor can move in 3 dimensions
|
// don't use pathgrid when actor can move in 3 dimensions
|
||||||
|
@ -467,7 +477,7 @@ namespace MWMechanics
|
||||||
mReadyToAttack = false;
|
mReadyToAttack = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(distToTarget > rangeAttack && !distantCombat)
|
if(!isStuck && distToTarget > rangeAttack && !distantCombat)
|
||||||
{
|
{
|
||||||
//special run attack; it shouldn't affect melee combat tactics
|
//special run attack; it shouldn't affect melee combat tactics
|
||||||
if(actorCls.getMovementSettings(actor).mPosition[1] == 1)
|
if(actorCls.getMovementSettings(actor).mPosition[1] == 1)
|
||||||
|
|
|
@ -137,7 +137,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||||
float nearestDist = std::numeric_limits<float>::max();
|
float nearestDist = std::numeric_limits<float>::max();
|
||||||
Ogre::Vector3 vActorPos = Ogre::Vector3(actor.getRefData().getPosition().pos);
|
Ogre::Vector3 vActorPos = Ogre::Vector3(actor.getRefData().getPosition().pos);
|
||||||
|
|
||||||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end();)
|
||||||
{
|
{
|
||||||
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
|
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||||
if (target.isEmpty())
|
if (target.isEmpty())
|
||||||
{
|
{
|
||||||
delete *it;
|
delete *it;
|
||||||
mPackages.erase(it++);
|
it = mPackages.erase(it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -159,20 +159,26 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||||
nearestDist = distTo;
|
nearestDist = distTo;
|
||||||
itActualCombat = it;
|
itActualCombat = it;
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// all targets disappeared
|
if (!mPackages.empty())
|
||||||
if (nearestDist == std::numeric_limits<float>::max())
|
{
|
||||||
|
if (nearestDist < std::numeric_limits<float>::max() && mPackages.begin() != itActualCombat)
|
||||||
|
{
|
||||||
|
// move combat package with nearest target to the front
|
||||||
|
mPackages.splice(mPackages.begin(), mPackages, itActualCombat);
|
||||||
|
}
|
||||||
|
|
||||||
|
package = mPackages.front();
|
||||||
|
mLastAiPackage = package->getTypeId();
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
mDone = true;
|
mDone = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (mPackages.begin() != itActualCombat)
|
|
||||||
{
|
|
||||||
// move combat package with nearest target to the front
|
|
||||||
mPackages.splice(mPackages.begin(), mPackages, itActualCombat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (package->execute (actor,duration))
|
if (package->execute (actor,duration))
|
||||||
|
|
|
@ -1022,7 +1022,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
|
void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getDialogueManager()->say(ptr, "attack");
|
if (ptr.getClass().isNpc())
|
||||||
|
MWBase::Environment::get().getDialogueManager()->say(ptr, "attack");
|
||||||
|
|
||||||
ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr);
|
ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr);
|
||||||
if (target == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
if (target == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||||
ptr.getClass().getCreatureStats(ptr).setHostile(true);
|
ptr.getClass().getCreatureStats(ptr).setHostile(true);
|
||||||
|
|
|
@ -496,10 +496,10 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
|
||||||
mExpelled.insert (iter->first);
|
mExpelled.insert (iter->first);
|
||||||
|
|
||||||
if (iter->second.mRank >= 0)
|
if (iter->second.mRank >= 0)
|
||||||
mFactionRank.insert (std::make_pair (iter->first, iter->second.mRank));
|
mFactionRank[iter->first] = iter->second.mRank;
|
||||||
|
|
||||||
if (iter->second.mReputation)
|
if (iter->second.mReputation)
|
||||||
mFactionReputation.insert (std::make_pair (iter->first, iter->second.mReputation));
|
mFactionReputation[iter->first] = iter->second.mReputation;
|
||||||
}
|
}
|
||||||
|
|
||||||
mDisposition = state.mDisposition;
|
mDisposition = state.mDisposition;
|
||||||
|
|
|
@ -42,7 +42,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
|
||||||
for (int i = 0; i < esm.getIndex(); i++) {
|
for (int i = 0; i < esm.getIndex(); i++) {
|
||||||
const std::string &candidate = allPlugins->at(i).getContext().filename;
|
const std::string &candidate = allPlugins->at(i).getContext().filename;
|
||||||
std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();
|
std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();
|
||||||
if (fname == fnamecandidate) {
|
if (Misc::StringUtils::ciEqual(fname, fnamecandidate)) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1375,11 +1375,11 @@ namespace MWWorld
|
||||||
|
|
||||||
updateWeather(duration);
|
updateWeather(duration);
|
||||||
|
|
||||||
mWorldScene->update (duration, paused);
|
|
||||||
|
|
||||||
if (!paused)
|
if (!paused)
|
||||||
doPhysics (duration);
|
doPhysics (duration);
|
||||||
|
|
||||||
|
mWorldScene->update (duration, paused);
|
||||||
|
|
||||||
performUpdateSceneQueries ();
|
performUpdateSceneQueries ();
|
||||||
|
|
||||||
updateWindowManager ();
|
updateWindowManager ();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
#include "collections.hpp"
|
#include "collections.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
namespace Files
|
namespace Files
|
||||||
{
|
{
|
||||||
Collections::Collections()
|
Collections::Collections()
|
||||||
|
@ -36,9 +37,19 @@ namespace Files
|
||||||
for (Files::PathContainer::const_iterator iter = mDirectories.begin();
|
for (Files::PathContainer::const_iterator iter = mDirectories.begin();
|
||||||
iter != mDirectories.end(); ++iter)
|
iter != mDirectories.end(); ++iter)
|
||||||
{
|
{
|
||||||
const boost::filesystem::path path = *iter / file;
|
for (boost::filesystem::directory_iterator iter2 (*iter);
|
||||||
if (boost::filesystem::exists(path))
|
iter2!=boost::filesystem::directory_iterator(); ++iter2)
|
||||||
return path.string();
|
{
|
||||||
|
boost::filesystem::path path = *iter2;
|
||||||
|
|
||||||
|
if (mFoldCase)
|
||||||
|
{
|
||||||
|
if (Misc::StringUtils::ciEqual(file, path.filename().string()))
|
||||||
|
return path.string();
|
||||||
|
}
|
||||||
|
else if (path.filename().string() == file)
|
||||||
|
return path.string();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error ("file " + file + " not found");
|
throw std::runtime_error ("file " + file + " not found");
|
||||||
|
@ -49,9 +60,19 @@ namespace Files
|
||||||
for (Files::PathContainer::const_iterator iter = mDirectories.begin();
|
for (Files::PathContainer::const_iterator iter = mDirectories.begin();
|
||||||
iter != mDirectories.end(); ++iter)
|
iter != mDirectories.end(); ++iter)
|
||||||
{
|
{
|
||||||
const boost::filesystem::path path = *iter / file;
|
for (boost::filesystem::directory_iterator iter2 (*iter);
|
||||||
if (boost::filesystem::exists(path))
|
iter2!=boost::filesystem::directory_iterator(); ++iter2)
|
||||||
return true;
|
{
|
||||||
|
boost::filesystem::path path = *iter2;
|
||||||
|
|
||||||
|
if (mFoldCase)
|
||||||
|
{
|
||||||
|
if (Misc::StringUtils::ciEqual(file, path.filename().string()))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (path.filename().string() == file)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -417,7 +417,7 @@
|
||||||
<UserString key="SettingType" value="CheckButton"/>
|
<UserString key="SettingType" value="CheckButton"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
|
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
|
||||||
<Property key="Caption" value="Enabled"/>
|
<Property key="Caption" value="Shadows"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue