mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
Merge pull request #374 from OpenMW/master while resolving conflicts
# Conflicts: # apps/openmw/CMakeLists.txt
This commit is contained in:
commit
5d4b97645d
31 changed files with 291 additions and 134 deletions
|
@ -192,9 +192,8 @@ if (ANDROID)
|
|||
dl
|
||||
z
|
||||
${OPENSCENEGRAPH_LIBRARIES}
|
||||
${OSG_PLUGINS}
|
||||
freetype
|
||||
jpeg
|
||||
gif
|
||||
png
|
||||
)
|
||||
endif (ANDROID)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
int stderr = 0; // Hack: fix linker error
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "SDL_main.h"
|
||||
|
@ -17,6 +18,8 @@ void releaseArgv();
|
|||
int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls,
|
||||
jobject obj) {
|
||||
|
||||
setenv("OPENMW_DECOMPRESS_TEXTURES", "1", 1);
|
||||
|
||||
SDL_Android_Init(env, cls);
|
||||
|
||||
SDL_SetMainReady();
|
||||
|
|
|
@ -351,7 +351,11 @@ private:
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
extern "C" int SDL_main(int argc, char**argv)
|
||||
#else
|
||||
int main(int argc, char**argv)
|
||||
#endif
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
||||
|
|
|
@ -207,8 +207,6 @@ namespace MWGui
|
|||
, mMessageBoxManager(parMessageBoxManager)
|
||||
, mButtonPressed(-1)
|
||||
{
|
||||
setVisible(true);
|
||||
|
||||
int textPadding = 10; // padding between text-widget and main-widget
|
||||
int textButtonPadding = 10; // padding between the text-widget und the button-widget
|
||||
int buttonLeftPadding = 10; // padding between the buttons if horizontal
|
||||
|
@ -358,7 +356,11 @@ namespace MWGui
|
|||
mMessageWidget->setCoord(messageWidgetCoord);
|
||||
}
|
||||
|
||||
// Set key focus to "Ok" button
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
MyGUI::Widget* InteractiveMessageBox::getDefaultKeyFocus()
|
||||
{
|
||||
std::vector<std::string> keywords { "sOk", "sYes" };
|
||||
for(std::vector<MyGUI::Button*>::const_iterator button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||
{
|
||||
|
@ -366,11 +368,11 @@ namespace MWGui
|
|||
{
|
||||
if(Misc::StringUtils::ciEqual(MyGUI::LanguageManager::getInstance().replaceTags("#{" + keyword + "}"), (*button)->getCaption()))
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button);
|
||||
return;
|
||||
return *button;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)
|
||||
|
|
|
@ -28,6 +28,8 @@ namespace MWGui
|
|||
bool createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons);
|
||||
bool isInteractiveMessageBox ();
|
||||
|
||||
const InteractiveMessageBox* getInteractiveMessageBox() const { return mInterMessageBoxe; }
|
||||
|
||||
/// Remove all message boxes
|
||||
void clear();
|
||||
|
||||
|
@ -77,6 +79,8 @@ namespace MWGui
|
|||
void mousePressed (MyGUI::Widget* _widget);
|
||||
int readPressedButton ();
|
||||
|
||||
MyGUI::Widget* getDefaultKeyFocus() override;
|
||||
|
||||
virtual bool exit() { return false; }
|
||||
|
||||
bool mMarkedToDelete;
|
||||
|
|
|
@ -908,6 +908,22 @@ namespace MWGui
|
|||
window->onFrame(frameDuration);
|
||||
}
|
||||
|
||||
// Make sure message boxes are always in front
|
||||
// This is an awful workaround for a series of awfully interwoven issues that couldn't be worked around
|
||||
// in a better way because of an impressive number of even more awfully interwoven issues.
|
||||
if (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox() && mCurrentModals.back() != mMessageBoxManager->getInteractiveMessageBox())
|
||||
{
|
||||
std::vector<WindowModal*>::iterator found = std::find(mCurrentModals.begin(), mCurrentModals.end(), mMessageBoxManager->getInteractiveMessageBox());
|
||||
if (found != mCurrentModals.end())
|
||||
{
|
||||
WindowModal* msgbox = *found;
|
||||
std::swap(*found, mCurrentModals.back());
|
||||
MyGUI::InputManager::getInstance().addWidgetModal(msgbox->mMainWidget);
|
||||
mKeyboardNavigation->setModalWindow(msgbox->mMainWidget);
|
||||
mKeyboardNavigation->setDefaultFocus(msgbox->mMainWidget, msgbox->getDefaultKeyFocus());
|
||||
}
|
||||
}
|
||||
|
||||
if (!mCurrentModals.empty())
|
||||
mCurrentModals.back()->onFrame(frameDuration);
|
||||
|
||||
|
|
|
@ -1312,13 +1312,13 @@ namespace MWInput
|
|||
clearAllKeyBindings(control);
|
||||
|
||||
if (defaultKeyBindings.find(i) != defaultKeyBindings.end()
|
||||
&& !mInputBinder->isKeyBound(defaultKeyBindings[i]))
|
||||
&& (force || !mInputBinder->isKeyBound(defaultKeyBindings[i])))
|
||||
{
|
||||
control->setInitialValue(0.0f);
|
||||
mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE);
|
||||
}
|
||||
else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()
|
||||
&& !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i]))
|
||||
&& (force || !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i])))
|
||||
{
|
||||
control->setInitialValue(0.0f);
|
||||
mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE);
|
||||
|
@ -1392,12 +1392,12 @@ namespace MWInput
|
|||
clearAllControllerBindings(control);
|
||||
|
||||
if (defaultButtonBindings.find(i) != defaultButtonBindings.end()
|
||||
&& !mInputBinder->isJoystickButtonBound(mFakeDeviceID, defaultButtonBindings[i]))
|
||||
&& (force || !mInputBinder->isJoystickButtonBound(mFakeDeviceID, defaultButtonBindings[i])))
|
||||
{
|
||||
control->setInitialValue(0.0f);
|
||||
mInputBinder->addJoystickButtonBinding(control, mFakeDeviceID, defaultButtonBindings[i], ICS::Control::INCREASE);
|
||||
}
|
||||
else if (defaultAxisBindings.find(i) != defaultAxisBindings.end() && !mInputBinder->isJoystickAxisBound(mFakeDeviceID, defaultAxisBindings[i]))
|
||||
else if (defaultAxisBindings.find(i) != defaultAxisBindings.end() && (force || !mInputBinder->isJoystickAxisBound(mFakeDeviceID, defaultAxisBindings[i])))
|
||||
{
|
||||
control->setValue(0.5f);
|
||||
control->setInitialValue(0.5f);
|
||||
|
|
|
@ -1302,14 +1302,12 @@ namespace MWMechanics
|
|||
// AI and magic effects update
|
||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2();
|
||||
// AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this
|
||||
// (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not)
|
||||
// This distance could be made configurable later, but the setting must be marked with a big warning:
|
||||
// using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876)
|
||||
bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2()
|
||||
<= sqrAiProcessingDistance;
|
||||
|
||||
iter->second->getCharacterController()->setActive(inProcessingRange);
|
||||
bool inProcessingRange = distSqr <= sqrAiProcessingDistance;
|
||||
|
||||
/*
|
||||
Start of tes3mp change (minor)
|
||||
|
@ -1480,9 +1478,24 @@ namespace MWMechanics
|
|||
CharacterController* playerCharacter = NULL;
|
||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
if (iter->first != player &&
|
||||
(player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2()
|
||||
> sqrAiProcessingDistance)
|
||||
const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation.
|
||||
const float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2();
|
||||
bool isPlayer = iter->first == player;
|
||||
bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance);
|
||||
int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower)
|
||||
if (isPlayer)
|
||||
activeFlag = 2;
|
||||
int active = inAnimationRange ? activeFlag : 0;
|
||||
bool canFly = iter->first.getClass().canFly(iter->first);
|
||||
if (canFly)
|
||||
{
|
||||
// Keep animating flying creatures so they don't just hover in-air
|
||||
inAnimationRange = true;
|
||||
active = std::max(1, active);
|
||||
}
|
||||
iter->second->getCharacterController()->setActive(active);
|
||||
|
||||
if (!inAnimationRange)
|
||||
continue;
|
||||
|
||||
if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed())
|
||||
|
|
|
@ -182,29 +182,11 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
float bestArrowRating = 0;
|
||||
MWWorld::Ptr bestArrow;
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Arrow);
|
||||
if (rating > bestArrowRating)
|
||||
{
|
||||
bestArrowRating = rating;
|
||||
bestArrow = *it;
|
||||
}
|
||||
}
|
||||
float bestArrowRating = rateAmmo(actor, enemy, bestArrow, ESM::Weapon::Arrow);
|
||||
|
||||
float bestBoltRating = 0;
|
||||
MWWorld::Ptr bestBolt;
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Bolt);
|
||||
if (rating > bestBoltRating)
|
||||
{
|
||||
bestBoltRating = rating;
|
||||
bestBolt = *it;
|
||||
}
|
||||
}
|
||||
float bestBoltRating = rateAmmo(actor, enemy, bestBolt, ESM::Weapon::Bolt);
|
||||
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
|
@ -277,25 +259,9 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
float bestArrowRating = 0;
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Arrow);
|
||||
if (rating > bestArrowRating)
|
||||
{
|
||||
bestArrowRating = rating;
|
||||
}
|
||||
}
|
||||
float bestArrowRating = rateAmmo(actor, enemy, ESM::Weapon::Arrow);
|
||||
|
||||
float bestBoltRating = 0;
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Bolt);
|
||||
if (rating > bestBoltRating)
|
||||
{
|
||||
bestBoltRating = rating;
|
||||
}
|
||||
}
|
||||
float bestBoltRating = rateAmmo(actor, enemy, ESM::Weapon::Bolt);
|
||||
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
|
|
|
@ -2531,7 +2531,7 @@ float CharacterController::getAttackStrength() const
|
|||
return mAttackStrength;
|
||||
}
|
||||
|
||||
void CharacterController::setActive(bool active)
|
||||
void CharacterController::setActive(int active)
|
||||
{
|
||||
mAnimation->setActive(active);
|
||||
}
|
||||
|
|
|
@ -292,7 +292,7 @@ public:
|
|||
float getAttackStrength() const;
|
||||
|
||||
/// @see Animation::setActive
|
||||
void setActive(bool active);
|
||||
void setActive(int active);
|
||||
|
||||
/// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr.
|
||||
void setHeadTrackTarget(const MWWorld::ConstPtr& target);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "spellpriority.hpp"
|
||||
#include "weaponpriority.hpp"
|
||||
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadmgef.hpp>
|
||||
|
@ -335,6 +336,12 @@ namespace MWMechanics
|
|||
return 0.f;
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::BoundLongbow:
|
||||
// AI should not summon the bow if there is no suitable ammo.
|
||||
if (rateAmmo(actor, enemy, ESM::Weapon::Arrow) <= 0.f)
|
||||
return 0.f;
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::RestoreHealth:
|
||||
case ESM::MagicEffect::RestoreMagicka:
|
||||
case ESM::MagicEffect::RestoreFatigue:
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "npcstats.hpp"
|
||||
#include "combat.hpp"
|
||||
|
@ -111,6 +112,33 @@ namespace MWMechanics
|
|||
return rating + bonus;
|
||||
}
|
||||
|
||||
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType)
|
||||
{
|
||||
float bestAmmoRating = 0.f;
|
||||
if (!actor.getClass().hasInventoryStore(actor))
|
||||
return bestAmmoRating;
|
||||
|
||||
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
|
||||
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
float rating = rateWeapon(*it, actor, enemy, ammoType);
|
||||
if (rating > bestAmmoRating)
|
||||
{
|
||||
bestAmmoRating = rating;
|
||||
bestAmmo = *it;
|
||||
}
|
||||
}
|
||||
|
||||
return bestAmmoRating;
|
||||
}
|
||||
|
||||
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, ESM::Weapon::Type ammoType)
|
||||
{
|
||||
MWWorld::Ptr emptyPtr;
|
||||
return rateAmmo(actor, enemy, emptyPtr, ammoType);
|
||||
}
|
||||
|
||||
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef OPENMW_WEAPON_PRIORITY_H
|
||||
#define OPENMW_WEAPON_PRIORITY_H
|
||||
|
||||
#include <components/esm/loadweap.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
|
@ -8,6 +10,9 @@ namespace MWMechanics
|
|||
float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy,
|
||||
int type=-1, float arrowRating=0.f, float boltRating=0.f);
|
||||
|
||||
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType);
|
||||
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, ESM::Weapon::Type ammoType);
|
||||
|
||||
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
}
|
||||
|
||||
|
|
|
@ -486,10 +486,10 @@ namespace MWRender
|
|||
return mPtr;
|
||||
}
|
||||
|
||||
void Animation::setActive(bool active)
|
||||
void Animation::setActive(int active)
|
||||
{
|
||||
if (mSkeleton)
|
||||
mSkeleton->setActive(active);
|
||||
mSkeleton->setActive(static_cast<SceneUtil::Skeleton::ActiveType>(active));
|
||||
}
|
||||
|
||||
void Animation::updatePtr(const MWWorld::Ptr &ptr)
|
||||
|
|
|
@ -347,7 +347,8 @@ public:
|
|||
|
||||
/// Set active flag on the object skeleton, if one exists.
|
||||
/// @see SceneUtil::Skeleton::setActive
|
||||
void setActive(bool active);
|
||||
/// 0 = Inactive, 1 = Active in place, 2 = Active
|
||||
void setActive(int active);
|
||||
|
||||
osg::Group* getOrCreateObjectRoot();
|
||||
|
||||
|
|
|
@ -272,6 +272,12 @@ public:
|
|||
|
||||
void setWaterLevel(float waterLevel)
|
||||
{
|
||||
const float refractionScale = std::min(1.0f,std::max(0.0f,
|
||||
Settings::Manager::getFloat("refraction scale", "Water")));
|
||||
|
||||
setViewMatrix(osg::Matrix::scale(1,1,refractionScale) *
|
||||
osg::Matrix::translate(0,0,(1.0 - refractionScale) * waterLevel));
|
||||
|
||||
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,-1), osg::Vec3d(0,0, waterLevel)));
|
||||
}
|
||||
|
||||
|
|
|
@ -545,7 +545,7 @@ namespace MWScript
|
|||
const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr(name, false);
|
||||
|
||||
// If the objects are in different worldspaces, return a large value (just like vanilla)
|
||||
if (ref.getCell()->getCell()->getCellId().mWorldspace != ref2.getCell()->getCell()->getCellId().mWorldspace)
|
||||
if (!ref.isInCell() || !ref2.isInCell() || ref.getCell()->getCell()->getCellId().mWorldspace != ref2.getCell()->getCell()->getCellId().mWorldspace)
|
||||
return std::numeric_limits<float>::max();
|
||||
|
||||
double diff[3];
|
||||
|
|
|
@ -468,6 +468,7 @@ namespace MWWorld
|
|||
gmst["fNPCHealthBarFade"] = ESM::Variant(1.f);
|
||||
gmst["fFleeDistance"] = ESM::Variant(3000.f);
|
||||
gmst["sMaxSale"] = ESM::Variant("Max Sale");
|
||||
gmst["sAnd"] = ESM::Variant("and");
|
||||
|
||||
// Werewolf (BM)
|
||||
gmst["fWereWolfRunMult"] = ESM::Variant(1.3f);
|
||||
|
@ -1856,6 +1857,8 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
void World::updateWindowManager ()
|
||||
{
|
||||
try
|
||||
{
|
||||
// inform the GUI about focused object
|
||||
MWWorld::Ptr object = getFacedObject ();
|
||||
|
@ -1871,6 +1874,11 @@ namespace MWWorld
|
|||
|
||||
MWBase::Environment::get().getWindowManager()->setFocusObject(object);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Error updating window manager: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer)
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@ Wizard::InstallationTargetPage::InstallationTargetPage(QWidget *parent, const Fi
|
|||
void Wizard::InstallationTargetPage::initializePage()
|
||||
{
|
||||
QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str()));
|
||||
path.append(QDir::separator() + QLatin1String("data"));
|
||||
path.append(QDir::separator() + QLatin1String("basedata"));
|
||||
|
||||
QDir dir(path);
|
||||
targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath()));
|
||||
|
|
|
@ -63,6 +63,7 @@ namespace Compiler
|
|||
if (mState==BeginState && keyword==Scanner::K_begin)
|
||||
{
|
||||
mState = NameState;
|
||||
scanner.enableTolerantNames(); /// \todo disable
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace Compiler
|
|||
if (c=='\n')
|
||||
{
|
||||
mStrictKeywords = false;
|
||||
mTolerantNames = false;
|
||||
mLoc.mColumn = 0;
|
||||
++mLoc.mLine;
|
||||
mLoc.mLiteral.clear();
|
||||
|
@ -363,7 +364,7 @@ namespace Compiler
|
|||
}
|
||||
else if (!(c=='"' && name.empty()))
|
||||
{
|
||||
if (!isStringCharacter (c))
|
||||
if (!isStringCharacter (c) && !(mTolerantNames && (c=='.' || c=='-')))
|
||||
{
|
||||
putback (c);
|
||||
break;
|
||||
|
@ -577,7 +578,7 @@ namespace Compiler
|
|||
const Extensions *extensions)
|
||||
: mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),
|
||||
mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0),
|
||||
mStrictKeywords (false)
|
||||
mStrictKeywords (false), mTolerantNames (false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -634,4 +635,9 @@ namespace Compiler
|
|||
{
|
||||
mStrictKeywords = true;
|
||||
}
|
||||
|
||||
void Scanner::enableTolerantNames()
|
||||
{
|
||||
mTolerantNames = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace Compiler
|
|||
std::string mPutbackName;
|
||||
TokenLoc mPutbackLoc;
|
||||
bool mStrictKeywords;
|
||||
bool mTolerantNames;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -129,6 +130,11 @@ namespace Compiler
|
|||
///
|
||||
/// \attention This mode lasts only until the next newline is reached.
|
||||
void enableStrictKeywords();
|
||||
|
||||
/// Continue parsing a name when hitting a '.' or a '-'
|
||||
///
|
||||
/// \attention This mode lasts only until the next newline is reached.
|
||||
void enableTolerantNames();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,9 @@ private:
|
|||
Skeleton::Skeleton()
|
||||
: mBoneCacheInit(false)
|
||||
, mNeedToUpdateBoneMatrices(true)
|
||||
, mActive(true)
|
||||
, mActive(Active)
|
||||
, mLastFrameNumber(0)
|
||||
, mLastCullFrameNumber(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -48,6 +49,7 @@ Skeleton::Skeleton(const Skeleton ©, const osg::CopyOp ©op)
|
|||
, mNeedToUpdateBoneMatrices(true)
|
||||
, mActive(copy.mActive)
|
||||
, mLastFrameNumber(0)
|
||||
, mLastCullFrameNumber(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -123,14 +125,14 @@ void Skeleton::updateBoneMatrices(unsigned int traversalNumber)
|
|||
}
|
||||
}
|
||||
|
||||
void Skeleton::setActive(bool active)
|
||||
void Skeleton::setActive(ActiveType active)
|
||||
{
|
||||
mActive = active;
|
||||
}
|
||||
|
||||
bool Skeleton::getActive() const
|
||||
{
|
||||
return mActive;
|
||||
return mActive != Inactive;
|
||||
}
|
||||
|
||||
void Skeleton::markDirty()
|
||||
|
@ -142,8 +144,16 @@ void Skeleton::markDirty()
|
|||
|
||||
void Skeleton::traverse(osg::NodeVisitor& nv)
|
||||
{
|
||||
if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && mLastFrameNumber != 0)
|
||||
if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
|
||||
{
|
||||
if (mActive == Inactive && mLastFrameNumber != 0)
|
||||
return;
|
||||
if (mActive == SemiActive && mLastFrameNumber != 0 && mLastCullFrameNumber+3 <= nv.getTraversalNumber())
|
||||
return;
|
||||
}
|
||||
else if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
|
||||
mLastCullFrameNumber = nv.getTraversalNumber();
|
||||
|
||||
osg::Group::traverse(nv);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,16 @@ namespace SceneUtil
|
|||
/// Request an update of bone matrices. May be a no-op if already updated in this frame.
|
||||
void updateBoneMatrices(unsigned int traversalNumber);
|
||||
|
||||
enum ActiveType
|
||||
{
|
||||
Inactive=0,
|
||||
SemiActive, /// Like Active, but don't bother with Update (including new bounding box) if we're off-screen
|
||||
Active
|
||||
};
|
||||
|
||||
/// Set the skinning active flag. Inactive skeletons will not have their child rigs updated.
|
||||
/// You should set this flag to false if you know that bones are not currently moving.
|
||||
void setActive(bool active);
|
||||
void setActive(ActiveType active);
|
||||
|
||||
bool getActive() const;
|
||||
|
||||
|
@ -71,9 +78,10 @@ namespace SceneUtil
|
|||
|
||||
bool mNeedToUpdateBoneMatrices;
|
||||
|
||||
bool mActive;
|
||||
ActiveType mActive;
|
||||
|
||||
unsigned int mLastFrameNumber;
|
||||
unsigned int mLastCullFrameNumber;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#include "imagetosurface.hpp"
|
||||
|
||||
#ifdef OSG_LIBRARY_STATIC
|
||||
#if defined(OSG_LIBRARY_STATIC) && !defined(ANDROID)
|
||||
// Sets the default windowing system interface according to the OS.
|
||||
// Necessary for OpenSceneGraph to do some things, like decompression.
|
||||
USE_GRAPHICSWINDOW()
|
||||
|
@ -220,7 +220,9 @@ namespace SDLUtil
|
|||
|
||||
void SDLCursorManager::createCursor(const std::string& name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y)
|
||||
{
|
||||
#ifndef ANDROID
|
||||
_createCursorFromResource(name, rotDegrees, image, hotspot_x, hotspot_y);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "filesystemarchive.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace VFS
|
||||
|
@ -38,7 +40,8 @@ namespace VFS
|
|||
|
||||
std::transform(proper.begin() + prefix, proper.end(), std::back_inserter(searchable), normalize_function);
|
||||
|
||||
mIndex.insert (std::make_pair (searchable, file));
|
||||
if (!mIndex.insert (std::make_pair (searchable, file)).second)
|
||||
std::cerr << "Warning: found duplicate file for '" << proper << "', please check your file system for two files with the same name in different cases." << std::endl;
|
||||
}
|
||||
|
||||
mBuiltIndex = true;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "registerarchives.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -33,12 +34,20 @@ namespace VFS
|
|||
}
|
||||
|
||||
if (useLooseFiles)
|
||||
{
|
||||
std::set<boost::filesystem::path> seen;
|
||||
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
||||
{
|
||||
if (seen.insert(*iter).second)
|
||||
{
|
||||
std::cout << "Adding data directory " << iter->string() << std::endl;
|
||||
// Last data dir has the highest priority
|
||||
vfs->addArchive(new FileSystemArchive(iter->string()));
|
||||
}
|
||||
else
|
||||
std::cerr << "Ignoring duplicate data directory " << iter->string() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
vfs->buildIndex();
|
||||
}
|
||||
|
|
|
@ -211,15 +211,11 @@ Adding the ring to the game's world
|
|||
|
||||
Now that we have defined the ring it is time add it to the game world so the
|
||||
player can find it legitimately. We will add the ring to a merchant, place it
|
||||
in a chest and put it somewhere in plain sight. To this end we will have to
|
||||
in a chest, and put it somewhere in plain sight. To this end we will have to
|
||||
actually modify the contents of the game.
|
||||
|
||||
|
||||
Subsection to come...
|
||||
=====================
|
||||
|
||||
Adding to an npc
|
||||
****************
|
||||
Adding to an NPC
|
||||
================
|
||||
|
||||
The simplest way is probably to add it to the inventory of a shopkeeper.
|
||||
An obvious candidate is Arrille in Seyda Neen - he's quick to find in a new game
|
||||
|
@ -258,6 +254,79 @@ to give the ring to the player - the same as used earlier in the console
|
|||
.. figure:: _static/images/chapter-1/Ring_to_Fargoth_2.png
|
||||
:alt: Editing Fargoth to give ring to player
|
||||
|
||||
Placing in a chest
|
||||
==================
|
||||
|
||||
For this example we will use the small chest intended for lockpick practice,
|
||||
located in the Census and Excise Office in Seyda Neen.
|
||||
|
||||
First we need the ID of the chest - this can be obtained either by clicking on it in the console
|
||||
in the game, or by applying a similar process in the CS -
|
||||
|
||||
World/Cells
|
||||
|
||||
Select "Seyda Neen, Census and Excise Office"
|
||||
|
||||
Right-click and select "View"
|
||||
|
||||
Use mouse wheel to zoom in/out, and mouse plus WASD keys to navigate
|
||||
|
||||
Click on the small chest
|
||||
|
||||
Either way, you should find the ID, which is "chest_small_02_lockprac".
|
||||
|
||||
Open the Objects table (World/Objects) and scroll down to find this item.
|
||||
|
||||
Alternatively use the Edit/Search facility, selecting ID rather than text,
|
||||
enter "lockprac" (without the quotes) into the search box, press "Search",
|
||||
which should return two rows, then select the "Container" one rather than the "Instance"
|
||||
|
||||
Right-click and "Edit Record".
|
||||
|
||||
Right-click the "Content" section and select "Add a row"
|
||||
|
||||
Set the Item ID of the new row to be your new ring - simplest way is probably to open the Objects
|
||||
table if it's not already open, sort on the "Modified" column which should bring the ring,
|
||||
with its status of "Added" to the top, then drag and drop to the chest row.
|
||||
|
||||
Increase the Count to 1.
|
||||
|
||||
Save the addon, then test to ensure it works - e.g. start a new game and lockpick the chest.
|
||||
|
||||
Placing in plain sight
|
||||
=====================
|
||||
|
||||
Let's hide the Ring of Night vision in the cabin of the [Ancient Shipwreck]
|
||||
(http://en.uesp.net/wiki/Morrowind:Ancient_Shipwreck), a derelict vessel
|
||||
southeast of Dagon Fel. Open the list of Cells (*World* → *Cells*) and find
|
||||
"Ancient Shipwreck, Cabin".
|
||||
|
||||
This will open a visualization of the cabin. You can navigate around the scene
|
||||
just like you would when playing Morrowind. Use the WASD keys to move forward,
|
||||
backwards, and sideways. Click and drag with the left mouse button to change the
|
||||
direction you are looking. Navigate to the table in the cabin.
|
||||
|
||||
If you've closed the Objects table, reopen it via *World* → *Objects*. Navigate
|
||||
to your Ring of Night Vision (you can find it easily if you sort by the "Modified"
|
||||
column). Drag the ring from the Objects table onto the table in the Cell view.
|
||||
|
||||
Now let's move the ring to the precise location we want. Hover over the ring and
|
||||
click the middle mouse button. If you don't have a middle mouse button, you can
|
||||
select an alternative command by going to *Edit* → *Preferences…* (Windows, Linux)
|
||||
or *OpenMW* → *Preferences…* (macOS). Go to the Key Bindings section and choose
|
||||
"Scene" from the dropdown menu. Then click on the button for "Primary Select" and
|
||||
choose an alternative binding.
|
||||
|
||||
After you have switched to movement mode, you will see several arrows. Clicking
|
||||
and dragging them with the right mouse button will allow you to move the object
|
||||
in the direction you want.
|
||||
|
||||
If you'd like an easy way to test this, you can start OpenMW with the [game
|
||||
arguments](https://wiki.openmw.org/index.php?title=Testing)
|
||||
`--start="Ancient Shipwreck, Cabin" --skip-menu`. This will place you right in
|
||||
the cell and allow you to pick up and equip the ring in order to check that it
|
||||
works.
|
||||
|
||||
Navigation in the CS
|
||||
====================
|
||||
This is probably a suitable place to start talking about how navigation differs from TESCS
|
||||
|
@ -304,42 +373,3 @@ already checked. Load a game and make your way to Seyda Neen - or start a new ga
|
|||
|
||||
Check whether Arrille has one (or more) for sale, and whether Fargoth give you one
|
||||
when you return his healing ring.
|
||||
|
||||
Placing in a chest
|
||||
******************
|
||||
|
||||
For this example we will use the small chest intended for lockpick practice,
|
||||
located in the Census and Excise Office in Seyda Neen.
|
||||
|
||||
First we need the ID of the chest - this can be obtained either by clicking on it in the console
|
||||
in the game, or by applying a similar process in the CS -
|
||||
|
||||
World/Cells
|
||||
|
||||
Select "Seyda Neen, Census and Excise Office"
|
||||
|
||||
Right-click and select "View"
|
||||
|
||||
Use mouse wheel to zoom in/out, and mouse plus WASD keys to navigate
|
||||
|
||||
Click on the small chest
|
||||
|
||||
Either way, you should find the ID, which is "chest_small_02_lockprac".
|
||||
|
||||
Open the Objects table (World/Objects) and scroll down to find this item.
|
||||
|
||||
Alternatively use the Edit/Search facility, selecting ID rather than text,
|
||||
enter "lockprac" (without the quotes) into the search box, press "Search",
|
||||
which should return two rows, then select the "Container" one rather than the "Instance"
|
||||
|
||||
Right-click and "Edit Record".
|
||||
|
||||
Right-click the "Content" section and select "Add a row"
|
||||
|
||||
Set the Item ID of the new row to be your new ring - simplest way is probably to open the Objects
|
||||
table if it's not already open, sort on the "Modified" column which should bring the ring,
|
||||
with its status of "Added" to the top, then drag and drop to the chest row.
|
||||
|
||||
Increase the Count to 1.
|
||||
|
||||
Save the addon, then test to ensure it works - e.g. start a new game and lockpick the chest.
|
||||
|
|
|
@ -88,3 +88,20 @@ This setting will have no effect if the shader setting is false,
|
|||
or the 'small feature culling' (in the 'Camera' section) is disabled.
|
||||
|
||||
This setting can only be configured by editing the settings configuration file.
|
||||
|
||||
refraction scale
|
||||
----------------
|
||||
|
||||
:Type: floating point
|
||||
:Range: 0 to 1
|
||||
:Default: 1.0
|
||||
|
||||
Simulates light rays refracting when transitioning from air to water, which causes the space under water look scaled down
|
||||
in height when viewed from above the water surface. Though adding realism, the setting can cause distortion which can
|
||||
make for example aiming at enemies in water more challenging, so it is off by default (i.e. set to 1.0). To get a realistic
|
||||
look of real-life water, set the value to 0.75.
|
||||
|
||||
This setting only applies if water shader is on and refractions are enabled. Note that if refractions are enabled and this
|
||||
setting if off, there will still be small refractions caused by the water waves, which however do not cause such significant
|
||||
distortion.
|
||||
|
||||
|
|
|
@ -376,6 +376,9 @@ reflect actors = false
|
|||
# Overrides the value in '[Camera] small feature culling pixel size' specifically for water reflection/refraction textures.
|
||||
small feature culling pixel size = 20.0
|
||||
|
||||
# By what factor water downscales objects. Only works with water shader and refractions on.
|
||||
refraction scale = 1.0
|
||||
|
||||
[Windows]
|
||||
|
||||
# Location and sizes of windows as a fraction of the OpenMW window or
|
||||
|
|
Loading…
Reference in a new issue