mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:49:56 +00:00
Merge branch 'master' of https://github.com/OpenMW/openmw into script-respawn
This commit is contained in:
commit
2a53f6bc3e
26 changed files with 197 additions and 60 deletions
|
@ -22,6 +22,7 @@ Programmers
|
|||
alexanderkjall
|
||||
Alexander Nadeau (wareya)
|
||||
Alexander Olofsson (Ace)
|
||||
Alex Rice
|
||||
Alex S (docwest)
|
||||
Allofich
|
||||
Andrei Kortunov (akortunov)
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
Bug #5038: Enchanting success chance calculations are blatantly wrong
|
||||
Bug #5047: # in cell names sets color
|
||||
Bug #5050: Invalid spell effects are not handled gracefully
|
||||
Bug #5055: Mark, Recall, Intervention magic effect abilities have no effect when added and removed in the same frame
|
||||
Bug #5056: Calling Cast function on player doesn't equip the spell but casts it
|
||||
Bug #5060: Magic effect visuals stop when death animation begins instead of when it ends
|
||||
Bug #5063: Shape named "Tri Shadow" in creature mesh is visible if it isn't hidden
|
||||
|
@ -112,8 +113,13 @@
|
|||
Bug #5074: Paralyzed actors greet the player
|
||||
Bug #5075: Enchanting cast style can be changed if there's no object
|
||||
Bug #5082: Scrolling with controller in GUI mode is broken
|
||||
Bug #5089: Swimming/Underwater creatures only swim around ground level
|
||||
Bug #5092: NPCs with enchanted weapons play sound when out of charges
|
||||
Bug #5093: Hand to hand sound plays on knocked out enemies
|
||||
Bug #5099: Non-swimming enemies will enter water if player is water walking
|
||||
Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels
|
||||
Bug #5105: NPCs start combat with werewolves from any distance
|
||||
Bug #5110: ModRegion with a redundant numerical argument breaks script execution
|
||||
Feature #1774: Handle AvoidNode
|
||||
Feature #2229: Improve pathfinding AI
|
||||
Feature #3025: Analogue gamepad movement controls
|
||||
|
@ -149,6 +155,8 @@
|
|||
Feature #5036: Allow scripted faction leaving
|
||||
Feature #5046: Gamepad thumbstick cursor speed
|
||||
Feature #5051: Provide a separate textures for scrollbars
|
||||
Feature #5094: Unix like console hotkeys
|
||||
Feature #5098: Allow user controller bindings
|
||||
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
||||
Task #4695: Optimize Distant Terrain memory consumption
|
||||
Task #4789: Optimize cell transitions
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include "intsetting.hpp"
|
||||
#include "doublesetting.hpp"
|
||||
|
@ -415,7 +416,9 @@ CSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble (const std::string& key,
|
|||
if (mCurrentCategory==mCategories.end())
|
||||
throw std::logic_error ("no category for setting");
|
||||
|
||||
setDefault(key, std::to_string(default_));
|
||||
std::ostringstream stream;
|
||||
stream << default_;
|
||||
setDefault(key, stream.str());
|
||||
|
||||
default_ = mSettings.getFloat (key, mCurrentCategory->second.getKey());
|
||||
|
||||
|
|
|
@ -515,8 +515,17 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
controllerFileName = "gamecontrollerdb.txt";
|
||||
}
|
||||
|
||||
const std::string userdefault = mCfgMgr.getUserConfigPath().string() + "/" + controllerFileName;
|
||||
const std::string localdefault = mCfgMgr.getLocalPath().string() + "/" + controllerFileName;
|
||||
const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/" + controllerFileName;
|
||||
|
||||
std::string userGameControllerdb;
|
||||
if (boost::filesystem::exists(userdefault)){
|
||||
userGameControllerdb = userdefault;
|
||||
}
|
||||
else
|
||||
userGameControllerdb = "";
|
||||
|
||||
std::string gameControllerdb;
|
||||
if (boost::filesystem::exists(localdefault))
|
||||
gameControllerdb = localdefault;
|
||||
|
@ -525,7 +534,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
else
|
||||
gameControllerdb = ""; //if it doesn't exist, pass in an empty string
|
||||
|
||||
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, gameControllerdb, mGrab);
|
||||
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
||||
mEnvironment.setInputManager (input);
|
||||
|
||||
std::string myguiResources = (mResDir / "mygui").string();
|
||||
|
|
|
@ -1181,7 +1181,7 @@ namespace MWClass
|
|||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
||||
|
||||
// Race weight should not affect 1st-person meshes, otherwise it will change hand proportions and can break aiming.
|
||||
if (ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())
|
||||
if (ptr == MWMechanics::getPlayer() && ptr.isInCell() && MWBase::Environment::get().getWorld()->isFirstPerson())
|
||||
{
|
||||
if (ref->mBase->isMale())
|
||||
scale *= race->mData.mHeight.mMale;
|
||||
|
|
|
@ -112,10 +112,7 @@ struct TypesetBookImpl : TypesetBook
|
|||
if (i->empty())
|
||||
return Range (Utf8Point (nullptr), Utf8Point (nullptr));
|
||||
|
||||
Utf8Point begin = &i->front ();
|
||||
Utf8Point end = &i->front () + i->size ();
|
||||
|
||||
return Range (begin, end);
|
||||
return Range (i->data(), i->data() + i->size());
|
||||
}
|
||||
|
||||
size_t pageCount () const { return mPages.size (); }
|
||||
|
@ -346,8 +343,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
|
|||
assert (end <= mCurrentContent->size ());
|
||||
assert (begin <= mCurrentContent->size ());
|
||||
|
||||
Utf8Point begin_ = &mCurrentContent->front () + begin;
|
||||
Utf8Point end_ = &mCurrentContent->front () + end ;
|
||||
Utf8Point begin_ = mCurrentContent->data() + begin;
|
||||
Utf8Point end_ = mCurrentContent->data() + end;
|
||||
|
||||
writeImpl (static_cast <StyleImpl*> (style), begin_, end_);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "console.hpp"
|
||||
|
||||
#include <MyGUI_EditBox.h>
|
||||
#include <MyGUI_InputManager.h>
|
||||
#include <MyGUI_LayerManager.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
@ -224,11 +225,48 @@ namespace MWGui
|
|||
resetReference();
|
||||
}
|
||||
|
||||
bool isWhitespace(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
void Console::keyPress(MyGUI::Widget* _sender,
|
||||
MyGUI::KeyCode key,
|
||||
MyGUI::Char _char)
|
||||
{
|
||||
if( key == MyGUI::KeyCode::Tab)
|
||||
if(MyGUI::InputManager::getInstance().isControlPressed())
|
||||
{
|
||||
if(key == MyGUI::KeyCode::W)
|
||||
{
|
||||
const auto& caption = mCommandLine->getCaption();
|
||||
if(caption.empty())
|
||||
return;
|
||||
size_t max = mCommandLine->getTextCursor();
|
||||
while(max > 0 && (isWhitespace(caption[max - 1]) || caption[max - 1] == '>'))
|
||||
max--;
|
||||
while(max > 0 && !isWhitespace(caption[max - 1]) && caption[max - 1] != '>')
|
||||
max--;
|
||||
size_t length = mCommandLine->getTextCursor() - max;
|
||||
if(length > 0)
|
||||
{
|
||||
std::string text = caption;
|
||||
text.erase(max, length);
|
||||
mCommandLine->setCaption(text);
|
||||
mCommandLine->setTextCursor(max);
|
||||
}
|
||||
}
|
||||
else if(key == MyGUI::KeyCode::U)
|
||||
{
|
||||
if(mCommandLine->getTextCursor() > 0)
|
||||
{
|
||||
std::string text = mCommandLine->getCaption();
|
||||
text.erase(0, mCommandLine->getTextCursor());
|
||||
mCommandLine->setCaption(text);
|
||||
mCommandLine->setTextCursor(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(key == MyGUI::KeyCode::Tab)
|
||||
{
|
||||
std::vector<std::string> matches;
|
||||
listNames();
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace MWInput
|
|||
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
|
||||
osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,
|
||||
const std::string& userFile, bool userFileExists,
|
||||
const std::string& userControllerBindingsFile,
|
||||
const std::string& controllerBindingsFile, bool grab)
|
||||
: mWindow(window)
|
||||
, mWindowVisible(true)
|
||||
|
@ -113,10 +114,14 @@ namespace MWInput
|
|||
|
||||
// Load controller mappings
|
||||
#if SDL_VERSION_ATLEAST(2,0,2)
|
||||
if(controllerBindingsFile!="")
|
||||
if(!controllerBindingsFile.empty())
|
||||
{
|
||||
SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str());
|
||||
}
|
||||
if(!userControllerBindingsFile.empty())
|
||||
{
|
||||
SDL_GameControllerAddMappingsFromFile(userControllerBindingsFile.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Open all presently connected sticks
|
||||
|
|
|
@ -76,6 +76,7 @@ namespace MWInput
|
|||
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
|
||||
osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,
|
||||
const std::string& userFile, bool userFileExists,
|
||||
const std::string& userControllerBindingsFile,
|
||||
const std::string& controllerBindingsFile, bool grab);
|
||||
|
||||
virtual ~InputManager();
|
||||
|
|
|
@ -422,6 +422,11 @@ namespace MWMechanics
|
|||
return true;
|
||||
}
|
||||
|
||||
if (actor.getClass().isPureLandCreature(actor) && MWBase::Environment::get().getWorld()->isWalkingOnWater(enemy))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor.getClass().isPureFlyingCreature(actor) || actor.getClass().isPureLandCreature(actor))
|
||||
{
|
||||
if (MWBase::Environment::get().getWorld()->isSwimming(enemy))
|
||||
|
|
|
@ -301,7 +301,7 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin
|
|||
return true;
|
||||
|
||||
const float actorSpeed = actor.getClass().getSpeed(actor);
|
||||
const float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / MAX_VEL_ANGULAR_RADIANS * 2; // *2 - for reliability
|
||||
const float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / getAngularVelocity(actorSpeed) * 2; // *2 - for reliability
|
||||
const float distToTarget = osg::Vec2f(endPoint.x(), endPoint.y()).length();
|
||||
|
||||
const float offsetXY = distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2;
|
||||
|
@ -363,7 +363,7 @@ bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& act
|
|||
// get actor's shortest radius for moving in circle
|
||||
float speed = actor.getClass().getSpeed(actor);
|
||||
speed += speed * 0.1f; // 10% real speed inaccuracy
|
||||
float radius = speed / MAX_VEL_ANGULAR_RADIANS;
|
||||
float radius = speed / getAngularVelocity(speed);
|
||||
|
||||
// get radius direction to the center
|
||||
const float* rot = actor.getRefData().getPosition().rot;
|
||||
|
|
|
@ -88,6 +88,7 @@ namespace MWMechanics
|
|||
// that is the user's responsibility
|
||||
MWBase::Environment::get().getWorld()->moveObject(actor, mX, mY, mZ);
|
||||
actor.getClass().adjustPosition(actor, false);
|
||||
reset();
|
||||
}
|
||||
|
||||
void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||
|
|
|
@ -448,7 +448,7 @@ namespace MWMechanics
|
|||
else
|
||||
{
|
||||
// have not yet reached the destination
|
||||
evadeObstacles(actor, storage);
|
||||
evadeObstacles(actor, duration, storage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -479,8 +479,17 @@ namespace MWMechanics
|
|||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
|
||||
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)
|
||||
{
|
||||
if (mUsePathgrid)
|
||||
{
|
||||
const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
|
||||
const float actorTolerance = 2 * actor.getClass().getSpeed(actor) * duration
|
||||
+ 1.2 * std::max(halfExtents.x(), halfExtents.y());
|
||||
const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance);
|
||||
mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor), pointTolerance);
|
||||
}
|
||||
|
||||
if (mObstacleCheck.isEvading())
|
||||
{
|
||||
// first check if we're walking into a door
|
||||
|
|
|
@ -137,7 +137,7 @@ namespace MWMechanics
|
|||
short unsigned getRandomIdle();
|
||||
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
||||
void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||
void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||
void evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
||||
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||
void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||
|
|
|
@ -1785,8 +1785,8 @@ namespace MWMechanics
|
|||
if (ptr.getClass().isNpc())
|
||||
disposition = getDerivedDisposition(ptr, true);
|
||||
|
||||
int fight = std::max(0, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()
|
||||
+ static_cast<int>(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast<float>(disposition))));
|
||||
int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()
|
||||
+ static_cast<int>(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast<float>(disposition)));
|
||||
|
||||
if (ptr.getClass().isNpc() && target.getClass().isNpc())
|
||||
{
|
||||
|
|
|
@ -73,6 +73,13 @@ namespace
|
|||
{
|
||||
return sqrDistance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y()));
|
||||
}
|
||||
|
||||
float getPathStepSize(const MWWorld::ConstPtr& actor)
|
||||
{
|
||||
const auto world = MWBase::Environment::get().getWorld();
|
||||
const auto realHalfExtents = world->getHalfExtents(actor);
|
||||
return 2 * std::max(realHalfExtents.x(), realHalfExtents.y());
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
|
@ -320,8 +327,7 @@ namespace MWMechanics
|
|||
try
|
||||
{
|
||||
const auto world = MWBase::Environment::get().getWorld();
|
||||
const auto realHalfExtents = world->getHalfExtents(actor); // This may differ from halfExtents argument
|
||||
const auto stepSize = 2 * std::max(realHalfExtents.x(), realHalfExtents.y());
|
||||
const auto stepSize = getPathStepSize(actor);
|
||||
const auto navigator = world->getNavigator();
|
||||
navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out);
|
||||
}
|
||||
|
@ -333,4 +339,39 @@ namespace MWMechanics
|
|||
<< DetourNavigator::WriteFlags {flags} << ")";
|
||||
}
|
||||
}
|
||||
|
||||
void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
|
||||
const DetourNavigator::Flags flags, const float pointTolerance)
|
||||
{
|
||||
if (mPath.empty())
|
||||
return;
|
||||
|
||||
const auto stepSize = getPathStepSize(actor);
|
||||
const auto startPoint = actor.getRefData().getPosition().asVec3();
|
||||
|
||||
if (sqrDistanceIgnoreZ(mPath.front(), startPoint) <= 4 * stepSize * stepSize)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
|
||||
std::deque<osg::Vec3f> prePath;
|
||||
navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, std::back_inserter(prePath));
|
||||
|
||||
while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.front(), startPoint) < stepSize * stepSize)
|
||||
prePath.pop_front();
|
||||
|
||||
while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.back(), mPath.front()) < stepSize * stepSize)
|
||||
prePath.pop_back();
|
||||
|
||||
std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath));
|
||||
}
|
||||
catch (const DetourNavigator::NavigatorException& exception)
|
||||
{
|
||||
Log(Debug::Debug) << "Build path by navigator exception: \"" << exception.what()
|
||||
<< "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase()
|
||||
<< ") from " << startPoint << " to " << mPath.front() << " with flags ("
|
||||
<< DetourNavigator::WriteFlags {flags} << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,9 @@ namespace MWMechanics
|
|||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
||||
const DetourNavigator::Flags flags);
|
||||
|
||||
void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
|
||||
const DetourNavigator::Flags flags, const float pointTolerance);
|
||||
|
||||
/// Remove front point if exist and within tolerance
|
||||
void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance);
|
||||
|
||||
|
|
|
@ -823,18 +823,22 @@ namespace MWMechanics
|
|||
mStack = false;
|
||||
|
||||
bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
||||
bool isProjectile = false;
|
||||
if (item.getTypeName() == typeid(ESM::Weapon).name())
|
||||
{
|
||||
const ESM::Weapon* ref = item.get<ESM::Weapon>()->mBase;
|
||||
isProjectile = ref->mData.mType >= ESM::Weapon::MarksmanThrown;
|
||||
}
|
||||
int type = enchantment->mData.mType;
|
||||
|
||||
// Check if there's enough charge left
|
||||
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||
if (!godmode && (type == ESM::Enchantment::WhenUsed || (!isProjectile && type == ESM::Enchantment::WhenStrikes)))
|
||||
{
|
||||
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), mCaster);
|
||||
|
||||
if (item.getCellRef().getEnchantmentCharge() == -1)
|
||||
item.getCellRef().setEnchantmentCharge(static_cast<float>(enchantment->mData.mCharge));
|
||||
|
||||
if (godmode)
|
||||
castCost = 0;
|
||||
|
||||
if (item.getCellRef().getEnchantmentCharge() < castCost)
|
||||
{
|
||||
if (mCaster == getPlayer())
|
||||
|
@ -862,17 +866,17 @@ namespace MWMechanics
|
|||
item.getCellRef().setEnchantmentCharge(item.getCellRef().getEnchantmentCharge() - castCost);
|
||||
}
|
||||
|
||||
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed)
|
||||
if (type == ESM::Enchantment::WhenUsed)
|
||||
{
|
||||
if (mCaster == getPlayer())
|
||||
mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1);
|
||||
}
|
||||
else if (enchantment->mData.mType == ESM::Enchantment::CastOnce)
|
||||
else if (type == ESM::Enchantment::CastOnce)
|
||||
{
|
||||
if (!godmode)
|
||||
item.getContainerStore()->remove(item, 1, mCaster);
|
||||
}
|
||||
else if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||
else if (type == ESM::Enchantment::WhenStrikes)
|
||||
{
|
||||
if (mCaster == getPlayer())
|
||||
mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3);
|
||||
|
@ -880,13 +884,6 @@ namespace MWMechanics
|
|||
|
||||
inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self);
|
||||
|
||||
bool isProjectile = false;
|
||||
if (item.getTypeName() == typeid(ESM::Weapon).name())
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::Weapon> *ref = item.get<ESM::Weapon>();
|
||||
isProjectile = ref->mBase->mData.mType == ESM::Weapon::Arrow || ref->mBase->mData.mType == ESM::Weapon::Bolt || ref->mBase->mData.mType == ESM::Weapon::MarksmanThrown;
|
||||
}
|
||||
|
||||
if (isProjectile || !mTarget.isEmpty())
|
||||
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f
|
|||
if (absDiff < epsilonRadians)
|
||||
return true;
|
||||
|
||||
float limit = MAX_VEL_ANGULAR_RADIANS * MWBase::Environment::get().getFrameDuration();
|
||||
float limit = getAngularVelocity(actor.getClass().getSpeed(actor)) * MWBase::Environment::get().getFrameDuration();
|
||||
if (absDiff > limit)
|
||||
diff = osg::sign(diff) * limit;
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <osg/Math>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Ptr;
|
||||
|
@ -12,7 +14,12 @@ namespace MWMechanics
|
|||
{
|
||||
|
||||
// Max rotating speed, radian/sec
|
||||
const float MAX_VEL_ANGULAR_RADIANS(10);
|
||||
inline float getAngularVelocity(const float actorSpeed)
|
||||
{
|
||||
const float baseAngluarVelocity = 10;
|
||||
const float baseSpeed = 200;
|
||||
return baseAngluarVelocity * std::max(actorSpeed / baseSpeed, 1.0f);
|
||||
}
|
||||
|
||||
/// configure rotation settings for an actor to reach this target angle (eventually)
|
||||
/// @return have we reached the target angle?
|
||||
|
|
|
@ -102,8 +102,9 @@ namespace MWMechanics
|
|||
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||
{
|
||||
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);
|
||||
float charge = item.getCellRef().getEnchantmentCharge();
|
||||
|
||||
if (item.getCellRef().getEnchantmentCharge() == -1 || item.getCellRef().getEnchantmentCharge() >= castCost)
|
||||
if (charge == -1 || charge >= castCost || weapon->mData.mType >= ESM::Weapon::MarksmanThrown)
|
||||
rating += rateEffects(enchantment->mEffects, actor, enemy);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
||||
#include "ref.hpp"
|
||||
|
||||
|
@ -464,6 +465,16 @@ namespace MWScript
|
|||
{
|
||||
// Apply looping particles immediately for constant effects
|
||||
MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr);
|
||||
|
||||
// The spell may have an instant effect which must be handled immediately.
|
||||
for (const auto& effect : creatureStats.getSpells().getMagicEffects())
|
||||
{
|
||||
if (effect.second.getMagnitude() <= 0)
|
||||
continue;
|
||||
MWMechanics::CastSpell cast(ptr, ptr);
|
||||
if (cast.applyInstantEffect(ptr, ptr, effect.first, effect.second.getMagnitude()))
|
||||
creatureStats.getSpells().purgeEffect(effect.first.mId);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1401,7 +1401,7 @@ namespace MWWorld
|
|||
|
||||
pos.z() += 20; // place slightly above. will snap down to ground with code below
|
||||
|
||||
if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && isActorCollisionEnabled(ptr)))
|
||||
if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !isSwimming(ptr) && isActorCollisionEnabled(ptr)))
|
||||
{
|
||||
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits);
|
||||
if (traced.z() < pos.z())
|
||||
|
|
|
@ -340,7 +340,7 @@ namespace Compiler
|
|||
extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase);
|
||||
extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase);
|
||||
extensions.registerFunction ("getcurrentweather", 'l', "", opcodeGetCurrentWeather);
|
||||
extensions.registerInstruction ("modregion", "S/llllllllll", opcodeModRegion);
|
||||
extensions.registerInstruction ("modregion", "S/llllllllllX", opcodeModRegion);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -160,9 +160,9 @@ public:
|
|||
{
|
||||
std::vector<char> str(length + 1, 0);
|
||||
|
||||
inp->read(&str[0], length);
|
||||
inp->read(str.data(), length);
|
||||
|
||||
return &str[0];
|
||||
return str.data();
|
||||
}
|
||||
///Read in a string of the length specified in the file
|
||||
std::string getString()
|
||||
|
@ -181,34 +181,34 @@ public:
|
|||
void getUShorts(std::vector<unsigned short> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<unsigned short,unsigned short>(inp, &vec.front(), size);
|
||||
readLittleEndianDynamicBufferOfType<unsigned short,unsigned short>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
void getFloats(std::vector<float> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, &vec.front(), size);
|
||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
void getVector2s(std::vector<osg::Vec2f> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
/* The packed storage of each Vec2f is 2 floats exactly */
|
||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp,(float*) &vec.front(), size*2);
|
||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp,(float*)vec.data(), size*2);
|
||||
}
|
||||
|
||||
void getVector3s(std::vector<osg::Vec3f> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
/* The packed storage of each Vec3f is 3 floats exactly */
|
||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*) &vec.front(), size*3);
|
||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*)vec.data(), size*3);
|
||||
}
|
||||
|
||||
void getVector4s(std::vector<osg::Vec4f> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
/* The packed storage of each Vec4f is 4 floats exactly */
|
||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*) &vec.front(), size*4);
|
||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*)vec.data(), size*4);
|
||||
}
|
||||
|
||||
void getQuaternions(std::vector<osg::Quat> &quat, size_t size)
|
||||
|
|
|
@ -1034,11 +1034,10 @@ namespace NifOsg
|
|||
{
|
||||
const Nif::NiTriShapeData* data = triShape->data.getPtr();
|
||||
|
||||
{
|
||||
geometry->setVertexArray(new osg::Vec3Array(data->vertices.size(), &data->vertices[0]));
|
||||
if (!data->vertices.empty())
|
||||
geometry->setVertexArray(new osg::Vec3Array(data->vertices.size(), data->vertices.data()));
|
||||
if (!data->normals.empty())
|
||||
geometry->setNormalArray(new osg::Vec3Array(data->normals.size(), &data->normals[0]), osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
geometry->setNormalArray(new osg::Vec3Array(data->normals.size(), data->normals.data()), osg::Array::BIND_PER_VERTEX);
|
||||
|
||||
int textureStage = 0;
|
||||
for (std::vector<int>::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it,++textureStage)
|
||||
|
@ -1048,19 +1047,20 @@ namespace NifOsg
|
|||
{
|
||||
Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename;
|
||||
if (!data->uvlist.empty())
|
||||
geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[0].size(), &data->uvlist[0][0]), osg::Array::BIND_PER_VERTEX);
|
||||
geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[0].size(), data->uvlist[0].data()), osg::Array::BIND_PER_VERTEX);
|
||||
continue;
|
||||
}
|
||||
|
||||
geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[uvSet].size(), &data->uvlist[uvSet][0]), osg::Array::BIND_PER_VERTEX);
|
||||
geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[uvSet].size(), data->uvlist[uvSet].data()), osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (!data->colors.empty())
|
||||
geometry->setColorArray(new osg::Vec4Array(data->colors.size(), &data->colors[0]), osg::Array::BIND_PER_VERTEX);
|
||||
geometry->setColorArray(new osg::Vec4Array(data->colors.size(), data->colors.data()), osg::Array::BIND_PER_VERTEX);
|
||||
|
||||
if (!data->triangles.empty())
|
||||
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES,
|
||||
data->triangles.size(),
|
||||
(unsigned short*)&data->triangles[0]));
|
||||
(unsigned short*)data->triangles.data()));
|
||||
|
||||
// osg::Material properties are handled here for two reasons:
|
||||
// - if there are no vertex colors, we need to disable colorMode.
|
||||
|
@ -1115,7 +1115,7 @@ namespace NifOsg
|
|||
return morphGeom;
|
||||
// Note we are not interested in morph 0, which just contains the original vertices
|
||||
for (unsigned int i = 1; i < morphs.size(); ++i)
|
||||
morphGeom->addMorphTarget(new osg::Vec3Array(morphs[i].mVertices.size(), &morphs[i].mVertices[0]), 0.f);
|
||||
morphGeom->addMorphTarget(new osg::Vec3Array(morphs[i].mVertices.size(), morphs[i].mVertices.data()), 0.f);
|
||||
|
||||
return morphGeom;
|
||||
}
|
||||
|
@ -1282,7 +1282,7 @@ namespace NifOsg
|
|||
}
|
||||
|
||||
unsigned char* data = new unsigned char[pixelData->data.size()];
|
||||
memcpy(data, &pixelData->data[0], pixelData->data.size());
|
||||
memcpy(data, pixelData->data.data(), pixelData->data.size());
|
||||
|
||||
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);
|
||||
image->setMipmapLevels(mipmapVector);
|
||||
|
|
Loading…
Reference in a new issue