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
|
dl
|
||||||
z
|
z
|
||||||
${OPENSCENEGRAPH_LIBRARIES}
|
${OPENSCENEGRAPH_LIBRARIES}
|
||||||
${OSG_PLUGINS}
|
freetype
|
||||||
jpeg
|
jpeg
|
||||||
gif
|
|
||||||
png
|
png
|
||||||
)
|
)
|
||||||
endif (ANDROID)
|
endif (ANDROID)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
int stderr = 0; // Hack: fix linker error
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
#include "SDL_main.h"
|
#include "SDL_main.h"
|
||||||
|
@ -17,6 +18,8 @@ void releaseArgv();
|
||||||
int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls,
|
int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls,
|
||||||
jobject obj) {
|
jobject obj) {
|
||||||
|
|
||||||
|
setenv("OPENMW_DECOMPRESS_TEXTURES", "1", 1);
|
||||||
|
|
||||||
SDL_Android_Init(env, cls);
|
SDL_Android_Init(env, cls);
|
||||||
|
|
||||||
SDL_SetMainReady();
|
SDL_SetMainReady();
|
||||||
|
|
|
@ -351,7 +351,11 @@ private:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
extern "C" int SDL_main(int argc, char**argv)
|
||||||
|
#else
|
||||||
int main(int argc, char**argv)
|
int main(int argc, char**argv)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
||||||
|
|
|
@ -207,8 +207,6 @@ namespace MWGui
|
||||||
, mMessageBoxManager(parMessageBoxManager)
|
, mMessageBoxManager(parMessageBoxManager)
|
||||||
, mButtonPressed(-1)
|
, mButtonPressed(-1)
|
||||||
{
|
{
|
||||||
setVisible(true);
|
|
||||||
|
|
||||||
int textPadding = 10; // padding between text-widget and main-widget
|
int textPadding = 10; // padding between text-widget and main-widget
|
||||||
int textButtonPadding = 10; // padding between the text-widget und the button-widget
|
int textButtonPadding = 10; // padding between the text-widget und the button-widget
|
||||||
int buttonLeftPadding = 10; // padding between the buttons if horizontal
|
int buttonLeftPadding = 10; // padding between the buttons if horizontal
|
||||||
|
@ -358,7 +356,11 @@ namespace MWGui
|
||||||
mMessageWidget->setCoord(messageWidgetCoord);
|
mMessageWidget->setCoord(messageWidgetCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set key focus to "Ok" button
|
setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
MyGUI::Widget* InteractiveMessageBox::getDefaultKeyFocus()
|
||||||
|
{
|
||||||
std::vector<std::string> keywords { "sOk", "sYes" };
|
std::vector<std::string> keywords { "sOk", "sYes" };
|
||||||
for(std::vector<MyGUI::Button*>::const_iterator button = mButtons.begin(); button != mButtons.end(); ++button)
|
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()))
|
if(Misc::StringUtils::ciEqual(MyGUI::LanguageManager::getInstance().replaceTags("#{" + keyword + "}"), (*button)->getCaption()))
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button);
|
return *button;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)
|
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 createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons);
|
||||||
bool isInteractiveMessageBox ();
|
bool isInteractiveMessageBox ();
|
||||||
|
|
||||||
|
const InteractiveMessageBox* getInteractiveMessageBox() const { return mInterMessageBoxe; }
|
||||||
|
|
||||||
/// Remove all message boxes
|
/// Remove all message boxes
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
@ -77,6 +79,8 @@ namespace MWGui
|
||||||
void mousePressed (MyGUI::Widget* _widget);
|
void mousePressed (MyGUI::Widget* _widget);
|
||||||
int readPressedButton ();
|
int readPressedButton ();
|
||||||
|
|
||||||
|
MyGUI::Widget* getDefaultKeyFocus() override;
|
||||||
|
|
||||||
virtual bool exit() { return false; }
|
virtual bool exit() { return false; }
|
||||||
|
|
||||||
bool mMarkedToDelete;
|
bool mMarkedToDelete;
|
||||||
|
|
|
@ -908,6 +908,22 @@ namespace MWGui
|
||||||
window->onFrame(frameDuration);
|
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())
|
if (!mCurrentModals.empty())
|
||||||
mCurrentModals.back()->onFrame(frameDuration);
|
mCurrentModals.back()->onFrame(frameDuration);
|
||||||
|
|
||||||
|
|
|
@ -1312,13 +1312,13 @@ namespace MWInput
|
||||||
clearAllKeyBindings(control);
|
clearAllKeyBindings(control);
|
||||||
|
|
||||||
if (defaultKeyBindings.find(i) != defaultKeyBindings.end()
|
if (defaultKeyBindings.find(i) != defaultKeyBindings.end()
|
||||||
&& !mInputBinder->isKeyBound(defaultKeyBindings[i]))
|
&& (force || !mInputBinder->isKeyBound(defaultKeyBindings[i])))
|
||||||
{
|
{
|
||||||
control->setInitialValue(0.0f);
|
control->setInitialValue(0.0f);
|
||||||
mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE);
|
mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE);
|
||||||
}
|
}
|
||||||
else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()
|
else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()
|
||||||
&& !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i]))
|
&& (force || !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i])))
|
||||||
{
|
{
|
||||||
control->setInitialValue(0.0f);
|
control->setInitialValue(0.0f);
|
||||||
mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE);
|
mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE);
|
||||||
|
@ -1392,12 +1392,12 @@ namespace MWInput
|
||||||
clearAllControllerBindings(control);
|
clearAllControllerBindings(control);
|
||||||
|
|
||||||
if (defaultButtonBindings.find(i) != defaultButtonBindings.end()
|
if (defaultButtonBindings.find(i) != defaultButtonBindings.end()
|
||||||
&& !mInputBinder->isJoystickButtonBound(mFakeDeviceID, defaultButtonBindings[i]))
|
&& (force || !mInputBinder->isJoystickButtonBound(mFakeDeviceID, defaultButtonBindings[i])))
|
||||||
{
|
{
|
||||||
control->setInitialValue(0.0f);
|
control->setInitialValue(0.0f);
|
||||||
mInputBinder->addJoystickButtonBinding(control, mFakeDeviceID, defaultButtonBindings[i], ICS::Control::INCREASE);
|
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->setValue(0.5f);
|
||||||
control->setInitialValue(0.5f);
|
control->setInitialValue(0.5f);
|
||||||
|
|
|
@ -1302,14 +1302,12 @@ namespace MWMechanics
|
||||||
// AI and magic effects update
|
// AI and magic effects update
|
||||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
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
|
// 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)
|
// (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:
|
// 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)
|
// 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()
|
bool inProcessingRange = distSqr <= sqrAiProcessingDistance;
|
||||||
<= sqrAiProcessingDistance;
|
|
||||||
|
|
||||||
iter->second->getCharacterController()->setActive(inProcessingRange);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp change (minor)
|
Start of tes3mp change (minor)
|
||||||
|
@ -1480,9 +1478,24 @@ namespace MWMechanics
|
||||||
CharacterController* playerCharacter = NULL;
|
CharacterController* playerCharacter = NULL;
|
||||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||||
{
|
{
|
||||||
if (iter->first != player &&
|
const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation.
|
||||||
(player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2()
|
const float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2();
|
||||||
> sqrAiProcessingDistance)
|
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;
|
continue;
|
||||||
|
|
||||||
if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed())
|
if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed())
|
||||||
|
|
|
@ -182,29 +182,11 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float bestArrowRating = 0;
|
|
||||||
MWWorld::Ptr bestArrow;
|
MWWorld::Ptr bestArrow;
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
float bestArrowRating = rateAmmo(actor, enemy, bestArrow, ESM::Weapon::Arrow);
|
||||||
{
|
|
||||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Arrow);
|
|
||||||
if (rating > bestArrowRating)
|
|
||||||
{
|
|
||||||
bestArrowRating = rating;
|
|
||||||
bestArrow = *it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float bestBoltRating = 0;
|
|
||||||
MWWorld::Ptr bestBolt;
|
MWWorld::Ptr bestBolt;
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
float bestBoltRating = rateAmmo(actor, enemy, bestBolt, ESM::Weapon::Bolt);
|
||||||
{
|
|
||||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Bolt);
|
|
||||||
if (rating > bestBoltRating)
|
|
||||||
{
|
|
||||||
bestBoltRating = rating;
|
|
||||||
bestBolt = *it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||||
{
|
{
|
||||||
|
@ -277,25 +259,9 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float bestArrowRating = 0;
|
float bestArrowRating = rateAmmo(actor, enemy, ESM::Weapon::Arrow);
|
||||||
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 bestBoltRating = 0;
|
float bestBoltRating = rateAmmo(actor, enemy, ESM::Weapon::Bolt);
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
|
||||||
{
|
|
||||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Bolt);
|
|
||||||
if (rating > bestBoltRating)
|
|
||||||
{
|
|
||||||
bestBoltRating = rating;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2531,7 +2531,7 @@ float CharacterController::getAttackStrength() const
|
||||||
return mAttackStrength;
|
return mAttackStrength;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::setActive(bool active)
|
void CharacterController::setActive(int active)
|
||||||
{
|
{
|
||||||
mAnimation->setActive(active);
|
mAnimation->setActive(active);
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,7 +292,7 @@ public:
|
||||||
float getAttackStrength() const;
|
float getAttackStrength() const;
|
||||||
|
|
||||||
/// @see Animation::setActive
|
/// @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.
|
/// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr.
|
||||||
void setHeadTrackTarget(const MWWorld::ConstPtr& target);
|
void setHeadTrackTarget(const MWWorld::ConstPtr& target);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "spellpriority.hpp"
|
#include "spellpriority.hpp"
|
||||||
|
#include "weaponpriority.hpp"
|
||||||
|
|
||||||
#include <components/esm/loadench.hpp>
|
#include <components/esm/loadench.hpp>
|
||||||
#include <components/esm/loadmgef.hpp>
|
#include <components/esm/loadmgef.hpp>
|
||||||
|
@ -335,6 +336,12 @@ namespace MWMechanics
|
||||||
return 0.f;
|
return 0.f;
|
||||||
break;
|
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::RestoreHealth:
|
||||||
case ESM::MagicEffect::RestoreMagicka:
|
case ESM::MagicEffect::RestoreMagicka:
|
||||||
case ESM::MagicEffect::RestoreFatigue:
|
case ESM::MagicEffect::RestoreFatigue:
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
#include "combat.hpp"
|
#include "combat.hpp"
|
||||||
|
@ -111,6 +112,33 @@ namespace MWMechanics
|
||||||
return rating + bonus;
|
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)
|
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>();
|
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef OPENMW_WEAPON_PRIORITY_H
|
#ifndef OPENMW_WEAPON_PRIORITY_H
|
||||||
#define OPENMW_WEAPON_PRIORITY_H
|
#define OPENMW_WEAPON_PRIORITY_H
|
||||||
|
|
||||||
|
#include <components/esm/loadweap.hpp>
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
|
@ -8,6 +10,9 @@ namespace MWMechanics
|
||||||
float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy,
|
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);
|
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);
|
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;
|
return mPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::setActive(bool active)
|
void Animation::setActive(int active)
|
||||||
{
|
{
|
||||||
if (mSkeleton)
|
if (mSkeleton)
|
||||||
mSkeleton->setActive(active);
|
mSkeleton->setActive(static_cast<SceneUtil::Skeleton::ActiveType>(active));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::updatePtr(const MWWorld::Ptr &ptr)
|
void Animation::updatePtr(const MWWorld::Ptr &ptr)
|
||||||
|
|
|
@ -347,7 +347,8 @@ public:
|
||||||
|
|
||||||
/// Set active flag on the object skeleton, if one exists.
|
/// Set active flag on the object skeleton, if one exists.
|
||||||
/// @see SceneUtil::Skeleton::setActive
|
/// @see SceneUtil::Skeleton::setActive
|
||||||
void setActive(bool active);
|
/// 0 = Inactive, 1 = Active in place, 2 = Active
|
||||||
|
void setActive(int active);
|
||||||
|
|
||||||
osg::Group* getOrCreateObjectRoot();
|
osg::Group* getOrCreateObjectRoot();
|
||||||
|
|
||||||
|
|
|
@ -272,6 +272,12 @@ public:
|
||||||
|
|
||||||
void setWaterLevel(float waterLevel)
|
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)));
|
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);
|
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 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();
|
return std::numeric_limits<float>::max();
|
||||||
|
|
||||||
double diff[3];
|
double diff[3];
|
||||||
|
|
|
@ -468,6 +468,7 @@ namespace MWWorld
|
||||||
gmst["fNPCHealthBarFade"] = ESM::Variant(1.f);
|
gmst["fNPCHealthBarFade"] = ESM::Variant(1.f);
|
||||||
gmst["fFleeDistance"] = ESM::Variant(3000.f);
|
gmst["fFleeDistance"] = ESM::Variant(3000.f);
|
||||||
gmst["sMaxSale"] = ESM::Variant("Max Sale");
|
gmst["sMaxSale"] = ESM::Variant("Max Sale");
|
||||||
|
gmst["sAnd"] = ESM::Variant("and");
|
||||||
|
|
||||||
// Werewolf (BM)
|
// Werewolf (BM)
|
||||||
gmst["fWereWolfRunMult"] = ESM::Variant(1.3f);
|
gmst["fWereWolfRunMult"] = ESM::Variant(1.3f);
|
||||||
|
@ -1857,19 +1858,26 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::updateWindowManager ()
|
void World::updateWindowManager ()
|
||||||
{
|
{
|
||||||
// inform the GUI about focused object
|
try
|
||||||
MWWorld::Ptr object = getFacedObject ();
|
|
||||||
|
|
||||||
// retrieve object dimensions so we know where to place the floating label
|
|
||||||
if (!object.isEmpty ())
|
|
||||||
{
|
{
|
||||||
osg::Vec4f screenBounds = mRendering->getScreenBounds(object);
|
// inform the GUI about focused object
|
||||||
|
MWWorld::Ptr object = getFacedObject ();
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords(
|
// retrieve object dimensions so we know where to place the floating label
|
||||||
screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w());
|
if (!object.isEmpty ())
|
||||||
|
{
|
||||||
|
osg::Vec4f screenBounds = mRendering->getScreenBounds(object);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords(
|
||||||
|
screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w());
|
||||||
|
}
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->setFocusObject(object);
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error updating window manager: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->setFocusObject(object);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer)
|
MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer)
|
||||||
|
|
|
@ -20,7 +20,7 @@ Wizard::InstallationTargetPage::InstallationTargetPage(QWidget *parent, const Fi
|
||||||
void Wizard::InstallationTargetPage::initializePage()
|
void Wizard::InstallationTargetPage::initializePage()
|
||||||
{
|
{
|
||||||
QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str()));
|
QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str()));
|
||||||
path.append(QDir::separator() + QLatin1String("data"));
|
path.append(QDir::separator() + QLatin1String("basedata"));
|
||||||
|
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath()));
|
targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath()));
|
||||||
|
|
|
@ -63,6 +63,7 @@ namespace Compiler
|
||||||
if (mState==BeginState && keyword==Scanner::K_begin)
|
if (mState==BeginState && keyword==Scanner::K_begin)
|
||||||
{
|
{
|
||||||
mState = NameState;
|
mState = NameState;
|
||||||
|
scanner.enableTolerantNames(); /// \todo disable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ namespace Compiler
|
||||||
if (c=='\n')
|
if (c=='\n')
|
||||||
{
|
{
|
||||||
mStrictKeywords = false;
|
mStrictKeywords = false;
|
||||||
|
mTolerantNames = false;
|
||||||
mLoc.mColumn = 0;
|
mLoc.mColumn = 0;
|
||||||
++mLoc.mLine;
|
++mLoc.mLine;
|
||||||
mLoc.mLiteral.clear();
|
mLoc.mLiteral.clear();
|
||||||
|
@ -363,7 +364,7 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
else if (!(c=='"' && name.empty()))
|
else if (!(c=='"' && name.empty()))
|
||||||
{
|
{
|
||||||
if (!isStringCharacter (c))
|
if (!isStringCharacter (c) && !(mTolerantNames && (c=='.' || c=='-')))
|
||||||
{
|
{
|
||||||
putback (c);
|
putback (c);
|
||||||
break;
|
break;
|
||||||
|
@ -577,7 +578,7 @@ namespace Compiler
|
||||||
const Extensions *extensions)
|
const Extensions *extensions)
|
||||||
: mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),
|
: mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),
|
||||||
mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0),
|
mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0),
|
||||||
mStrictKeywords (false)
|
mStrictKeywords (false), mTolerantNames (false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,4 +635,9 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
mStrictKeywords = true;
|
mStrictKeywords = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Scanner::enableTolerantNames()
|
||||||
|
{
|
||||||
|
mTolerantNames = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace Compiler
|
||||||
std::string mPutbackName;
|
std::string mPutbackName;
|
||||||
TokenLoc mPutbackLoc;
|
TokenLoc mPutbackLoc;
|
||||||
bool mStrictKeywords;
|
bool mStrictKeywords;
|
||||||
|
bool mTolerantNames;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -129,6 +130,11 @@ namespace Compiler
|
||||||
///
|
///
|
||||||
/// \attention This mode lasts only until the next newline is reached.
|
/// \attention This mode lasts only until the next newline is reached.
|
||||||
void enableStrictKeywords();
|
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()
|
Skeleton::Skeleton()
|
||||||
: mBoneCacheInit(false)
|
: mBoneCacheInit(false)
|
||||||
, mNeedToUpdateBoneMatrices(true)
|
, mNeedToUpdateBoneMatrices(true)
|
||||||
, mActive(true)
|
, mActive(Active)
|
||||||
, mLastFrameNumber(0)
|
, mLastFrameNumber(0)
|
||||||
|
, mLastCullFrameNumber(0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,6 +49,7 @@ Skeleton::Skeleton(const Skeleton ©, const osg::CopyOp ©op)
|
||||||
, mNeedToUpdateBoneMatrices(true)
|
, mNeedToUpdateBoneMatrices(true)
|
||||||
, mActive(copy.mActive)
|
, mActive(copy.mActive)
|
||||||
, mLastFrameNumber(0)
|
, 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;
|
mActive = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Skeleton::getActive() const
|
bool Skeleton::getActive() const
|
||||||
{
|
{
|
||||||
return mActive;
|
return mActive != Inactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Skeleton::markDirty()
|
void Skeleton::markDirty()
|
||||||
|
@ -142,8 +144,16 @@ void Skeleton::markDirty()
|
||||||
|
|
||||||
void Skeleton::traverse(osg::NodeVisitor& nv)
|
void Skeleton::traverse(osg::NodeVisitor& nv)
|
||||||
{
|
{
|
||||||
if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && mLastFrameNumber != 0)
|
if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
|
||||||
return;
|
{
|
||||||
|
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);
|
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.
|
/// Request an update of bone matrices. May be a no-op if already updated in this frame.
|
||||||
void updateBoneMatrices(unsigned int traversalNumber);
|
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.
|
/// 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.
|
/// 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;
|
bool getActive() const;
|
||||||
|
|
||||||
|
@ -71,9 +78,10 @@ namespace SceneUtil
|
||||||
|
|
||||||
bool mNeedToUpdateBoneMatrices;
|
bool mNeedToUpdateBoneMatrices;
|
||||||
|
|
||||||
bool mActive;
|
ActiveType mActive;
|
||||||
|
|
||||||
unsigned int mLastFrameNumber;
|
unsigned int mLastFrameNumber;
|
||||||
|
unsigned int mLastCullFrameNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
#include "imagetosurface.hpp"
|
#include "imagetosurface.hpp"
|
||||||
|
|
||||||
#ifdef OSG_LIBRARY_STATIC
|
#if defined(OSG_LIBRARY_STATIC) && !defined(ANDROID)
|
||||||
// Sets the default windowing system interface according to the OS.
|
// Sets the default windowing system interface according to the OS.
|
||||||
// Necessary for OpenSceneGraph to do some things, like decompression.
|
// Necessary for OpenSceneGraph to do some things, like decompression.
|
||||||
USE_GRAPHICSWINDOW()
|
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)
|
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);
|
_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)
|
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 "filesystemarchive.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
namespace VFS
|
namespace VFS
|
||||||
|
@ -38,7 +40,8 @@ namespace VFS
|
||||||
|
|
||||||
std::transform(proper.begin() + prefix, proper.end(), std::back_inserter(searchable), normalize_function);
|
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;
|
mBuiltIndex = true;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "registerarchives.hpp"
|
#include "registerarchives.hpp"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -33,12 +34,20 @@ namespace VFS
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useLooseFiles)
|
if (useLooseFiles)
|
||||||
|
{
|
||||||
|
std::set<boost::filesystem::path> seen;
|
||||||
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
||||||
{
|
{
|
||||||
std::cout << "Adding data directory " << iter->string() << std::endl;
|
if (seen.insert(*iter).second)
|
||||||
// Last data dir has the highest priority
|
{
|
||||||
vfs->addArchive(new FileSystemArchive(iter->string()));
|
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();
|
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
|
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
|
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.
|
actually modify the contents of the game.
|
||||||
|
|
||||||
|
Adding to an NPC
|
||||||
Subsection to come...
|
================
|
||||||
=====================
|
|
||||||
|
|
||||||
Adding to an npc
|
|
||||||
****************
|
|
||||||
|
|
||||||
The simplest way is probably to add it to the inventory of a shopkeeper.
|
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
|
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
|
.. figure:: _static/images/chapter-1/Ring_to_Fargoth_2.png
|
||||||
:alt: Editing Fargoth to give ring to player
|
: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
|
Navigation in the CS
|
||||||
====================
|
====================
|
||||||
This is probably a suitable place to start talking about how navigation differs from TESCS
|
This is probably a suitable place to start talking about how navigation differs from TESCS
|
||||||
|
@ -303,43 +372,4 @@ Launch OpenMW and in the launcher under *Data Files* check your addon, if it's n
|
||||||
already checked. Load a game and make your way to Seyda Neen - or start a new game.
|
already checked. Load a game and make your way to Seyda Neen - or start a new game.
|
||||||
|
|
||||||
Check whether Arrille has one (or more) for sale, and whether Fargoth give you one
|
Check whether Arrille has one (or more) for sale, and whether Fargoth give you one
|
||||||
when you return his healing ring.
|
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.
|
or the 'small feature culling' (in the 'Camera' section) is disabled.
|
||||||
|
|
||||||
This setting can only be configured by editing the settings configuration file.
|
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.
|
# Overrides the value in '[Camera] small feature culling pixel size' specifically for water reflection/refraction textures.
|
||||||
small feature culling pixel size = 20.0
|
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]
|
[Windows]
|
||||||
|
|
||||||
# Location and sizes of windows as a fraction of the OpenMW window or
|
# Location and sizes of windows as a fraction of the OpenMW window or
|
||||||
|
|
Loading…
Reference in a new issue