Merge remote-tracking branch 'upstream/master' into osgshadow-test-vdsm

pull/1547/head
AnyOldName3 6 years ago
commit a2b54714d4

@ -12,6 +12,7 @@
Bug #2835: Player able to slowly move when overencumbered
Bug #2852: No murder bounty when a player follower commits murder
Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit
Bug #2872: Tab completion in console doesn't work with explicit reference
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
Bug #3249: Fixed revert function not updating views properly
Bug #3374: Touch spells not hitting kwama foragers
@ -64,6 +65,7 @@
Bug #4475: Scripted animations should not cause movement
Bug #4479: "Game" category on Advanced page is getting too long
Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory
Bug #4483: Shapes without NiTexturingProperty are rendered
Bug #4489: Goodbye doesn't block dialogue hyperlinks
Bug #4490: PositionCell on player gives "Error: tried to add local script twice"
Bug #4494: Training cap based off Base Skill instead of Modified Skill
@ -74,11 +76,18 @@
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
Bug #4519: Knockdown does not discard movement in the 1st-person mode
Bug #4539: Paper Doll is affected by GUI scaling
Bug #4545: Creatures flee from werewolves
Bug #4551: Replace 0 sound range with default range separately
Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed
Bug #4557: Topics with reserved names are handled differently from vanilla
Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive
Feature #2606: Editor: Implemented (optional) case sensitive global search
Feature #3083: Play animation when NPC is casting spell via script
Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results
Feature #3103: Provide option for disposition to get increased by successful trade
Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results
Feature #3641: Editor: Limit FPS in 3d preview window
Feature #3703: Ranged sneak attack criticals
Feature #4012: Editor: Write a log file if OpenCS crashes
Feature #4222: 360° screenshots
Feature #4256: Implement ToggleBorders (TB) console command
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
@ -87,9 +96,11 @@
Feature #4444: Per-group KF-animation files support
Feature #4466: Editor: Add option to ignore "Base" records when running verifier
Feature #4488: Make water shader rougher during rain
Feature #4012: Editor: Write a log file if OpenCS crashes
Feature #4509: Show count of enchanted items in stack in the spells list
Feature #4512: Editor: Use markers for lights and creatures levelled lists
Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill
Feature #4549: Weapon priority: use the actual damage in weapon rating calculations
Feature #4550: Weapon priority: make ranged weapon bonus more sensible
Task #2490: Don't open command prompt window on Release-mode builds automatically
Task #4545: Enable is_pod string test

@ -20,6 +20,7 @@ namespace ESSImport
item.mId = contItem.mItem.toString();
item.mCount = contItem.mCount;
item.mRelativeEquipmentSlot = -1;
item.mLockLevel = 0;
unsigned int itemCount = std::abs(item.mCount);
bool separateStacks = false;

@ -75,6 +75,7 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
// Input Settings
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
@ -129,6 +130,7 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
// Input Settings
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");

@ -8,8 +8,9 @@
#include <QIcon>
#include <QMetaType>
#include "model/doc/messages.hpp"
#include <components/misc/debugging.hpp>
#include "model/doc/messages.hpp"
#include "model/world/universalid.hpp"
#ifdef Q_OS_MAC
@ -41,45 +42,43 @@ class Application : public QApplication
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
};
int main(int argc, char *argv[])
int runApplication(int argc, char *argv[])
{
#ifdef Q_OS_MAC
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
#ifdef Q_OS_MAC
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
try
{
// To allow background thread drawing in OSG
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
// To allow background thread drawing in OSG
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
Q_INIT_RESOURCE (resources);
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
Application application (argc, argv);
Application application (argc, argv);
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
QDir::setCurrent(dir.absolutePath());
#endif
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
QDir::setCurrent(dir.absolutePath());
#endif
application.setWindowIcon (QIcon (":./openmw-cs.png"));
application.setWindowIcon (QIcon (":./openmw-cs.png"));
CS::Editor editor(argc, argv);
CS::Editor editor(argc, argv);
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
return editor.run();
}
catch (std::exception& e)
if(!editor.makeIPCServer())
{
std::cerr << "ERROR: " << e.what() << std::endl;
editor.connectToIPCServer();
return 0;
}
return editor.run();
}
int main(int argc, char *argv[])
{
return wrapApplication(&runApplication, argc, argv, "/openmw-cs.log");
}

@ -198,6 +198,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mWindow(NULL)
, mEncoding(ToUTF8::WINDOWS_1252)
, mEncoder(NULL)
, mScreenCaptureOperation(nullptr)
, mSkipMenu (false)
, mUseSound (true)
, mCompileAll (false)

@ -5,12 +5,10 @@
#include <components/files/configurationmanager.hpp>
#include <components/files/escape.hpp>
#include <components/fallback/validate.hpp>
#include <components/misc/debugging.hpp>
#include <SDL_messagebox.h>
#include "engine.hpp"
#include <boost/filesystem/fstream.hpp>
#if defined(_WIN32)
// For OutputDebugString
#ifndef WIN32_LEAN_AND_MEAN
@ -241,43 +239,25 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
return true;
}
#if defined(_WIN32) && defined(_DEBUG)
class DebugOutput : public boost::iostreams::sink
{
public:
std::streamsize write(const char *str, std::streamsize size)
{
// Make a copy for null termination
std::string tmp (str, static_cast<unsigned int>(size));
// Write string to Visual Studio Debug output
OutputDebugString (tmp.c_str ());
return size;
}
};
#else
class Tee : public boost::iostreams::sink
int runApplication(int argc, char *argv[])
{
public:
Tee(std::ostream &stream, std::ostream &stream2)
: out(stream), out2(stream2)
{
}
#ifdef __APPLE__
boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
boost::filesystem::current_path(binary_path.parent_path());
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
std::streamsize write(const char *str, std::streamsize size)
Files::ConfigurationManager cfgMgr;
std::unique_ptr<OMW::Engine> engine;
engine.reset(new OMW::Engine(cfgMgr));
if (parseOptions(argc, argv, *engine, cfgMgr))
{
out.write (str, size);
out.flush();
out2.write (str, size);
out2.flush();
return size;
engine->go();
}
private:
std::ostream &out;
std::ostream &out2;
};
#endif
return 0;
}
#ifdef ANDROID
extern "C" int SDL_main(int argc, char**argv)
@ -285,80 +265,7 @@ extern "C" int SDL_main(int argc, char**argv)
int main(int argc, char**argv)
#endif
{
#if defined(__APPLE__)
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
// Some objects used to redirect cout and cerr
// Scope must be here, so this still works inside the catch block for logging exceptions
std::streambuf* cout_rdbuf = std::cout.rdbuf ();
std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();
#if !(defined(_WIN32) && defined(_DEBUG))
boost::iostreams::stream_buffer<Tee> coutsb;
boost::iostreams::stream_buffer<Tee> cerrsb;
#endif
std::ostream oldcout(cout_rdbuf);
std::ostream oldcerr(cerr_rdbuf);
boost::filesystem::ofstream logfile;
std::unique_ptr<OMW::Engine> engine;
int ret = 0;
try
{
Files::ConfigurationManager cfgMgr;
#if defined(_WIN32) && defined(_DEBUG)
// Redirect cout and cerr to VS debug output when running in debug mode
boost::iostreams::stream_buffer<DebugOutput> sb;
sb.open(DebugOutput());
std::cout.rdbuf (&sb);
std::cerr.rdbuf (&sb);
#else
// Redirect cout and cerr to openmw.log
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/openmw.log"));
coutsb.open (Tee(logfile, oldcout));
cerrsb.open (Tee(logfile, oldcerr));
std::cout.rdbuf (&coutsb);
std::cerr.rdbuf (&cerrsb);
#endif
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / "crash.log").string());
#ifdef __APPLE__
boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
boost::filesystem::current_path(binary_path.parent_path());
#endif
engine.reset(new OMW::Engine(cfgMgr));
if (parseOptions(argc, argv, *engine, cfgMgr))
{
engine->go();
}
}
catch (std::exception &e)
{
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
if (!isatty(fileno(stdin)))
#endif
SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL);
std::cerr << "\nERROR: " << e.what() << std::endl;
ret = 1;
}
// Restore cout and cerr
std::cout.rdbuf(cout_rdbuf);
std::cerr.rdbuf(cerr_rdbuf);
return ret;
return wrapApplication(&runApplication, argc, argv, "/openmw.log");
}
// Platform specific for Windows when there is no console built into the executable.

@ -75,8 +75,8 @@ namespace MWBase
virtual void persuade (int type, ResponseCallback* callback) = 0;
virtual int getTemporaryDispositionChange () const = 0;
/// @note This change is temporary and gets discarded when dialogue ends.
virtual void applyDispositionChange (int delta) = 0;
/// @note Controlled by an option, gets discarded when dialogue ends by default
virtual void applyBarterDispositionChange (int delta) = 0;
virtual int countSavedGameRecords() const = 0;

@ -22,6 +22,8 @@
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/defines.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp"
@ -508,9 +510,11 @@ namespace MWDialogue
return static_cast<int>(mTemporaryDispositionChange);
}
void DialogueManager::applyDispositionChange(int delta)
void DialogueManager::applyBarterDispositionChange(int delta)
{
mTemporaryDispositionChange += delta;
if (Settings::Manager::getBool("barter disposition change is permanent", "Game"))
mPermanentDispositionChange += delta;
}
bool DialogueManager::checkServiceRefused(ResponseCallback* callback)

@ -94,8 +94,8 @@ namespace MWDialogue
virtual void persuade (int type, ResponseCallback* callback);
virtual int getTemporaryDispositionChange () const;
/// @note This change is temporary and gets discarded when dialogue ends.
virtual void applyDispositionChange (int delta);
/// @note Controlled by an option, gets discarded when dialogue ends by default
virtual void applyBarterDispositionChange (int delta);
virtual int countSavedGameRecords() const;

@ -303,6 +303,14 @@ namespace MWGui
bool has_front_quote = false;
/* Does the input string contain things that don't have to be completed? If yes erase them. */
/* Erase a possible call to an explicit reference. */
size_t explicitPos = tmp.find("->");
if (explicitPos != std::string::npos)
{
tmp.erase(0, explicitPos+2);
}
/* Are there quotation marks? */
if( tmp.find('"') != std::string::npos ) {
int numquotes=0;
@ -339,6 +347,7 @@ namespace MWGui
}
}
}
/* Erase the input from the output string so we can easily append the completed form later. */
output.erase(output.end()-tmp.length(), output.end());

@ -52,7 +52,7 @@ namespace MWGui
void ContainerWindow::onItemSelected(int index)
{
if (mDragAndDrop->mIsOnDragAndDrop && mModel)
if (mDragAndDrop->mIsOnDragAndDrop)
{
dropItem();
return;
@ -88,6 +88,9 @@ namespace MWGui
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
{
if (!mModel)
return;
if (!onTakeItem(mModel->getItem(mSelectedItem), count))
return;
@ -96,6 +99,9 @@ namespace MWGui
void ContainerWindow::dropItem()
{
if (!mModel)
return;
bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount);
if (success)
@ -104,7 +110,7 @@ namespace MWGui
void ContainerWindow::onBackgroundSelected()
{
if (mDragAndDrop->mIsOnDragAndDrop && mModel)
if (mDragAndDrop->mIsOnDragAndDrop)
dropItem();
}

@ -362,52 +362,59 @@ namespace MWGui
if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice())
return;
int separatorPos = 0;
for (unsigned int i=0; i<mTopicsList->getItemCount(); ++i)
{
if (mTopicsList->getItemNameAt(i) == "")
separatorPos = i;
}
if (id >= separatorPos)
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
const std::string sPersuasion = gmst.find("sPersuasion")->getString();
const std::string sCompanionShare = gmst.find("sCompanionShare")->getString();
const std::string sBarter = gmst.find("sBarter")->getString();
const std::string sSpells = gmst.find("sSpells")->getString();
const std::string sTravel = gmst.find("sTravel")->getString();
const std::string sSpellMakingMenuTitle = gmst.find("sSpellMakingMenuTitle")->getString();
const std::string sEnchanting = gmst.find("sEnchanting")->getString();
const std::string sServiceTrainingTitle = gmst.find("sServiceTrainingTitle")->getString();
const std::string sRepair = gmst.find("sRepair")->getString();
if (topic != sPersuasion && topic != sCompanionShare && topic != sBarter
&& topic != sSpells && topic != sTravel && topic != sSpellMakingMenuTitle
&& topic != sEnchanting && topic != sServiceTrainingTitle && topic != sRepair)
{
onTopicActivated(topic);
if (mGoodbyeButton->getEnabled())
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);
}
else
else if (topic == sPersuasion)
mPersuasionDialog.setVisible(true);
else if (topic == sCompanionShare)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr);
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(mCallback.get()))
{
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
if (topic == gmst.find("sPersuasion")->getString())
mPersuasionDialog.setVisible(true);
else if (topic == gmst.find("sCompanionShare")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr);
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(mCallback.get()))
{
if (topic == gmst.find("sBarter")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);
else if (topic == gmst.find("sSpells")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr);
else if (topic == gmst.find("sTravel")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr);
else if (topic == gmst.find("sSpellMakingMenuTitle")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr);
else if (topic == gmst.find("sEnchanting")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr);
else if (topic == gmst.find("sServiceTrainingTitle")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr);
else if (topic == gmst.find("sRepair")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr);
}
else
updateTopics();
if (topic == sBarter)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);
else if (topic == sSpells)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr);
else if (topic == sTravel)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr);
else if (topic == sSpellMakingMenuTitle)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr);
else if (topic == sEnchanting)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr);
else if (topic == sServiceTrainingTitle)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr);
else if (topic == sRepair)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr);
}
else
updateTopics();
}
void DialogueWindow::setPtr(const MWWorld::Ptr& actor)
{
if (!actor.getClass().isActor())
{
std::cerr << "Warning: can not talk with non-actor object." << std::endl;
return;
}
bool sameActor = (mPtr == actor);
if (!sameActor)
{

@ -37,6 +37,7 @@ namespace MWGui
, mLastRenderTime(0.0)
, mLoadingOnTime(0.0)
, mImportantLabel(false)
, mVisible(false)
, mProgress(0)
, mShowWallpaper(true)
{

@ -161,6 +161,12 @@ namespace MWGui
}
}
assert(index != -1);
if (index < 0)
{
mSelected = nullptr;
return;
}
mSelected = &mKey[index];
// prevent reallocation of zero key from Type_HandToHand

@ -26,6 +26,7 @@ namespace MWGui
Spell()
: mType(Type_Spell)
, mCount(0)
, mSelected(false)
, mActive(false)
{

@ -334,7 +334,7 @@ namespace MWGui
? gmst.find("iBarterSuccessDisposition")->getInt()
: gmst.find("iBarterFailDisposition")->getInt();
MWBase::Environment::get().getDialogueManager()->applyDispositionChange(dispositionDelta);
MWBase::Environment::get().getDialogueManager()->applyBarterDispositionChange(dispositionDelta);
}
// display message on haggle failure

@ -898,7 +898,8 @@ namespace MWGui
mKeyboardNavigation->onFrame();
mMessageBoxManager->onFrame(frameDuration);
if (mMessageBoxManager)
mMessageBoxManager->onFrame(frameDuration);
mToolTips->onFrame(frameDuration);

@ -494,10 +494,13 @@ namespace MWMechanics
static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt();
if (enemy.getClass().isNpc() && enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack)
if (actor.getClass().isNpc() && enemy.getClass().isNpc())
{
static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt();
rating = iWereWolfFleeMod;
if (enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack)
{
static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt();
rating = iWereWolfFleeMod;
}
}
if (rating != 0.0f)

@ -1221,11 +1221,11 @@ bool CharacterController::updateWeaponState()
bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell &&
mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell;
if(weaptype != mWeaponType && !isKnockedOut() &&
!isKnockedDown() && !isRecovery())
if(!isKnockedOut() && !isKnockedDown() && !isRecovery())
{
std::string weapgroup;
if ((!isWerewolf || mWeaponType != WeapType_Spell)
&& weaptype != mWeaponType
&& mUpperBodyState != UpperCharState_UnEquipingWeap
&& !isStillWeapon)
{
@ -1250,49 +1250,60 @@ bool CharacterController::updateWeaponState()
float complete;
bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if (!animPlaying || complete >= 1.0f)
{
forcestateupdate = true;
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
// Weapon is changed, no current animation (e.g. unequipping or attack).
// Start equipping animation now.
if (weaptype != mWeaponType)
{
forcestateupdate = true;
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
getWeaponGroup(weaptype, weapgroup);
mAnimation->setWeaponGroup(weapgroup);
getWeaponGroup(weaptype, weapgroup);
mAnimation->setWeaponGroup(weapgroup);
if (!isStillWeapon)
{
if (weaptype == WeapType_None)
if (!isStillWeapon)
{
// Disable current weapon animation manually
mAnimation->disable(mCurrentWeapon);
if (weaptype != WeapType_None)
{
mAnimation->showWeapons(false);
mAnimation->play(weapgroup, priorityWeapon,
MWRender::Animation::BlendMask_All, true,
1.0f, "equip start", "equip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_EquipingWeap;
}
}
else
if(isWerewolf)
{
mAnimation->showWeapons(false);
mAnimation->play(weapgroup, priorityWeapon,
MWRender::Animation::BlendMask_All, true,
1.0f, "equip start", "equip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_EquipingWeap;
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfEquip");
if(sound)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);
}
}
}
if(isWerewolf)
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfEquip");
if(sound)
mWeaponType = weaptype;
getWeaponGroup(mWeaponType, mCurrentWeapon);
if(!upSoundId.empty() && !isStillWeapon)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);
sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);
}
}
mWeaponType = weaptype;
getWeaponGroup(mWeaponType, mCurrentWeapon);
if(!upSoundId.empty() && !isStillWeapon)
// Make sure that we disabled unequipping animation
if (mUpperBodyState == UpperCharState_UnEquipingWeap)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);
mUpperBodyState = UpperCharState_Nothing;
mAnimation->disable(mCurrentWeapon);
mWeaponType = WeapType_None;
getWeaponGroup(mWeaponType, mCurrentWeapon);
}
}
}

@ -315,14 +315,14 @@ namespace MWMechanics
return true;
}
CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool isScripted)
CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool manualSpell)
: mCaster(caster)
, mTarget(target)
, mStack(false)
, mHitPosition(0,0,0)
, mAlwaysSucceed(false)
, mFromProjectile(fromProjectile)
, mIsScripted(isScripted)
, mManualSpell(manualSpell)
{
}
@ -864,7 +864,7 @@ namespace MWMechanics
bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
if (mCaster.getClass().isActor() && !mAlwaysSucceed && !mIsScripted)
if (mCaster.getClass().isActor() && !mAlwaysSucceed && !mManualSpell)
{
school = getSpellSchool(spell, mCaster);
@ -1037,7 +1037,7 @@ namespace MWMechanics
bool CastSpell::spellIncreasesSkill()
{
if (mIsScripted)
if (mManualSpell)
return false;
return MWMechanics::spellIncreasesSkill(mId);

@ -88,10 +88,10 @@ namespace MWMechanics
osg::Vec3f mHitPosition; // Used for spawning area orb
bool mAlwaysSucceed; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false)
bool mFromProjectile; // True if spell is cast by enchantment of some projectile (arrow, bolt or thrown weapon)
bool mIsScripted; // True if spell is casted from script and ignores some checks (mana level, success chance, etc.)
bool mManualSpell; // True if spell is casted from script and ignores some checks (mana level, success chance, etc.)
public:
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const bool fromProjectile=false, const bool isScripted=false);
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const bool fromProjectile=false, const bool manualSpell=false);
bool cast (const ESM::Spell* spell);

@ -30,7 +30,7 @@ namespace MWMechanics
return 0.f;
float rating=0.f;
float bonus=0.f;
float rangedMult=1.f;
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown)
{
@ -44,25 +44,33 @@ namespace MWMechanics
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))
return 0.f;
bonus+=1.5f;
if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy))
rangedMult = 1.5f;
}
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
{
rating = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f;
float rangedDamage = weapon->mData.mChop[0] + weapon->mData.mChop[1];
MWMechanics::adjustWeaponDamage(rangedDamage, item, actor);
rating = rangedDamage / 2.f;
if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown)
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
}
else
{
float meleeDamage = 0.f;
for (int i=0; i<2; ++i)
{
rating += weapon->mData.mSlash[i];
rating += weapon->mData.mThrust[i];
rating += weapon->mData.mChop[i];
meleeDamage += weapon->mData.mSlash[i];
meleeDamage += weapon->mData.mThrust[i];
meleeDamage += weapon->mData.mChop[i];
}
rating /= 6.f;
MWMechanics::adjustWeaponDamage(meleeDamage, item, actor);
rating = meleeDamage / 6.f;
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
}
@ -71,7 +79,6 @@ namespace MWMechanics
{
if (item.getClass().getItemHealth(item) == 0)
return 0.f;
rating *= item.getClass().getItemHealth(item) / float(item.getClass().getItemMaxHealth(item));
}
if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
@ -103,13 +110,12 @@ namespace MWMechanics
int skill = item.getClass().getEquipmentSkill(item);
if (skill != -1)
rating *= actor.getClass().getSkill(actor, skill) / 100.f;
// There is no need to apply bonus if weapon rating == 0
if (rating == 0.f)
return 0.f;
{
int value = actor.getClass().getSkill(actor, skill);
rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f;
}
return rating + bonus;
return rating * rangedMult;
}
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType)

@ -204,6 +204,7 @@ namespace MWRender
, mNightEyeFactor(0.f)
, mDistantFog(false)
, mDistantTerrain(false)
, mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f)
, mBorders(false)
{
@ -717,9 +718,6 @@ namespace MWRender
int screenshotW = mViewer->getCamera()->getViewport()->width();
int screenshotH = mViewer->getCamera()->getViewport()->height();
int screenshotMapping = 0;
int cubeSize = screenshotMapping == 2 ?
screenshotW: // planet mapping needs higher resolution
screenshotW / 2;
std::vector<std::string> settingArgs;
boost::algorithm::split(settingArgs,settingStr,boost::is_any_of(" "));
@ -743,7 +741,10 @@ namespace MWRender
return false;
}
}
// planet mapping needs higher resolution
int cubeSize = screenshotMapping == 2 ? screenshotW : screenshotW / 2;
if (settingArgs.size() > 1)
screenshotW = std::min(10000,std::atoi(settingArgs[1].c_str()));

@ -1118,6 +1118,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
, mWindSpeed(0.f)
, mEnabled(true)
, mSunEnabled(true)
, mWeatherAlpha(0.f)
{
osg::ref_ptr<CameraRelativeTransform> skyroot (new CameraRelativeTransform);
skyroot->setName("Sky Root");

@ -1,3 +1,5 @@
#include <iostream>
#include "dialogueextensions.hpp"
#include <components/compiler/extensions.hpp>
@ -134,6 +136,14 @@ namespace MWScript
if (!ptr.getRefData().isEnabled())
return;
if (!ptr.getClass().isActor())
{
const std::string error = "Warning: \"forcegreeting\" command works only for actors.";
runtime.getContext().report (error);
std::cerr << error << std::endl;
return;
}
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, ptr);
}
};

@ -142,16 +142,12 @@ namespace MWSound
float volume, min, max;
volume = static_cast<float>(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0));
if(sound->mData.mMinRange == 0 && sound->mData.mMaxRange == 0)
{
min = sound->mData.mMinRange;
max = sound->mData.mMaxRange;
if (min == 0)
min = fAudioDefaultMinDistance;
if (max == 0)
max = fAudioDefaultMaxDistance;
}
else
{
min = sound->mData.mMinRange;
max = sound->mData.mMaxRange;
}
min *= fAudioMinDistanceMult;
max *= fAudioMaxDistanceMult;

@ -1180,9 +1180,9 @@ namespace MWWorld
addContainerScripts (getPlayerPtr(), newCell);
newPtr = getPlayerPtr();
}
else
else if (currCell)
{
bool currCellActive = currCell && mWorldScene->isCellActive(*currCell);
bool currCellActive = mWorldScene->isCellActive(*currCell);
bool newCellActive = newCell && mWorldScene->isCellActive(*newCell);
if (!currCellActive && newCellActive)
{

@ -7,7 +7,7 @@ struct PartialBinarySearchTest : public ::testing::Test
std::vector<std::string> mDataVec;
virtual void SetUp()
{
const char* data[] = { "Head", "Chest", "Tri Head", "Tri Chest", "Bip01" };
const char* data[] = { "Head", "Chest", "Tri Head", "Tri Chest", "Bip01", "Tri Bip01" };
mDataVec = std::vector<std::string>(data, data+sizeof(data)/sizeof(data[0]));
std::sort(mDataVec.begin(), mDataVec.end(), Misc::StringUtils::ciLess);
}
@ -29,7 +29,15 @@ TEST_F(PartialBinarySearchTest, partial_binary_search_test)
EXPECT_TRUE( matches("Tri Head 01") );
EXPECT_TRUE( matches("Tri Head") );
EXPECT_TRUE( matches("tri head") );
EXPECT_TRUE( matches("Tri bip01") );
EXPECT_TRUE( matches("bip01") );
EXPECT_TRUE( matches("bip01 head") );
EXPECT_TRUE( matches("Bip01 L Hand") );
EXPECT_TRUE( matches("BIp01 r Clavicle") );
EXPECT_TRUE( matches("Bip01 SpiNe1") );
EXPECT_FALSE( matches("") );
EXPECT_FALSE( matches("Node Bip01") );
EXPECT_FALSE( matches("Hea") );
EXPECT_FALSE( matches(" Head") );
EXPECT_FALSE( matches("Tri Head") );

@ -12,6 +12,9 @@ environment:
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- msvc: 2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
matrix:
allow_failures:
- msvc: 2015
platform:
# - Win32

@ -85,7 +85,7 @@ add_component_dir (esmterrain
)
add_component_dir (misc
utf8stream stringops resourcehelpers rng messageformatparser
utf8stream stringops resourcehelpers rng debugging messageformatparser
)
IF(NOT WIN32 AND NOT APPLE)

@ -182,8 +182,10 @@ static void gdb_info(pid_t pid)
/* Error creating temp file */
if(fd >= 0)
{
close(fd);
remove(respfile);
if (close(fd) == 0)
remove(respfile);
else
std::cerr << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno) << std::endl;
}
printf("!!! Could not create gdb command file\n");
}

@ -37,6 +37,8 @@ namespace ESMTerrain
}
LandObject::LandObject(const LandObject &copy, const osg::CopyOp &copyop)
: mLand(nullptr)
, mLoadFlags(0)
{
}

@ -0,0 +1,112 @@
#ifndef MISC_DEBUGGING_H
#define MISC_DEBUGGING_H
#include <boost/filesystem/fstream.hpp>
#include <boost/iostreams/stream.hpp>
#include <SDL_messagebox.h>
namespace Misc
{
#if defined(_WIN32) && defined(_DEBUG)
class DebugOutput : public boost::iostreams::sink
{
public:
std::streamsize write(const char *str, std::streamsize size)
{
// Make a copy for null termination
std::string tmp (str, static_cast<unsigned int>(size));
// Write string to Visual Studio Debug output
OutputDebugString (tmp.c_str ());
return size;
}
};
#else
class Tee : public boost::iostreams::sink
{
public:
Tee(std::ostream &stream, std::ostream &stream2)
: out(stream), out2(stream2)
{
}
std::streamsize write(const char *str, std::streamsize size)
{
out.write (str, size);
out.flush();
out2.write (str, size);
out2.flush();
return size;
}
private:
std::ostream &out;
std::ostream &out2;
};
#endif
}
int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& logName)
{
// Some objects used to redirect cout and cerr
// Scope must be here, so this still works inside the catch block for logging exceptions
std::streambuf* cout_rdbuf = std::cout.rdbuf ();
std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();
#if !(defined(_WIN32) && defined(_DEBUG))
boost::iostreams::stream_buffer<Misc::Tee> coutsb;
boost::iostreams::stream_buffer<Misc::Tee> cerrsb;
#endif
std::ostream oldcout(cout_rdbuf);
std::ostream oldcerr(cerr_rdbuf);
boost::filesystem::ofstream logfile;
int ret = 0;
try
{
Files::ConfigurationManager cfgMgr;
#if defined(_WIN32) && defined(_DEBUG)
// Redirect cout and cerr to VS debug output when running in debug mode
boost::iostreams::stream_buffer<Misc::DebugOutput> sb;
sb.open(Misc::DebugOutput());
std::cout.rdbuf (&sb);
std::cerr.rdbuf (&sb);
#else
// Redirect cout and cerr to the log file
boost::filesystem::ofstream logfile;
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName));
std::ostream oldcout(cout_rdbuf);
std::ostream oldcerr(cerr_rdbuf);
coutsb.open (Misc::Tee(logfile, oldcout));
cerrsb.open (Misc::Tee(logfile, oldcerr));
std::cout.rdbuf (&coutsb);
std::cerr.rdbuf (&cerrsb);
#endif
ret = innerApplication(argc, argv);
}
catch (std::exception& e)
{
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
if (!isatty(fileno(stdin)))
#endif
SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL);
std::cerr << "\nERROR: " << e.what() << std::endl;
ret = 1;
}
// Restore cout and cerr
std::cout.rdbuf(cout_rdbuf);
std::cerr.rdbuf(cerr_rdbuf);
return ret;
}
#endif

@ -146,8 +146,15 @@ public:
std::string::const_iterator yit = y.begin();
for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len)
{
int res = *xit - *yit;
if(res != 0 && toLower(*xit) != toLower(*yit))
char left = *xit;
char right = *yit;
if (left == right)
continue;
left = toLower(left);
right = toLower(right);
int res = left - right;
if(res != 0)
return (res > 0) ? 1 : -1;
}
if(len > 0)

@ -573,6 +573,10 @@ namespace NifOsg
}
}
// Make sure we don't render untextured shapes
if (nifNode->recType == Nif::RC_NiTriShape && boundTextures.empty())
node->setNodeMask(0x0);
if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles)
handleParticleSystem(nifNode, node, composite, animflags, rootNode);

@ -393,9 +393,9 @@ namespace Resource
static std::vector<std::string> reservedNames;
if (reservedNames.empty())
{
const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm", "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle", "Left Clavicle", "Weapon Bone", "Tail",
"Bip01 L Hand", "Bip01 R Hand", "Bip01 Head", "Bip01 Spine1", "Bip01 Spine2", "Bip01 L Clavicle", "Bip01 R Clavicle", "bip01", "Root Bone", "Bip01 Neck",
"BoneOffset", "AttachLight", "ArrowBone", "Camera"};
const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
"Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "ArrowBone", "Camera"};
reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
for (unsigned int i=0; i<sizeof(reserved)/sizeof(reserved[0]); ++i)

@ -130,7 +130,7 @@ enchanted weapons are magical
:Range: True/False
:Default: True
Makes enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have.
Make enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have.
This is how original Morrowind behaves.
This setting can only be configured by editing the settings configuration file.
@ -142,7 +142,7 @@ prevent merchant equipping
:Range: True/False
:Default: False
Prevents merchants from equipping items that are sold to them.
Prevent merchants from equipping items that are sold to them.
This setting can only be configured by editing the settings configuration file.
@ -153,7 +153,7 @@ followers attack on sight
:Range: True/False
:Default: False
Makes player followers and escorters start combat with enemies who have started combat with them or the player.
Make player followers and escorters start combat with enemies who have started combat with them or the player.
Otherwise they wait for the enemies or the player to do an attack first.
Please note this setting has not been extensively tested and could have side effects with certain quests.
@ -171,3 +171,15 @@ For example, if the main animation mesh has name Meshes/x.nif, an engine will lo
Can be useful if you want to use several animation replacers without merging them.
Attention: animations from AnimKit have own format and are not supposed to be directly loaded in-game!
This setting can only be configured by editing the settings configuration file.
barter disposition change is permanent
--------------------------------------
:Type: boolean
:Range: True/False
:Default: False
If this setting is true, disposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them.
This imitates the option Morrowind Code Patch offers.
This setting can be toggled with a checkbox in Advanced tab of the launcher.

@ -1295,9 +1295,10 @@ const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEnc
++p;
}
if ( !p )
if ( !p || !*p )
{
if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
return 0;
}
if ( *p == '>' )
return p+1;

@ -203,25 +203,28 @@ show effect duration = false
# Account for the first follower in fast travel cost calculations.
charge for every follower travelling = false
# Prevents merchants from equipping items that are sold to them.
# Prevent merchants from equipping items that are sold to them.
prevent merchant equipping = false
# Make enchanted weaponry without Magical flag bypass normal weapons resistance
enchanted weapons are magical = true
# Makes player followers and escorters start combat with enemies who have started combat with them
# Make player followers and escorters start combat with enemies who have started combat with them
# or the player. Otherwise they wait for the enemies or the player to do an attack first.
followers attack on sight = false
# Can loot non-fighting actors during death animation
can loot during death animation = true
# Makes the value of filled soul gems dependent only on soul magnitude (with formula from the Morrowind Code Patch)
# Make the value of filled soul gems dependent only on soul magnitude (with formula from the Morrowind Code Patch)
rebalance soul gem values = false
# Allow to load per-group KF-files from Animations folder
use additional anim sources = false
# Make the disposition change of merchants caused by barter dealings permanent
barter disposition change is permanent = false
[General]
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).

@ -22,7 +22,7 @@
<x>0</x>
<y>0</y>
<width>641</width>
<height>968</height>
<height>998</height>
</rect>
</property>
<layout class="QVBoxLayout" name="scrollAreaVerticalLayout">
@ -45,7 +45,7 @@
<item>
<widget class="QCheckBox" name="followersAttackOnSightCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Makes player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Followers attack on sight</string>
@ -65,7 +65,7 @@
<item>
<widget class="QCheckBox" name="rebalanceSoulGemValuesCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, the value of filled soul gems is dependent only on soul magnitude.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make the value of filled soul gems dependent only on soul magnitude.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Rebalance soul gem values</string>
@ -89,6 +89,16 @@
</property>
<property name="text">
<string>Enchanted weapons are magical</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="permanentBarterDispositionChangeCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make disposition change of merchants caused by trading permanent.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Barter disposition change is permanent</string>
</property>
</widget>
</item>

Loading…
Cancel
Save