forked from mirror/openmw-tes3mp
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[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()->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)
|
||||
|
|
|
@ -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 ©, const osg::CopyOp ©op)
|
|||
, mNeedToUpdateBoneMatrices(true)
|
||||
, mActive(copy.mActive)
|
||||
, mLastFrameNumber(0)
|
||||
, mLastCullFrameNumber(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -123,14 +125,14 @@ void Skeleton::updateBoneMatrices(unsigned int traversalNumber)
|
|||
}
|
||||
}
|
||||
|
||||
void Skeleton::setActive(bool active)
|
||||
void Skeleton::setActive(ActiveType active)
|
||||
{
|
||||
mActive = active;
|
||||
}
|
||||
|
||||
bool Skeleton::getActive() const
|
||||
{
|
||||
return mActive;
|
||||
return mActive != Inactive;
|
||||
}
|
||||
|
||||
void Skeleton::markDirty()
|
||||
|
@ -142,8 +144,16 @@ void Skeleton::markDirty()
|
|||
|
||||
void Skeleton::traverse(osg::NodeVisitor& nv)
|
||||
{
|
||||
if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && mLastFrameNumber != 0)
|
||||
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.
|
||||
|
||||
|
|
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->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…
Reference in a new issue