Merge pull request #469 from davidcernat/master while resolving conflicts
# Conflicts: # apps/openmw/main.cpp # apps/openmw/mwbase/world.hpp # apps/openmw/mwdialogue/dialoguemanagerimp.cpp # apps/openmw/mwmechanics/actors.cpp # apps/openmw/mwscript/dialogueextensions.cpp # apps/openmw/mwworld/worldimp.hpp
30
CHANGELOG.md
|
@ -7,10 +7,12 @@
|
||||||
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
|
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
|
||||||
Bug #2455: Creatures attacks degrade armor
|
Bug #2455: Creatures attacks degrade armor
|
||||||
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
|
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
|
||||||
|
Bug #2626: Resurrecting the player does not resume the game
|
||||||
Bug #2772: Non-existing class or faction freezes the game
|
Bug #2772: Non-existing class or faction freezes the game
|
||||||
Bug #2835: Player able to slowly move when overencumbered
|
Bug #2835: Player able to slowly move when overencumbered
|
||||||
Bug #2852: No murder bounty when a player follower commits murder
|
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 #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 #2971: Compiler did not reject lines with naked expressions beginning with x.y
|
||||||
Bug #3249: Fixed revert function not updating views properly
|
Bug #3249: Fixed revert function not updating views properly
|
||||||
Bug #3374: Touch spells not hitting kwama foragers
|
Bug #3374: Touch spells not hitting kwama foragers
|
||||||
|
@ -20,6 +22,7 @@
|
||||||
Bug #3876: Landscape texture painting is misaligned
|
Bug #3876: Landscape texture painting is misaligned
|
||||||
Bug #3897: Have Goodbye give all choices the effects of Goodbye
|
Bug #3897: Have Goodbye give all choices the effects of Goodbye
|
||||||
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
|
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
|
||||||
|
Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes
|
||||||
Bug #3993: Terrain texture blending map is not upscaled
|
Bug #3993: Terrain texture blending map is not upscaled
|
||||||
Bug #3997: Almalexia doesn't pace
|
Bug #3997: Almalexia doesn't pace
|
||||||
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
|
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
|
||||||
|
@ -28,15 +31,18 @@
|
||||||
Bug #4125: OpenMW logo cropped on bugtracker
|
Bug #4125: OpenMW logo cropped on bugtracker
|
||||||
Bug #4215: OpenMW shows book text after last EOL tag
|
Bug #4215: OpenMW shows book text after last EOL tag
|
||||||
Bug #4221: Characters get stuck in V-shaped terrain
|
Bug #4221: Characters get stuck in V-shaped terrain
|
||||||
|
Bug #4230: AiTravel package issues break some Tribunal quests
|
||||||
Bug #4251: Stationary NPCs do not return to their position after combat
|
Bug #4251: Stationary NPCs do not return to their position after combat
|
||||||
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
|
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
|
||||||
Bug #4286: Scripted animations can be interrupted
|
Bug #4286: Scripted animations can be interrupted
|
||||||
Bug #4291: Non-persistent actors that started the game as dead do not play death animations
|
Bug #4291: Non-persistent actors that started the game as dead do not play death animations
|
||||||
Bug #4293: Faction members are not aware of faction ownerships in barter
|
Bug #4293: Faction members are not aware of faction ownerships in barter
|
||||||
Bug #4307: World cleanup should remove dead bodies only if death animation is finished
|
Bug #4307: World cleanup should remove dead bodies only if death animation is finished
|
||||||
|
Bug #4311: OpenMW does not handle RootCollisionNode correctly
|
||||||
Bug #4327: Missing animations during spell/weapon stance switching
|
Bug #4327: Missing animations during spell/weapon stance switching
|
||||||
Bug #4358: Running animation is interrupted when magic mode is toggled
|
Bug #4358: Running animation is interrupted when magic mode is toggled
|
||||||
Bug #4368: Settings window ok button doesn't have key focus by default
|
Bug #4368: Settings window ok button doesn't have key focus by default
|
||||||
|
Bug #4378: On-self absorb spells restore stats
|
||||||
Bug #4393: NPCs walk back to where they were after using ResetActors
|
Bug #4393: NPCs walk back to where they were after using ResetActors
|
||||||
Bug #4416: Handle exception if we try to play non-music file
|
Bug #4416: Handle exception if we try to play non-music file
|
||||||
Bug #4419: MRK NiStringExtraData is handled incorrectly
|
Bug #4419: MRK NiStringExtraData is handled incorrectly
|
||||||
|
@ -62,17 +68,32 @@
|
||||||
Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory
|
Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory
|
||||||
Bug #4489: Goodbye doesn't block dialogue hyperlinks
|
Bug #4489: Goodbye doesn't block dialogue hyperlinks
|
||||||
Bug #4490: PositionCell on player gives "Error: tried to add local script twice"
|
Bug #4490: PositionCell on player gives "Error: tried to add local script twice"
|
||||||
Bug #4491: Training cap based off Base Skill instead of Modified Skill
|
Bug #4494: Training cap based off Base Skill instead of Modified Skill
|
||||||
Bug #4495: Crossbow animations blending is buggy
|
Bug #4495: Crossbow animations blending is buggy
|
||||||
Bug #4496: SpellTurnLeft and SpellTurnRight animation groups are unused
|
Bug #4496: SpellTurnLeft and SpellTurnRight animation groups are unused
|
||||||
Bug #4497: File names starting with x or X are not classified as animation
|
Bug #4497: File names starting with x or X are not classified as animation
|
||||||
Bug #4503: Cast and ExplodeSpell commands increase alteration skill
|
Bug #4503: Cast and ExplodeSpell commands increase alteration skill
|
||||||
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
|
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
|
||||||
Bug #4519: Knockdown does not discard movement in the 1st-person mode
|
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
|
||||||
|
Bug #4563: Fast travel price logic checks destination cell instead of service actor cell
|
||||||
|
Bug #4565: Underwater view distance should be limited
|
||||||
|
Bug #4573: Player uses headtracking in the 1st-person mode
|
||||||
|
Bug #4574: Player turning animations are twitchy
|
||||||
|
Bug #4575: Weird result of attack animation blending with movement animations
|
||||||
|
Bug #4576: Reset of idle animations when attack can not be started
|
||||||
Feature #2606: Editor: Implemented (optional) case sensitive global search
|
Feature #2606: Editor: Implemented (optional) case sensitive global search
|
||||||
Feature #3083: Play animation when NPC is casting spell via script
|
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 #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 #4222: 360° screenshots
|
||||||
Feature #4256: Implement ToggleBorders (TB) console command
|
Feature #4256: Implement ToggleBorders (TB) console command
|
||||||
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
|
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
|
||||||
|
@ -81,10 +102,13 @@
|
||||||
Feature #4444: Per-group KF-animation files support
|
Feature #4444: Per-group KF-animation files support
|
||||||
Feature #4466: Editor: Add option to ignore "Base" records when running verifier
|
Feature #4466: Editor: Add option to ignore "Base" records when running verifier
|
||||||
Feature #4488: Make water shader rougher during rain
|
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 #4509: Show count of enchanted items in stack in the spells list
|
||||||
Feature #4512: Editor: Use markers for lights and creatures levelled lists
|
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 #2490: Don't open command prompt window on Release-mode builds automatically
|
||||||
|
Task #4545: Enable is_pod string test
|
||||||
|
|
||||||
0.44.0
|
0.44.0
|
||||||
------
|
------
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace ESSImport
|
||||||
item.mId = contItem.mItem.toString();
|
item.mId = contItem.mItem.toString();
|
||||||
item.mCount = contItem.mCount;
|
item.mCount = contItem.mCount;
|
||||||
item.mRelativeEquipmentSlot = -1;
|
item.mRelativeEquipmentSlot = -1;
|
||||||
|
item.mLockLevel = 0;
|
||||||
|
|
||||||
unsigned int itemCount = std::abs(item.mCount);
|
unsigned int itemCount = std::abs(item.mCount);
|
||||||
bool separateStacks = false;
|
bool separateStacks = false;
|
||||||
|
|
|
@ -43,7 +43,6 @@ void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
|
||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
QString(tr("Text file (*.txt)")));
|
QString(tr("Text file (*.txt)")));
|
||||||
|
|
||||||
|
|
||||||
if (scriptFile.isEmpty())
|
if (scriptFile.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@ void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
|
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
|
||||||
|
runScriptAfterStartupField->setText(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launcher::AdvancedPage::loadSettings()
|
bool Launcher::AdvancedPage::loadSettings()
|
||||||
|
@ -74,6 +73,9 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
||||||
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
||||||
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
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
|
// Input Settings
|
||||||
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
||||||
|
@ -126,6 +128,9 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
||||||
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
||||||
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
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
|
// Input Settings
|
||||||
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
||||||
|
|
|
@ -90,19 +90,19 @@ void Launcher::MainDialog::createIcons()
|
||||||
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
||||||
graphicsButton->setIcon(QIcon::fromTheme("video-display"));
|
graphicsButton->setIcon(QIcon(":/images/preferences-video.png"));
|
||||||
graphicsButton->setText(tr("Graphics"));
|
graphicsButton->setText(tr("Graphics"));
|
||||||
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||||
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
|
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
|
||||||
settingsButton->setIcon(QIcon::fromTheme("preferences-system"));
|
settingsButton->setIcon(QIcon(":/images/preferences.png"));
|
||||||
settingsButton->setText(tr("Settings"));
|
settingsButton->setText(tr("Settings"));
|
||||||
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
|
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
|
||||||
advancedButton->setIcon(QIcon::fromTheme("emblem-system"));
|
advancedButton->setIcon(QIcon(":/images/preferences-advanced.png"));
|
||||||
advancedButton->setText(tr("Advanced"));
|
advancedButton->setText(tr("Advanced"));
|
||||||
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
|
@ -8,8 +8,9 @@
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
|
|
||||||
#include "model/doc/messages.hpp"
|
#include <components/misc/debugging.hpp>
|
||||||
|
|
||||||
|
#include "model/doc/messages.hpp"
|
||||||
#include "model/world/universalid.hpp"
|
#include "model/world/universalid.hpp"
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
|
@ -41,45 +42,43 @@ class Application : public QApplication
|
||||||
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
|
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int runApplication(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
try
|
// To allow background thread drawing in OSG
|
||||||
|
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
|
||||||
|
|
||||||
|
Q_INIT_RESOURCE (resources);
|
||||||
|
|
||||||
|
qRegisterMetaType<std::string> ("std::string");
|
||||||
|
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
|
||||||
|
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
|
||||||
|
|
||||||
|
Application application (argc, argv);
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
|
QDir::setCurrent(dir.absolutePath());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
||||||
|
|
||||||
|
CS::Editor editor(argc, argv);
|
||||||
|
|
||||||
|
if(!editor.makeIPCServer())
|
||||||
{
|
{
|
||||||
// To allow background thread drawing in OSG
|
editor.connectToIPCServer();
|
||||||
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
|
|
||||||
|
|
||||||
Q_INIT_RESOURCE (resources);
|
|
||||||
|
|
||||||
qRegisterMetaType<std::string> ("std::string");
|
|
||||||
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
|
|
||||||
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
|
|
||||||
|
|
||||||
Application application (argc, argv);
|
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
QDir dir(QCoreApplication::applicationDirPath());
|
|
||||||
QDir::setCurrent(dir.absolutePath());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
|
||||||
|
|
||||||
CS::Editor editor(argc, argv);
|
|
||||||
|
|
||||||
if(!editor.makeIPCServer())
|
|
||||||
{
|
|
||||||
editor.connectToIPCServer();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return editor.run();
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return editor.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return wrapApplication(&runApplication, argc, argv, "/openmw-cs.log");
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,6 +289,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
: mWindow(NULL)
|
: mWindow(NULL)
|
||||||
, mEncoding(ToUTF8::WINDOWS_1252)
|
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||||
, mEncoder(NULL)
|
, mEncoder(NULL)
|
||||||
|
, mScreenCaptureOperation(nullptr)
|
||||||
, mSkipMenu (false)
|
, mSkipMenu (false)
|
||||||
, mUseSound (true)
|
, mUseSound (true)
|
||||||
, mCompileAll (false)
|
, mCompileAll (false)
|
||||||
|
|
|
@ -5,12 +5,10 @@
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
#include <components/files/escape.hpp>
|
#include <components/files/escape.hpp>
|
||||||
#include <components/fallback/validate.hpp>
|
#include <components/fallback/validate.hpp>
|
||||||
|
#include <components/misc/debugging.hpp>
|
||||||
|
|
||||||
#include <SDL_messagebox.h>
|
|
||||||
#include "engine.hpp"
|
#include "engine.hpp"
|
||||||
|
|
||||||
#include <boost/filesystem/fstream.hpp>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
@ -331,143 +329,42 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN32) && defined(_DEBUG)
|
int runApplication(int argc, char *argv[])
|
||||||
|
|
||||||
class DebugOutput : public boost::iostreams::sink
|
|
||||||
{
|
{
|
||||||
public:
|
#ifdef __APPLE__
|
||||||
std::streamsize write(const char *str, std::streamsize size)
|
boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
|
||||||
{
|
boost::filesystem::current_path(binary_path.parent_path());
|
||||||
// Make a copy for null termination
|
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
||||||
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
|
#endif
|
||||||
|
|
||||||
|
Files::ConfigurationManager cfgMgr;
|
||||||
|
std::unique_ptr<OMW::Engine> engine;
|
||||||
|
engine.reset(new OMW::Engine(cfgMgr));
|
||||||
|
|
||||||
|
if (parseOptions(argc, argv, *engine, cfgMgr))
|
||||||
|
{
|
||||||
|
engine->go();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
extern "C" int SDL_main(int argc, char**argv)
|
extern "C" int SDL_main(int argc, char**argv)
|
||||||
#else
|
#else
|
||||||
int main(int argc, char**argv)
|
int main(int argc, char**argv)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
#if defined(__APPLE__)
|
/*
|
||||||
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
Start of tes3mp change (major)
|
||||||
#endif
|
|
||||||
|
|
||||||
// Some objects used to redirect cout and cerr
|
Instead of logging information in openmw.log, use a more descriptive filename
|
||||||
// Scope must be here, so this still works inside the catch block for logging exceptions
|
that includes a timestamp
|
||||||
std::streambuf* cout_rdbuf = std::cout.rdbuf ();
|
*/
|
||||||
std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();
|
return wrapApplication(&runApplication, argc, argv, "/tes3mp-client-" + Log::getFilenameTimestamp() + ".log");
|
||||||
|
/*
|
||||||
#if !(defined(_WIN32) && defined(_DEBUG))
|
End of tes3mp change (major)
|
||||||
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
|
|
||||||
/*
|
|
||||||
Start of tes3mp change (major)
|
|
||||||
|
|
||||||
Instead of logging information in openmw.log, use a more descriptive filename
|
|
||||||
that includes a timestamp
|
|
||||||
*/
|
|
||||||
// Redirect cout and cerr to tes3mp client log
|
|
||||||
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/tes3mp-client-" += Log::getFilenameTimestamp() += ".log"));
|
|
||||||
/*
|
|
||||||
End of tes3mp change (major)
|
|
||||||
*/
|
|
||||||
|
|
||||||
coutsb.open (Tee(logfile, oldcout));
|
|
||||||
cerrsb.open (Tee(logfile, oldcerr));
|
|
||||||
|
|
||||||
std::cout.rdbuf (&coutsb);
|
|
||||||
std::cerr.rdbuf (&cerrsb);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
Start of tes3mp addition
|
|
||||||
|
|
||||||
Initialize the logger added for multiplayer
|
|
||||||
*/
|
|
||||||
LOG_INIT(Log::LOG_INFO);
|
|
||||||
/*
|
|
||||||
End of tes3mp addition
|
|
||||||
*/
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Platform specific for Windows when there is no console built into the executable.
|
// Platform specific for Windows when there is no console built into the executable.
|
||||||
|
|
|
@ -86,8 +86,8 @@ namespace MWBase
|
||||||
virtual void persuade (int type, ResponseCallback* callback) = 0;
|
virtual void persuade (int type, ResponseCallback* callback) = 0;
|
||||||
virtual int getTemporaryDispositionChange () const = 0;
|
virtual int getTemporaryDispositionChange () const = 0;
|
||||||
|
|
||||||
/// @note This change is temporary and gets discarded when dialogue ends.
|
/// @note Controlled by an option, gets discarded when dialogue ends by default
|
||||||
virtual void applyDispositionChange (int delta) = 0;
|
virtual void applyBarterDispositionChange (int delta) = 0;
|
||||||
|
|
||||||
virtual int countSavedGameRecords() const = 0;
|
virtual int countSavedGameRecords() const = 0;
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void endGame() = 0;
|
virtual void endGame() = 0;
|
||||||
|
|
||||||
|
virtual void resumeGame() = 0;
|
||||||
|
|
||||||
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
|
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
|
||||||
|
|
||||||
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
|
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
|
||||||
|
|
|
@ -445,7 +445,7 @@ namespace MWBase
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
|
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) = 0;
|
||||||
///< cast a Ray and return true if there is an object in the ray path.
|
///< cast a Ray and return true if there is an object in the ray path.
|
||||||
|
|
||||||
virtual bool toggleCollisionMode() = 0;
|
virtual bool toggleCollisionMode() = 0;
|
||||||
|
|
|
@ -747,7 +747,7 @@ namespace MWClass
|
||||||
float Creature::getCapacity (const MWWorld::Ptr& ptr) const
|
float Creature::getCapacity (const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
||||||
return static_cast<float>(stats.getAttribute(0).getModified() * 5);
|
return static_cast<float>(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Creature::getServices(const MWWorld::ConstPtr &actor) const
|
int Creature::getServices(const MWWorld::ConstPtr &actor) const
|
||||||
|
|
|
@ -1250,7 +1250,7 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
||||||
static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEncumbranceStrMult")->getFloat();
|
static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEncumbranceStrMult")->getFloat();
|
||||||
return stats.getAttribute(0).getModified()*fEncumbranceStrMult;
|
return stats.getAttribute(ESM::Attribute::Strength).getModified()*fEncumbranceStrMult;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const
|
float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include <components/interpreter/interpreter.hpp>
|
#include <components/interpreter/interpreter.hpp>
|
||||||
#include <components/interpreter/defines.hpp>
|
#include <components/interpreter/defines.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
@ -559,9 +560,11 @@ namespace MWDialogue
|
||||||
return static_cast<int>(mTemporaryDispositionChange);
|
return static_cast<int>(mTemporaryDispositionChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueManager::applyDispositionChange(int delta)
|
void DialogueManager::applyBarterDispositionChange(int delta)
|
||||||
{
|
{
|
||||||
mTemporaryDispositionChange += delta;
|
mTemporaryDispositionChange += delta;
|
||||||
|
if (Settings::Manager::getBool("barter disposition change is permanent", "Game"))
|
||||||
|
mPermanentDispositionChange += delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DialogueManager::checkServiceRefused(ResponseCallback* callback)
|
bool DialogueManager::checkServiceRefused(ResponseCallback* callback)
|
||||||
|
|
|
@ -105,8 +105,8 @@ namespace MWDialogue
|
||||||
virtual void persuade (int type, ResponseCallback* callback);
|
virtual void persuade (int type, ResponseCallback* callback);
|
||||||
virtual int getTemporaryDispositionChange () const;
|
virtual int getTemporaryDispositionChange () const;
|
||||||
|
|
||||||
/// @note This change is temporary and gets discarded when dialogue ends.
|
/// @note Controlled by an option, gets discarded when dialogue ends by default
|
||||||
virtual void applyDispositionChange (int delta);
|
virtual void applyBarterDispositionChange (int delta);
|
||||||
|
|
||||||
virtual int countSavedGameRecords() const;
|
virtual int countSavedGameRecords() const;
|
||||||
|
|
||||||
|
|
|
@ -316,6 +316,14 @@ namespace MWGui
|
||||||
bool has_front_quote = false;
|
bool has_front_quote = false;
|
||||||
|
|
||||||
/* Does the input string contain things that don't have to be completed? If yes erase them. */
|
/* 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? */
|
/* Are there quotation marks? */
|
||||||
if( tmp.find('"') != std::string::npos ) {
|
if( tmp.find('"') != std::string::npos ) {
|
||||||
int numquotes=0;
|
int numquotes=0;
|
||||||
|
@ -352,6 +360,7 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Erase the input from the output string so we can easily append the completed form later. */
|
/* Erase the input from the output string so we can easily append the completed form later. */
|
||||||
output.erase(output.end()-tmp.length(), output.end());
|
output.erase(output.end()-tmp.length(), output.end());
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ namespace MWGui
|
||||||
|
|
||||||
void ContainerWindow::onItemSelected(int index)
|
void ContainerWindow::onItemSelected(int index)
|
||||||
{
|
{
|
||||||
if (mDragAndDrop->mIsOnDragAndDrop && mModel)
|
if (mDragAndDrop->mIsOnDragAndDrop)
|
||||||
{
|
{
|
||||||
dropItem();
|
dropItem();
|
||||||
return;
|
return;
|
||||||
|
@ -103,6 +103,9 @@ namespace MWGui
|
||||||
|
|
||||||
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
|
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
|
||||||
{
|
{
|
||||||
|
if (!mModel)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!onTakeItem(mModel->getItem(mSelectedItem), count))
|
if (!onTakeItem(mModel->getItem(mSelectedItem), count))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -148,6 +151,9 @@ namespace MWGui
|
||||||
|
|
||||||
void ContainerWindow::dropItem()
|
void ContainerWindow::dropItem()
|
||||||
{
|
{
|
||||||
|
if (!mModel)
|
||||||
|
return;
|
||||||
|
|
||||||
bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount);
|
bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -206,7 +212,7 @@ namespace MWGui
|
||||||
|
|
||||||
void ContainerWindow::onBackgroundSelected()
|
void ContainerWindow::onBackgroundSelected()
|
||||||
{
|
{
|
||||||
if (mDragAndDrop->mIsOnDragAndDrop && mModel)
|
if (mDragAndDrop->mIsOnDragAndDrop)
|
||||||
dropItem();
|
dropItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -362,52 +362,59 @@ namespace MWGui
|
||||||
if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice())
|
if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int separatorPos = 0;
|
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
for (unsigned int i=0; i<mTopicsList->getItemCount(); ++i)
|
|
||||||
{
|
|
||||||
if (mTopicsList->getItemNameAt(i) == "")
|
|
||||||
separatorPos = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id >= separatorPos)
|
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);
|
onTopicActivated(topic);
|
||||||
if (mGoodbyeButton->getEnabled())
|
if (mGoodbyeButton->getEnabled())
|
||||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);
|
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 =
|
if (topic == sBarter)
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);
|
||||||
|
else if (topic == sSpells)
|
||||||
if (topic == gmst.find("sPersuasion")->getString())
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr);
|
||||||
mPersuasionDialog.setVisible(true);
|
else if (topic == sTravel)
|
||||||
else if (topic == gmst.find("sCompanionShare")->getString())
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr);
|
||||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr);
|
else if (topic == sSpellMakingMenuTitle)
|
||||||
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(mCallback.get()))
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr);
|
||||||
{
|
else if (topic == sEnchanting)
|
||||||
if (topic == gmst.find("sBarter")->getString())
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr);
|
||||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);
|
else if (topic == sServiceTrainingTitle)
|
||||||
else if (topic == gmst.find("sSpells")->getString())
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr);
|
||||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr);
|
else if (topic == sRepair)
|
||||||
else if (topic == gmst.find("sTravel")->getString())
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
updateTopics();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::setPtr(const MWWorld::Ptr& actor)
|
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);
|
bool sameActor = (mPtr == actor);
|
||||||
if (!sameActor)
|
if (!sameActor)
|
||||||
{
|
{
|
||||||
|
|
|
@ -81,7 +81,12 @@ namespace MWGui
|
||||||
, mLastYSize(0)
|
, mLastYSize(0)
|
||||||
, mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer()))
|
, mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer()))
|
||||||
, mTrading(false)
|
, mTrading(false)
|
||||||
|
, mScaleFactor(1.0f)
|
||||||
{
|
{
|
||||||
|
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
||||||
|
if (uiScale > 1.0)
|
||||||
|
mScaleFactor = uiScale;
|
||||||
|
|
||||||
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
|
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
|
||||||
mPreview->rebuild();
|
mPreview->rebuild();
|
||||||
|
|
||||||
|
@ -445,10 +450,10 @@ namespace MWGui
|
||||||
MyGUI::IntSize size = mAvatarImage->getSize();
|
MyGUI::IntSize size = mAvatarImage->getSize();
|
||||||
int width = std::min(mPreview->getTextureWidth(), size.width);
|
int width = std::min(mPreview->getTextureWidth(), size.width);
|
||||||
int height = std::min(mPreview->getTextureHeight(), size.height);
|
int height = std::min(mPreview->getTextureHeight(), size.height);
|
||||||
mPreview->setViewport(width, height);
|
mPreview->setViewport(int(width*mScaleFactor), int(height*mScaleFactor));
|
||||||
|
|
||||||
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f,
|
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f,
|
||||||
width/float(mPreview->getTextureWidth()), height/float(mPreview->getTextureHeight())));
|
width*mScaleFactor/float(mPreview->getTextureWidth()), height*mScaleFactor/float(mPreview->getTextureHeight())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender)
|
void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender)
|
||||||
|
@ -615,6 +620,11 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
// convert to OpenGL lower-left origin
|
// convert to OpenGL lower-left origin
|
||||||
y = (mAvatarImage->getHeight()-1) - y;
|
y = (mAvatarImage->getHeight()-1) - y;
|
||||||
|
|
||||||
|
// Scale coordinates
|
||||||
|
x = int(x*mScaleFactor);
|
||||||
|
y = int(y*mScaleFactor);
|
||||||
|
|
||||||
int slot = mPreview->getSlotSelected (x, y);
|
int slot = mPreview->getSlotSelected (x, y);
|
||||||
|
|
||||||
if (slot == -1)
|
if (slot == -1)
|
||||||
|
|
|
@ -101,6 +101,7 @@ namespace MWGui
|
||||||
std::unique_ptr<MWRender::InventoryPreview> mPreview;
|
std::unique_ptr<MWRender::InventoryPreview> mPreview;
|
||||||
|
|
||||||
bool mTrading;
|
bool mTrading;
|
||||||
|
float mScaleFactor;
|
||||||
|
|
||||||
void onItemSelected(int index);
|
void onItemSelected(int index);
|
||||||
void onItemSelectedFromSourceModel(int index);
|
void onItemSelectedFromSourceModel(int index);
|
||||||
|
|
|
@ -37,6 +37,7 @@ namespace MWGui
|
||||||
, mLastRenderTime(0.0)
|
, mLastRenderTime(0.0)
|
||||||
, mLoadingOnTime(0.0)
|
, mLoadingOnTime(0.0)
|
||||||
, mImportantLabel(false)
|
, mImportantLabel(false)
|
||||||
|
, mVisible(false)
|
||||||
, mProgress(0)
|
, mProgress(0)
|
||||||
, mShowWallpaper(true)
|
, mShowWallpaper(true)
|
||||||
{
|
{
|
||||||
|
@ -169,12 +170,18 @@ namespace MWGui
|
||||||
// We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
|
// We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
|
||||||
mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
|
mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
|
||||||
|
|
||||||
|
mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState()
|
||||||
|
== MWBase::StateManager::State_NoGame);
|
||||||
|
|
||||||
|
if (!visible)
|
||||||
|
{
|
||||||
|
draw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mVisible = visible;
|
mVisible = visible;
|
||||||
mLoadingBox->setVisible(mVisible);
|
mLoadingBox->setVisible(mVisible);
|
||||||
|
|
||||||
mShowWallpaper = mVisible && (MWBase::Environment::get().getStateManager()->getState()
|
|
||||||
== MWBase::StateManager::State_NoGame);
|
|
||||||
|
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
|
||||||
if (mShowWallpaper)
|
if (mShowWallpaper)
|
||||||
|
@ -183,9 +190,6 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading);
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading);
|
||||||
|
|
||||||
if (!mVisible)
|
|
||||||
draw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadingScreen::loadingOff()
|
void LoadingScreen::loadingOff()
|
||||||
|
|
|
@ -201,6 +201,12 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(index != -1);
|
assert(index != -1);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
mSelected = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mSelected = &mKey[index];
|
mSelected = &mKey[index];
|
||||||
|
|
||||||
// prevent reallocation of zero key from Type_HandToHand
|
// prevent reallocation of zero key from Type_HandToHand
|
||||||
|
@ -398,15 +404,19 @@ namespace MWGui
|
||||||
|
|
||||||
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
|
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
|
||||||
|
|
||||||
if (isReturnNeeded)
|
if (isReturnNeeded && key->type != Type_Item)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
else if (isDelayNeeded)
|
else if (isDelayNeeded && key->type != Type_Item)
|
||||||
|
{
|
||||||
mActivated = key;
|
mActivated = key;
|
||||||
|
return;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
mActivated = nullptr;
|
mActivated = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (key->type == Type_Item || key->type == Type_MagicItem)
|
if (key->type == Type_Item || key->type == Type_MagicItem)
|
||||||
{
|
{
|
||||||
|
@ -444,6 +454,11 @@ namespace MWGui
|
||||||
|
|
||||||
// delay weapon switching if player is busy
|
// delay weapon switching if player is busy
|
||||||
if (isDelayNeeded && (isWeapon || isTool))
|
if (isDelayNeeded && (isWeapon || isTool))
|
||||||
|
{
|
||||||
|
mActivated = key;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (isReturnNeeded && (isWeapon || isTool))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace MWGui
|
||||||
|
|
||||||
Spell()
|
Spell()
|
||||||
: mType(Type_Spell)
|
: mType(Type_Spell)
|
||||||
|
, mCount(0)
|
||||||
, mSelected(false)
|
, mSelected(false)
|
||||||
, mActive(false)
|
, mActive(false)
|
||||||
{
|
{
|
||||||
|
|
|
@ -345,7 +345,7 @@ namespace MWGui
|
||||||
? gmst.find("iBarterSuccessDisposition")->getInt()
|
? gmst.find("iBarterSuccessDisposition")->getInt()
|
||||||
: gmst.find("iBarterFailDisposition")->getInt();
|
: gmst.find("iBarterFailDisposition")->getInt();
|
||||||
|
|
||||||
MWBase::Environment::get().getDialogueManager()->applyDispositionChange(dispositionDelta);
|
MWBase::Environment::get().getDialogueManager()->applyBarterDispositionChange(dispositionDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
// display message on haggle failure
|
// display message on haggle failure
|
||||||
|
|
|
@ -154,7 +154,7 @@ namespace MWGui
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats (mPtr);
|
MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats (mPtr);
|
||||||
if (npcStats.getSkill (skillId).getBase () <= pcStats.getSkill (skillId).getBase ())
|
if (npcStats.getSkill (skillId).getModified () <= pcStats.getSkill (skillId).getBase ())
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}");
|
MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace MWGui
|
||||||
mSelect->getHeight());
|
mSelect->getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior)
|
void TravelWindow::addDestination(const std::string& name, ESM::Position pos, bool interior)
|
||||||
{
|
{
|
||||||
int price;
|
int price;
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ namespace MWGui
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
||||||
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);
|
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);
|
||||||
|
|
||||||
if(interior)
|
if (!mPtr.getCell()->isExterior())
|
||||||
{
|
{
|
||||||
price = gmst.find("fMagesGuildTravel")->getInt();
|
price = gmst.find("fMagesGuildTravel")->getInt();
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ namespace MWGui
|
||||||
ESM::Position pos = *_sender->getUserData<ESM::Position>();
|
ESM::Position pos = *_sender->getUserData<ESM::Position>();
|
||||||
std::string cellname = _sender->getUserString("Destination");
|
std::string cellname = _sender->getUserString("Destination");
|
||||||
bool interior = _sender->getUserString("interior") == "y";
|
bool interior = _sender->getUserString("interior") == "y";
|
||||||
if (!interior)
|
if (mPtr.getCell()->isExterior())
|
||||||
{
|
{
|
||||||
ESM::Position playerPos = player.getRefData().getPosition();
|
ESM::Position playerPos = player.getRefData().getPosition();
|
||||||
float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length();
|
float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length();
|
||||||
|
|
|
@ -941,7 +941,8 @@ namespace MWGui
|
||||||
|
|
||||||
mKeyboardNavigation->onFrame();
|
mKeyboardNavigation->onFrame();
|
||||||
|
|
||||||
mMessageBoxManager->onFrame(frameDuration);
|
if (mMessageBoxManager)
|
||||||
|
mMessageBoxManager->onFrame(frameDuration);
|
||||||
|
|
||||||
mToolTips->onFrame(frameDuration);
|
mToolTips->onFrame(frameDuration);
|
||||||
|
|
||||||
|
|
|
@ -805,6 +805,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||||
{
|
{
|
||||||
|
bool actorKilled = false;
|
||||||
|
|
||||||
const ActiveSpells::ActiveSpellParams& spell = it->second;
|
const ActiveSpells::ActiveSpellParams& spell = it->second;
|
||||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
|
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
|
||||||
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
|
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
|
||||||
|
@ -832,10 +834,14 @@ namespace MWMechanics
|
||||||
caster.getClass().getNpcStats(caster).addWerewolfKill();
|
caster.getClass().getNpcStats(caster).addWerewolfKill();
|
||||||
|
|
||||||
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);
|
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);
|
||||||
|
actorKilled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actorKilled)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1377,6 +1383,8 @@ namespace MWMechanics
|
||||||
// AI and magic effects update
|
// AI and magic effects update
|
||||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||||
{
|
{
|
||||||
|
bool isPlayer = iter->first == player;
|
||||||
|
|
||||||
float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2();
|
float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2();
|
||||||
// AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this
|
// AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this
|
||||||
// (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not)
|
// (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not)
|
||||||
|
@ -1390,7 +1398,7 @@ namespace MWMechanics
|
||||||
Instead of merely updating the player character's mAttackingOrSpell here,
|
Instead of merely updating the player character's mAttackingOrSpell here,
|
||||||
prepare an Attack packet for the LocalPlayer
|
prepare an Attack packet for the LocalPlayer
|
||||||
*/
|
*/
|
||||||
if (iter->first == player)
|
if (isPlayer)
|
||||||
{
|
{
|
||||||
bool state = MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell();
|
bool state = MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell();
|
||||||
DrawState_ dstate = player.getClass().getNpcStats(player).getDrawState();
|
DrawState_ dstate = player.getClass().getNpcStats(player).getDrawState();
|
||||||
|
@ -1478,12 +1486,12 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
if (timerUpdateAITargets == 0 && (isLocalActor || isAIActive))
|
if (timerUpdateAITargets == 0 && (isLocalActor || isAIActive))
|
||||||
{
|
{
|
||||||
if (iter->first != player)
|
if (!isPlayer)
|
||||||
adjustCommandedActor(iter->first);
|
adjustCommandedActor(iter->first);
|
||||||
|
|
||||||
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
||||||
{
|
{
|
||||||
if (it->first == iter->first || iter->first == player) // player is not AI-controlled
|
if (it->first == iter->first || isPlayer) // player is not AI-controlled
|
||||||
continue;
|
continue;
|
||||||
engageCombat(iter->first, it->first, cachedAllies, it->first == player);
|
engageCombat(iter->first, it->first, cachedAllies, it->first == player);
|
||||||
}
|
}
|
||||||
|
@ -1494,12 +1502,15 @@ namespace MWMechanics
|
||||||
MWWorld::Ptr headTrackTarget;
|
MWWorld::Ptr headTrackTarget;
|
||||||
|
|
||||||
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||||
|
bool firstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson();
|
||||||
|
|
||||||
// Unconsious actor can not track target
|
// 1. Unconsious actor can not track target
|
||||||
// Also actors in combat and pursue mode do not bother to headtrack
|
// 2. Actors in combat and pursue mode do not bother to headtrack
|
||||||
|
// 3. Player character does not use headtracking in the 1st-person view
|
||||||
if (!stats.getKnockedDown() &&
|
if (!stats.getKnockedDown() &&
|
||||||
!stats.getAiSequence().isInCombat() &&
|
!stats.getAiSequence().isInCombat() &&
|
||||||
!stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue))
|
!stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) &&
|
||||||
|
!firstPersonPlayer)
|
||||||
{
|
{
|
||||||
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
||||||
{
|
{
|
||||||
|
|
|
@ -494,10 +494,13 @@ namespace MWMechanics
|
||||||
|
|
||||||
static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt();
|
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();
|
if (enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack)
|
||||||
rating = iWereWolfFleeMod;
|
{
|
||||||
|
static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt();
|
||||||
|
rating = iWereWolfFleeMod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rating != 0.0f)
|
if (rating != 0.0f)
|
||||||
|
|
|
@ -53,7 +53,23 @@ namespace MWMechanics
|
||||||
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), pos.asVec3()))
|
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), pos.asVec3()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (pathTo(actor, ESM::Pathgrid::Point(static_cast<int>(mX), static_cast<int>(mY), static_cast<int>(mZ)), duration))
|
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
|
||||||
|
// If we got close to target, check for actors nearby. If they are, finish AI package.
|
||||||
|
int destinationTolerance = 64;
|
||||||
|
ESM::Pathgrid::Point dest(static_cast<int>(mX), static_cast<int>(mY), static_cast<int>(mZ));
|
||||||
|
if (distance(pos.pos, dest) <= destinationTolerance)
|
||||||
|
{
|
||||||
|
std::vector<MWWorld::Ptr> targetActors;
|
||||||
|
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors);
|
||||||
|
|
||||||
|
if (!result.first.isEmpty())
|
||||||
|
{
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathTo(actor, dest, duration))
|
||||||
{
|
{
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1272,8 +1272,10 @@ bool CharacterController::updateWeaponState()
|
||||||
mWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr();
|
mWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply 1st-person weapon animations only for upper body
|
||||||
MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon);
|
MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon);
|
||||||
priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
if (mPtr != MWMechanics::getPlayer() || !MWBase::Environment::get().getWorld()->isFirstPerson())
|
||||||
|
priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
||||||
|
|
||||||
bool forcestateupdate = false;
|
bool forcestateupdate = false;
|
||||||
|
|
||||||
|
@ -1281,11 +1283,11 @@ bool CharacterController::updateWeaponState()
|
||||||
bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell &&
|
bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell &&
|
||||||
mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell;
|
mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell;
|
||||||
|
|
||||||
if(weaptype != mWeaponType && !isKnockedOut() &&
|
if(!isKnockedOut() && !isKnockedDown() && !isRecovery())
|
||||||
!isKnockedDown() && !isRecovery())
|
|
||||||
{
|
{
|
||||||
std::string weapgroup;
|
std::string weapgroup;
|
||||||
if ((!isWerewolf || mWeaponType != WeapType_Spell)
|
if ((!isWerewolf || mWeaponType != WeapType_Spell)
|
||||||
|
&& weaptype != mWeaponType
|
||||||
&& mUpperBodyState != UpperCharState_UnEquipingWeap
|
&& mUpperBodyState != UpperCharState_UnEquipingWeap
|
||||||
&& !isStillWeapon)
|
&& !isStillWeapon)
|
||||||
{
|
{
|
||||||
|
@ -1310,49 +1312,60 @@ bool CharacterController::updateWeaponState()
|
||||||
|
|
||||||
float complete;
|
float complete;
|
||||||
bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
||||||
|
|
||||||
if (!animPlaying || complete >= 1.0f)
|
if (!animPlaying || complete >= 1.0f)
|
||||||
{
|
{
|
||||||
forcestateupdate = true;
|
// Weapon is changed, no current animation (e.g. unequipping or attack).
|
||||||
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
// Start equipping animation now.
|
||||||
|
if (weaptype != mWeaponType)
|
||||||
getWeaponGroup(weaptype, weapgroup);
|
|
||||||
mAnimation->setWeaponGroup(weapgroup);
|
|
||||||
|
|
||||||
if (!isStillWeapon)
|
|
||||||
{
|
{
|
||||||
if (weaptype == WeapType_None)
|
forcestateupdate = true;
|
||||||
|
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
||||||
|
|
||||||
|
getWeaponGroup(weaptype, weapgroup);
|
||||||
|
mAnimation->setWeaponGroup(weapgroup);
|
||||||
|
|
||||||
|
if (!isStillWeapon)
|
||||||
{
|
{
|
||||||
// Disable current weapon animation manually
|
|
||||||
mAnimation->disable(mCurrentWeapon);
|
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
|
|
||||||
{
|
|
||||||
mAnimation->showWeapons(false);
|
|
||||||
mAnimation->play(weapgroup, priorityWeapon,
|
|
||||||
MWRender::Animation::BlendMask_All, true,
|
|
||||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
|
||||||
mUpperBodyState = UpperCharState_EquipingWeap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isWerewolf)
|
if(isWerewolf)
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfEquip");
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfEquip");
|
||||||
if(sound)
|
if(sound)
|
||||||
|
{
|
||||||
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
|
sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mWeaponType = weaptype;
|
||||||
|
getWeaponGroup(mWeaponType, mCurrentWeapon);
|
||||||
|
|
||||||
|
if(!upSoundId.empty() && !isStillWeapon)
|
||||||
{
|
{
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
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;
|
// Make sure that we disabled unequipping animation
|
||||||
getWeaponGroup(mWeaponType, mCurrentWeapon);
|
if (mUpperBodyState == UpperCharState_UnEquipingWeap)
|
||||||
|
|
||||||
if(!upSoundId.empty() && !isStillWeapon)
|
|
||||||
{
|
{
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
mUpperBodyState = UpperCharState_Nothing;
|
||||||
sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
|
mWeaponType = WeapType_None;
|
||||||
|
getWeaponGroup(mWeaponType, mCurrentWeapon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1407,14 +1420,7 @@ bool CharacterController::updateWeaponState()
|
||||||
{
|
{
|
||||||
MWWorld::Ptr player = getPlayer();
|
MWWorld::Ptr player = getPlayer();
|
||||||
|
|
||||||
// We should reset player's idle animation in the first-person mode.
|
bool resetIdle = ammunition;
|
||||||
if (mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson())
|
|
||||||
mIdleState = CharState_None;
|
|
||||||
|
|
||||||
// In other cases we should not break swim and sneak animations
|
|
||||||
if (mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
|
|
||||||
mIdleState = CharState_None;
|
|
||||||
|
|
||||||
if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block))
|
if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block))
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
|
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
|
||||||
|
@ -1498,6 +1504,11 @@ bool CharacterController::updateWeaponState()
|
||||||
0.0f, 0);
|
0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_CastingSpell;
|
mUpperBodyState = UpperCharState_CastingSpell;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resetIdle = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (mPtr.getClass().hasInventoryStore(mPtr))
|
if (mPtr.getClass().hasInventoryStore(mPtr))
|
||||||
{
|
{
|
||||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
||||||
|
@ -1578,6 +1589,14 @@ bool CharacterController::updateWeaponState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should reset player's idle animation in the first-person mode.
|
||||||
|
if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson())
|
||||||
|
mIdleState = CharState_None;
|
||||||
|
|
||||||
|
// In other cases we should not break swim and sneak animations
|
||||||
|
if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
|
||||||
|
mIdleState = CharState_None;
|
||||||
|
|
||||||
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
||||||
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
|
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
|
||||||
mAttackStrength = complete;
|
mAttackStrength = complete;
|
||||||
|
@ -1741,18 +1760,22 @@ bool CharacterController::updateWeaponState()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: apply reload animations only for upper body since blending with movement animations can give weird result.
|
// Note: apply crossbow reload animation only for upper body
|
||||||
// Especially noticable with crossbow reload animation.
|
// since blending with movement animations can give weird result.
|
||||||
if(!start.empty())
|
if(!start.empty())
|
||||||
{
|
{
|
||||||
|
int mask = MWRender::Animation::BlendMask_All;
|
||||||
|
if (mWeaponType == WeapType_Crossbow)
|
||||||
|
mask = MWRender::Animation::BlendMask_UpperBody;
|
||||||
|
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::BlendMask_UpperBody, true,
|
mask, true,
|
||||||
weapSpeed, start, stop, 0.0f, 0);
|
weapSpeed, start, stop, 0.0f, 0);
|
||||||
else
|
else
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::BlendMask_UpperBody, false,
|
mask, false,
|
||||||
weapSpeed, start, stop, 0.0f, 0);
|
weapSpeed, start, stop, 0.0f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2091,25 +2114,37 @@ void CharacterController::update(float duration)
|
||||||
else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()))
|
else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()))
|
||||||
{
|
{
|
||||||
if(rot.z() > 0.0f)
|
if(rot.z() > 0.0f)
|
||||||
{
|
|
||||||
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
|
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
|
||||||
mAnimation->disable(mCurrentJump);
|
|
||||||
}
|
|
||||||
else if(rot.z() < 0.0f)
|
else if(rot.z() < 0.0f)
|
||||||
{
|
|
||||||
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
|
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
|
||||||
mAnimation->disable(mCurrentJump);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mTurnAnimationThreshold -= duration;
|
// Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering
|
||||||
if (isTurning())
|
if (mPtr == getPlayer())
|
||||||
mTurnAnimationThreshold = 0.05f;
|
|
||||||
else if (movestate == CharState_None && isTurning()
|
|
||||||
&& mTurnAnimationThreshold > 0)
|
|
||||||
{
|
{
|
||||||
movestate = mMovementState;
|
float threshold = mCurrentMovement.find("swim") == std::string::npos ? 0.4f : 0.8f;
|
||||||
|
float complete;
|
||||||
|
bool animPlaying = mAnimation->getInfo(mCurrentMovement, &complete);
|
||||||
|
if (movestate == CharState_None && isTurning())
|
||||||
|
{
|
||||||
|
if (animPlaying && complete < threshold)
|
||||||
|
movestate = mMovementState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mTurnAnimationThreshold -= duration;
|
||||||
|
if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft ||
|
||||||
|
movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft)
|
||||||
|
{
|
||||||
|
mTurnAnimationThreshold = 0.05f;
|
||||||
|
}
|
||||||
|
else if (movestate == CharState_None && isTurning()
|
||||||
|
&& mTurnAnimationThreshold > 0)
|
||||||
|
{
|
||||||
|
movestate = mMovementState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(movestate != CharState_None && !isTurning())
|
if(movestate != CharState_None && !isTurning())
|
||||||
|
@ -2140,8 +2175,10 @@ void CharacterController::update(float duration)
|
||||||
|
|
||||||
if (isTurning())
|
if (isTurning())
|
||||||
{
|
{
|
||||||
|
// Adjust animation speed from 1.0 to 1.5 multiplier
|
||||||
|
float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast<float>(osg::PI));
|
||||||
if (duration > 0)
|
if (duration > 0)
|
||||||
mAnimation->adjustSpeedMult(mCurrentMovement, std::min(1.5f, std::abs(rot.z()) / duration / static_cast<float>(osg::PI)));
|
mAnimation->adjustSpeedMult(mCurrentMovement, std::max(turnSpeed, 1.0f));
|
||||||
}
|
}
|
||||||
else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed)
|
else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed)
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,8 +31,10 @@ enum Priority {
|
||||||
Priority_WeaponLowerBody,
|
Priority_WeaponLowerBody,
|
||||||
Priority_SneakIdleLowerBody,
|
Priority_SneakIdleLowerBody,
|
||||||
Priority_SwimIdle,
|
Priority_SwimIdle,
|
||||||
Priority_Jump,
|
|
||||||
Priority_Movement,
|
Priority_Movement,
|
||||||
|
// Note: in vanilla movement anims have higher priority than jump ones.
|
||||||
|
// It causes issues with landing animations during movement.
|
||||||
|
Priority_Jump,
|
||||||
Priority_Hit,
|
Priority_Hit,
|
||||||
Priority_Weapon,
|
Priority_Weapon,
|
||||||
Priority_Block,
|
Priority_Block,
|
||||||
|
|
|
@ -282,8 +282,21 @@ namespace MWMechanics
|
||||||
|
|
||||||
adjustWeaponDamage(damage, weapon, attacker);
|
adjustWeaponDamage(damage, weapon, attacker);
|
||||||
|
|
||||||
if(attacker == getPlayer())
|
if (attacker == getPlayer())
|
||||||
|
{
|
||||||
attacker.getClass().skillUsageSucceeded(attacker, weaponSkill, 0);
|
attacker.getClass().skillUsageSucceeded(attacker, weaponSkill, 0);
|
||||||
|
const MWMechanics::AiSequence& sequence = victim.getClass().getCreatureStats(victim).getAiSequence();
|
||||||
|
|
||||||
|
bool unaware = !sequence.isInCombat()
|
||||||
|
&& !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim);
|
||||||
|
|
||||||
|
if (unaware)
|
||||||
|
{
|
||||||
|
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
|
||||||
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
|
||||||
|
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (victim.getClass().getCreatureStats(victim).getKnockedDown())
|
if (victim.getClass().getCreatureStats(victim).getKnockedDown())
|
||||||
damage *= gmst.find("fCombatKODamageMult")->getFloat();
|
damage *= gmst.find("fCombatKODamageMult")->getFloat();
|
||||||
|
|
|
@ -23,8 +23,10 @@ float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr& player = MWMechanics::getPlayer();
|
const MWWorld::Ptr& player = MWMechanics::getPlayer();
|
||||||
|
|
||||||
// [-100, 100]
|
// [-500, 500]
|
||||||
int difficultySetting = Settings::Manager::getInt("difficulty", "Game");
|
int difficultySetting = Settings::Manager::getInt("difficulty", "Game");
|
||||||
|
difficultySetting = std::min(difficultySetting, 500);
|
||||||
|
difficultySetting = std::max(difficultySetting, -500);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp change (major)
|
Start of tes3mp change (major)
|
||||||
|
|
|
@ -231,6 +231,28 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
mPath = pathgridGraph.aStarSearch(startNode, endNode.first);
|
mPath = pathgridGraph.aStarSearch(startNode, endNode.first);
|
||||||
|
|
||||||
|
// If nearest path node is in opposite direction from second, remove it from path.
|
||||||
|
// Especially useful for wandering actors, if the nearest node is blocked for some reason.
|
||||||
|
if (mPath.size() > 1)
|
||||||
|
{
|
||||||
|
ESM::Pathgrid::Point secondNode = *(++mPath.begin());
|
||||||
|
osg::Vec3f firstNodeVec3f = MakeOsgVec3(mPathgrid->mPoints[startNode]);
|
||||||
|
osg::Vec3f secondNodeVec3f = MakeOsgVec3(secondNode);
|
||||||
|
osg::Vec3f toSecondNodeVec3f = secondNodeVec3f - firstNodeVec3f;
|
||||||
|
osg::Vec3f toStartPointVec3f = startPointInLocalCoords - firstNodeVec3f;
|
||||||
|
if (toSecondNodeVec3f * toStartPointVec3f > 0)
|
||||||
|
{
|
||||||
|
ESM::Pathgrid::Point temp(secondNode);
|
||||||
|
converter.toWorld(temp);
|
||||||
|
// Add Z offset since path node can overlap with other objects.
|
||||||
|
// Also ignore doors in raytesting.
|
||||||
|
bool isPathClear = !MWBase::Environment::get().getWorld()->castRay(
|
||||||
|
startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, true);
|
||||||
|
if (isPathClear)
|
||||||
|
mPath.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convert supplied path to world coordinates
|
// convert supplied path to world coordinates
|
||||||
for (std::list<ESM::Pathgrid::Point>::iterator iter(mPath.begin()); iter != mPath.end(); ++iter)
|
for (std::list<ESM::Pathgrid::Point>::iterator iter(mPath.begin()); iter != mPath.end(); ++iter)
|
||||||
{
|
{
|
||||||
|
|
|
@ -332,14 +332,14 @@ namespace MWMechanics
|
||||||
return true;
|
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)
|
: mCaster(caster)
|
||||||
, mTarget(target)
|
, mTarget(target)
|
||||||
, mStack(false)
|
, mStack(false)
|
||||||
, mHitPosition(0,0,0)
|
, mHitPosition(0,0,0)
|
||||||
, mAlwaysSucceed(false)
|
, mAlwaysSucceed(false)
|
||||||
, mFromProjectile(fromProjectile)
|
, mFromProjectile(fromProjectile)
|
||||||
, mIsScripted(isScripted)
|
, mManualSpell(manualSpell)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,9 +437,9 @@ namespace MWMechanics
|
||||||
if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer))
|
if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// caster needs to be an actor for linked effects (e.g. Absorb)
|
// caster needs to be an actor that's not the target for linked effects (e.g. Absorb)
|
||||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::CasterLinked
|
if (magicEffect->mData.mFlags & ESM::MagicEffect::CasterLinked
|
||||||
&& (caster.isEmpty() || !caster.getClass().isActor()))
|
&& (caster.isEmpty() || !caster.getClass().isActor() || caster == target))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If player is healing someone, show the target's HP bar
|
// If player is healing someone, show the target's HP bar
|
||||||
|
@ -595,7 +595,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
// For absorb effects, also apply the effect to the caster - but with a negative
|
// For absorb effects, also apply the effect to the caster - but with a negative
|
||||||
// magnitude, since we're transferring stats from the target to the caster
|
// magnitude, since we're transferring stats from the target to the caster
|
||||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
if (!caster.isEmpty() && caster != target && caster.getClass().isActor())
|
||||||
{
|
{
|
||||||
for (int i=0; i<5; ++i)
|
for (int i=0; i<5; ++i)
|
||||||
{
|
{
|
||||||
|
@ -983,7 +983,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
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);
|
school = getSpellSchool(spell, mCaster);
|
||||||
|
|
||||||
|
@ -1173,7 +1173,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool CastSpell::spellIncreasesSkill()
|
bool CastSpell::spellIncreasesSkill()
|
||||||
{
|
{
|
||||||
if (mIsScripted)
|
if (mManualSpell)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return MWMechanics::spellIncreasesSkill(mId);
|
return MWMechanics::spellIncreasesSkill(mId);
|
||||||
|
|
|
@ -88,10 +88,10 @@ namespace MWMechanics
|
||||||
osg::Vec3f mHitPosition; // Used for spawning area orb
|
osg::Vec3f mHitPosition; // Used for spawning area orb
|
||||||
bool mAlwaysSucceed; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false)
|
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 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:
|
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);
|
bool cast (const ESM::Spell* spell);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace MWMechanics
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
float rating=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)
|
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))
|
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
bonus+=1.5f;
|
if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy))
|
||||||
|
rangedMult = 1.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
|
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)
|
if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown)
|
||||||
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
float meleeDamage = 0.f;
|
||||||
|
|
||||||
for (int i=0; i<2; ++i)
|
for (int i=0; i<2; ++i)
|
||||||
{
|
{
|
||||||
rating += weapon->mData.mSlash[i];
|
meleeDamage += weapon->mData.mSlash[i];
|
||||||
rating += weapon->mData.mThrust[i];
|
meleeDamage += weapon->mData.mThrust[i];
|
||||||
rating += weapon->mData.mChop[i];
|
meleeDamage += weapon->mData.mChop[i];
|
||||||
}
|
}
|
||||||
rating /= 6.f;
|
|
||||||
|
MWMechanics::adjustWeaponDamage(meleeDamage, item, actor);
|
||||||
|
rating = meleeDamage / 6.f;
|
||||||
|
|
||||||
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +79,6 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
if (item.getClass().getItemHealth(item) == 0)
|
if (item.getClass().getItemHealth(item) == 0)
|
||||||
return 0.f;
|
return 0.f;
|
||||||
rating *= item.getClass().getItemHealth(item) / float(item.getClass().getItemMaxHealth(item));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
|
if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
|
||||||
|
@ -103,13 +110,12 @@ namespace MWMechanics
|
||||||
|
|
||||||
int skill = item.getClass().getEquipmentSkill(item);
|
int skill = item.getClass().getEquipmentSkill(item);
|
||||||
if (skill != -1)
|
if (skill != -1)
|
||||||
rating *= actor.getClass().getSkill(actor, skill) / 100.f;
|
{
|
||||||
|
int value = actor.getClass().getSkill(actor, skill);
|
||||||
|
rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f;
|
||||||
|
}
|
||||||
|
|
||||||
// There is no need to apply bonus if weapon rating == 0
|
return rating * rangedMult;
|
||||||
if (rating == 0.f)
|
|
||||||
return 0.f;
|
|
||||||
|
|
||||||
return rating + bonus;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType)
|
||||||
|
|
|
@ -626,7 +626,6 @@ namespace MWPhysics
|
||||||
assert (mShapeInstance->getCollisionShape()->isCompound());
|
assert (mShapeInstance->getCollisionShape()->isCompound());
|
||||||
|
|
||||||
btCompoundShape* compound = static_cast<btCompoundShape*>(mShapeInstance->getCollisionShape());
|
btCompoundShape* compound = static_cast<btCompoundShape*>(mShapeInstance->getCollisionShape());
|
||||||
|
|
||||||
for (std::map<int, int>::const_iterator it = mShapeInstance->mAnimatedShapes.begin(); it != mShapeInstance->mAnimatedShapes.end(); ++it)
|
for (std::map<int, int>::const_iterator it = mShapeInstance->mAnimatedShapes.begin(); it != mShapeInstance->mAnimatedShapes.end(); ++it)
|
||||||
{
|
{
|
||||||
int recIndex = it->first;
|
int recIndex = it->first;
|
||||||
|
@ -640,6 +639,9 @@ namespace MWPhysics
|
||||||
if (!visitor.mFound)
|
if (!visitor.mFound)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: animateCollisionShapes can't find node " << recIndex << " for " << mPtr.getCellRef().getRefId() << std::endl;
|
std::cerr << "Error: animateCollisionShapes can't find node " << recIndex << " for " << mPtr.getCellRef().getRefId() << std::endl;
|
||||||
|
|
||||||
|
// Remove nonexistent nodes from animated shapes map and early out
|
||||||
|
mShapeInstance->mAnimatedShapes.erase(recIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
osg::NodePath nodePath = visitor.mFoundPath;
|
osg::NodePath nodePath = visitor.mFoundPath;
|
||||||
|
|
|
@ -203,6 +203,7 @@ namespace MWRender
|
||||||
, mNightEyeFactor(0.f)
|
, mNightEyeFactor(0.f)
|
||||||
, mDistantFog(false)
|
, mDistantFog(false)
|
||||||
, mDistantTerrain(false)
|
, mDistantTerrain(false)
|
||||||
|
, mFieldOfViewOverridden(false)
|
||||||
, mFieldOfViewOverride(0.f)
|
, mFieldOfViewOverride(0.f)
|
||||||
, mBorders(false)
|
, mBorders(false)
|
||||||
{
|
{
|
||||||
|
@ -553,8 +554,8 @@ namespace MWRender
|
||||||
mLandFogStart = mViewDistance * (1 - fogDepth);
|
mLandFogStart = mViewDistance * (1 - fogDepth);
|
||||||
mLandFogEnd = mViewDistance;
|
mLandFogEnd = mViewDistance;
|
||||||
}
|
}
|
||||||
mUnderwaterFogStart = mViewDistance * (1 - underwaterFog);
|
mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog);
|
||||||
mUnderwaterFogEnd = mViewDistance;
|
mUnderwaterFogEnd = std::min(mViewDistance, 6666.f);
|
||||||
}
|
}
|
||||||
mFogColor = color;
|
mFogColor = color;
|
||||||
}
|
}
|
||||||
|
@ -584,8 +585,6 @@ namespace MWRender
|
||||||
mCurrentCameraPos = cameraPos;
|
mCurrentCameraPos = cameraPos;
|
||||||
if (mWater->isUnderwater(cameraPos))
|
if (mWater->isUnderwater(cameraPos))
|
||||||
{
|
{
|
||||||
float viewDistance = mViewDistance;
|
|
||||||
viewDistance = std::min(viewDistance, 6666.f);
|
|
||||||
setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight));
|
setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight));
|
||||||
mStateUpdater->setFogStart(mUnderwaterFogStart);
|
mStateUpdater->setFogStart(mUnderwaterFogStart);
|
||||||
mStateUpdater->setFogEnd(mUnderwaterFogEnd);
|
mStateUpdater->setFogEnd(mUnderwaterFogEnd);
|
||||||
|
@ -691,9 +690,6 @@ namespace MWRender
|
||||||
int screenshotW = mViewer->getCamera()->getViewport()->width();
|
int screenshotW = mViewer->getCamera()->getViewport()->width();
|
||||||
int screenshotH = mViewer->getCamera()->getViewport()->height();
|
int screenshotH = mViewer->getCamera()->getViewport()->height();
|
||||||
int screenshotMapping = 0;
|
int screenshotMapping = 0;
|
||||||
int cubeSize = screenshotMapping == 2 ?
|
|
||||||
screenshotW: // planet mapping needs higher resolution
|
|
||||||
screenshotW / 2;
|
|
||||||
|
|
||||||
std::vector<std::string> settingArgs;
|
std::vector<std::string> settingArgs;
|
||||||
boost::algorithm::split(settingArgs,settingStr,boost::is_any_of(" "));
|
boost::algorithm::split(settingArgs,settingStr,boost::is_any_of(" "));
|
||||||
|
@ -718,6 +714,9 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// planet mapping needs higher resolution
|
||||||
|
int cubeSize = screenshotMapping == 2 ? screenshotW : screenshotW / 2;
|
||||||
|
|
||||||
if (settingArgs.size() > 1)
|
if (settingArgs.size() > 1)
|
||||||
screenshotW = std::min(10000,std::atoi(settingArgs[1].c_str()));
|
screenshotW = std::min(10000,std::atoi(settingArgs[1].c_str()));
|
||||||
|
|
||||||
|
|
|
@ -1117,6 +1117,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
|
||||||
, mWindSpeed(0.f)
|
, mWindSpeed(0.f)
|
||||||
, mEnabled(true)
|
, mEnabled(true)
|
||||||
, mSunEnabled(true)
|
, mSunEnabled(true)
|
||||||
|
, mWeatherAlpha(0.f)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<CameraRelativeTransform> skyroot (new CameraRelativeTransform);
|
osg::ref_ptr<CameraRelativeTransform> skyroot (new CameraRelativeTransform);
|
||||||
skyroot->setName("Sky Root");
|
skyroot->setName("Sky Root");
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "dialogueextensions.hpp"
|
#include "dialogueextensions.hpp"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -181,6 +183,14 @@ namespace MWScript
|
||||||
if (!ptr.getRefData().isEnabled())
|
if (!ptr.getRefData().isEnabled())
|
||||||
return;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp change (major)
|
Start of tes3mp change (major)
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/dialoguemanager.hpp"
|
#include "../mwbase/dialoguemanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
#include "../mwbase/statemanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
@ -1260,7 +1261,11 @@ namespace MWScript
|
||||||
MWWorld::Ptr ptr = R()(runtime);
|
MWWorld::Ptr ptr = R()(runtime);
|
||||||
|
|
||||||
if (ptr == MWMechanics::getPlayer())
|
if (ptr == MWMechanics::getPlayer())
|
||||||
|
{
|
||||||
ptr.getClass().getCreatureStats(ptr).resurrect();
|
ptr.getClass().getCreatureStats(ptr).resurrect();
|
||||||
|
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Ended)
|
||||||
|
MWBase::Environment::get().getStateManager()->resumeGame();
|
||||||
|
}
|
||||||
else if (ptr.getClass().getCreatureStats(ptr).isDead())
|
else if (ptr.getClass().getCreatureStats(ptr).isDead())
|
||||||
{
|
{
|
||||||
bool wasEnabled = ptr.getRefData().isEnabled();
|
bool wasEnabled = ptr.getRefData().isEnabled();
|
||||||
|
|
|
@ -142,16 +142,12 @@ namespace MWSound
|
||||||
float volume, min, max;
|
float volume, min, max;
|
||||||
|
|
||||||
volume = static_cast<float>(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0));
|
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;
|
min = fAudioDefaultMinDistance;
|
||||||
|
if (max == 0)
|
||||||
max = fAudioDefaultMaxDistance;
|
max = fAudioDefaultMaxDistance;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
min = sound->mData.mMinRange;
|
|
||||||
max = sound->mData.mMaxRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
min *= fAudioMinDistanceMult;
|
min *= fAudioMinDistanceMult;
|
||||||
max *= fAudioMaxDistanceMult;
|
max *= fAudioMaxDistanceMult;
|
||||||
|
|
|
@ -173,6 +173,11 @@ void MWState::StateManager::endGame()
|
||||||
mState = State_Ended;
|
mState = State_Ended;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWState::StateManager::resumeGame()
|
||||||
|
{
|
||||||
|
mState = State_Running;
|
||||||
|
}
|
||||||
|
|
||||||
void MWState::StateManager::saveGame (const std::string& description, const Slot *slot)
|
void MWState::StateManager::saveGame (const std::string& description, const Slot *slot)
|
||||||
{
|
{
|
||||||
MWState::Character* character = getCurrentCharacter();
|
MWState::Character* character = getCurrentCharacter();
|
||||||
|
|
|
@ -48,6 +48,8 @@ namespace MWState
|
||||||
|
|
||||||
virtual void endGame();
|
virtual void endGame();
|
||||||
|
|
||||||
|
virtual void resumeGame();
|
||||||
|
|
||||||
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot);
|
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot);
|
||||||
///< Delete a saved game slot from this character. If all save slots are deleted, the character will be deleted too.
|
///< Delete a saved game slot from this character. If all save slots are deleted, the character will be deleted too.
|
||||||
|
|
||||||
|
|
|
@ -1373,9 +1373,9 @@ namespace MWWorld
|
||||||
addContainerScripts (getPlayerPtr(), newCell);
|
addContainerScripts (getPlayerPtr(), newCell);
|
||||||
newPtr = getPlayerPtr();
|
newPtr = getPlayerPtr();
|
||||||
}
|
}
|
||||||
else
|
else if (currCell)
|
||||||
{
|
{
|
||||||
bool currCellActive = currCell && mWorldScene->isCellActive(*currCell);
|
bool currCellActive = mWorldScene->isCellActive(*currCell);
|
||||||
bool newCellActive = newCell && mWorldScene->isCellActive(*newCell);
|
bool newCellActive = newCell && mWorldScene->isCellActive(*newCell);
|
||||||
if (!currCellActive && newCellActive)
|
if (!currCellActive && newCellActive)
|
||||||
{
|
{
|
||||||
|
@ -1708,12 +1708,16 @@ namespace MWWorld
|
||||||
moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false);
|
moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2)
|
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors)
|
||||||
{
|
{
|
||||||
osg::Vec3f a(x1,y1,z1);
|
osg::Vec3f a(x1,y1,z1);
|
||||||
osg::Vec3f b(x2,y2,z2);
|
osg::Vec3f b(x2,y2,z2);
|
||||||
|
|
||||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door);
|
int mask = MWPhysics::CollisionType_World;
|
||||||
|
if (!ignoreDoors)
|
||||||
|
mask |= MWPhysics::CollisionType_Door;
|
||||||
|
|
||||||
|
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), mask);
|
||||||
return result.mHit;
|
return result.mHit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3164,7 +3168,7 @@ namespace MWWorld
|
||||||
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
||||||
std::vector<MWWorld::Ptr> targetActors;
|
std::vector<MWWorld::Ptr> targetActors;
|
||||||
if (!actor.isEmpty() && actor != MWMechanics::getPlayer() && !manualSpell)
|
if (!actor.isEmpty() && actor != MWMechanics::getPlayer() && !manualSpell)
|
||||||
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);
|
stats.getAiSequence().getCombatTargets(targetActors);
|
||||||
|
|
||||||
const float fCombatDistance = getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat();
|
const float fCombatDistance = getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat();
|
||||||
|
|
||||||
|
@ -3187,7 +3191,6 @@ namespace MWWorld
|
||||||
// Actors that are targeted by this actor's Follow or Escort packages also side with them
|
// Actors that are targeted by this actor's Follow or Escort packages also side with them
|
||||||
if (actor != MWMechanics::getPlayer())
|
if (actor != MWMechanics::getPlayer())
|
||||||
{
|
{
|
||||||
const MWMechanics::CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
|
||||||
for (std::list<MWMechanics::AiPackage*>::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
|
for (std::list<MWMechanics::AiPackage*>::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
|
||||||
{
|
{
|
||||||
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast)
|
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast)
|
||||||
|
|
|
@ -535,7 +535,7 @@ namespace MWWorld
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override;
|
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) override;
|
||||||
///< cast a Ray and return true if there is an object in the ray path.
|
///< cast a Ray and return true if there is an object in the ray path.
|
||||||
|
|
||||||
bool toggleCollisionMode() override;
|
bool toggleCollisionMode() override;
|
||||||
|
|
|
@ -64,7 +64,7 @@ TEST(EsmFixedString, struct_size)
|
||||||
ASSERT_EQ(256, sizeof(ESM::NAME256));
|
ASSERT_EQ(256, sizeof(ESM::NAME256));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(EsmFixedString, DISABLED_is_pod)
|
TEST(EsmFixedString, is_pod)
|
||||||
{
|
{
|
||||||
ASSERT_TRUE(std::is_pod<ESM::NAME>::value);
|
ASSERT_TRUE(std::is_pod<ESM::NAME>::value);
|
||||||
ASSERT_TRUE(std::is_pod<ESM::NAME32>::value);
|
ASSERT_TRUE(std::is_pod<ESM::NAME32>::value);
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct PartialBinarySearchTest : public ::testing::Test
|
||||||
std::vector<std::string> mDataVec;
|
std::vector<std::string> mDataVec;
|
||||||
virtual void SetUp()
|
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]));
|
mDataVec = std::vector<std::string>(data, data+sizeof(data)/sizeof(data[0]));
|
||||||
std::sort(mDataVec.begin(), mDataVec.end(), Misc::StringUtils::ciLess);
|
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 01") );
|
||||||
EXPECT_TRUE( matches("Tri Head") );
|
EXPECT_TRUE( matches("Tri Head") );
|
||||||
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("Hea") );
|
||||||
EXPECT_FALSE( matches(" Head") );
|
EXPECT_FALSE( matches(" Head") );
|
||||||
EXPECT_FALSE( matches("Tri Head") );
|
EXPECT_FALSE( matches("Tri Head") );
|
||||||
|
|
|
@ -12,6 +12,9 @@ environment:
|
||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||||
- msvc: 2017
|
- msvc: 2017
|
||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- msvc: 2015
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
# - Win32
|
# - Win32
|
||||||
|
|
|
@ -87,7 +87,7 @@ add_component_dir (esmterrain
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (misc
|
add_component_dir (misc
|
||||||
utf8stream stringops resourcehelpers rng messageformatparser
|
utf8stream stringops resourcehelpers rng debugging messageformatparser
|
||||||
)
|
)
|
||||||
|
|
||||||
IF(NOT WIN32 AND NOT APPLE)
|
IF(NOT WIN32 AND NOT APPLE)
|
||||||
|
|
|
@ -182,8 +182,10 @@ static void gdb_info(pid_t pid)
|
||||||
/* Error creating temp file */
|
/* Error creating temp file */
|
||||||
if(fd >= 0)
|
if(fd >= 0)
|
||||||
{
|
{
|
||||||
close(fd);
|
if (close(fd) == 0)
|
||||||
remove(respfile);
|
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");
|
printf("!!! Could not create gdb command file\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ namespace ESMTerrain
|
||||||
}
|
}
|
||||||
|
|
||||||
LandObject::LandObject(const LandObject ©, const osg::CopyOp ©op)
|
LandObject::LandObject(const LandObject ©, const osg::CopyOp ©op)
|
||||||
|
: mLand(nullptr)
|
||||||
|
, mLoadFlags(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
128
components/misc/debugging.hpp
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#ifndef MISC_DEBUGGING_H
|
||||||
|
#define MISC_DEBUGGING_H
|
||||||
|
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
#include <boost/iostreams/stream.hpp>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Include additional headers for multiplayer purposes
|
||||||
|
*/
|
||||||
|
#include <components/openmw-mp/Log.hpp>
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
||||||
|
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName));
|
||||||
|
|
||||||
|
coutsb.open (Misc::Tee(logfile, oldcout));
|
||||||
|
cerrsb.open (Misc::Tee(logfile, oldcerr));
|
||||||
|
|
||||||
|
std::cout.rdbuf (&coutsb);
|
||||||
|
std::cerr.rdbuf (&cerrsb);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Initialize the logger added for multiplayer
|
||||||
|
*/
|
||||||
|
LOG_INIT(Log::LOG_INFO);
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
#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();
|
std::string::const_iterator yit = y.begin();
|
||||||
for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len)
|
for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len)
|
||||||
{
|
{
|
||||||
int res = *xit - *yit;
|
char left = *xit;
|
||||||
if(res != 0 && toLower(*xit) != toLower(*yit))
|
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;
|
return (res > 0) ? 1 : -1;
|
||||||
}
|
}
|
||||||
if(len > 0)
|
if(len > 0)
|
||||||
|
|
|
@ -94,13 +94,16 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::NIFFilePtr&
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool autogenerated = hasAutoGeneratedCollision(node);
|
|
||||||
|
|
||||||
// files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource).
|
// files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource).
|
||||||
// assume all nodes in the file will be animated
|
// assume all nodes in the file will be animated
|
||||||
const bool isAnimated = pathFileNameStartsWithX(nif->getFilename());
|
const bool isAnimated = pathFileNameStartsWithX(nif->getFilename());
|
||||||
|
|
||||||
handleNode(node, 0, autogenerated, isAnimated, autogenerated);
|
// If the mesh has RootCollisionNode, attached to actual root node, use it as collision mesh
|
||||||
|
const Nif::Node* rootCollisionNode = getCollisionNode(node);
|
||||||
|
if (rootCollisionNode)
|
||||||
|
handleNode(nif->getFilename(), rootCollisionNode, 0, false, isAnimated, false);
|
||||||
|
else
|
||||||
|
handleNode(nif->getFilename(), node, 0, true, isAnimated, true);
|
||||||
|
|
||||||
if (mCompoundShape)
|
if (mCompoundShape)
|
||||||
{
|
{
|
||||||
|
@ -153,25 +156,34 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node, int flags)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode)
|
const Nif::Node* BulletNifLoader::getCollisionNode(const Nif::Node* rootNode)
|
||||||
{
|
{
|
||||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(rootNode);
|
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(rootNode);
|
||||||
if(ninode)
|
if(ninode)
|
||||||
{
|
{
|
||||||
|
// If root NiNode has only other NiNode as child, consider it as a wrapper, not as actual root node
|
||||||
const Nif::NodeList &list = ninode->children;
|
const Nif::NodeList &list = ninode->children;
|
||||||
for(size_t i = 0;i < list.length();i++)
|
if (list.length() == 1 &&
|
||||||
|
rootNode->recType == Nif::RC_NiNode &&
|
||||||
|
list[0].getPtr()->recType == Nif::RC_NiNode)
|
||||||
{
|
{
|
||||||
if(!list[i].empty())
|
return getCollisionNode(list[0].getPtr());
|
||||||
{
|
}
|
||||||
if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode)
|
|
||||||
return false;
|
for(size_t i = 0; i < list.length(); i++)
|
||||||
}
|
{
|
||||||
|
if(list[i].empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Nif::Node* childNode = list[i].getPtr();
|
||||||
|
if(childNode->recType == Nif::RC_RootCollisionNode)
|
||||||
|
return childNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BulletNifLoader::handleNode(const Nif::Node *node, int flags,
|
void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *node, int flags,
|
||||||
bool isCollisionNode, bool isAnimated, bool autogenerated)
|
bool isCollisionNode, bool isAnimated, bool autogenerated)
|
||||||
{
|
{
|
||||||
// Accumulate the flags from all the child nodes. This works for all
|
// Accumulate the flags from all the child nodes. This works for all
|
||||||
|
@ -184,6 +196,9 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags,
|
||||||
|
|
||||||
isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode);
|
isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode);
|
||||||
|
|
||||||
|
if (node->recType == Nif::RC_RootCollisionNode && autogenerated)
|
||||||
|
std::cerr << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape." << std::endl;
|
||||||
|
|
||||||
// Don't collide with AvoidNode shapes
|
// Don't collide with AvoidNode shapes
|
||||||
if(node->recType == Nif::RC_AvoidNode)
|
if(node->recType == Nif::RC_AvoidNode)
|
||||||
flags |= 0x800;
|
flags |= 0x800;
|
||||||
|
@ -212,7 +227,6 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags,
|
||||||
// Marker can still have collision if the model explicitely specifies it via a RootCollisionNode.
|
// Marker can still have collision if the model explicitely specifies it via a RootCollisionNode.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +249,7 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags,
|
||||||
for(size_t i = 0;i < list.length();i++)
|
for(size_t i = 0;i < list.length();i++)
|
||||||
{
|
{
|
||||||
if(!list[i].empty())
|
if(!list[i].empty())
|
||||||
handleNode(list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated);
|
handleNode(fileName, list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,9 +55,9 @@ public:
|
||||||
private:
|
private:
|
||||||
bool findBoundingBox(const Nif::Node* node, int flags = 0);
|
bool findBoundingBox(const Nif::Node* node, int flags = 0);
|
||||||
|
|
||||||
void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false);
|
void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false);
|
||||||
|
|
||||||
bool hasAutoGeneratedCollision(const Nif::Node *rootNode);
|
const Nif::Node* getCollisionNode(const Nif::Node* rootNode);
|
||||||
|
|
||||||
void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated);
|
void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated);
|
||||||
|
|
||||||
|
|
|
@ -547,6 +547,11 @@ namespace NifOsg
|
||||||
node->setDataVariance(osg::Object::DYNAMIC);
|
node->setDataVariance(osg::Object::DYNAMIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nifNode->recType == Nif::RC_NiTriShape && isAnimated) // the same thing for animated NiTriShapes
|
||||||
|
{
|
||||||
|
node->setDataVariance(osg::Object::DYNAMIC);
|
||||||
|
}
|
||||||
|
|
||||||
osg::ref_ptr<SceneUtil::CompositeStateSetUpdater> composite = new SceneUtil::CompositeStateSetUpdater;
|
osg::ref_ptr<SceneUtil::CompositeStateSetUpdater> composite = new SceneUtil::CompositeStateSetUpdater;
|
||||||
|
|
||||||
applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags);
|
applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags);
|
||||||
|
@ -574,13 +579,11 @@ namespace NifOsg
|
||||||
if (composite->getNumControllers() > 0)
|
if (composite->getNumControllers() > 0)
|
||||||
node->addUpdateCallback(composite);
|
node->addUpdateCallback(composite);
|
||||||
|
|
||||||
|
|
||||||
// Note: NiTriShapes are not allowed to have KeyframeControllers (the vanilla engine just crashes when there is one).
|
// Note: NiTriShapes are not allowed to have KeyframeControllers (the vanilla engine just crashes when there is one).
|
||||||
// We can take advantage of this constraint for optimizations later.
|
// We can take advantage of this constraint for optimizations later.
|
||||||
if (!nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC)
|
if (nifNode->recType != Nif::RC_NiTriShape && !nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC)
|
||||||
handleNodeControllers(nifNode, static_cast<osg::MatrixTransform*>(node.get()), animflags);
|
handleNodeControllers(nifNode, static_cast<osg::MatrixTransform*>(node.get()), animflags);
|
||||||
|
|
||||||
|
|
||||||
if (nifNode->recType == Nif::RC_NiLODNode)
|
if (nifNode->recType == Nif::RC_NiLODNode)
|
||||||
{
|
{
|
||||||
const Nif::NiLODNode* niLodNode = static_cast<const Nif::NiLODNode*>(nifNode);
|
const Nif::NiLODNode* niLodNode = static_cast<const Nif::NiLODNode*>(nifNode);
|
||||||
|
|
|
@ -393,9 +393,9 @@ namespace Resource
|
||||||
static std::vector<std::string> reservedNames;
|
static std::vector<std::string> reservedNames;
|
||||||
if (reservedNames.empty())
|
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",
|
const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
|
||||||
"Bip01 L Hand", "Bip01 R Hand", "Bip01 Head", "Bip01 Spine1", "Bip01 Spine2", "Bip01 L Clavicle", "Bip01 R Clavicle", "bip01", "Root Bone", "Bip01 Neck",
|
"Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
|
||||||
"BoneOffset", "AttachLight", "ArrowBone", "Camera"};
|
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "ArrowBone", "Camera"};
|
||||||
reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
|
reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
|
||||||
|
|
||||||
for (unsigned int i=0; i<sizeof(reserved)/sizeof(reserved[0]); ++i)
|
for (unsigned int i=0; i<sizeof(reserved)/sizeof(reserved[0]); ++i)
|
||||||
|
|
|
@ -130,7 +130,7 @@ enchanted weapons are magical
|
||||||
:Range: True/False
|
:Range: True/False
|
||||||
:Default: True
|
: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 is how original Morrowind behaves.
|
||||||
|
|
||||||
This setting can only be configured by editing the settings configuration file.
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
@ -142,7 +142,7 @@ prevent merchant equipping
|
||||||
:Range: True/False
|
:Range: True/False
|
||||||
:Default: 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.
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ followers attack on sight
|
||||||
:Range: True/False
|
:Range: True/False
|
||||||
:Default: 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.
|
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.
|
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.
|
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!
|
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.
|
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.
|
||||||
|
|
35
extern/oics/tinyxmlparser.cpp
vendored
|
@ -104,25 +104,17 @@ void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* leng
|
||||||
|
|
||||||
output += *length;
|
output += *length;
|
||||||
|
|
||||||
// Scary scary fall throughs.
|
int lengthLeft = *length;
|
||||||
switch (*length)
|
while (lengthLeft > 1)
|
||||||
{
|
{
|
||||||
case 4:
|
--output;
|
||||||
--output;
|
*output = (char)((input | BYTE_MARK) & BYTE_MASK);
|
||||||
*output = (char)((input | BYTE_MARK) & BYTE_MASK);
|
input >>= 6;
|
||||||
input >>= 6;
|
--lengthLeft;
|
||||||
case 3:
|
}
|
||||||
--output;
|
|
||||||
*output = (char)((input | BYTE_MARK) & BYTE_MASK);
|
--output;
|
||||||
input >>= 6;
|
*output = (char)(input | FIRST_BYTE_MARK[*length]);
|
||||||
case 2:
|
|
||||||
--output;
|
|
||||||
*output = (char)((input | BYTE_MARK) & BYTE_MASK);
|
|
||||||
input >>= 6;
|
|
||||||
case 1:
|
|
||||||
--output;
|
|
||||||
*output = (char)(input | FIRST_BYTE_MARK[*length]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1295,9 +1287,10 @@ const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEnc
|
||||||
++p;
|
++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 == '>' )
|
if ( *p == '>' )
|
||||||
return p+1;
|
return p+1;
|
||||||
|
|
Before Width: | Height: | Size: 590 B After Width: | Height: | Size: 699 B |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 362 B |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 228 KiB |
BIN
files/launcher/images/preferences-advanced.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
files/launcher/images/preferences-video.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
files/launcher/images/preferences.png
Normal file
After Width: | Height: | Size: 51 KiB |
|
@ -5,6 +5,9 @@
|
||||||
<file alias="openmw.png">images/openmw.png</file>
|
<file alias="openmw.png">images/openmw.png</file>
|
||||||
<file alias="openmw-plugin.png">images/openmw-plugin.png</file>
|
<file alias="openmw-plugin.png">images/openmw-plugin.png</file>
|
||||||
<file alias="openmw-header.png">images/openmw-header.png</file>
|
<file alias="openmw-header.png">images/openmw-header.png</file>
|
||||||
|
<file alias="preferences.png">images/preferences.png</file>
|
||||||
|
<file alias="preferences-advanced.png">images/preferences-advanced.png</file>
|
||||||
|
<file alias="preferences-video.png">images/preferences-video.png</file>
|
||||||
<file alias="playpage-background.png">images/playpage-background.png</file>
|
<file alias="playpage-background.png">images/playpage-background.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="icons/tango">
|
<qresource prefix="icons/tango">
|
||||||
|
|
|
@ -203,25 +203,28 @@ show effect duration = false
|
||||||
# Account for the first follower in fast travel cost calculations.
|
# Account for the first follower in fast travel cost calculations.
|
||||||
charge for every follower travelling = false
|
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
|
prevent merchant equipping = false
|
||||||
|
|
||||||
# Make enchanted weaponry without Magical flag bypass normal weapons resistance
|
# Make enchanted weaponry without Magical flag bypass normal weapons resistance
|
||||||
enchanted weapons are magical = true
|
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.
|
# or the player. Otherwise they wait for the enemies or the player to do an attack first.
|
||||||
followers attack on sight = false
|
followers attack on sight = false
|
||||||
|
|
||||||
# Can loot non-fighting actors during death animation
|
# Can loot non-fighting actors during death animation
|
||||||
can loot during death animation = true
|
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
|
rebalance soul gem values = false
|
||||||
|
|
||||||
# Allow to load per-group KF-files from Animations folder
|
# Allow to load per-group KF-files from Animations folder
|
||||||
use additional anim sources = false
|
use additional anim sources = false
|
||||||
|
|
||||||
|
# Make the disposition change of merchants caused by barter dealings permanent
|
||||||
|
barter disposition change is permanent = false
|
||||||
|
|
||||||
[General]
|
[General]
|
||||||
|
|
||||||
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
||||||
|
|
|
@ -194,9 +194,9 @@ void main(void)
|
||||||
float bump = mix(BUMP,BUMP_RAIN,rainIntensity);
|
float bump = mix(BUMP,BUMP_RAIN,rainIntensity);
|
||||||
|
|
||||||
vec3 normal = (normal0 * bigWaves.x + normal1 * bigWaves.y +
|
vec3 normal = (normal0 * bigWaves.x + normal1 * bigWaves.y +
|
||||||
normal2 * midWaves.x + normal3 * midWaves.y +
|
normal2 * midWaves.x + normal3 * midWaves.y +
|
||||||
normal4 * smallWaves.x + normal5 * smallWaves.y +
|
normal4 * smallWaves.x + normal5 * smallWaves.y +
|
||||||
rippleAdd);
|
rippleAdd);
|
||||||
|
|
||||||
normal = normalize(vec3(normal.x * bump, normal.y * bump, normal.z));
|
normal = normalize(vec3(normal.x * bump, normal.y * bump, normal.z));
|
||||||
|
|
||||||
|
@ -204,9 +204,9 @@ void main(void)
|
||||||
|
|
||||||
// normal for sunlight scattering
|
// normal for sunlight scattering
|
||||||
vec3 lNormal = (normal0 * bigWaves.x * 0.5 + normal1 * bigWaves.y * 0.5 +
|
vec3 lNormal = (normal0 * bigWaves.x * 0.5 + normal1 * bigWaves.y * 0.5 +
|
||||||
normal2 * midWaves.x * 0.2 + normal3 * midWaves.y * 0.2 +
|
normal2 * midWaves.x * 0.2 + normal3 * midWaves.y * 0.2 +
|
||||||
normal4 * smallWaves.x * 0.1 + normal5 * smallWaves.y * 0.1 +
|
normal4 * smallWaves.x * 0.1 + normal5 * smallWaves.y * 0.1 +
|
||||||
rippleAdd).xyz;
|
rippleAdd).xyz;
|
||||||
|
|
||||||
lNormal = normalize(vec3(lNormal.x * bump, lNormal.y * bump, lNormal.z));
|
lNormal = normalize(vec3(lNormal.x * bump, lNormal.y * bump, lNormal.z));
|
||||||
lNormal = vec3(-lNormal.x, -lNormal.y, lNormal.z);
|
lNormal = vec3(-lNormal.x, -lNormal.y, lNormal.z);
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>630</width>
|
<width>641</width>
|
||||||
<height>791</height>
|
<height>998</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="scrollAreaVerticalLayout">
|
<layout class="QVBoxLayout" name="scrollAreaVerticalLayout">
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="followersAttackOnSightCheckBox">
|
<widget class="QCheckBox" name="followersAttackOnSightCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>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.</p></body></html></string>
|
<string><html><head/><body><p>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.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Followers attack on sight</string>
|
<string>Followers attack on sight</string>
|
||||||
|
@ -65,13 +65,43 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="rebalanceSoulGemValuesCheckBox">
|
<widget class="QCheckBox" name="rebalanceSoulGemValuesCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>If this setting is true, the value of filled soul gems is dependent only on soul magnitude.</p><p>The default value is false.</p></body></html></string>
|
<string><html><head/><body><p>Make the value of filled soul gems dependent only on soul magnitude.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Rebalance soul gem values</string>
|
<string>Rebalance soul gem values</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="chargeForEveryFollowerCheckBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Account for the first follower in fast travel cost calculations.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Charge for every follower travelling</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="enchantedWeaponsMagicalCheckBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Make enchanted weaponry without Magical flag bypass normal weapons resistance, like in Morrowind.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Enchanted weapons are magical</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="permanentBarterDispositionChangeCheckBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Make disposition change of merchants caused by trading permanent.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Barter disposition change is permanent</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -137,7 +167,7 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="maximumQuicksavesLayout">
|
<layout class="QHBoxLayout" name="maximumQuicksavesLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>-1</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
|
@ -312,7 +342,7 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>-1</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
|
@ -379,7 +409,7 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="screenshotFormatLayout">
|
<layout class="QHBoxLayout" name="screenshotFormatLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>-1</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
|
|