mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-04-01 20:06:41 +00:00
Merge pull request #376 from TES3MP/0.6.3 while resolving conflicts
Conflicts: README.md apps/openmw-mp/Script/Functions/World.cpp apps/openmw/mwmp/LocalPlayer.cpp apps/openmw/mwmp/LocalPlayer.hpp apps/openmw/mwworld/scene.cpp components/openmw-mp/Version.hpp
This commit is contained in:
commit
1a8a518897
45 changed files with 420 additions and 193 deletions
|
@ -58,6 +58,7 @@ void Object::setPosition(float x, float y, float z)
|
||||||
object.position.pos[0] = x;
|
object.position.pos[0] = x;
|
||||||
object.position.pos[1] = y;
|
object.position.pos[1] = y;
|
||||||
object.position.pos[2] = z;
|
object.position.pos[2] = z;
|
||||||
|
object.droppedByPlayer = false;
|
||||||
changedObjectPlace = true;
|
changedObjectPlace = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,6 +1,9 @@
|
||||||
|
int stderr = 0; // Hack: fix linker error
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
#include "SDL_main.h"
|
#include "SDL_main.h"
|
||||||
|
#include <SDL_gamecontroller.h>
|
||||||
|
#include <SDL_mouse.h>
|
||||||
|
#include <SDL_events.h>
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
Functions called by JNI
|
Functions called by JNI
|
||||||
|
@ -14,13 +17,37 @@ extern int argcData;
|
||||||
extern const char **argvData;
|
extern const char **argvData;
|
||||||
void releaseArgv();
|
void releaseArgv();
|
||||||
|
|
||||||
|
|
||||||
|
int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) {
|
||||||
|
int ret = 0;
|
||||||
|
SDL_GetMouseState(&ret, NULL);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) {
|
||||||
|
int ret = 0;
|
||||||
|
SDL_GetMouseState(NULL, &ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Java_org_libsdl_app_SDLActivity_isMouseShown(JNIEnv *env, jclass cls, jobject obj) {
|
||||||
|
return SDL_ShowCursor(SDL_QUERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
|
// On Android, we use a virtual controller with guid="Virtual"
|
||||||
|
SDL_GameControllerAddMapping("5669727475616c000000000000000000,Virtual,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4");
|
||||||
|
|
||||||
/* Run the application code! */
|
/* Run the application code! */
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
|
@ -33,5 +60,3 @@ int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __ANDROID__ */
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -249,6 +249,17 @@ namespace MWBase
|
||||||
virtual MWWorld::Ptr getFacedObject() = 0;
|
virtual MWWorld::Ptr getFacedObject() = 0;
|
||||||
///< Return pointer to the object the player is looking at, if it is within activation range
|
///< Return pointer to the object the player is looking at, if it is within activation range
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
This has been declared here so it can be accessed from places
|
||||||
|
other than MWWorld::World
|
||||||
|
*/
|
||||||
|
virtual void PCDropped(const MWWorld::Ptr& item) = 0;
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
virtual float getDistanceToFacedObject() = 0;
|
virtual float getDistanceToFacedObject() = 0;
|
||||||
|
|
||||||
virtual float getMaxActivationDistance() = 0;
|
virtual float getMaxActivationDistance() = 0;
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace MWGui
|
||||||
*/
|
*/
|
||||||
mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent();
|
mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent();
|
||||||
worldEvent->reset();
|
worldEvent->reset();
|
||||||
worldEvent->addObjectPlace(dropped);
|
worldEvent->addObjectPlace(dropped, true);
|
||||||
worldEvent->sendObjectPlace();
|
worldEvent->sendObjectPlace();
|
||||||
/*
|
/*
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,8 +90,8 @@ namespace mwmp
|
||||||
void clearCellStates();
|
void clearCellStates();
|
||||||
void clearCurrentContainer();
|
void clearCurrentContainer();
|
||||||
|
|
||||||
void storeCurrentContainer(const MWWorld::Ptr& container);
|
|
||||||
void storeCellState(const ESM::Cell& cell, mwmp::CellState::Type stateType);
|
void storeCellState(const ESM::Cell& cell, mwmp::CellState::Type stateType);
|
||||||
|
void storeCurrentContainer(const MWWorld::Ptr& container);
|
||||||
|
|
||||||
void playAnimation();
|
void playAnimation();
|
||||||
void playSpeech();
|
void playSpeech();
|
||||||
|
|
|
@ -150,6 +150,8 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
|
||||||
|
|
||||||
void WorldEvent::placeObjects(MWWorld::CellStore* cellStore)
|
void WorldEvent::placeObjects(MWWorld::CellStore* cellStore)
|
||||||
{
|
{
|
||||||
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
for (const auto &worldObject : worldObjects)
|
for (const auto &worldObject : worldObjects)
|
||||||
{
|
{
|
||||||
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, count: %i, charge: %i, enchantmentCharge: %i", worldObject.refId.c_str(),
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, count: %i, charge: %i, enchantmentCharge: %i", worldObject.refId.c_str(),
|
||||||
|
@ -164,7 +166,7 @@ void WorldEvent::placeObjects(MWWorld::CellStore* cellStore)
|
||||||
// Only create this object if it doesn't already exist
|
// Only create this object if it doesn't already exist
|
||||||
if (!ptrFound)
|
if (!ptrFound)
|
||||||
{
|
{
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), worldObject.refId, 1);
|
MWWorld::ManualRef ref(world->getStore(), worldObject.refId, 1);
|
||||||
MWWorld::Ptr newPtr = ref.getPtr();
|
MWWorld::Ptr newPtr = ref.getPtr();
|
||||||
|
|
||||||
if (worldObject.count > 1)
|
if (worldObject.count > 1)
|
||||||
|
@ -177,10 +179,13 @@ void WorldEvent::placeObjects(MWWorld::CellStore* cellStore)
|
||||||
newPtr.getCellRef().setEnchantmentCharge(worldObject.enchantmentCharge);
|
newPtr.getCellRef().setEnchantmentCharge(worldObject.enchantmentCharge);
|
||||||
|
|
||||||
newPtr.getCellRef().setGoldValue(worldObject.goldValue);
|
newPtr.getCellRef().setGoldValue(worldObject.goldValue);
|
||||||
newPtr = MWBase::Environment::get().getWorld()->placeObject(newPtr, cellStore, worldObject.position);
|
newPtr = world->placeObject(newPtr, cellStore, worldObject.position);
|
||||||
|
|
||||||
// Because gold automatically gets replaced with a new object, make sure we set the mpNum at the end
|
// Because gold automatically gets replaced with a new object, make sure we set the mpNum at the end
|
||||||
newPtr.getCellRef().setMpNum(worldObject.mpNum);
|
newPtr.getCellRef().setMpNum(worldObject.mpNum);
|
||||||
|
|
||||||
|
if (guid == Main::get().getLocalPlayer()->guid && worldObject.droppedByPlayer)
|
||||||
|
world->PCDropped(newPtr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LOG_APPEND(Log::LOG_VERBOSE, "-- Object already existed!");
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Object already existed!");
|
||||||
|
@ -600,7 +605,7 @@ void WorldEvent::playVideo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr)
|
void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer)
|
||||||
{
|
{
|
||||||
if (ptr.getCellRef().getRefId().find("$dynamic") != string::npos)
|
if (ptr.getCellRef().getRefId().find("$dynamic") != string::npos)
|
||||||
{
|
{
|
||||||
|
@ -616,6 +621,7 @@ void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr)
|
||||||
worldObject.mpNum = 0;
|
worldObject.mpNum = 0;
|
||||||
worldObject.charge = ptr.getCellRef().getCharge();
|
worldObject.charge = ptr.getCellRef().getCharge();
|
||||||
worldObject.enchantmentCharge = ptr.getCellRef().getEnchantmentCharge();
|
worldObject.enchantmentCharge = ptr.getCellRef().getEnchantmentCharge();
|
||||||
|
worldObject.droppedByPlayer = droppedByPlayer;
|
||||||
|
|
||||||
// Make sure we send the RefData position instead of the CellRef one, because that's what
|
// Make sure we send the RefData position instead of the CellRef one, because that's what
|
||||||
// we actually see on this client
|
// we actually see on this client
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace mwmp
|
||||||
void playMusic();
|
void playMusic();
|
||||||
void playVideo();
|
void playVideo();
|
||||||
|
|
||||||
void addObjectPlace(const MWWorld::Ptr& ptr);
|
void addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer = false);
|
||||||
void addObjectSpawn(const MWWorld::Ptr& ptr);
|
void addObjectSpawn(const MWWorld::Ptr& ptr);
|
||||||
void addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master);
|
void addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master);
|
||||||
void addObjectDelete(const MWWorld::Ptr& ptr);
|
void addObjectDelete(const MWWorld::Ptr& ptr);
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -60,8 +60,9 @@ namespace MWScript
|
||||||
Send an ID_OBJECT_SCALE every time an object's scale is changed
|
Send an ID_OBJECT_SCALE every time an object's scale is changed
|
||||||
through a script
|
through a script
|
||||||
*/
|
*/
|
||||||
if (ptr.isInCell())
|
if (ptr.isInCell() && (ptr.getCellRef().getScale() != scale))
|
||||||
{
|
{
|
||||||
|
|
||||||
mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent();
|
mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent();
|
||||||
worldEvent->reset();
|
worldEvent->reset();
|
||||||
worldEvent->addObjectScale(ptr, scale);
|
worldEvent->addObjectScale(ptr, scale);
|
||||||
|
|
|
@ -243,6 +243,17 @@ namespace MWWorld
|
||||||
std::cout << "Unloading cell\n";
|
std::cout << "Unloading cell\n";
|
||||||
ListAndResetObjectsVisitor visitor;
|
ListAndResetObjectsVisitor visitor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Set a const pointer to the iterator's ESM::Cell here, because
|
||||||
|
(*iter)->getCell() can become invalid later down
|
||||||
|
*/
|
||||||
|
const ESM::Cell* cell = (*iter)->getCell();
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
(*iter)->forEach<ListAndResetObjectsVisitor>(visitor);
|
(*iter)->forEach<ListAndResetObjectsVisitor>(visitor);
|
||||||
for (std::vector<MWWorld::Ptr>::const_iterator iter2 (visitor.mObjects.begin());
|
for (std::vector<MWWorld::Ptr>::const_iterator iter2 (visitor.mObjects.begin());
|
||||||
iter2!=visitor.mObjects.end(); ++iter2)
|
iter2!=visitor.mObjects.end(); ++iter2)
|
||||||
|
@ -261,16 +272,6 @@ namespace MWWorld
|
||||||
mPhysics->removeHeightField ((*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY());
|
mPhysics->removeHeightField ((*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Start of tes3mp addition
|
|
||||||
|
|
||||||
Store a cell unload for the LocalPlayer
|
|
||||||
*/
|
|
||||||
mwmp::Main::get().getLocalPlayer()->storeCellState(*(*iter)->getCell(), mwmp::CellState::Type::Unload);
|
|
||||||
/*
|
|
||||||
End of tes3mp addition
|
|
||||||
*/
|
|
||||||
|
|
||||||
MWBase::Environment::get().getMechanicsManager()->drop (*iter);
|
MWBase::Environment::get().getMechanicsManager()->drop (*iter);
|
||||||
|
|
||||||
mRendering.removeCell(*iter);
|
mRendering.removeCell(*iter);
|
||||||
|
@ -280,6 +281,16 @@ namespace MWWorld
|
||||||
|
|
||||||
MWBase::Environment::get().getSoundManager()->stopSound (*iter);
|
MWBase::Environment::get().getSoundManager()->stopSound (*iter);
|
||||||
mActiveCells.erase(*iter);
|
mActiveCells.erase(*iter);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Store a cell unload for the LocalPlayer
|
||||||
|
*/
|
||||||
|
mwmp::Main::get().getLocalPlayer()->storeCellState(*cell, mwmp::CellState::UNLOAD);
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn)
|
void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn)
|
||||||
|
|
|
@ -470,6 +470,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);
|
||||||
|
@ -1859,19 +1860,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)
|
||||||
|
|
|
@ -136,11 +136,21 @@ namespace MWWorld
|
||||||
|
|
||||||
MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true);
|
MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp change (major)
|
||||||
|
|
||||||
|
This has been turned into a public method so it can be used in
|
||||||
|
multiplayer's different approach to placing items
|
||||||
|
*/
|
||||||
|
void PCDropped(const Ptr& item);
|
||||||
|
/*
|
||||||
|
End of tes3mp change (major)
|
||||||
|
*/
|
||||||
|
|
||||||
public: // FIXME
|
public: // FIXME
|
||||||
void removeContainerScripts(const Ptr& reference) override;
|
void removeContainerScripts(const Ptr& reference) override;
|
||||||
private:
|
private:
|
||||||
void addContainerScripts(const Ptr& reference, CellStore* cell);
|
void addContainerScripts(const Ptr& reference, CellStore* cell);
|
||||||
void PCDropped (const Ptr& item);
|
|
||||||
|
|
||||||
void processDoors(float duration);
|
void processDoors(float duration);
|
||||||
///< Run physics simulation and modify \a world accordingly.
|
///< Run physics simulation and modify \a world accordingly.
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,24 +13,37 @@ namespace ESM
|
||||||
#pragma pack(push)
|
#pragma pack(push)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
|
||||||
|
struct Data
|
||||||
|
{
|
||||||
|
/* File format version. This is actually a float, the supported
|
||||||
|
versions are 1.2 and 1.3. These correspond to:
|
||||||
|
1.2 = 0x3f99999a and 1.3 = 0x3fa66666
|
||||||
|
*/
|
||||||
|
unsigned int version;
|
||||||
|
int type; // 0=esp, 1=esm, 32=ess (unused)
|
||||||
|
NAME32 author; // Author's name
|
||||||
|
NAME256 desc; // File description
|
||||||
|
int records; // Number of records
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GMDT
|
||||||
|
{
|
||||||
|
float mCurrentHealth;
|
||||||
|
float mMaximumHealth;
|
||||||
|
float mHour;
|
||||||
|
unsigned char unknown1[12];
|
||||||
|
NAME64 mCurrentCell;
|
||||||
|
unsigned char unknown2[4];
|
||||||
|
NAME32 mPlayerName;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
/// \brief File header record
|
/// \brief File header record
|
||||||
struct Header
|
struct Header
|
||||||
{
|
{
|
||||||
static const int CurrentFormat = 0; // most recent known format
|
static const int CurrentFormat = 0; // most recent known format
|
||||||
|
|
||||||
struct Data
|
|
||||||
{
|
|
||||||
/* File format version. This is actually a float, the supported
|
|
||||||
versions are 1.2 and 1.3. These correspond to:
|
|
||||||
1.2 = 0x3f99999a and 1.3 = 0x3fa66666
|
|
||||||
*/
|
|
||||||
unsigned int version;
|
|
||||||
int type; // 0=esp, 1=esm, 32=ess (unused)
|
|
||||||
NAME32 author; // Author's name
|
|
||||||
NAME256 desc; // File description
|
|
||||||
int records; // Number of records
|
|
||||||
};
|
|
||||||
|
|
||||||
// Defines another files (esm or esp) that this file depends upon.
|
// Defines another files (esm or esp) that this file depends upon.
|
||||||
struct MasterData
|
struct MasterData
|
||||||
{
|
{
|
||||||
|
@ -39,16 +52,6 @@ namespace ESM
|
||||||
int index; // Position of the parent file in the global list of loaded files
|
int index; // Position of the parent file in the global list of loaded files
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GMDT
|
|
||||||
{
|
|
||||||
float mCurrentHealth;
|
|
||||||
float mMaximumHealth;
|
|
||||||
float mHour;
|
|
||||||
unsigned char unknown1[12];
|
|
||||||
NAME64 mCurrentCell;
|
|
||||||
unsigned char unknown2[4];
|
|
||||||
NAME32 mPlayerName;
|
|
||||||
};
|
|
||||||
GMDT mGameData; // Used in .ess savegames only
|
GMDT mGameData; // Used in .ess savegames only
|
||||||
std::vector<unsigned char> mSCRD; // Used in .ess savegames only, unknown
|
std::vector<unsigned char> mSCRD; // Used in .ess savegames only, unknown
|
||||||
std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot
|
std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot
|
||||||
|
@ -62,7 +65,6 @@ namespace ESM
|
||||||
void load (ESMReader &esm);
|
void load (ESMReader &esm);
|
||||||
void save (ESMWriter &esm);
|
void save (ESMWriter &esm);
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ namespace mwmp
|
||||||
std::string varName;
|
std::string varName;
|
||||||
|
|
||||||
bool isDisarmed;
|
bool isDisarmed;
|
||||||
|
bool droppedByPlayer;
|
||||||
|
|
||||||
Target master;
|
Target master;
|
||||||
bool hasMaster;
|
bool hasMaster;
|
||||||
|
|
|
@ -17,4 +17,5 @@ void PacketObjectPlace::Object(WorldObject &worldObject, bool send)
|
||||||
RW(worldObject.enchantmentCharge, send);
|
RW(worldObject.enchantmentCharge, send);
|
||||||
RW(worldObject.goldValue, send);
|
RW(worldObject.goldValue, send);
|
||||||
RW(worldObject.position, send);
|
RW(worldObject.position, send);
|
||||||
|
RW(worldObject.droppedByPlayer, send);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
@ -203,24 +203,21 @@ namespace SDLUtil
|
||||||
void SDLCursorManager::cursorChanged(const std::string& name)
|
void SDLCursorManager::cursorChanged(const std::string& name)
|
||||||
{
|
{
|
||||||
mCurrentCursor = name;
|
mCurrentCursor = name;
|
||||||
|
_setGUICursor(name);
|
||||||
CursorMap::const_iterator curs_iter = mCursorMap.find(name);
|
|
||||||
|
|
||||||
if(curs_iter != mCursorMap.end())
|
|
||||||
{
|
|
||||||
//we have this cursor
|
|
||||||
_setGUICursor(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLCursorManager::_setGUICursor(const std::string &name)
|
void SDLCursorManager::_setGUICursor(const std::string &name)
|
||||||
{
|
{
|
||||||
SDL_SetCursor(mCursorMap.find(name)->second);
|
auto it = mCursorMap.find(name);
|
||||||
|
if (it != mCursorMap.end())
|
||||||
|
SDL_SetCursor(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -92,9 +92,18 @@ void GraphicsWindowSDL2::init()
|
||||||
SDL_GLContext oldCtx = SDL_GL_GetCurrentContext();
|
SDL_GLContext oldCtx = SDL_GL_GetCurrentContext();
|
||||||
|
|
||||||
#if defined(OPENGL_ES) || defined(ANDROID)
|
#if defined(OPENGL_ES) || defined(ANDROID)
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
int major = 1;
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
|
int minor = 1;
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
char *ver = getenv("OPENMW_GLES_VERSION");
|
||||||
|
|
||||||
|
if (ver && strcmp(ver, "2") == 0) {
|
||||||
|
major = 2;
|
||||||
|
minor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mContext = SDL_GL_CreateContext(mWindow);
|
mContext = SDL_GL_CreateContext(mWindow);
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
4
extern/osg-ffmpeg-videoplayer/videostate.cpp
vendored
4
extern/osg-ffmpeg-videoplayer/videostate.cpp
vendored
|
@ -315,9 +315,9 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts)
|
||||||
vp->pts = pts;
|
vp->pts = pts;
|
||||||
vp->data.resize((*this->video_st)->codec->width * (*this->video_st)->codec->height * 4);
|
vp->data.resize((*this->video_st)->codec->width * (*this->video_st)->codec->height * 4);
|
||||||
|
|
||||||
uint8_t *dst = &vp->data[0];
|
uint8_t *dst[4] = { &vp->data[0], nullptr, nullptr, nullptr };
|
||||||
sws_scale(this->sws_context, pFrame->data, pFrame->linesize,
|
sws_scale(this->sws_context, pFrame->data, pFrame->linesize,
|
||||||
0, (*this->video_st)->codec->height, &dst, this->rgbaFrame->linesize);
|
0, (*this->video_st)->codec->height, dst, this->rgbaFrame->linesize);
|
||||||
|
|
||||||
// now we inform our display thread that we have a pic ready
|
// now we inform our display thread that we have a pic ready
|
||||||
this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_ARRAY_SIZE;
|
this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_ARRAY_SIZE;
|
||||||
|
|
|
@ -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