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
sol2-server-rewrite
David Cernat 7 years ago
commit 1a8a518897

@ -58,6 +58,7 @@ void Object::setPosition(float x, float y, float z)
object.position.pos[0] = x;
object.position.pos[1] = y;
object.position.pos[2] = z;
object.droppedByPlayer = false;
changedObjectPlace = true;
}

@ -192,9 +192,8 @@ if (ANDROID)
dl
z
${OPENSCENEGRAPH_LIBRARIES}
${OSG_PLUGINS}
freetype
jpeg
gif
png
)
endif (ANDROID)

@ -1,6 +1,9 @@
int stderr = 0; // Hack: fix linker error
#ifdef __ANDROID__
#include "SDL_main.h"
#include <SDL_gamecontroller.h>
#include <SDL_mouse.h>
#include <SDL_events.h>
/*******************************************************************************
Functions called by JNI
@ -14,13 +17,37 @@ extern int argcData;
extern const char **argvData;
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,
jobject obj) {
setenv("OPENMW_DECOMPRESS_TEXTURES", "1", 1);
SDL_Android_Init(env, cls);
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! */
int status;
@ -33,5 +60,3 @@ int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls,
return status;
}
#endif /* __ANDROID__ */

@ -351,7 +351,11 @@ private:
};
#endif
#ifdef ANDROID
extern "C" int SDL_main(int argc, char**argv)
#else
int main(int argc, char**argv)
#endif
{
#if defined(__APPLE__)
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);

@ -249,6 +249,17 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0;
///< 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 getMaxActivationDistance() = 0;

@ -74,7 +74,7 @@ namespace MWGui
*/
mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent();
worldEvent->reset();
worldEvent->addObjectPlace(dropped);
worldEvent->addObjectPlace(dropped, true);
worldEvent->sendObjectPlace();
/*
End of tes3mp addition

@ -207,8 +207,6 @@ namespace MWGui
, mMessageBoxManager(parMessageBoxManager)
, mButtonPressed(-1)
{
setVisible(true);
int textPadding = 10; // padding between text-widget and main-widget
int textButtonPadding = 10; // padding between the text-widget und the button-widget
int buttonLeftPadding = 10; // padding between the buttons if horizontal
@ -358,7 +356,11 @@ namespace MWGui
mMessageWidget->setCoord(messageWidgetCoord);
}
// Set key focus to "Ok" button
setVisible(true);
}
MyGUI::Widget* InteractiveMessageBox::getDefaultKeyFocus()
{
std::vector<std::string> keywords { "sOk", "sYes" };
for(std::vector<MyGUI::Button*>::const_iterator button = mButtons.begin(); button != mButtons.end(); ++button)
{
@ -366,11 +368,11 @@ namespace MWGui
{
if(Misc::StringUtils::ciEqual(MyGUI::LanguageManager::getInstance().replaceTags("#{" + keyword + "}"), (*button)->getCaption()))
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button);
return;
return *button;
}
}
}
return NULL;
}
void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)

@ -28,6 +28,8 @@ namespace MWGui
bool createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons);
bool isInteractiveMessageBox ();
const InteractiveMessageBox* getInteractiveMessageBox() const { return mInterMessageBoxe; }
/// Remove all message boxes
void clear();
@ -77,6 +79,8 @@ namespace MWGui
void mousePressed (MyGUI::Widget* _widget);
int readPressedButton ();
MyGUI::Widget* getDefaultKeyFocus() override;
virtual bool exit() { return false; }
bool mMarkedToDelete;

@ -908,6 +908,22 @@ namespace MWGui
window->onFrame(frameDuration);
}
// Make sure message boxes are always in front
// This is an awful workaround for a series of awfully interwoven issues that couldn't be worked around
// in a better way because of an impressive number of even more awfully interwoven issues.
if (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox() && mCurrentModals.back() != mMessageBoxManager->getInteractiveMessageBox())
{
std::vector<WindowModal*>::iterator found = std::find(mCurrentModals.begin(), mCurrentModals.end(), mMessageBoxManager->getInteractiveMessageBox());
if (found != mCurrentModals.end())
{
WindowModal* msgbox = *found;
std::swap(*found, mCurrentModals.back());
MyGUI::InputManager::getInstance().addWidgetModal(msgbox->mMainWidget);
mKeyboardNavigation->setModalWindow(msgbox->mMainWidget);
mKeyboardNavigation->setDefaultFocus(msgbox->mMainWidget, msgbox->getDefaultKeyFocus());
}
}
if (!mCurrentModals.empty())
mCurrentModals.back()->onFrame(frameDuration);

@ -1312,13 +1312,13 @@ namespace MWInput
clearAllKeyBindings(control);
if (defaultKeyBindings.find(i) != defaultKeyBindings.end()
&& !mInputBinder->isKeyBound(defaultKeyBindings[i]))
&& (force || !mInputBinder->isKeyBound(defaultKeyBindings[i])))
{
control->setInitialValue(0.0f);
mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE);
}
else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()
&& !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i]))
&& (force || !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i])))
{
control->setInitialValue(0.0f);
mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE);
@ -1392,12 +1392,12 @@ namespace MWInput
clearAllControllerBindings(control);
if (defaultButtonBindings.find(i) != defaultButtonBindings.end()
&& !mInputBinder->isJoystickButtonBound(mFakeDeviceID, defaultButtonBindings[i]))
&& (force || !mInputBinder->isJoystickButtonBound(mFakeDeviceID, defaultButtonBindings[i])))
{
control->setInitialValue(0.0f);
mInputBinder->addJoystickButtonBinding(control, mFakeDeviceID, defaultButtonBindings[i], ICS::Control::INCREASE);
}
else if (defaultAxisBindings.find(i) != defaultAxisBindings.end() && !mInputBinder->isJoystickAxisBound(mFakeDeviceID, defaultAxisBindings[i]))
else if (defaultAxisBindings.find(i) != defaultAxisBindings.end() && (force || !mInputBinder->isJoystickAxisBound(mFakeDeviceID, defaultAxisBindings[i])))
{
control->setValue(0.5f);
control->setInitialValue(0.5f);

@ -1302,14 +1302,12 @@ namespace MWMechanics
// AI and magic effects update
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2();
// AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this
// (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not)
// This distance could be made configurable later, but the setting must be marked with a big warning:
// using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876)
bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2()
<= sqrAiProcessingDistance;
iter->second->getCharacterController()->setActive(inProcessingRange);
bool inProcessingRange = distSqr <= sqrAiProcessingDistance;
/*
Start of tes3mp change (minor)
@ -1480,9 +1478,24 @@ namespace MWMechanics
CharacterController* playerCharacter = NULL;
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
if (iter->first != player &&
(player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2()
> sqrAiProcessingDistance)
const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation.
const float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2();
bool isPlayer = iter->first == player;
bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance);
int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower)
if (isPlayer)
activeFlag = 2;
int active = inAnimationRange ? activeFlag : 0;
bool canFly = iter->first.getClass().canFly(iter->first);
if (canFly)
{
// Keep animating flying creatures so they don't just hover in-air
inAnimationRange = true;
active = std::max(1, active);
}
iter->second->getCharacterController()->setActive(active);
if (!inAnimationRange)
continue;
if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed())

@ -182,29 +182,11 @@ namespace MWMechanics
}
}
float bestArrowRating = 0;
MWWorld::Ptr bestArrow;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Arrow);
if (rating > bestArrowRating)
{
bestArrowRating = rating;
bestArrow = *it;
}
}
float bestArrowRating = rateAmmo(actor, enemy, bestArrow, ESM::Weapon::Arrow);
float bestBoltRating = 0;
MWWorld::Ptr bestBolt;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Bolt);
if (rating > bestBoltRating)
{
bestBoltRating = rating;
bestBolt = *it;
}
}
float bestBoltRating = rateAmmo(actor, enemy, bestBolt, ESM::Weapon::Bolt);
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
@ -277,25 +259,9 @@ namespace MWMechanics
}
}
float bestArrowRating = 0;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Arrow);
if (rating > bestArrowRating)
{
bestArrowRating = rating;
}
}
float bestArrowRating = rateAmmo(actor, enemy, ESM::Weapon::Arrow);
float bestBoltRating = 0;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Bolt);
if (rating > bestBoltRating)
{
bestBoltRating = rating;
}
}
float bestBoltRating = rateAmmo(actor, enemy, ESM::Weapon::Bolt);
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{

@ -2531,7 +2531,7 @@ float CharacterController::getAttackStrength() const
return mAttackStrength;
}
void CharacterController::setActive(bool active)
void CharacterController::setActive(int active)
{
mAnimation->setActive(active);
}

@ -292,7 +292,7 @@ public:
float getAttackStrength() const;
/// @see Animation::setActive
void setActive(bool active);
void setActive(int active);
/// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr.
void setHeadTrackTarget(const MWWorld::ConstPtr& target);

@ -1,4 +1,5 @@
#include "spellpriority.hpp"
#include "weaponpriority.hpp"
#include <components/esm/loadench.hpp>
#include <components/esm/loadmgef.hpp>
@ -335,6 +336,12 @@ namespace MWMechanics
return 0.f;
break;
case ESM::MagicEffect::BoundLongbow:
// AI should not summon the bow if there is no suitable ammo.
if (rateAmmo(actor, enemy, ESM::Weapon::Arrow) <= 0.f)
return 0.f;
break;
case ESM::MagicEffect::RestoreHealth:
case ESM::MagicEffect::RestoreMagicka:
case ESM::MagicEffect::RestoreFatigue:

@ -8,6 +8,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "npcstats.hpp"
#include "combat.hpp"
@ -111,6 +112,33 @@ namespace MWMechanics
return rating + bonus;
}
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType)
{
float bestAmmoRating = 0.f;
if (!actor.getClass().hasInventoryStore(actor))
return bestAmmoRating;
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
float rating = rateWeapon(*it, actor, enemy, ammoType);
if (rating > bestAmmoRating)
{
bestAmmoRating = rating;
bestAmmo = *it;
}
}
return bestAmmoRating;
}
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, ESM::Weapon::Type ammoType)
{
MWWorld::Ptr emptyPtr;
return rateAmmo(actor, enemy, emptyPtr, ammoType);
}
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
{
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

@ -1,6 +1,8 @@
#ifndef OPENMW_WEAPON_PRIORITY_H
#define OPENMW_WEAPON_PRIORITY_H
#include <components/esm/loadweap.hpp>
#include "../mwworld/ptr.hpp"
namespace MWMechanics
@ -8,6 +10,9 @@ namespace MWMechanics
float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy,
int type=-1, float arrowRating=0.f, float boltRating=0.f);
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType);
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, ESM::Weapon::Type ammoType);
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
}

@ -90,8 +90,8 @@ namespace mwmp
void clearCellStates();
void clearCurrentContainer();
void storeCurrentContainer(const MWWorld::Ptr& container);
void storeCellState(const ESM::Cell& cell, mwmp::CellState::Type stateType);
void storeCurrentContainer(const MWWorld::Ptr& container);
void playAnimation();
void playSpeech();

@ -150,6 +150,8 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
void WorldEvent::placeObjects(MWWorld::CellStore* cellStore)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
for (const auto &worldObject : worldObjects)
{
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
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();
if (worldObject.count > 1)
@ -177,10 +179,13 @@ void WorldEvent::placeObjects(MWWorld::CellStore* cellStore)
newPtr.getCellRef().setEnchantmentCharge(worldObject.enchantmentCharge);
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
newPtr.getCellRef().setMpNum(worldObject.mpNum);
if (guid == Main::get().getLocalPlayer()->guid && worldObject.droppedByPlayer)
world->PCDropped(newPtr);
}
else
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)
{
@ -616,6 +621,7 @@ void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr)
worldObject.mpNum = 0;
worldObject.charge = ptr.getCellRef().getCharge();
worldObject.enchantmentCharge = ptr.getCellRef().getEnchantmentCharge();
worldObject.droppedByPlayer = droppedByPlayer;
// Make sure we send the RefData position instead of the CellRef one, because that's what
// we actually see on this client

@ -40,7 +40,7 @@ namespace mwmp
void playMusic();
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, const MWWorld::Ptr& master);
void addObjectDelete(const MWWorld::Ptr& ptr);

@ -486,10 +486,10 @@ namespace MWRender
return mPtr;
}
void Animation::setActive(bool active)
void Animation::setActive(int active)
{
if (mSkeleton)
mSkeleton->setActive(active);
mSkeleton->setActive(static_cast<SceneUtil::Skeleton::ActiveType>(active));
}
void Animation::updatePtr(const MWWorld::Ptr &ptr)

@ -347,7 +347,8 @@ public:
/// Set active flag on the object skeleton, if one exists.
/// @see SceneUtil::Skeleton::setActive
void setActive(bool active);
/// 0 = Inactive, 1 = Active in place, 2 = Active
void setActive(int active);
osg::Group* getOrCreateObjectRoot();

@ -272,6 +272,12 @@ public:
void setWaterLevel(float waterLevel)
{
const float refractionScale = std::min(1.0f,std::max(0.0f,
Settings::Manager::getFloat("refraction scale", "Water")));
setViewMatrix(osg::Matrix::scale(1,1,refractionScale) *
osg::Matrix::translate(0,0,(1.0 - refractionScale) * waterLevel));
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,-1), osg::Vec3d(0,0, waterLevel)));
}

@ -545,7 +545,7 @@ namespace MWScript
const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr(name, false);
// If the objects are in different worldspaces, return a large value (just like vanilla)
if (ref.getCell()->getCell()->getCellId().mWorldspace != ref2.getCell()->getCell()->getCellId().mWorldspace)
if (!ref.isInCell() || !ref2.isInCell() || ref.getCell()->getCell()->getCellId().mWorldspace != ref2.getCell()->getCell()->getCellId().mWorldspace)
return std::numeric_limits<float>::max();
double diff[3];

@ -60,8 +60,9 @@ namespace MWScript
Send an ID_OBJECT_SCALE every time an object's scale is changed
through a script
*/
if (ptr.isInCell())
if (ptr.isInCell() && (ptr.getCellRef().getScale() != scale))
{
mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent();
worldEvent->reset();
worldEvent->addObjectScale(ptr, scale);

@ -243,6 +243,17 @@ namespace MWWorld
std::cout << "Unloading cell\n";
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);
for (std::vector<MWWorld::Ptr>::const_iterator iter2 (visitor.mObjects.begin());
iter2!=visitor.mObjects.end(); ++iter2)
@ -261,16 +272,6 @@ namespace MWWorld
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);
mRendering.removeCell(*iter);
@ -280,6 +281,16 @@ namespace MWWorld
MWBase::Environment::get().getSoundManager()->stopSound (*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)

@ -470,6 +470,7 @@ namespace MWWorld
gmst["fNPCHealthBarFade"] = ESM::Variant(1.f);
gmst["fFleeDistance"] = ESM::Variant(3000.f);
gmst["sMaxSale"] = ESM::Variant("Max Sale");
gmst["sAnd"] = ESM::Variant("and");
// Werewolf (BM)
gmst["fWereWolfRunMult"] = ESM::Variant(1.3f);
@ -1859,19 +1860,26 @@ namespace MWWorld
void World::updateWindowManager ()
{
// inform the GUI about focused object
MWWorld::Ptr object = getFacedObject ();
// retrieve object dimensions so we know where to place the floating label
if (!object.isEmpty ())
try
{
osg::Vec4f screenBounds = mRendering->getScreenBounds(object);
// inform the GUI about focused object
MWWorld::Ptr object = getFacedObject ();
MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords(
screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w());
}
// retrieve object dimensions so we know where to place the floating label
if (!object.isEmpty ())
{
osg::Vec4f screenBounds = mRendering->getScreenBounds(object);
MWBase::Environment::get().getWindowManager()->setFocusObject(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;
}
}
MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer)

@ -136,11 +136,21 @@ namespace MWWorld
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
void removeContainerScripts(const Ptr& reference) override;
private:
void addContainerScripts(const Ptr& reference, CellStore* cell);
void PCDropped (const Ptr& item);
void processDoors(float duration);
///< Run physics simulation and modify \a world accordingly.

@ -20,7 +20,7 @@ Wizard::InstallationTargetPage::InstallationTargetPage(QWidget *parent, const Fi
void Wizard::InstallationTargetPage::initializePage()
{
QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str()));
path.append(QDir::separator() + QLatin1String("data"));
path.append(QDir::separator() + QLatin1String("basedata"));
QDir dir(path);
targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath()));

@ -63,6 +63,7 @@ namespace Compiler
if (mState==BeginState && keyword==Scanner::K_begin)
{
mState = NameState;
scanner.enableTolerantNames(); /// \todo disable
return true;
}

@ -27,6 +27,7 @@ namespace Compiler
if (c=='\n')
{
mStrictKeywords = false;
mTolerantNames = false;
mLoc.mColumn = 0;
++mLoc.mLine;
mLoc.mLiteral.clear();
@ -363,7 +364,7 @@ namespace Compiler
}
else if (!(c=='"' && name.empty()))
{
if (!isStringCharacter (c))
if (!isStringCharacter (c) && !(mTolerantNames && (c=='.' || c=='-')))
{
putback (c);
break;
@ -577,7 +578,7 @@ namespace Compiler
const Extensions *extensions)
: mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),
mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0),
mStrictKeywords (false)
mStrictKeywords (false), mTolerantNames (false)
{
}
@ -634,4 +635,9 @@ namespace Compiler
{
mStrictKeywords = true;
}
void Scanner::enableTolerantNames()
{
mTolerantNames = true;
}
}

@ -38,6 +38,7 @@ namespace Compiler
std::string mPutbackName;
TokenLoc mPutbackLoc;
bool mStrictKeywords;
bool mTolerantNames;
public:
@ -129,6 +130,11 @@ namespace Compiler
///
/// \attention This mode lasts only until the next newline is reached.
void enableStrictKeywords();
/// Continue parsing a name when hitting a '.' or a '-'
///
/// \attention This mode lasts only until the next newline is reached.
void enableTolerantNames();
};
}

@ -13,24 +13,37 @@ namespace ESM
#pragma pack(push)
#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
struct Header
{
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.
struct MasterData
{
@ -39,16 +52,6 @@ namespace ESM
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
std::vector<unsigned char> mSCRD; // Used in .ess savegames only, unknown
std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot
@ -62,7 +65,6 @@ namespace ESM
void load (ESMReader &esm);
void save (ESMWriter &esm);
};
#pragma pack(pop)
}

@ -51,6 +51,7 @@ namespace mwmp
std::string varName;
bool isDisarmed;
bool droppedByPlayer;
Target master;
bool hasMaster;

@ -17,4 +17,5 @@ void PacketObjectPlace::Object(WorldObject &worldObject, bool send)
RW(worldObject.enchantmentCharge, send);
RW(worldObject.goldValue, send);
RW(worldObject.position, send);
RW(worldObject.droppedByPlayer, send);
}

@ -36,8 +36,9 @@ private:
Skeleton::Skeleton()
: mBoneCacheInit(false)
, mNeedToUpdateBoneMatrices(true)
, mActive(true)
, mActive(Active)
, mLastFrameNumber(0)
, mLastCullFrameNumber(0)
{
}
@ -48,6 +49,7 @@ Skeleton::Skeleton(const Skeleton &copy, const osg::CopyOp &copyop)
, mNeedToUpdateBoneMatrices(true)
, mActive(copy.mActive)
, mLastFrameNumber(0)
, mLastCullFrameNumber(0)
{
}
@ -123,14 +125,14 @@ void Skeleton::updateBoneMatrices(unsigned int traversalNumber)
}
}
void Skeleton::setActive(bool active)
void Skeleton::setActive(ActiveType active)
{
mActive = active;
}
bool Skeleton::getActive() const
{
return mActive;
return mActive != Inactive;
}
void Skeleton::markDirty()
@ -142,8 +144,16 @@ void Skeleton::markDirty()
void Skeleton::traverse(osg::NodeVisitor& nv)
{
if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && mLastFrameNumber != 0)
return;
if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
{
if (mActive == Inactive && mLastFrameNumber != 0)
return;
if (mActive == SemiActive && mLastFrameNumber != 0 && mLastCullFrameNumber+3 <= nv.getTraversalNumber())
return;
}
else if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
mLastCullFrameNumber = nv.getTraversalNumber();
osg::Group::traverse(nv);
}

@ -47,9 +47,16 @@ namespace SceneUtil
/// Request an update of bone matrices. May be a no-op if already updated in this frame.
void updateBoneMatrices(unsigned int traversalNumber);
enum ActiveType
{
Inactive=0,
SemiActive, /// Like Active, but don't bother with Update (including new bounding box) if we're off-screen
Active
};
/// Set the skinning active flag. Inactive skeletons will not have their child rigs updated.
/// You should set this flag to false if you know that bones are not currently moving.
void setActive(bool active);
void setActive(ActiveType active);
bool getActive() const;
@ -71,9 +78,10 @@ namespace SceneUtil
bool mNeedToUpdateBoneMatrices;
bool mActive;
ActiveType mActive;
unsigned int mLastFrameNumber;
unsigned int mLastCullFrameNumber;
};
}

@ -16,7 +16,7 @@
#include "imagetosurface.hpp"
#ifdef OSG_LIBRARY_STATIC
#if defined(OSG_LIBRARY_STATIC) && !defined(ANDROID)
// Sets the default windowing system interface according to the OS.
// Necessary for OpenSceneGraph to do some things, like decompression.
USE_GRAPHICSWINDOW()
@ -203,24 +203,21 @@ namespace SDLUtil
void SDLCursorManager::cursorChanged(const std::string& name)
{
mCurrentCursor = name;
CursorMap::const_iterator curs_iter = mCursorMap.find(name);
if(curs_iter != mCursorMap.end())
{
//we have this cursor
_setGUICursor(name);
}
_setGUICursor(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)
{
#ifndef ANDROID
_createCursorFromResource(name, rotDegrees, image, hotspot_x, hotspot_y);
#endif
}
void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y)

@ -92,9 +92,18 @@ void GraphicsWindowSDL2::init()
SDL_GLContext oldCtx = SDL_GL_GetCurrentContext();
#if defined(OPENGL_ES) || defined(ANDROID)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
int major = 1;
int minor = 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
mContext = SDL_GL_CreateContext(mWindow);

@ -1,5 +1,7 @@
#include "filesystemarchive.hpp"
#include <iostream>
#include <boost/filesystem.hpp>
namespace VFS
@ -38,7 +40,8 @@ namespace VFS
std::transform(proper.begin() + prefix, proper.end(), std::back_inserter(searchable), normalize_function);
mIndex.insert (std::make_pair (searchable, file));
if (!mIndex.insert (std::make_pair (searchable, file)).second)
std::cerr << "Warning: found duplicate file for '" << proper << "', please check your file system for two files with the same name in different cases." << std::endl;
}
mBuiltIndex = true;

@ -1,5 +1,6 @@
#include "registerarchives.hpp"
#include <set>
#include <iostream>
#include <sstream>
@ -33,12 +34,20 @@ namespace VFS
}
if (useLooseFiles)
{
std::set<boost::filesystem::path> seen;
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
std::cout << "Adding data directory " << iter->string() << std::endl;
// Last data dir has the highest priority
vfs->addArchive(new FileSystemArchive(iter->string()));
if (seen.insert(*iter).second)
{
std::cout << "Adding data directory " << iter->string() << std::endl;
// Last data dir has the highest priority
vfs->addArchive(new FileSystemArchive(iter->string()));
}
else
std::cerr << "Ignoring duplicate data directory " << iter->string() << std::endl;
}
}
vfs->buildIndex();
}

@ -211,15 +211,11 @@ Adding the ring to the game's world
Now that we have defined the ring it is time add it to the game world so the
player can find it legitimately. We will add the ring to a merchant, place it
in a chest and put it somewhere in plain sight. To this end we will have to
in a chest, and put it somewhere in plain sight. To this end we will have to
actually modify the contents of the game.
Subsection to come...
=====================
Adding to an npc
****************
Adding to an NPC
================
The simplest way is probably to add it to the inventory of a shopkeeper.
An obvious candidate is Arrille in Seyda Neen - he's quick to find in a new game
@ -258,6 +254,79 @@ to give the ring to the player - the same as used earlier in the console
.. figure:: _static/images/chapter-1/Ring_to_Fargoth_2.png
:alt: Editing Fargoth to give ring to player
Placing in a chest
==================
For this example we will use the small chest intended for lockpick practice,
located in the Census and Excise Office in Seyda Neen.
First we need the ID of the chest - this can be obtained either by clicking on it in the console
in the game, or by applying a similar process in the CS -
World/Cells
Select "Seyda Neen, Census and Excise Office"
Right-click and select "View"
Use mouse wheel to zoom in/out, and mouse plus WASD keys to navigate
Click on the small chest
Either way, you should find the ID, which is "chest_small_02_lockprac".
Open the Objects table (World/Objects) and scroll down to find this item.
Alternatively use the Edit/Search facility, selecting ID rather than text,
enter "lockprac" (without the quotes) into the search box, press "Search",
which should return two rows, then select the "Container" one rather than the "Instance"
Right-click and "Edit Record".
Right-click the "Content" section and select "Add a row"
Set the Item ID of the new row to be your new ring - simplest way is probably to open the Objects
table if it's not already open, sort on the "Modified" column which should bring the ring,
with its status of "Added" to the top, then drag and drop to the chest row.
Increase the Count to 1.
Save the addon, then test to ensure it works - e.g. start a new game and lockpick the chest.
Placing in plain sight
=====================
Let's hide the Ring of Night vision in the cabin of the [Ancient Shipwreck]
(http://en.uesp.net/wiki/Morrowind:Ancient_Shipwreck), a derelict vessel
southeast of Dagon Fel. Open the list of Cells (*World**Cells*) and find
"Ancient Shipwreck, Cabin".
This will open a visualization of the cabin. You can navigate around the scene
just like you would when playing Morrowind. Use the WASD keys to move forward,
backwards, and sideways. Click and drag with the left mouse button to change the
direction you are looking. Navigate to the table in the cabin.
If you've closed the Objects table, reopen it via *World**Objects*. Navigate
to your Ring of Night Vision (you can find it easily if you sort by the "Modified"
column). Drag the ring from the Objects table onto the table in the Cell view.
Now let's move the ring to the precise location we want. Hover over the ring and
click the middle mouse button. If you don't have a middle mouse button, you can
select an alternative command by going to *Edit**Preferences…* (Windows, Linux)
or *OpenMW**Preferences…* (macOS). Go to the Key Bindings section and choose
"Scene" from the dropdown menu. Then click on the button for "Primary Select" and
choose an alternative binding.
After you have switched to movement mode, you will see several arrows. Clicking
and dragging them with the right mouse button will allow you to move the object
in the direction you want.
If you'd like an easy way to test this, you can start OpenMW with the [game
arguments](https://wiki.openmw.org/index.php?title=Testing)
`--start="Ancient Shipwreck, Cabin" --skip-menu`. This will place you right in
the cell and allow you to pick up and equip the ring in order to check that it
works.
Navigation in the CS
====================
This is probably a suitable place to start talking about how navigation differs from TESCS
@ -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.
Check whether Arrille has one (or more) for sale, and whether Fargoth give you one
when you return his healing ring.
Placing in a chest
******************
For this example we will use the small chest intended for lockpick practice,
located in the Census and Excise Office in Seyda Neen.
First we need the ID of the chest - this can be obtained either by clicking on it in the console
in the game, or by applying a similar process in the CS -
World/Cells
Select "Seyda Neen, Census and Excise Office"
Right-click and select "View"
Use mouse wheel to zoom in/out, and mouse plus WASD keys to navigate
Click on the small chest
Either way, you should find the ID, which is "chest_small_02_lockprac".
Open the Objects table (World/Objects) and scroll down to find this item.
Alternatively use the Edit/Search facility, selecting ID rather than text,
enter "lockprac" (without the quotes) into the search box, press "Search",
which should return two rows, then select the "Container" one rather than the "Instance"
Right-click and "Edit Record".
Right-click the "Content" section and select "Add a row"
Set the Item ID of the new row to be your new ring - simplest way is probably to open the Objects
table if it's not already open, sort on the "Modified" column which should bring the ring,
with its status of "Added" to the top, then drag and drop to the chest row.
Increase the Count to 1.
Save the addon, then test to ensure it works - e.g. start a new game and lockpick the chest.
when you return his healing ring.

@ -88,3 +88,20 @@ This setting will have no effect if the shader setting is false,
or the 'small feature culling' (in the 'Camera' section) is disabled.
This setting can only be configured by editing the settings configuration file.
refraction scale
----------------
:Type: floating point
:Range: 0 to 1
:Default: 1.0
Simulates light rays refracting when transitioning from air to water, which causes the space under water look scaled down
in height when viewed from above the water surface. Though adding realism, the setting can cause distortion which can
make for example aiming at enemies in water more challenging, so it is off by default (i.e. set to 1.0). To get a realistic
look of real-life water, set the value to 0.75.
This setting only applies if water shader is on and refractions are enabled. Note that if refractions are enabled and this
setting if off, there will still be small refractions caused by the water waves, which however do not cause such significant
distortion.

@ -315,9 +315,9 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts)
vp->pts = pts;
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,
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
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.
small feature culling pixel size = 20.0
# By what factor water downscales objects. Only works with water shader and refractions on.
refraction scale = 1.0
[Windows]
# Location and sizes of windows as a fraction of the OpenMW window or

Loading…
Cancel
Save