From e413bebe99b0dc2ab1e5f07c8d20b594f6a4c5f4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 16 Oct 2014 15:07:51 +0200 Subject: [PATCH 001/404] Toggle addon files when activated or clicked on checkbox rather than clicked (Fixes #1980) --- apps/opencs/view/doc/filedialog.cpp | 10 +++++++--- apps/opencs/view/doc/filedialog.hpp | 1 + components/contentselector/model/contentmodel.cpp | 4 ++-- components/contentselector/view/contentselector.cpp | 11 +++-------- components/contentselector/view/contentselector.hpp | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 300656f334..c0e5502ec0 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -125,13 +125,17 @@ void CSVDoc::FileDialog::buildOpenFileView() if(!mDialogBuilt) { - connect (mSelector, SIGNAL (signalAddonFileSelected (int)), this, SLOT (slotUpdateAcceptButton (int))); - connect (mSelector, SIGNAL (signalAddonFileUnselected (int)), this, SLOT (slotUpdateAcceptButton (int))); + connect (mSelector, SIGNAL (signalAddonDataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (slotAddonDataChanged(const QModelIndex&, const QModelIndex&))); } connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile())); } -void CSVDoc::FileDialog::slotUpdateAcceptButton (int) +void CSVDoc::FileDialog::slotAddonDataChanged(const QModelIndex &topleft, const QModelIndex &bottomright) +{ + slotUpdateAcceptButton(0); +} + +void CSVDoc::FileDialog::slotUpdateAcceptButton(int) { QString name = ""; diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 111cc0d807..3c23a5cb5a 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -70,6 +70,7 @@ namespace CSVDoc void slotUpdateAcceptButton (int); void slotUpdateAcceptButton (const QString &, bool); void slotRejected(); + void slotAddonDataChanged(const QModelIndex& topleft, const QModelIndex& bottomright); }; } #endif // FILEDIALOG_HPP diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 0d4f2365a6..fde6223f14 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -106,7 +106,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index //game files can always be checked if (file->isGameFile()) - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; Qt::ItemFlags returnFlags; bool allDependenciesFound = true; @@ -145,7 +145,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index if (gamefileChecked) { if (allDependenciesFound) - returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | mDragDropFlags; + returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | mDragDropFlags; else returnFlags = Qt::ItemIsSelectable; } diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index e9599de498..4508d7bd7a 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -58,7 +58,8 @@ void ContentSelectorView::ContentSelector::buildAddonView() ui.addonView->setModel(mAddonProxyModel); - connect(ui.addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); + connect(ui.addonView, SIGNAL(activated(const QModelIndex&)), this, SLOT(slotAddonTableItemActivated(const QModelIndex&))); + connect(mContentModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SIGNAL(signalAddonDataChanged(QModelIndex,QModelIndex))); } void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList) @@ -181,7 +182,7 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i emit signalCurrentGamefileIndexChanged (index); } -void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) +void ContentSelectorView::ContentSelector::slotAddonTableItemActivated(const QModelIndex &index) { QModelIndex sourceIndex = mAddonProxyModel->mapToSource (index); @@ -194,10 +195,4 @@ void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QMode checkState = Qt::Checked; mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole); - - if (checkState == Qt::Checked) - emit signalAddonFileSelected (index.row()); - else - emit signalAddonFileUnselected (index.row()); - } diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index a25eb20ae3..5fba9cd2ed 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -55,13 +55,13 @@ namespace ContentSelectorView signals: void signalCurrentGamefileIndexChanged (int); - void signalAddonFileSelected (int); - void signalAddonFileUnselected (int); + + void signalAddonDataChanged (const QModelIndex& topleft, const QModelIndex& bottomright); private slots: void slotCurrentGameFileIndexChanged(int index); - void slotAddonTableItemClicked(const QModelIndex &index); + void slotAddonTableItemActivated(const QModelIndex& index); }; } From 9c5f15679309fabbccce29cf6bdf96f1c42847e2 Mon Sep 17 00:00:00 2001 From: Internecine Date: Wed, 5 Nov 2014 00:46:33 +1300 Subject: [PATCH 002/404] Fixes tooltip displaying 0 durations --- apps/openmw/mwgui/widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index d7bc2c96e9..8eeb02a452 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -446,7 +446,7 @@ namespace MWGui // constant effects have no duration and no target if (!mEffectParams.mIsConstant) { - if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + if (mEffectParams.mDuration > 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) { spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); } From 6741fbe7a9ff84f226db2fdbc37c5bdca00af3a1 Mon Sep 17 00:00:00 2001 From: Internecine Date: Wed, 5 Nov 2014 15:22:44 +1300 Subject: [PATCH 003/404] Fixes bug #2031 --- apps/openmw/mwmechanics/spellcasting.cpp | 30 +++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d33bb6ad15..e124c8b79b 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -434,7 +434,8 @@ namespace MWMechanics float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; magnitude *= magnitudeMult; - bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) && effectIt->mDuration > 0; + std::cout << (hasDuration == true ? "true" : "false") << std::endl; if (target.getClass().isActor() && hasDuration) { ActiveSpells::ActiveEffect effect; @@ -578,6 +579,33 @@ namespace MWMechanics value.restore(magnitude); target.getClass().getCreatureStats(target).setAttribute(attribute, value); } + else if (effectId == ESM::MagicEffect::DamageHealth || effectId == ESM::MagicEffect::RestoreHealth) + { + MWMechanics::DynamicStat health = target.getClass().getCreatureStats(target).getHealth(); + if (effectId == ESM::MagicEffect::DamageHealth) + health.setCurrent(health.getCurrent() - magnitude); + else + health.setCurrent(health.getCurrent() + magnitude); + target.getClass().getCreatureStats(target).setHealth(health); + } + else if (effectId == ESM::MagicEffect::DamageFatigue || effectId == ESM::MagicEffect::RestoreFatigue) + { + MWMechanics::DynamicStat fatigue = target.getClass().getCreatureStats(target).getFatigue(); + if (effectId == ESM::MagicEffect::DamageFatigue) + fatigue.setCurrent(fatigue.getCurrent() - magnitude); + else + fatigue.setCurrent(fatigue.getCurrent() + magnitude); + target.getClass().getCreatureStats(target).setHealth(fatigue); + } + else if (effectId == ESM::MagicEffect::DamageMagicka || effectId == ESM::MagicEffect::RestoreMagicka) + { + MWMechanics::DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); + if (effectId == ESM::MagicEffect::DamageMagicka) + magicka.setCurrent(magicka.getCurrent() - magnitude); + else + magicka.setCurrent(magicka.getCurrent() + magnitude); + target.getClass().getCreatureStats(target).setHealth(magicka); + } else if (effectId == ESM::MagicEffect::DamageSkill || effectId == ESM::MagicEffect::RestoreSkill) { if (target.getTypeName() != typeid(ESM::NPC).name()) From edc51ab768d7413ac894f23e7044e7c55e74ce74 Mon Sep 17 00:00:00 2001 From: Internecine Date: Wed, 5 Nov 2014 15:26:13 +1300 Subject: [PATCH 004/404] Removed debug output --- apps/openmw/mwmechanics/spellcasting.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index e124c8b79b..fd4c9406cb 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -435,7 +435,6 @@ namespace MWMechanics magnitude *= magnitudeMult; bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) && effectIt->mDuration > 0; - std::cout << (hasDuration == true ? "true" : "false") << std::endl; if (target.getClass().isActor() && hasDuration) { ActiveSpells::ActiveEffect effect; From d67ad9037e6586306b5d82d792e8924c89e4280b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 3 Dec 2014 20:55:33 +0100 Subject: [PATCH 005/404] increased version number --- CMakeLists.txt | 4 ++-- readme.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a923c6312..9587c652c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 33) -set(OPENMW_VERSION_RELEASE 1) +set(OPENMW_VERSION_MINOR 34) +set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_TAGHASH "") diff --git a/readme.txt b/readme.txt index 810a0e055e..f4493c80e7 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.33.1 +Version: 0.34.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 41ec6e42cbbebc7cd11c3bf41328397b8497d3e6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 3 Dec 2014 20:58:44 +0100 Subject: [PATCH 006/404] updated changelog --- readme.txt | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/readme.txt b/readme.txt index f4493c80e7..91ec7962f1 100644 --- a/readme.txt +++ b/readme.txt @@ -98,6 +98,82 @@ Allowed options: CHANGELOG +0.34.0 + +Bug #904: omwlauncher doesn't allow installing Tribunal and Bloodmoon if only MW is installed +Bug #1061: "Browse to CD..." launcher crash +Bug #1135: Launcher crashes if user does not have write permission +Bug #1231: Current installer in launcher does not correctly import russian Morrowind.ini settings from setup.inx +Bug #1288: Fix the Alignment of the Resolution Combobox +Bug #1343: BIK videos occasionally out of sync with audio +Bug #1734: NPC in fight with invisible/sneaking player +Bug #1982: Long class names are cut off in the UI +Bug #2012: Editor: OpenCS script compiler sometimes fails to find IDs +Bug #2015: Running while levitating does not affect speed but still drains fatigue +Bug #2018: OpenMW don´t reset modified cells to vanilla when a plugin is deselected and don´t apply changes to cells already visited. +Bug #2045: ToggleMenus command should close dialogue windows +Bug #2046: Crash: light_de_streetlight_01_223 +Bug #2047: Buglamp tooltip minor correction +Bug #2050: Roobrush floating texture bits +Bug #2053: Slaves react negatively to PC picking up slave's bracers +Bug #2055: Dremora corpses use the wrong model +Bug #2056: Mansilamat Vabdas's corpse is floating in the water +Bug #2057: "Quest: Larius Varro Tells A Little Story": Bounty not completely removed after finishing quest +Bug #2059: Silenced enemies try to cast spells anyway +Bug #2060: Editor: Special case implementation for top level window with single sub-window should be optional +Bug #2061: Editor: SubView closing that is not directly triggered by the user isn't handled properly +Bug #2063: Tribunal: Quest 'The Warlords' doesn't work +Bug #2064: Sneak attack on hostiles causes bounty +Bug #2065: Editor: Qt signal-slot error when closing a dialogue subview +Bug #2070: Loading ESP in OpenMW works but fails in OpenCS +Bug #2071: CTD in 0.33 +Bug #2073: Storm atronach animation stops now and then +Bug #2075: Molag Amur Region, Map shows water on solid ground +Bug #2080: game won't work with fair magicka regen +Bug #2082: NPCs appear frozen or switched off after leaving and quickly reentering a cell +Bug #2088: OpenMW is unable to play OGG files. +Bug #2093: Darth Gares talks to you in Ilunibi even when he's not there, screwing up the Main Quests +Bug #2095: Coordinate and rotation editing in the Reference table does not work. +Bug #2096: Some overflow fun and bartering exploit +Bug #2098: [D3D] Game crash on maximize +Bug #2099: Activate, player seems not to work +Bug #2104: Only labels are sensitive in buttons +Bug #2107: "Slowfall" effect is too weak +Bug #2114: OpenCS doesn't load an ESP file full of errors even though Vanilla MW Construction Set can +Bug #2117: Crash when encountering bandits on opposite side of river from the egg mine south of Balmora +Bug #2124: [Mod: Baldurians Transparent Glass Amor] Armor above head +Bug #2125: Unnamed NiNodes in weapons problem in First Person +Bug #2126: Dirty dialog script in tribunal.esm causing bug in Tribunal MQ +Bug #2128: Crash when picking character's face +Bug #2129: Disable the third-person zoom feature by default +Bug #2130: Ash storm particles shown too long during transition to clear sky +Bug #2137: Editor: exception caused by following the Creature column of a SoundGen record +Bug #2139: Mouse movement should be ignored during intro video +Bug #2143: Editor: Saving is broken +Bug #2145: OpenMW - crash while exiting x64 debug build +Bug #2152: You can attack Almalexia during her final monologue +Bug #2154: Visual effects behave weirdly after loading/taking a screenshot +Bug #2155: Vivec has too little magicka +Bug #2156: Azura's spirit fades away too fast +Bug #2158: [Mod]Julan Ashlander Companion 2.0: Negative magicka +Bug #2161: Editor: combat/magic/stealth values of creature not displayed correctly +Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon. +Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly. +Bug #2170: Mods using conversations to update PC inconsistant +Bug #2173: Launcher: disabling plugin files is broken +Bug #2175: Pathgrid mods do not overwrite the existing pathgrid +Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources +Feature #238: Add UI to run INI-importer from the launcher +Feature #854: Editor: Add user setting to show status bar +Feature #987: Launcher: first launch instructions for CD need to be more explicit +Feature #1232: There is no way to set the "encoding" option using launcher UI. +Feature #1281: Editor: Render cell markers +Feature #1918: Editor: Functionality for Double-Clicking in Tables +Feature #1966: Editor: User Settings dialogue grouping/labelling/tooltips +Feature #2097: Editor: Edit position of references in 3D scene +Feature #2121: Editor: Add edit mode button to scene toolbar +Task #1965: Editor: Improve layout of user settings dialogue + 0.33.1 Bug #2108: OpenCS fails to build From 9f90a1e44bba06c581eb423172fe0648d4a646e9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Dec 2014 22:37:50 +0100 Subject: [PATCH 007/404] Remove script access to deleted references that have no content file In original MW these objects are permanently deleted and can not be accessed anymore. --- apps/openmw/mwworld/cellreflist.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/cellreflist.hpp b/apps/openmw/mwworld/cellreflist.hpp index cf12896548..037de8645e 100644 --- a/apps/openmw/mwworld/cellreflist.hpp +++ b/apps/openmw/mwworld/cellreflist.hpp @@ -27,7 +27,9 @@ namespace MWWorld LiveRef *find (const std::string& name) { for (typename List::iterator iter (mList.begin()); iter!=mList.end(); ++iter) - if (iter->mRef.getRefId() == name) + if (!iter->mData.isDeletedByContentFile() + && (iter->mRef.getRefNum().mContentFile != -1 || iter->mData.getCount() > 0) + && iter->mRef.getRefId() == name) return &*iter; return 0; From fbed429b257503417ecc32ef26f03c90a728a0d5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Dec 2014 17:24:45 +0100 Subject: [PATCH 008/404] Use GMSTs for sound fading distance --- apps/openmw/mwsound/soundmanagerimp.cpp | 36 ++++++++++++++++++------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 781bfb5d5a..251b05890b 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -103,24 +103,31 @@ namespace MWSound std::string SoundManager::lookup(const std::string &soundId, float &volume, float &min, float &max) { - const ESM::Sound *snd = - MWBase::Environment::get().getWorld()->getStore().get().find(soundId); + MWBase::World* world = MWBase::Environment::get().getWorld(); + const ESM::Sound *snd = world->getStore().get().find(soundId); volume *= pow(10.0, (snd->mData.mVolume/255.0*3348.0 - 3348.0) / 2000.0); if(snd->mData.mMinRange == 0 && snd->mData.mMaxRange == 0) { - min = 100.0f; - max = 2000.0f; + static const float fAudioDefaultMinDistance = world->getStore().get().find("fAudioDefaultMinDistance")->getFloat(); + static const float fAudioDefaultMaxDistance = world->getStore().get().find("fAudioDefaultMaxDistance")->getFloat(); + min = fAudioDefaultMinDistance; + max = fAudioDefaultMaxDistance; } else { - min = snd->mData.mMinRange * 20.0f; - max = snd->mData.mMaxRange * 50.0f; - min = std::max(min, 1.0f); - max = std::max(min, max); + min = snd->mData.mMinRange; + max = snd->mData.mMaxRange; } + static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); + static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); + min *= fAudioMinDistanceMult; + max *= fAudioMaxDistanceMult; + min = std::max(min, 1.0f); + max = std::max(min, max); + return "Sound/"+snd->mSound; } @@ -250,8 +257,19 @@ namespace MWSound const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos); + MWBase::World* world = MWBase::Environment::get().getWorld(); + static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); + static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); + static const float fAudioVoiceDefaultMinDistance = world->getStore().get().find("fAudioVoiceDefaultMinDistance")->getFloat(); + static const float fAudioVoiceDefaultMaxDistance = world->getStore().get().find("fAudioVoiceDefaultMaxDistance")->getFloat(); + + float minDistance = fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult; + float maxDistance = fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult; + minDistance = std::max(minDistance, 1.f); + maxDistance = std::max(minDistance, maxDistance); + MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, - 20.0f, 1500.0f, Play_Normal|Play_TypeVoice, 0, true); + minDistance, maxDistance, Play_Normal|Play_TypeVoice, 0, true); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) From cf85cbbc8e48f790e41faad5a765d94219debaef Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Dec 2014 17:43:56 +0100 Subject: [PATCH 009/404] Switch sound distance model to AL_INVERSE_DISTANCE --- apps/openmw/mwsound/openal_output.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 3d2795ce10..fcdc60ee39 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -628,9 +628,7 @@ void OpenAL_Sound3D::update() { ALfloat gain = mVolume*mBaseVolume; ALfloat pitch = mPitch; - if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance) - gain = 0.0f; - else if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; @@ -696,7 +694,7 @@ void OpenAL_Output::init(const std::string &devname) fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice))); } - alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); + alDistanceModel(AL_INVERSE_DISTANCE); throwALerror(); ALCint maxmono=0, maxstereo=0; From ddad963312d4d21113be21792c9703655c071cd8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 8 Dec 2014 20:14:00 +0100 Subject: [PATCH 010/404] adjusted changelog (removed a regression that was specific to 0.34.0) --- readme.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.txt b/readme.txt index 91ec7962f1..e91455aebe 100644 --- a/readme.txt +++ b/readme.txt @@ -160,7 +160,6 @@ Bug #2161: Editor: combat/magic/stealth values of creature not displayed correct Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon. Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly. Bug #2170: Mods using conversations to update PC inconsistant -Bug #2173: Launcher: disabling plugin files is broken Bug #2175: Pathgrid mods do not overwrite the existing pathgrid Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources Feature #238: Add UI to run INI-importer from the launcher From f6960debcbf5d6b3758a6dca8da35c4e89daee1c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Dec 2014 23:26:09 +0100 Subject: [PATCH 011/404] Attach sound listener to the player head instead of camera --- apps/openmw/mwrender/camera.cpp | 11 ----------- apps/openmw/mwrender/camera.hpp | 3 --- apps/openmw/mwworld/worldimp.cpp | 13 +++++++++++++ apps/openmw/mwworld/worldimp.hpp | 1 + 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 1850df904b..c7a27dfe8f 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/refdata.hpp" @@ -120,15 +119,6 @@ namespace MWRender setPosition(Ogre::Vector3(x,y,z)); } - void Camera::updateListener() - { - Ogre::Vector3 pos = mCamera->getRealPosition(); - Ogre::Vector3 dir = mCamera->getRealDirection(); - Ogre::Vector3 up = mCamera->getRealUp(); - - MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); - } - void Camera::update(float duration, bool paused) { if (mAnimation->upperBodyReady()) @@ -148,7 +138,6 @@ namespace MWRender } } - updateListener(); if (paused) return; diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index c542dc96ce..691a80862b 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -50,9 +50,6 @@ namespace MWRender bool mVanityToggleQueued; bool mViewModeToggleQueued; - /// Updates sound manager listener data - void updateListener(); - void setPosition(const Ogre::Vector3& position); void setPosition(float x, float y, float z); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d783857f10..1b5d6e002f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1560,6 +1560,8 @@ namespace MWWorld updateWindowManager (); + updateSoundListener(); + if (!paused && mPlayer->getPlayer().getCell()->isExterior()) { ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); @@ -1567,6 +1569,17 @@ namespace MWWorld } } + void World::updateSoundListener() + { + Ogre::Vector3 playerPos = mPlayer->getPlayer().getRefData().getBaseNode()->getPosition(); + const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(getPlayerPtr().getRefData().getHandle()); + if(actor) playerPos.z += 1.85*actor->getHalfExtents().z; + Ogre::Quaternion playerOrient = Ogre::Quaternion(Ogre::Radian(getPlayerPtr().getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(getPlayerPtr().getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + MWBase::Environment::get().getSoundManager()->setListenerPosDir(playerPos, playerOrient.yAxis(), + playerOrient.zAxis()); + } + void World::updateWindowManager () { // inform the GUI about focused object diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2da6a6e05f..555ed7fb74 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -109,6 +109,7 @@ namespace MWWorld Ptr copyObjectToCell(const Ptr &ptr, CellStore* cell, ESM::Position pos, bool adjustPos=true); + void updateSoundListener(); void updateWindowManager (); void performUpdateSceneQueries (); void getFacedHandle(std::string& facedHandle, float maxDistance, bool ignorePlayer=true); From 855fe33c59458b2abc05887ace95733ebc44e1cb Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Dec 2014 23:26:54 +0100 Subject: [PATCH 012/404] Add vanilla-compatible range limiting for playloopsound (Fixes #244, Fixes #1342) --- apps/openmw/mwbase/soundmanager.hpp | 20 +++++++++++++------- apps/openmw/mwscript/soundextensions.cpp | 8 ++++---- apps/openmw/mwsound/openal_output.cpp | 6 ++++-- apps/openmw/mwsound/soundmanagerimp.cpp | 12 ++++++++++++ 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a02a463dde..bc2f3f1c61 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -42,15 +42,21 @@ namespace MWBase Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position * but do not keep it updated (the sound will not move with * the object and will not stop when the object is deleted. */ - - Play_LoopNoEnv = Play_Loop | Play_NoEnv + Play_RemoveAtDistance = 1<<3, /* (3D only) If the listener gets further than 2000 units away + from the sound source, the sound is removed. + This is weird stuff but apparently how vanilla works for sounds + played by the PlayLoopSound family of script functions. Perhaps we + can make this cut off a more subtle fade later, but have to + be careful to not change the overall volume of areas by too much. */ + Play_LoopNoEnv = Play_Loop | Play_NoEnv, + Play_LoopRemoveAtDistance = Play_Loop | Play_RemoveAtDistance }; enum PlayType { - Play_TypeSfx = 1<<3, /* Normal SFX sound */ - Play_TypeVoice = 1<<4, /* Voice sound */ - Play_TypeFoot = 1<<5, /* Footstep sound */ - Play_TypeMusic = 1<<6, /* Music track */ - Play_TypeMovie = 1<<7, /* Movie audio track */ + Play_TypeSfx = 1<<4, /* Normal SFX sound */ + Play_TypeVoice = 1<<5, /* Voice sound */ + Play_TypeFoot = 1<<6, /* Footstep sound */ + Play_TypeMusic = 1<<7, /* Music track */ + Play_TypeMovie = 1<<8, /* Movie audio track */ Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeFoot|Play_TypeMusic|Play_TypeMovie }; diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 73c3ec93a5..606de7aa01 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -121,8 +121,8 @@ namespace MWScript MWBase::Environment::get().getSoundManager()->playSound3D(ptr, sound, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, - mLoop ? MWBase::SoundManager::Play_Loop : - MWBase::SoundManager::Play_Normal); + mLoop ? MWBase::SoundManager::Play_LoopRemoveAtDistance + : MWBase::SoundManager::Play_Normal); } }; @@ -150,8 +150,8 @@ namespace MWScript MWBase::Environment::get().getSoundManager()->playSound3D(ptr, sound, volume, pitch, MWBase::SoundManager::Play_TypeSfx, - mLoop ? MWBase::SoundManager::Play_Loop : - MWBase::SoundManager::Play_Normal); + mLoop ? MWBase::SoundManager::Play_LoopRemoveAtDistance + : MWBase::SoundManager::Play_Normal); } }; diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index fcdc60ee39..bc94789456 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -628,7 +628,9 @@ void OpenAL_Sound3D::update() { ALfloat gain = mVolume*mBaseVolume; ALfloat pitch = mPitch; - if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance) + gain = 0.0f; + else if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; @@ -694,7 +696,7 @@ void OpenAL_Output::init(const std::string &devname) fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice))); } - alDistanceModel(AL_INVERSE_DISTANCE); + alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); throwALerror(); ALCint maxmono=0, maxstereo=0; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 251b05890b..d856f41ee6 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -385,6 +385,11 @@ namespace MWSound const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos); + if ((mode & Play_RemoveAtDistance) && mListenerPos.squaredDistance(objpos) > 2000*2000) + { + return MWBase::SoundPtr(); + } + sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|type, offset); if((mode&Play_NoTrack)) mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); @@ -650,6 +655,13 @@ namespace MWSound const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos); snditer->first->setPosition(objpos); + + if ((snditer->first->mFlags & Play_RemoveAtDistance) + && mListenerPos.squaredDistance(Ogre::Vector3(ptr.getRefData().getPosition().pos)) > 2000*2000) + { + mActiveSounds.erase(snditer++); + continue; + } } //update fade out if(snditer->first->mFadeOutTime>0) From 0fe7500f7437b34a6100a73adf5877daebd8e1aa Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Dec 2014 00:13:56 +0100 Subject: [PATCH 013/404] Work around pathgrid record limitation (Fixes #2195) --- apps/openmw/mwmechanics/aiwander.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 3 +- apps/openmw/mwmechanics/pathgrid.cpp | 9 +-- apps/openmw/mwmechanics/pathgrid.hpp | 2 +- apps/openmw/mwrender/debugging.cpp | 3 +- apps/openmw/mwworld/cellstore.cpp | 2 +- apps/openmw/mwworld/store.hpp | 83 ++++++++----------------- 7 files changed, 39 insertions(+), 65 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c8a0c85d58..3224127dfd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -382,7 +382,7 @@ namespace MWMechanics { // infrequently used, therefore no benefit in caching it as a member const ESM::Pathgrid * - pathgrid = world->getStore().get().search(*cell); + pathgrid = world->getStore().get().search(*cell, world->getCellName(currentCell)); // cache the current cell location cachedCellX = cell->mData.mX; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f1279c415e..f0e5b1d9de 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -188,7 +188,8 @@ namespace MWMechanics if(mCell != cell || !mPathgrid) { mCell = cell; - mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); + mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell(), + MWBase::Environment::get().getWorld()->getCellName(mCell)); } // Refer to AiWander reseach topic on openmw forums for some background. diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 4983a4a4f2..d380cba4e3 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -95,7 +95,7 @@ namespace MWMechanics * +----------------> * high cost */ - bool PathgridGraph::load(const ESM::Cell* cell) + bool PathgridGraph::load(const MWWorld::CellStore *cell) { if(!cell) return false; @@ -103,9 +103,10 @@ namespace MWMechanics if(mIsGraphConstructed) return true; - mCell = cell; - mIsExterior = cell->isExterior(); - mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); + mCell = cell->getCell(); + mIsExterior = cell->getCell()->isExterior(); + mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell->getCell(), + MWBase::Environment::get().getWorld()->getCellName(cell)); if(!mPathgrid) return false; diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp index 5d01dca009..2742957a68 100644 --- a/apps/openmw/mwmechanics/pathgrid.hpp +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -21,7 +21,7 @@ namespace MWMechanics public: PathgridGraph(); - bool load(const ESM::Cell *cell); + bool load(const MWWorld::CellStore *cell); // returns true if end point is strongly connected (i.e. reachable // from start point) both start and end are pathgrid point indexes diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 4f5536ca32..553a6379f5 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -231,8 +231,9 @@ void Debugging::togglePathgrid() void Debugging::enableCellPathgrid(MWWorld::CellStore *store) { + MWBase::World* world = MWBase::Environment::get().getWorld(); const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*store->getCell()); + world->getStore().get().search(*store->getCell(), world->getCellName(store)); if (!pathgrid) return; Vector3 cellPathGridPos(0, 0, 0); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 52e70fef26..f1a8451ea3 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -413,7 +413,7 @@ namespace MWWorld // TODO: the pathgrid graph only needs to be loaded for active cells, so move this somewhere else. // In a simple test, loading the graph for all cells in MW + expansions took 200 ms - mPathgridGraph.load(mCell); + mPathgridGraph.load(this); } } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index c8fa087c20..1aaf902f85 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -843,88 +843,59 @@ namespace MWWorld class Store : public StoreBase { private: - typedef std::map Interior; - typedef std::map, ESM::Pathgrid> Exterior; + // Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell. + // For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name. + // mX and mY will be (0,0) for interior cells, but there is also an exterior cell with the coordinates of (0,0), so that doesn't help. + // This is why we keep both interior and exterior pathgrids in the same container here. + typedef std::pair > PathgridKey; + typedef std::map Static; - Interior mInt; - Exterior mExt; + Static mStatic; public: void load(ESM::ESMReader &esm, const std::string &id) { - ESM::Pathgrid pathgrid; pathgrid.load(esm); + PathgridKey key = std::make_pair(pathgrid.mCell, std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY)); + // Try to overwrite existing record - if (!pathgrid.mCell.empty()) - { - std::pair ret = mInt.insert(std::make_pair(pathgrid.mCell, pathgrid)); - if (!ret.second) - ret.first->second = pathgrid; - } - else - { - std::pair ret = mExt.insert(std::make_pair(std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY), - pathgrid)); - if (!ret.second) - ret.first->second = pathgrid; - } + std::pair ret = mStatic.insert(std::make_pair(key, pathgrid)); + if (!ret.second) + ret.first->second = pathgrid; } size_t getSize() const { - return mInt.size() + mExt.size(); + return mStatic.size(); } void setUp() { } - const ESM::Pathgrid *search(int x, int y) const { - Exterior::const_iterator it = mExt.find(std::make_pair(x,y)); - if (it != mExt.end()) - return &(it->second); - return NULL; - } - - const ESM::Pathgrid *find(int x, int y) const { - const ESM::Pathgrid *ptr = search(x, y); - if (ptr == 0) { - std::ostringstream msg; - msg << "Pathgrid at (" << x << ", " << y << ") not found"; - throw std::runtime_error(msg.str()); + const ESM::Pathgrid *search(const ESM::Cell &cell, const std::string& cellName) const { + int x=0,y=0; + if (!(cell.mData.mFlags & ESM::Cell::Interior)) + { + x = cell.mData.mX; + y = cell.mData.mY; } - return ptr; - } + PathgridKey key = std::make_pair(cellName, std::make_pair(x,y)); - const ESM::Pathgrid *search(const std::string &name) const { - Interior::const_iterator it = mInt.find(name); - if (it != mInt.end()) + Static::const_iterator it = mStatic.find(key); + if (it != mStatic.end()) return &(it->second); return NULL; } - const ESM::Pathgrid *find(const std::string &name) const { - const ESM::Pathgrid *ptr = search(name); - if (ptr == 0) { + const ESM::Pathgrid *find(const ESM::Cell &cell, const std::string& cellName) const { + const ESM::Pathgrid* pathgrid = search(cell, cellName); + if (pathgrid == 0) { std::ostringstream msg; - msg << "Pathgrid in cell '" << name << "' not found"; + msg << "Pathgrid in cell '" << cellName << "' not found"; throw std::runtime_error(msg.str()); } - return ptr; - } - - const ESM::Pathgrid *search(const ESM::Cell &cell) const { - if (cell.mData.mFlags & ESM::Cell::Interior) { - return search(cell.mName); - } - return search(cell.mData.mX, cell.mData.mY); - } - - const ESM::Pathgrid *find(const ESM::Cell &cell) const { - if (cell.mData.mFlags & ESM::Cell::Interior) { - return find(cell.mName); - } - return find(cell.mData.mX, cell.mData.mY); + return pathgrid; } }; From 3ad01899828c42e6c314d291bd7e25cfffeef09f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Dec 2014 12:06:44 +0100 Subject: [PATCH 014/404] Take sound listener y rotation (roll) into account, though currently unused for actors --- apps/openmw/mwworld/worldimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1b5d6e002f..2607a6d4d0 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1575,7 +1575,8 @@ namespace MWWorld const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(getPlayerPtr().getRefData().getHandle()); if(actor) playerPos.z += 1.85*actor->getHalfExtents().z; Ogre::Quaternion playerOrient = Ogre::Quaternion(Ogre::Radian(getPlayerPtr().getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * - Ogre::Quaternion(Ogre::Radian(getPlayerPtr().getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + Ogre::Quaternion(Ogre::Radian(getPlayerPtr().getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X) * + Ogre::Quaternion(Ogre::Radian(getPlayerPtr().getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y); MWBase::Environment::get().getSoundManager()->setListenerPosDir(playerPos, playerOrient.yAxis(), playerOrient.zAxis()); } From 109a3f78a1b35990f11c80336242f1b8b9918921 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Dec 2014 16:02:07 +0100 Subject: [PATCH 015/404] Adjust AiFollow distance for groups of multiple followers (Fixes #1637) --- apps/openmw/mwbase/mechanicsmanager.hpp | 1 + apps/openmw/mwmechanics/actors.cpp | 30 ++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 3 ++ apps/openmw/mwmechanics/aifollow.cpp | 35 +++++++++++++++---- apps/openmw/mwmechanics/aifollow.hpp | 5 +++ .../mwmechanics/mechanicsmanagerimp.cpp | 5 +++ .../mwmechanics/mechanicsmanagerimp.hpp | 1 + 7 files changed, 73 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index ce213b316a..b7af1cbf72 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -183,6 +183,7 @@ namespace MWBase ///return the list of actors which are following the given actor /**ie AiFollow is active and the target is the actor**/ virtual std::list getActorsFollowing(const MWWorld::Ptr& actor) = 0; + virtual std::list getActorsFollowingIndices(const MWWorld::Ptr& actor) = 0; ///Returns a list of actors who are fighting the given actor within the fAlarmDistance /** ie AiCombat is active and the target is the actor **/ diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a3cfbfd49a..ae430e5c6a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1470,6 +1470,36 @@ namespace MWMechanics return list; } + std::list Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor) + { + std::list list; + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + const MWWorld::Class &cls = iter->first.getClass(); + CreatureStats &stats = cls.getCreatureStats(iter->first); + if (stats.isDead()) + continue; + + // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package + for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + { + if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) + { + MWWorld::Ptr followTarget = dynamic_cast(*it)->getTarget(); + if (followTarget.isEmpty()) + continue; + if (followTarget == actor) + list.push_back(dynamic_cast(*it)->getFollowIndex()); + else + break; + } + else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + break; + } + } + return list; + } + std::list Actors::getActorsFighting(const MWWorld::Ptr& actor) { std::list list; std::vector neighbors; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 0ccfaad78a..a24095c702 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -112,6 +112,9 @@ namespace MWMechanics /**ie AiFollow is active and the target is the actor **/ std::list getActorsFollowing(const MWWorld::Ptr& actor); + /// Get the list of AiFollow::mFollowIndex for all actors following this target + std::list getActorsFollowingIndices(const MWWorld::Ptr& actor); + ///Returns the list of actors which are fighting the given actor /**ie AiCombat is active and the target is the actor **/ std::list getActorsFighting(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index f309dc7402..8f3e19b465 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "creaturestats.hpp" @@ -15,28 +16,31 @@ #include "steering.hpp" +int MWMechanics::AiFollow::mFollowIndexCounter = 0; + MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z) -, mActorRefId(actorId), mCellId(""), mActorId(-1) +, mActorRefId(actorId), mCellId(""), mActorId(-1), mFollowIndex(mFollowIndexCounter++) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) : mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z) -, mActorRefId(actorId), mCellId(cellId), mActorId(-1) +, mActorRefId(actorId), mCellId(cellId), mActorId(-1), mFollowIndex(mFollowIndexCounter++) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded) : mAlwaysFollow(true), mCommanded(commanded), mRemainingDuration(0), mX(0), mY(0), mZ(0) -, mActorRefId(actorId), mCellId(""), mActorId(-1) +, mActorRefId(actorId), mCellId(""), mActorId(-1), mFollowIndex(mFollowIndexCounter++) { + } MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) : mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) , mActorRefId(follow->mTargetId), mActorId(-1), mCellId(follow->mCellId) - , mCommanded(follow->mCommanded) + , mCommanded(follow->mCommanded), mFollowIndex(mFollowIndexCounter++) { } @@ -48,12 +52,24 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, if (target.isEmpty() || !target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered // with the MechanicsManager ) - return true; //Target doesn't exist + return false; // Target is not here right now, wait for it to return actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); ESM::Position pos = actor.getRefData().getPosition(); //position of the actor + float followDistance = 180; + // When there are multiple actors following the same target, they form a group with each group member at 180*(i+1) distance to the target + int i=0; + std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingIndices(target); + followers.sort(); + for (std::list::iterator it = followers.begin(); it != followers.end(); ++it) + { + if (*it == mFollowIndex) + followDistance *= (i+1); + ++i; + } + if(!mAlwaysFollow) //Update if you only follow for a bit { //Check if we've run out of time @@ -66,7 +82,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, if((pos.pos[0]-mX)*(pos.pos[0]-mX) + (pos.pos[1]-mY)*(pos.pos[1]-mY) + - (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < 100*100) //Close-ish to final position + (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < followDistance*followDistance) //Close-ish to final position { if(actor.getCell()->isExterior()) //Outside? { @@ -84,7 +100,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, //Set the target destination from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; - if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close + if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < followDistance) //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; else { pathTo(actor, dest, duration); //Go to the destination @@ -159,3 +175,8 @@ MWWorld::Ptr MWMechanics::AiFollow::getTarget() else return MWWorld::Ptr(); } + +int MWMechanics::AiFollow::getFollowIndex() const +{ + return mFollowIndex; +} diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index d5dd42826d..23b159b882 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -46,6 +46,8 @@ namespace MWMechanics bool isCommanded() const; + int getFollowIndex() const; + private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ @@ -58,6 +60,9 @@ namespace MWMechanics std::string mActorRefId; int mActorId; std::string mCellId; + int mFollowIndex; + + static int mFollowIndexCounter; }; } #endif diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 9cafe9b3ce..1dbe6f950e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1268,6 +1268,11 @@ namespace MWMechanics return mActors.getActorsFollowing(actor); } + std::list MechanicsManager::getActorsFollowingIndices(const MWWorld::Ptr& actor) + { + return mActors.getActorsFollowingIndices(actor); + } + std::list MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) { return mActors.getActorsFighting(actor); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 9f9e85c5af..1eec26c8ac 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -147,6 +147,7 @@ namespace MWMechanics virtual void getActorsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects); virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); + virtual std::list getActorsFollowingIndices(const MWWorld::Ptr& actor); virtual std::list getActorsFighting(const MWWorld::Ptr& actor); From e0c6f845464f211fc7f87a11e5b2b9c9a3b3d554 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Dec 2014 22:25:28 +0100 Subject: [PATCH 016/404] AiFollow: target has to be seen in order to start following (Fixes #1637) --- apps/openmw/mwmechanics/aifollow.cpp | 77 +++++++++++++++++++++------- apps/openmw/mwmechanics/aifollow.hpp | 1 + components/esm/aisequence.cpp | 4 ++ components/esm/aisequence.hpp | 2 + 4 files changed, 66 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 8f3e19b465..161f4bb905 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -13,39 +13,51 @@ #include "movement.hpp" #include +#include #include "steering.hpp" -int MWMechanics::AiFollow::mFollowIndexCounter = 0; +namespace MWMechanics +{ + + +struct AiFollowStorage : AiTemporaryBase +{ + float mTimer; + + AiFollowStorage() : mTimer(0.f) {} +}; -MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) +int AiFollow::mFollowIndexCounter = 0; + +AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z) -, mActorRefId(actorId), mCellId(""), mActorId(-1), mFollowIndex(mFollowIndexCounter++) +, mActorRefId(actorId), mCellId(""), mActorId(-1), mFollowIndex(mFollowIndexCounter++), mActive(false) { } -MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) +AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) : mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z) -, mActorRefId(actorId), mCellId(cellId), mActorId(-1), mFollowIndex(mFollowIndexCounter++) +, mActorRefId(actorId), mCellId(cellId), mActorId(-1), mFollowIndex(mFollowIndexCounter++), mActive(false) { } -MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded) +AiFollow::AiFollow(const std::string &actorId, bool commanded) : mAlwaysFollow(true), mCommanded(commanded), mRemainingDuration(0), mX(0), mY(0), mZ(0) -, mActorRefId(actorId), mCellId(""), mActorId(-1), mFollowIndex(mFollowIndexCounter++) +, mActorRefId(actorId), mCellId(""), mActorId(-1), mFollowIndex(mFollowIndexCounter++), mActive(false) { } -MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) +AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) : mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) , mActorRefId(follow->mTargetId), mActorId(-1), mCellId(follow->mCellId) - , mCommanded(follow->mCommanded), mFollowIndex(mFollowIndexCounter++) + , mCommanded(follow->mCommanded), mFollowIndex(mFollowIndexCounter++), mActive(follow->mActive) { } -bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, float duration) +bool AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, float duration) { MWWorld::Ptr target = getTarget(); @@ -56,6 +68,24 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); + // AiFollow requires the target to be in range and within sight for the initial activation + if (!mActive) + { + AiFollowStorage& storage = state.get(); + storage.mTimer -= duration; + + if (storage.mTimer < 0) + { + if (Ogre::Vector3(actor.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(target.getRefData().getPosition().pos)) + < 500*500 + && MWBase::Environment::get().getWorld()->getLOS(actor, target)) + mActive = true; + storage.mTimer = 0.5f; + } + } + if (!mActive) + return false; + ESM::Position pos = actor.getRefData().getPosition(); //position of the actor float followDistance = 180; @@ -101,8 +131,16 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < followDistance) //Stop when you get close + { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - else { + + // turn towards target anyway + float directionX = target.getRefData().getPosition().pos[0] - actor.getRefData().getPosition().pos[0]; + float directionY = target.getRefData().getPosition().pos[1] - actor.getRefData().getPosition().pos[1]; + zTurn(actor, Ogre::Math::ATan2(directionX,directionY), Ogre::Degree(5)); + } + else + { pathTo(actor, dest, duration); //Go to the destination } @@ -115,27 +153,27 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, return false; } -std::string MWMechanics::AiFollow::getFollowedActor() +std::string AiFollow::getFollowedActor() { return mActorRefId; } -MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const +AiFollow *MWMechanics::AiFollow::clone() const { return new AiFollow(*this); } -int MWMechanics::AiFollow::getTypeId() const +int AiFollow::getTypeId() const { return TypeIdFollow; } -bool MWMechanics::AiFollow::isCommanded() const +bool AiFollow::isCommanded() const { return mCommanded; } -void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const +void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr follow(new ESM::AiSequence::AiFollow()); follow->mData.mX = mX; @@ -146,6 +184,7 @@ void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) co follow->mCellId = mCellId; follow->mAlwaysFollow = mAlwaysFollow; follow->mCommanded = mCommanded; + follow->mActive = mActive; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Follow; @@ -153,7 +192,7 @@ void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) co sequence.mPackages.push_back(package); } -MWWorld::Ptr MWMechanics::AiFollow::getTarget() +MWWorld::Ptr AiFollow::getTarget() { if (mActorId == -2) return MWWorld::Ptr(); @@ -176,7 +215,9 @@ MWWorld::Ptr MWMechanics::AiFollow::getTarget() return MWWorld::Ptr(); } -int MWMechanics::AiFollow::getFollowIndex() const +int AiFollow::getFollowIndex() const { return mFollowIndex; } + +} diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 23b159b882..68a1f0ea5f 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -60,6 +60,7 @@ namespace MWMechanics std::string mActorRefId; int mActorId; std::string mCellId; + bool mActive; // have we spotted the target? int mFollowIndex; static int mFollowIndexCounter; diff --git a/components/esm/aisequence.cpp b/components/esm/aisequence.cpp index 0e3e541024..339b390d74 100644 --- a/components/esm/aisequence.cpp +++ b/components/esm/aisequence.cpp @@ -60,6 +60,8 @@ namespace AiSequence esm.getHNT (mAlwaysFollow, "ALWY"); mCommanded = false; esm.getHNOT (mCommanded, "CMND"); + mActive = false; + esm.getHNOT (mActive, "ACTV"); } void AiFollow::save(ESMWriter &esm) const @@ -71,6 +73,8 @@ namespace AiSequence esm.writeHNString ("CELL", mCellId); esm.writeHNT ("ALWY", mAlwaysFollow); esm.writeHNT ("CMND", mCommanded); + if (mActive) + esm.writeHNT("ACTV", mActive); } void AiActivate::load(ESMReader &esm) diff --git a/components/esm/aisequence.hpp b/components/esm/aisequence.hpp index da16bf867a..fbf83c2455 100644 --- a/components/esm/aisequence.hpp +++ b/components/esm/aisequence.hpp @@ -98,6 +98,8 @@ namespace ESM bool mAlwaysFollow; bool mCommanded; + bool mActive; + void load(ESMReader &esm); void save(ESMWriter &esm) const; }; From 3c747195ae2f8bcc82d2c978ecdf845cafd10b03 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 17:21:34 +0100 Subject: [PATCH 017/404] Add fall damage for creatures (Fixes #2201) --- apps/openmw/mwclass/npc.cpp | 31 --------------------------- apps/openmw/mwclass/npc.hpp | 3 --- apps/openmw/mwmechanics/character.cpp | 31 ++++++++++++++++++++++++++- apps/openmw/mwworld/class.cpp | 5 ----- apps/openmw/mwworld/class.hpp | 3 --- 5 files changed, 30 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 641edcc834..49f76d25c6 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1002,37 +1002,6 @@ namespace MWClass return x; } - float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const - { - MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &store = world->getStore().get(); - - const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat(); - - if (fallHeight >= fallDistanceMin) - { - const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); - const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); - const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).getMagnitude(); - const float fallAcroBase = store.find("fFallAcroBase")->getFloat(); - const float fallAcroMult = store.find("fFallAcroMult")->getFloat(); - const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat(); - const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat(); - - float x = fallHeight - fallDistanceMin; - x -= (1.5 * acrobaticsSkill) + jumpSpellBonus; - x = std::max(0.0f, x); - - float a = fallAcroBase + fallAcroMult * (100 - acrobaticsSkill); - x = fallDistanceBase + fallDistanceMult * x; - x *= a; - - return x; - } - - return 0; - } - MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index fd16e6f083..a92e72af59 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -104,9 +104,6 @@ namespace MWClass virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) - virtual float getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const; - ///< Return amount of health points lost when falling - virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; ///< Return desired movement. diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 46e06f4604..87d9a5b679 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -92,6 +92,35 @@ MWMechanics::CharacterState runStateToWalkState (MWMechanics::CharacterState sta return ret; } +float getFallDamage(const MWWorld::Ptr& ptr, float fallHeight) +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &store = world->getStore().get(); + + const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat(); + + if (fallHeight >= fallDistanceMin) + { + const float acrobaticsSkill = ptr.getClass().getSkill(ptr, ESM::Skill::Acrobatics); + const float jumpSpellBonus = ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Jump).getMagnitude(); + const float fallAcroBase = store.find("fFallAcroBase")->getFloat(); + const float fallAcroMult = store.find("fFallAcroMult")->getFloat(); + const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat(); + const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat(); + + float x = fallHeight - fallDistanceMin; + x -= (1.5 * acrobaticsSkill) + jumpSpellBonus; + x = std::max(0.0f, x); + + float a = fallAcroBase + fallAcroMult * (100 - acrobaticsSkill); + x = fallDistanceBase + fallDistanceMult * x; + x *= a; + + return x; + } + return 0.f; +} + } namespace MWMechanics @@ -1449,7 +1478,7 @@ void CharacterController::update(float duration) vec.z = 0.0f; float height = cls.getCreatureStats(mPtr).land(); - float healthLost = cls.getFallDamage(mPtr, height); + float healthLost = getFallDamage(mPtr, height); if (healthLost > 0.0f) { const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm(); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 16c2469c14..0a84862097 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -180,11 +180,6 @@ namespace MWWorld throw std::runtime_error ("class does not support enchanting"); } - float Class::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const - { - return 0; - } - MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const { throw std::runtime_error ("movement settings not supported by class"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index b66ca74880..dcac16b06e 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -184,9 +184,6 @@ namespace MWWorld virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) - virtual float getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const; - ///< Return amount of health points lost when falling - virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const; ///< Return desired movement. From ead6bf16011bca8f4b6d35a01b1a6a2a68486d55 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 17:30:33 +0100 Subject: [PATCH 018/404] Enchanting: cast the enchant points for the item to int (Fixes #2202) --- apps/openmw/mwmechanics/enchanting.cpp | 6 +++--- apps/openmw/mwmechanics/enchanting.hpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index c134942b88..759b2a7bba 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -156,7 +156,7 @@ namespace MWMechanics * * Formula on UESPWiki is not entirely correct. */ - float Enchanting::getEnchantPoints() const + int Enchanting::getEnchantPoints() const { if (mEffectList.mList.empty()) // No effects added, cost = 0 @@ -195,7 +195,7 @@ namespace MWMechanics --effectsLeftCnt; } - return enchantmentCost; + return static_cast(enchantmentCost); } @@ -240,7 +240,7 @@ namespace MWMechanics return soul->mData.mSoul; } - float Enchanting::getMaxEnchantValue() const + int Enchanting::getMaxEnchantValue() const { if (itemEmpty()) return 0; diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index 01ca1e0e1d..d41305c4ac 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -35,10 +35,10 @@ namespace MWMechanics bool create(); //Return true if created, false if failed. void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object) int getCastStyle() const; - float getEnchantPoints() const; + int getEnchantPoints() const; float getCastCost() const; int getEnchantPrice() const; - float getMaxEnchantValue() const; + int getMaxEnchantValue() const; int getGemCharge() const; float getEnchantChance() const; bool soulEmpty() const; //Return true if empty From 74c345f79057504ed0976648b82954aa21fa95ee Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 17:40:59 +0100 Subject: [PATCH 019/404] Enchanting: fix being able to create On Touch / On Target constant effect enchantments (this combination makes no sense) --- apps/openmw/mwgui/spellcreationdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 00cab6c455..2124c27247 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -198,11 +198,11 @@ namespace MWGui mRangeButton->setCaptionWithReplacing ("#{sRangeTouch}"); // cycle through range types until we find something that's allowed - if (mEffect.mRange == ESM::RT_Target && !(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget)) + if (mEffect.mRange == ESM::RT_Target && (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) || constantEffect)) onRangeButtonClicked(sender); if (mEffect.mRange == ESM::RT_Self && !(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf)) onRangeButtonClicked(sender); - if (mEffect.mRange == ESM::RT_Touch && !(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch)) + if (mEffect.mRange == ESM::RT_Touch && (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) || constantEffect)) onRangeButtonClicked(sender); if(mEffect.mRange == ESM::RT_Self) From 619ea846b47ad4e45b6a4e90981810a6e9c3ff00 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 17:48:49 +0100 Subject: [PATCH 020/404] Enchanting: fixed case where no range types at all are allowed (e.g. a Constant Effect item with an effect that does not allow the Self range-type) --- apps/openmw/mwgui/spellcreationdialog.cpp | 47 ++++++++++++++--------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 2124c27247..5da33c67ae 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -89,15 +89,22 @@ namespace MWGui void EditEffectDialog::newEffect (const ESM::MagicEffect *effect) { + bool allowSelf = effect->mData.mFlags & ESM::MagicEffect::CastSelf; + bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !constantEffect; + bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !constantEffect; + + if (!allowSelf && !allowTouch && !allowTarget) + return; // TODO: Show an error message popup? + setMagicEffect(effect); mEditing = false; mDeleteButton->setVisible (false); mEffect.mRange = ESM::RT_Self; - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf)) + if (!allowSelf) mEffect.mRange = ESM::RT_Touch; - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch)) + if (!allowTouch) mEffect.mRange = ESM::RT_Target; mEffect.mMagnMin = 1; mEffect.mMagnMax = 1; @@ -118,6 +125,8 @@ namespace MWGui mMagnitudeMinValue->setCaption("1"); mMagnitudeMaxValue->setCaption("- 1"); mAreaValue->setCaption("0"); + + setVisible(true); } void EditEffectDialog::editEffect (ESM::ENAMstruct effect) @@ -190,26 +199,31 @@ namespace MWGui { mEffect.mRange = (mEffect.mRange+1)%3; - if (mEffect.mRange == ESM::RT_Self) - mRangeButton->setCaptionWithReplacing ("#{sRangeSelf}"); - else if (mEffect.mRange == ESM::RT_Target) - mRangeButton->setCaptionWithReplacing ("#{sRangeTarget}"); - else if (mEffect.mRange == ESM::RT_Touch) - mRangeButton->setCaptionWithReplacing ("#{sRangeTouch}"); - // cycle through range types until we find something that's allowed - if (mEffect.mRange == ESM::RT_Target && (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) || constantEffect)) - onRangeButtonClicked(sender); - if (mEffect.mRange == ESM::RT_Self && !(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf)) - onRangeButtonClicked(sender); - if (mEffect.mRange == ESM::RT_Touch && (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) || constantEffect)) - onRangeButtonClicked(sender); + // does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect dialog) + bool allowSelf = mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf; + bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !constantEffect; + bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !constantEffect; + if (mEffect.mRange == ESM::RT_Self && !allowSelf) + mEffect.mRange = (mEffect.mRange+1)%3; + if (mEffect.mRange == ESM::RT_Touch && !allowTouch) + mEffect.mRange = (mEffect.mRange+1)%3; + if (mEffect.mRange == ESM::RT_Target && !allowTarget) + mEffect.mRange = (mEffect.mRange+1)%3; if(mEffect.mRange == ESM::RT_Self) { mAreaSlider->setScrollPosition(0); onAreaChanged(mAreaSlider,0); } + + if (mEffect.mRange == ESM::RT_Self) + mRangeButton->setCaptionWithReplacing ("#{sRangeSelf}"); + else if (mEffect.mRange == ESM::RT_Target) + mRangeButton->setCaptionWithReplacing ("#{sRangeTarget}"); + else if (mEffect.mRange == ESM::RT_Touch) + mRangeButton->setCaptionWithReplacing ("#{sRangeTouch}"); + updateBoxes(); eventEffectModified(mEffect); } @@ -542,7 +556,6 @@ namespace MWGui mAddEffectDialog.newEffect(effect); mAddEffectDialog.setAttribute (mSelectAttributeDialog->getAttributeId()); - mAddEffectDialog.setVisible(true); MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog); mSelectAttributeDialog = 0; } @@ -554,7 +567,6 @@ namespace MWGui mAddEffectDialog.newEffect(effect); mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId()); - mAddEffectDialog.setVisible(true); MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog); mSelectSkillDialog = 0; } @@ -611,7 +623,6 @@ namespace MWGui else { mAddEffectDialog.newEffect(effect); - mAddEffectDialog.setVisible(true); } } From 623783cd6ac77da581a8106aee5abfa02bec2feb Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 18:05:11 +0100 Subject: [PATCH 021/404] Enchanting: fix cast cost for "on use" enchantments being set incorrectly --- apps/openmw/mwgui/enchantingdialog.cpp | 4 ++-- apps/openmw/mwmechanics/enchanting.cpp | 6 +++--- apps/openmw/mwmechanics/enchanting.hpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 30d67f5540..56caa6513b 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -109,8 +109,8 @@ namespace MWGui mCharge->setCaption(boost::lexical_cast(mEnchanting.getGemCharge())); std::stringstream castCost; - castCost << std::setprecision(1) << std::fixed << mEnchanting.getCastCost(); - mCastCost->setCaption(boost::lexical_cast(castCost.str())); + castCost << mEnchanting.getCastCost(); + mCastCost->setCaption(castCost.str()); mPrice->setCaption(boost::lexical_cast(mEnchanting.getEnchantPrice())); diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 759b2a7bba..8c85e5eef5 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -55,7 +55,7 @@ namespace MWMechanics enchantment.mData.mCharge = getGemCharge(); enchantment.mData.mAutocalc = 0; enchantment.mData.mType = mCastStyle; - enchantment.mData.mCost = getEnchantPoints(); + enchantment.mData.mCost = getCastCost(); store.remove(mSoulGemPtr, 1, player); @@ -199,7 +199,7 @@ namespace MWMechanics } - float Enchanting::getCastCost() const + int Enchanting::getCastCost() const { if (mCastStyle == ESM::Enchantment::ConstantEffect) return 0; @@ -215,7 +215,7 @@ namespace MWMechanics */ const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10); - return (castCost < 1) ? 1 : castCost; + return static_cast((castCost < 1) ? 1 : castCost); } diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index d41305c4ac..2ee5ccce4e 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -36,7 +36,7 @@ namespace MWMechanics void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object) int getCastStyle() const; int getEnchantPoints() const; - float getCastCost() const; + int getCastCost() const; int getEnchantPrice() const; int getMaxEnchantValue() const; int getGemCharge() const; From 6eebe9b44c2bb041b05854e70b27b4340a608181 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 20:51:32 +0100 Subject: [PATCH 022/404] Read NiFogProperty, but don't use it yet (Feature #920) --- components/nif/niffile.cpp | 1 + components/nif/property.hpp | 16 ++++++++++++++++ components/nif/record.hpp | 1 + 3 files changed, 18 insertions(+) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index c689e27b3a..9d63ac7ab5 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -57,6 +57,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiCamera", &construct , RC_NiCamera )); newFactory.insert(makeEntry("RootCollisionNode", &construct , RC_RootCollisionNode )); newFactory.insert(makeEntry("NiTexturingProperty", &construct , RC_NiTexturingProperty )); + newFactory.insert(makeEntry("NiFogProperty", &construct , RC_NiFogProperty )); newFactory.insert(makeEntry("NiMaterialProperty", &construct , RC_NiMaterialProperty )); newFactory.insert(makeEntry("NiZBufferProperty", &construct , RC_NiZBufferProperty )); newFactory.insert(makeEntry("NiAlphaProperty", &construct , RC_NiAlphaProperty )); diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 2c7747a3ec..77f61d0684 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -155,6 +155,22 @@ public: } }; +class NiFogProperty : public Property +{ +public: + float mFogDepth; + Ogre::Vector3 mColour; + + + void read(NIFStream *nif) + { + Property::read(nif); + + mFogDepth = nif->getFloat(); + mColour = nif->getVector3(); + } +}; + // These contain no other data than the 'flags' field in Property class NiShadeProperty : public Property { }; class NiDitherProperty : public Property { }; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 079b335f05..07d7540f85 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -44,6 +44,7 @@ enum RecordType RC_NiBSParticleNode, RC_NiCamera, RC_NiTexturingProperty, + RC_NiFogProperty, RC_NiMaterialProperty, RC_NiZBufferProperty, RC_NiAlphaProperty, From 33019b93b4a17c7facb077abb9836efcfa1409b7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 21:10:14 +0100 Subject: [PATCH 023/404] Fix bug setting current launcher profile on startup (Bug #2188) --- apps/launcher/datafilespage.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index f45b444707..4015579c20 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -238,10 +238,8 @@ void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurr if (profile.isEmpty()) return; - if (ui.profilesComboBox->findText (profile) != -1) - return; - - ui.profilesComboBox->addItem (profile); + if (ui.profilesComboBox->findText (profile) == -1) + ui.profilesComboBox->addItem (profile); if (setAsCurrent) setProfile (ui.profilesComboBox->findText (profile), false); From fb1aa096beb67042d3bcb997fead1be4a9b9e1b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 21:46:36 +0100 Subject: [PATCH 024/404] Settings: reduce scope for better readability --- components/config/settingsbase.hpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/components/config/settingsbase.hpp b/components/config/settingsbase.hpp index 92ca34cdfe..e6b0908e0a 100644 --- a/components/config/settingsbase.hpp +++ b/components/config/settingsbase.hpp @@ -50,7 +50,7 @@ namespace Config bool readFile(QTextStream &stream) { - mCache.clear(); + Map cache; QString sectionPrefix; @@ -79,31 +79,30 @@ namespace Config mSettings.remove(key); - QStringList values = mCache.values(key); + QStringList values = cache.values(key); if (!values.contains(value)) { if (mMultiValue) { - mCache.insertMulti(key, value); + cache.insertMulti(key, value); } else { - mCache.insert(key, value); + cache.insert(key, value); } } } } if (mSettings.isEmpty()) { - mSettings = mCache; // This is the first time we read a file + mSettings = cache; // This is the first time we read a file return true; } // Merge the changed keys with those which didn't - mSettings.unite(mCache); + mSettings.unite(cache); return true; } private: Map mSettings; - Map mCache; bool mMultiValue; }; From 1937ace1b748e66b9044e679fd3923172601c006 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 21:47:04 +0100 Subject: [PATCH 025/404] Launcher: fix bugs deleting profiles (Fixes #2188) --- apps/launcher/datafilespage.cpp | 18 ++++++++++-------- apps/launcher/datafilespage.hpp | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 4015579c20..7192ed7842 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -154,9 +154,11 @@ void Launcher::DataFilesPage::setProfile(int index, bool savePrevious) { if (index >= -1 && index < ui.profilesComboBox->count()) { - QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex()); + QString previous = mPreviousProfile; QString current = ui.profilesComboBox->itemText(index); + mPreviousProfile = current; + setProfile (previous, current, savePrevious); } } @@ -167,9 +169,6 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString if (previous == current) return; - if (previous.isEmpty()) - return; - if (!previous.isEmpty() && savePrevious) saveSettings (previous); @@ -212,7 +211,7 @@ void Launcher::DataFilesPage::slotProfileChanged(int index) void Launcher::DataFilesPage::on_newProfileAction_triggered() { - if (!mProfileDialog->exec() == QDialog::Accepted) + if (mProfileDialog->exec() != QDialog::Accepted) return; QString profile = mProfileDialog->lineEdit()->text(); @@ -222,9 +221,10 @@ void Launcher::DataFilesPage::on_newProfileAction_triggered() saveSettings(); - mSelector->clearCheckStates(); + mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); addProfile(profile, true); + mSelector->clearCheckStates(); mSelector->setGameFile(); @@ -255,10 +255,12 @@ void Launcher::DataFilesPage::on_deleteProfileAction_triggered() if (!showDeleteMessageBox (profile)) return; - // Remove the profile from the combobox - ui.profilesComboBox->removeItem (ui.profilesComboBox->findText (profile)); + // this should work since the Default profile can't be deleted and is always index 0 + int next = ui.profilesComboBox->currentIndex()-1; + ui.profilesComboBox->setCurrentIndex(next); removeProfile(profile); + ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile)); saveSettings(); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 5beeb0e03a..15fa00308d 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -67,6 +67,8 @@ namespace Launcher Config::GameSettings &mGameSettings; Config::LauncherSettings &mLauncherSettings; + QString mPreviousProfile; + QString mDataLocal; void setPluginsCheckstates(Qt::CheckState state); From 04a68fc9765eb57c613f0054c9d8f5198d2699bd Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 00:20:45 +0100 Subject: [PATCH 026/404] Revert "Implement overwriting pathgrid records" This broke pathgrid loading in exterior cells due to an unexpected problem in the pathgrid record system (bug #2195). This reverts commit dd0cea21b0ead17a13198f3e584d343c6bb31875. --- apps/openmw/mwworld/store.hpp | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 469b93f88e..55c5b8191f 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -876,36 +876,8 @@ namespace MWWorld public: void load(ESM::ESMReader &esm, const std::string &id) { - - ESM::Pathgrid pathgrid; - pathgrid.load(esm); - - // Try to overwrite existing record - // Can't use search() because we aren't sorted yet - if (!pathgrid.mCell.empty()) - { - for (std::vector::iterator it = mStatic.begin(); it != mStatic.end(); ++it) - { - if ((*it).mCell == pathgrid.mCell) - { - (*it) = pathgrid; - return; - } - } - } - else - { - for (std::vector::iterator it = mStatic.begin(); it != mStatic.end(); ++it) - { - if ((*it).mData.mX == pathgrid.mData.mX && (*it).mData.mY == pathgrid.mData.mY) - { - (*it) = pathgrid; - return; - } - } - } - - mStatic.push_back(pathgrid); + mStatic.push_back(ESM::Pathgrid()); + mStatic.back().load(esm); } size_t getSize() const { From 46cf2a7a774fd96bd15f2f22c48d23dbba332e7d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 11 Dec 2014 09:20:18 +0100 Subject: [PATCH 027/404] updated changelog --- readme.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.txt b/readme.txt index e91455aebe..83f139a1d8 100644 --- a/readme.txt +++ b/readme.txt @@ -160,7 +160,6 @@ Bug #2161: Editor: combat/magic/stealth values of creature not displayed correct Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon. Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly. Bug #2170: Mods using conversations to update PC inconsistant -Bug #2175: Pathgrid mods do not overwrite the existing pathgrid Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources Feature #238: Add UI to run INI-importer from the launcher Feature #854: Editor: Add user setting to show status bar From 9ef6e95bf6a1677f47ca0717a711ab0390bf057d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 21:47:04 +0100 Subject: [PATCH 028/404] Launcher: fix bugs deleting profiles (Fixes #2188) --- apps/launcher/datafilespage.cpp | 18 ++++++++++-------- apps/launcher/datafilespage.hpp | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index f45b444707..063d601975 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -154,9 +154,11 @@ void Launcher::DataFilesPage::setProfile(int index, bool savePrevious) { if (index >= -1 && index < ui.profilesComboBox->count()) { - QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex()); + QString previous = mPreviousProfile; QString current = ui.profilesComboBox->itemText(index); + mPreviousProfile = current; + setProfile (previous, current, savePrevious); } } @@ -167,9 +169,6 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString if (previous == current) return; - if (previous.isEmpty()) - return; - if (!previous.isEmpty() && savePrevious) saveSettings (previous); @@ -212,7 +211,7 @@ void Launcher::DataFilesPage::slotProfileChanged(int index) void Launcher::DataFilesPage::on_newProfileAction_triggered() { - if (!mProfileDialog->exec() == QDialog::Accepted) + if (mProfileDialog->exec() != QDialog::Accepted) return; QString profile = mProfileDialog->lineEdit()->text(); @@ -222,9 +221,10 @@ void Launcher::DataFilesPage::on_newProfileAction_triggered() saveSettings(); - mSelector->clearCheckStates(); + mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); addProfile(profile, true); + mSelector->clearCheckStates(); mSelector->setGameFile(); @@ -257,10 +257,12 @@ void Launcher::DataFilesPage::on_deleteProfileAction_triggered() if (!showDeleteMessageBox (profile)) return; - // Remove the profile from the combobox - ui.profilesComboBox->removeItem (ui.profilesComboBox->findText (profile)); + // this should work since the Default profile can't be deleted and is always index 0 + int next = ui.profilesComboBox->currentIndex()-1; + ui.profilesComboBox->setCurrentIndex(next); removeProfile(profile); + ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile)); saveSettings(); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 5beeb0e03a..15fa00308d 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -67,6 +67,8 @@ namespace Launcher Config::GameSettings &mGameSettings; Config::LauncherSettings &mLauncherSettings; + QString mPreviousProfile; + QString mDataLocal; void setPluginsCheckstates(Qt::CheckState state); From ed66bbb28d4b1dfb2bd00579c2c75aae5ad1b27e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Dec 2014 21:10:14 +0100 Subject: [PATCH 029/404] Fix bug setting current launcher profile on startup (Bug #2188) --- apps/launcher/datafilespage.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 063d601975..7192ed7842 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -238,10 +238,8 @@ void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurr if (profile.isEmpty()) return; - if (ui.profilesComboBox->findText (profile) != -1) - return; - - ui.profilesComboBox->addItem (profile); + if (ui.profilesComboBox->findText (profile) == -1) + ui.profilesComboBox->addItem (profile); if (setAsCurrent) setProfile (ui.profilesComboBox->findText (profile), false); From cda0363f29f9fa24e603ca0c375e8f8a0e365ba4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 11 Dec 2014 13:51:37 +0100 Subject: [PATCH 030/404] allow a space in the middle of multi-character comparison operators (Fixes #2185) --- components/compiler/scanner.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 16d54ff51d..203f27e6e8 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -391,6 +391,10 @@ namespace Compiler { if (get (c)) { + /// \todo hack to allow a space in comparison operators (add option to disable) + if (c==' ') + get (c); + if (c=='=') special = S_cmpEQ; else @@ -398,7 +402,7 @@ namespace Compiler special = S_cmpEQ; putback (c); // return false; -// Allow = as synonym for ==. \todo optionally disable for post-1.0 scripting improvements. +/// Allow = as synonym for ==. \todo optionally disable for post-1.0 scripting improvements. } } else @@ -411,6 +415,10 @@ namespace Compiler { if (get (c)) { + /// \todo hack to allow a space in comparison operators (add option to disable) + if (c==' ' && !get (c)) + return false; + if (c=='=') special = S_cmpNE; else @@ -441,6 +449,10 @@ namespace Compiler { if (get (c)) { + /// \todo hack to allow a space in comparison operators (add option to disable) + if (c==' ') + get (c); + if (c=='=') { special = S_cmpLE; @@ -461,6 +473,10 @@ namespace Compiler { if (get (c)) { + /// \todo hack to allow a space in comparison operators (add option to disable) + if (c==' ') + get (c); + if (c=='=') { special = S_cmpGE; From 3270f0e932de557a9e5ec72390e180ad53477de6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 15:19:48 +0100 Subject: [PATCH 031/404] Change pathgrid workaround to check for interior cell name --- apps/openmw/mwmechanics/aiwander.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 3 +- apps/openmw/mwmechanics/pathgrid.cpp | 4 +- apps/openmw/mwrender/debugging.cpp | 2 +- apps/openmw/mwworld/esmstore.hpp | 2 + apps/openmw/mwworld/store.hpp | 95 ++++++++++++++++++------- 6 files changed, 75 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 3224127dfd..c8a0c85d58 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -382,7 +382,7 @@ namespace MWMechanics { // infrequently used, therefore no benefit in caching it as a member const ESM::Pathgrid * - pathgrid = world->getStore().get().search(*cell, world->getCellName(currentCell)); + pathgrid = world->getStore().get().search(*cell); // cache the current cell location cachedCellX = cell->mData.mX; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f0e5b1d9de..f1279c415e 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -188,8 +188,7 @@ namespace MWMechanics if(mCell != cell || !mPathgrid) { mCell = cell; - mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell(), - MWBase::Environment::get().getWorld()->getCellName(mCell)); + mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); } // Refer to AiWander reseach topic on openmw forums for some background. diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index d380cba4e3..848d2c7a03 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -105,9 +105,7 @@ namespace MWMechanics mCell = cell->getCell(); mIsExterior = cell->getCell()->isExterior(); - mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell->getCell(), - MWBase::Environment::get().getWorld()->getCellName(cell)); - + mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell->getCell()); if(!mPathgrid) return false; diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 553a6379f5..972c1b6dd0 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -233,7 +233,7 @@ void Debugging::enableCellPathgrid(MWWorld::CellStore *store) { MWBase::World* world = MWBase::Environment::get().getWorld(); const ESM::Pathgrid *pathgrid = - world->getStore().get().search(*store->getCell(), world->getCellName(store)); + world->getStore().get().search(*store->getCell()); if (!pathgrid) return; Vector3 cellPathGridPos(0, 0, 0); diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 90786acd42..83e911174d 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -141,6 +141,8 @@ namespace MWWorld mStores[ESM::REC_SSCR] = &mStartScripts; mStores[ESM::REC_STAT] = &mStatics; mStores[ESM::REC_WEAP] = &mWeapons; + + mPathgrids.setCells(mCells); } void clearDynamic () diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 1aaf902f85..c4070f0327 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -843,60 +843,103 @@ namespace MWWorld class Store : public StoreBase { private: - // Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell. - // For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name. - // mX and mY will be (0,0) for interior cells, but there is also an exterior cell with the coordinates of (0,0), so that doesn't help. - // This is why we keep both interior and exterior pathgrids in the same container here. - typedef std::pair > PathgridKey; - typedef std::map Static; + typedef std::map Interior; + typedef std::map, ESM::Pathgrid> Exterior; - Static mStatic; + Interior mInt; + Exterior mExt; + + Store* mCells; public: + void setCells(Store& cells) + { + mCells = &cells; + } + void load(ESM::ESMReader &esm, const std::string &id) { ESM::Pathgrid pathgrid; pathgrid.load(esm); - PathgridKey key = std::make_pair(pathgrid.mCell, std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY)); + // Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell. + // For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name. + // mX and mY will be (0,0) for interior cells, but there is also an exterior cell with the coordinates of (0,0), so that doesn't help. + // Check whether mCell is an interior cell. This isn't perfect, will break if a Region with the same name as an interior cell is created. + // A proper fix should be made for future versions of the file format. + bool interior = mCells->search(pathgrid.mCell) != NULL; // Try to overwrite existing record - std::pair ret = mStatic.insert(std::make_pair(key, pathgrid)); - if (!ret.second) - ret.first->second = pathgrid; + if (interior) + { + std::pair ret = mInt.insert(std::make_pair(pathgrid.mCell, pathgrid)); + if (!ret.second) + ret.first->second = pathgrid; + } + else + { + std::pair ret = mExt.insert(std::make_pair(std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY), pathgrid)); + if (!ret.second) + ret.first->second = pathgrid; + } } size_t getSize() const { - return mStatic.size(); + return mInt.size() + mExt.size(); } void setUp() { } - const ESM::Pathgrid *search(const ESM::Cell &cell, const std::string& cellName) const { - int x=0,y=0; - if (!(cell.mData.mFlags & ESM::Cell::Interior)) - { - x = cell.mData.mX; - y = cell.mData.mY; - } - PathgridKey key = std::make_pair(cellName, std::make_pair(x,y)); + const ESM::Pathgrid *search(int x, int y) const { + Exterior::const_iterator it = mExt.find(std::make_pair(x,y)); + if (it != mExt.end()) + return &(it->second); + return NULL; + } - Static::const_iterator it = mStatic.find(key); - if (it != mStatic.end()) + const ESM::Pathgrid *search(const std::string& name) const { + Interior::const_iterator it = mInt.find(name); + if (it != mInt.end()) return &(it->second); return NULL; } - const ESM::Pathgrid *find(const ESM::Cell &cell, const std::string& cellName) const { - const ESM::Pathgrid* pathgrid = search(cell, cellName); - if (pathgrid == 0) { + const ESM::Pathgrid *find(int x, int y) const { + const ESM::Pathgrid* pathgrid = search(x,y); + if (!pathgrid) + { + std::ostringstream msg; + msg << "Pathgrid in cell '" << x << " " << y << "' not found"; + throw std::runtime_error(msg.str()); + } + return pathgrid; + } + + const ESM::Pathgrid* find(const std::string& name) const { + const ESM::Pathgrid* pathgrid = search(name); + if (!pathgrid) + { std::ostringstream msg; - msg << "Pathgrid in cell '" << cellName << "' not found"; + msg << "Pathgrid in cell '" << name << "' not found"; throw std::runtime_error(msg.str()); } return pathgrid; } + + const ESM::Pathgrid *search(const ESM::Cell &cell) const { + if (!(cell.mData.mFlags & ESM::Cell::Interior)) + return search(cell.mData.mX, cell.mData.mY); + else + return search(cell.mName); + } + + const ESM::Pathgrid *find(const ESM::Cell &cell) const { + if (!(cell.mData.mFlags & ESM::Cell::Interior)) + return find(cell.mData.mX, cell.mData.mY); + else + return find(cell.mName); + } }; template From 8ed376af5e09a975895fd858654c43f8318a46ad Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 15:59:27 +0100 Subject: [PATCH 032/404] Launcher: fix changing active profile through the Play page --- apps/launcher/datafilespage.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 7192ed7842..3c4d36de77 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -206,6 +206,10 @@ void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const void Launcher::DataFilesPage::slotProfileChanged(int index) { + // in case the event was triggered externally + if (ui.profilesComboBox->currentIndex() != index) + ui.profilesComboBox->setCurrentIndex(index); + setProfile (index, true); } From 7e8ca3fff1837b632145d9f076a5769d0c9a7fd8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 19:27:13 +0100 Subject: [PATCH 033/404] Fix object movement between cells producing a stale Ptr within the script execution (Bug #1942) --- apps/openmw/mwbase/world.hpp | 3 +- apps/openmw/mwscript/interpretercontext.cpp | 6 ++++ apps/openmw/mwscript/interpretercontext.hpp | 3 ++ .../mwscript/transformationextensions.cpp | 36 ++++++++++++------- apps/openmw/mwworld/worldimp.cpp | 10 +++--- apps/openmw/mwworld/worldimp.hpp | 7 ++-- 6 files changed, 45 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 2dd135f3da..c674145aef 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -281,7 +281,8 @@ namespace MWBase virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0; - virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; + virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; + ///< @return an updated Ptr in case the Ptr's cell changes virtual void moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z) = 0; diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index d8d13a9211..430389e30c 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -590,4 +590,10 @@ namespace MWScript { return mTargetId; } + + void InterpreterContext::updatePtr(const MWWorld::Ptr& updated) + { + if (!mReference.isEmpty()) + mReference = updated; + } } diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index b543399656..7f3172dd1b 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -169,6 +169,9 @@ namespace MWScript MWWorld::Ptr getReference(bool required=true); ///< Reference, that the script is running from (can be empty) + void updatePtr(const MWWorld::Ptr& updated); + ///< Update the Ptr stored in mReference, if there is one stored there. Should be called after the reference has been moved to a new cell. + virtual std::string getTargetId() const; }; } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 8e6d925b7c..ac9dea4081 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -224,20 +224,23 @@ namespace MWScript float ay = ptr.getRefData().getPosition().pos[1]; float az = ptr.getRefData().getPosition().pos[2]; + MWWorld::Ptr updated = ptr; if(axis == "x") { - MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az); } else if(axis == "y") { - MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az); } else if(axis == "z") { - MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos); } else throw std::runtime_error ("invalid axis: " + axis); + + dynamic_cast(runtime.getContext()).updatePtr(updated); } }; @@ -317,6 +320,8 @@ namespace MWScript { MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z); ptr = MWWorld::Ptr(ptr.getBase(), store); + dynamic_cast(runtime.getContext()).updatePtr(ptr); + float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); // Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200) @@ -365,15 +370,18 @@ namespace MWScript // another morrowind oddity: player will be moved to the exterior cell at this location, // non-player actors will move within the cell they are in. + MWWorld::Ptr updated; if (ptr.getRefData().getHandle() == "player") { - MWBase::Environment::get().getWorld()->moveObject(ptr, - MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z); + MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(cx,cy); + MWBase::Environment::get().getWorld()->moveObject(ptr,cell,x,y,z); + updated = MWWorld::Ptr(ptr.getBase(), cell); } else { - MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z); } + dynamic_cast(runtime.getContext()).updatePtr(updated); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); @@ -638,8 +646,10 @@ namespace MWScript ptr.getRefData().setLocalRotation(rot); MWBase::Environment::get().getWorld()->rotateObject(ptr, 0,0,0,true); - MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], - ptr.getCellRef().getPosition().pos[1], ptr.getCellRef().getPosition().pos[2]); + + dynamic_cast(runtime.getContext()).updatePtr( + MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], + ptr.getCellRef().getPosition().pos[1], ptr.getCellRef().getPosition().pos[2])); } }; @@ -678,7 +688,8 @@ namespace MWScript throw std::runtime_error ("invalid movement axis: " + axis); Ogre::Vector3 worldPos = ptr.getRefData().getBaseNode()->convertLocalToWorldPosition(posChange); - MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z); + dynamic_cast(runtime.getContext()).updatePtr( + MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z)); } }; @@ -701,17 +712,18 @@ namespace MWScript const float *objPos = ptr.getRefData().getPosition().pos; + MWWorld::Ptr updated; if (axis == "x") { - MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]); } else if (axis == "y") { - MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]); } else if (axis == "z") { - MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement); } else throw std::runtime_error ("invalid movement axis: " + axis); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2607a6d4d0..75de050e15 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1187,7 +1187,7 @@ namespace MWWorld } } - bool World::moveObjectImp(const Ptr& ptr, float x, float y, float z) + MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z) { CellStore *cell = ptr.getCell(); @@ -1200,12 +1200,14 @@ namespace MWWorld moveObject(ptr, cell, x, y, z); - return cell != ptr.getCell(); + MWWorld::Ptr updated = ptr; + updated.mCell = cell; + return updated; } - void World::moveObject (const Ptr& ptr, float x, float y, float z) + MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z) { - moveObjectImp(ptr, x, y, z); + return moveObjectImp(ptr, x, y, z); } void World::scaleObject (const Ptr& ptr, float scale) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 555ed7fb74..1548d0c87a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -104,8 +104,8 @@ namespace MWWorld void rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust); - bool moveObjectImp (const Ptr& ptr, float x, float y, float z); - ///< @return true if the active cell (cell player is in) changed + Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z); + ///< @return an updated Ptr in case the Ptr's cell changes Ptr copyObjectToCell(const Ptr &ptr, CellStore* cell, ESM::Position pos, bool adjustPos=true); @@ -341,7 +341,8 @@ namespace MWWorld virtual void deleteObject (const Ptr& ptr); virtual void undeleteObject (const Ptr& ptr); - virtual void moveObject (const Ptr& ptr, float x, float y, float z); + virtual MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z); + ///< @return an updated Ptr in case the Ptr's cell changes virtual void moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z); virtual void scaleObject (const Ptr& ptr, float scale); From ed2aa5a233e16d3d2fa8419607e866f67a7a5589 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 20:32:05 +0100 Subject: [PATCH 034/404] Fix crash caused by dangling baseNode pointer --- apps/openmw/mwworld/worldimp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 75de050e15..e24e99c305 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1158,6 +1158,7 @@ namespace MWWorld ptr.getClass().copyToCell(ptr, *newCell, pos); mRendering->updateObjectCell(ptr, copy); + ptr.getRefData().setBaseNode(NULL); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, copy); MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); From d955017079e69b46b60ba8824c181f3afb543daa Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 20:51:02 +0100 Subject: [PATCH 035/404] Don't report script operation status via messageBox (Bug #1942) --- apps/openmw/mwscript/interpretercontext.cpp | 1 - apps/openmw/mwscript/interpretercontext.hpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index d8d13a9211..88aa46a98f 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -205,7 +205,6 @@ namespace MWScript void InterpreterContext::report (const std::string& message) { - messageBox (message); } bool InterpreterContext::menuMode() diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index b543399656..bcf02e68e9 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -78,7 +78,7 @@ namespace MWScript const std::vector& buttons); virtual void report (const std::string& message); - ///< By default echo via messageBox. + ///< By default, do nothing. virtual bool menuMode(); From be16f1d0a53738f3a67c9278031476a2a9820838 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 20:57:25 +0100 Subject: [PATCH 036/404] Implement PcForce1stPerson, PcForce3rdPerson, PcGet3rdPerson (Bug #2078) --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwscript/docs/vmformat.txt | 5 +++- apps/openmw/mwscript/miscextensions.cpp | 32 +++++++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 4 ++++ components/compiler/extensions0.cpp | 3 +++ components/compiler/opcodes.hpp | 3 +++ 6 files changed, 47 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 2dd135f3da..2bec88998d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -387,6 +387,7 @@ namespace MWBase virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual void togglePOV() = 0; + virtual bool isFirstPerson() const = 0; virtual void togglePreviewMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable) = 0; virtual void allowVanityMode(bool allow) = 0; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b80c84d674..c90f63f7fa 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -433,5 +433,8 @@ op 0x20002c4-0x20002db: ModMagicEffect op 0x20002dc-0x20002f3: ModMagicEffect, explicit op 0x20002f4: ResetActors op 0x20002f5: ToggleWorld +op 0x20002f6: PCForce1stPerson +op 0x20002f7: PCForce3rdPerson +op 0x20002f8: PCGet3rdPerson -opcodes 0x20002f6-0x3ffffff unused +opcodes 0x20002f9-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ec2048e11f..186aa708db 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -312,6 +312,35 @@ namespace MWScript } }; + class OpPcForce1stPerson : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + if (!MWBase::Environment::get().getWorld()->isFirstPerson()) + MWBase::Environment::get().getWorld()->togglePOV(); + } + }; + + class OpPcForce3rdPerson : public Interpreter::Opcode0 + { + virtual void execute (Interpreter::Runtime& runtime) + { + if (MWBase::Environment::get().getWorld()->isFirstPerson()) + MWBase::Environment::get().getWorld()->togglePOV(); + } + }; + + class OpPcGet3rdPerson : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime& runtime) + { + runtime.push(!MWBase::Environment::get().getWorld()->isFirstPerson()); + } + }; + class OpToggleVanityMode : public Interpreter::Opcode0 { static bool sActivate; @@ -1002,6 +1031,9 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeToggleWater, new OpToggleWater); interpreter.installSegment5 (Compiler::Misc::opcodeToggleWorld, new OpToggleWorld); interpreter.installSegment5 (Compiler::Misc::opcodeDontSaveObject, new OpDontSaveObject); + interpreter.installSegment5 (Compiler::Misc::opcodePcForce1stPerson, new OpPcForce1stPerson); + interpreter.installSegment5 (Compiler::Misc::opcodePcForce3rdPerson, new OpPcForce3rdPerson); + interpreter.installSegment5 (Compiler::Misc::opcodePcGet3rdPerson, new OpPcGet3rdPerson); interpreter.installSegment5 (Compiler::Misc::opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcSleep, new OpGetPcSleep); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcJumping, new OpGetPcJumping); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 555ed7fb74..c93d517c19 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -451,6 +451,10 @@ namespace MWWorld mRendering->togglePOV(); } + virtual bool isFirstPerson() const { + return mRendering->getCamera()->isFirstPerson(); + } + virtual void togglePreviewMode(bool enable) { mRendering->togglePreviewMode(enable); } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 7531cdd5bf..cd5bdbe695 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -261,6 +261,9 @@ namespace Compiler extensions.registerInstruction ("togglepathgrid", "", opcodeTogglePathgrid); extensions.registerInstruction ("tpg", "", opcodeTogglePathgrid); extensions.registerInstruction ("dontsaveobject", "", opcodeDontSaveObject); + extensions.registerInstruction ("pcforce1stperson", "", opcodePcForce1stPerson); + extensions.registerInstruction ("pcforce3rdperson", "", opcodePcForce3rdPerson); + extensions.registerFunction ("pcget3rdperson", 'l', "", opcodePcGet3rdPerson); extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 5063397e11..65efc14fa4 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -215,6 +215,9 @@ namespace Compiler const int opcodeToggleWorld = 0x20002f5; const int opcodeTogglePathgrid = 0x2000146; const int opcodeDontSaveObject = 0x2000153; + const int opcodePcForce1stPerson = 0x20002f6; + const int opcodePcForce3rdPerson = 0x20002f7; + const int opcodePcGet3rdPerson = 0x20002f8; const int opcodeToggleVanityMode = 0x2000174; const int opcodeGetPcSleep = 0x200019f; const int opcodeGetPcJumping = 0x2000233; From a355550cabcf9383f35626d1c9f5f4b10c55a4b1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 21:43:31 +0100 Subject: [PATCH 037/404] Add support for NPCs with missing head/hair models (Fixes #2078) --- apps/openmw/mwrender/npcanimation.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index f2bc3df95f..4b5ddcbec3 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -56,8 +56,13 @@ std::string getVampireHead(const std::string& race, bool female) } } - assert(sVampireMapping[thisCombination]); - return "meshes\\" + sVampireMapping[thisCombination]->mModel; + if (sVampireMapping.find(thisCombination) == sVampireMapping.end()) + sVampireMapping[thisCombination] = NULL; + + const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination]; + if (!bodyPart) + return std::string(); + return "meshes\\" + bodyPart->mModel; } bool isSkinned (NifOgre::ObjectScenePtr scene) @@ -256,10 +261,15 @@ void NpcAnimation::updateNpcBase() { if (isVampire) mHeadModel = getVampireHead(mNpc->mRace, mNpc->mFlags & ESM::NPC::Female); - else + else if (!mNpc->mHead.empty()) mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; + else + mHeadModel = ""; - mHairModel = "meshes\\" + store.get().find(mNpc->mHair)->mModel; + if (!mNpc->mHair.empty()) + mHairModel = "meshes\\" + store.get().find(mNpc->mHair)->mModel; + else + mHairModel = ""; } bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; @@ -399,9 +409,9 @@ void NpcAnimation::updateParts() if(mViewMode != VM_FirstPerson) { - if(mPartPriorities[ESM::PRT_Head] < 1) + if(mPartPriorities[ESM::PRT_Head] < 1 && !mHeadModel.empty()) addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel); - if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1) + if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1 && !mHairModel.empty()) addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel); } if(mViewMode == VM_HeadOnly) From 5f00a3d5c3f0b934f21c4742ec8a7b6d4dec8cd3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 22:00:31 +0100 Subject: [PATCH 038/404] Reset lastHitObject when it is retrieved rather than every frame This seems to be how vanilla MW does it. --- apps/openmw/mwmechanics/actors.cpp | 9 --------- apps/openmw/mwscript/miscextensions.cpp | 2 ++ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ae430e5c6a..899ab95088 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1122,15 +1122,6 @@ namespace MWMechanics // target lists get updated once every 1.0 sec if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0; - // Reset data from previous frame - for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) - { - // Reset last hit object, which is only valid for one frame - // Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation - // (below) - iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); - } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int hostilesCount = 0; // need to know this to play Battle music diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 186aa708db..1d07c95915 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -732,6 +732,8 @@ namespace MWScript MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitObject())); + + stats.setLastHitObject(std::string()); } }; From 886903d70e62334cfeb5de1939ee457017462ca9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 22:25:41 +0100 Subject: [PATCH 039/404] Implement HitAttemptOnMe function (Bug #2078) --- apps/openmw/mwclass/creature.cpp | 5 +++-- apps/openmw/mwclass/npc.cpp | 5 +++-- apps/openmw/mwmechanics/creaturestats.cpp | 12 ++++++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 3 +++ apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 21 +++++++++++++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ components/esm/creaturestats.cpp | 5 +++++ components/esm/creaturestats.hpp | 1 + 10 files changed, 54 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 5910c471b3..8076e0619c 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -346,10 +346,11 @@ namespace MWClass setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); } + if(!object.isEmpty()) + getCreatureStats(ptr).setLastHitAttemptObject(object.getClass().getId(object)); + if(!successful) { - // TODO: Handle HitAttemptOnMe script function - // Missed MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f); return; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 49f76d25c6..6806558833 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -654,10 +654,11 @@ namespace MWClass setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); } + if(!object.isEmpty()) + getCreatureStats(ptr).setLastHitAttemptObject(object.getClass().getId(object)); + if(!successful) { - // TODO: Handle HitAttemptOnMe script function - // Missed sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f); return; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 21fd2203fd..72a710c656 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -359,6 +359,16 @@ namespace MWMechanics return mLastHitObject; } + void CreatureStats::setLastHitAttemptObject(const std::string& objectid) + { + mLastHitAttemptObject = objectid; + } + + const std::string &CreatureStats::getLastHitAttemptObject() const + { + return mLastHitAttemptObject; + } + void CreatureStats::addToFallHeight(float height) { mFallHeight += height; @@ -510,6 +520,7 @@ namespace MWMechanics state.mAttackStrength = mAttackStrength; state.mFallHeight = mFallHeight; // TODO: vertical velocity (move from PhysicActor to CreatureStats?) state.mLastHitObject = mLastHitObject; + state.mLastHitAttemptObject = mLastHitAttemptObject; state.mRecalcDynamicStats = mRecalcMagicka; state.mDrawState = mDrawState; state.mLevel = mLevel; @@ -558,6 +569,7 @@ namespace MWMechanics mAttackStrength = state.mAttackStrength; mFallHeight = state.mFallHeight; mLastHitObject = state.mLastHitObject; + mLastHitAttemptObject = state.mLastHitAttemptObject; mRecalcMagicka = state.mRecalcDynamicStats; mDrawState = DrawState_(state.mDrawState); mLevel = state.mLevel; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index f830dd310c..d13ced3b3a 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -52,6 +52,7 @@ namespace MWMechanics float mFallHeight; std::string mLastHitObject; // The last object to hit this actor + std::string mLastHitAttemptObject; // The last object to attempt to hit this actor bool mRecalcMagicka; @@ -241,7 +242,9 @@ namespace MWMechanics bool getStance (Stance flag) const; void setLastHitObject(const std::string &objectid); + void setLastHitAttemptObject(const std::string &objectid); const std::string &getLastHitObject() const; + const std::string &getLastHitAttemptObject() const; // Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves. // TODO: Put it somewhere else? diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index c90f63f7fa..800c6e2c7b 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -436,5 +436,7 @@ op 0x20002f5: ToggleWorld op 0x20002f6: PCForce1stPerson op 0x20002f7: PCForce3rdPerson op 0x20002f8: PCGet3rdPerson +op 0x20002f9: HitAttemptOnMe +op 0x20002fa: HitAttemptOnMe, explicit -opcodes 0x20002f9-0x3ffffff unused +opcodes 0x20002fb-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 1d07c95915..c92acff820 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -737,6 +737,25 @@ namespace MWScript } }; + template + class OpHitAttemptOnMe : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string objectID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); + runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitAttemptObject())); + + stats.setLastHitAttemptObject(std::string()); + } + }; + template class OpEnableTeleporting : public Interpreter::Opcode0 { @@ -1085,6 +1104,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeGetWindSpeed, new OpGetWindSpeed); interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMe, new OpHitOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMeExplicit, new OpHitOnMe); + interpreter.installSegment5 (Compiler::Misc::opcodeHitAttemptOnMe, new OpHitAttemptOnMe); + interpreter.installSegment5 (Compiler::Misc::opcodeHitAttemptOnMeExplicit, new OpHitAttemptOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeDisableTeleporting, new OpEnableTeleporting); interpreter.installSegment5 (Compiler::Misc::opcodeEnableTeleporting, new OpEnableTeleporting); interpreter.installSegment5 (Compiler::Misc::opcodeShowVars, new OpShowVars); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index cd5bdbe695..1e2aebd991 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -295,6 +295,7 @@ namespace Compiler extensions.registerInstruction ("hurtcollidingactor", "f", opcodeHurtCollidingActor, opcodeHurtCollidingActorExplicit); extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed); extensions.registerFunction ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit); + extensions.registerFunction ("hitattemptonme", 'l', "S", opcodeHitAttemptOnMe, opcodeHitAttemptOnMeExplicit); extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting); extensions.registerInstruction ("enableteleporting", "", opcodeEnableTeleporting); extensions.registerInstruction ("showvars", "", opcodeShowVars, opcodeShowVarsExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 65efc14fa4..da79555e22 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -269,6 +269,8 @@ namespace Compiler const int opcodePayFineThief = 0x2000237; const int opcodeHitOnMe = 0x2000213; const int opcodeHitOnMeExplicit = 0x2000214; + const int opcodeHitAttemptOnMe = 0x20002f9; + const int opcodeHitAttemptOnMeExplicit = 0x20002fa; const int opcodeDisableTeleporting = 0x2000215; const int opcodeEnableTeleporting = 0x2000216; const int opcodeShowVars = 0x200021d; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 21803e02f9..cc76ef0df7 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -68,6 +68,8 @@ void ESM::CreatureStats::load (ESMReader &esm) mLastHitObject = esm.getHNOString ("LHIT"); + mLastHitAttemptObject = esm.getHNOString ("LHAT"); + mRecalcDynamicStats = false; esm.getHNOT (mRecalcDynamicStats, "CALC"); @@ -179,6 +181,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (!mLastHitObject.empty()) esm.writeHNString ("LHIT", mLastHitObject); + if (!mLastHitAttemptObject.empty()) + esm.writeHNString ("LHAT", mLastHitAttemptObject); + if (mRecalcDynamicStats) esm.writeHNT ("CALC", mRecalcDynamicStats); diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 8f4d4df7b8..7946d0e45b 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -56,6 +56,7 @@ namespace ESM float mAttackStrength; float mFallHeight; std::string mLastHitObject; + std::string mLastHitAttemptObject; bool mRecalcDynamicStats; int mDrawState; unsigned char mDeathAnimation; From 7892ed35f32c4c322043f706be801c537201c34e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 11 Dec 2014 22:25:53 +0100 Subject: [PATCH 040/404] PlaceItem, PlaceItemCell: Make sure references are placed above terrain (Bug #2078) --- apps/openmw/mwscript/transformationextensions.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 8e6d925b7c..80b13d6bb3 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -433,7 +433,8 @@ namespace MWScript pos.rot[2] = zRot; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().setPosition(pos); - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); + placed.getClass().adjustPosition(placed, true); } else { @@ -480,7 +481,8 @@ namespace MWScript pos.rot[2] = zRot; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().setPosition(pos); - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); + placed.getClass().adjustPosition(placed, true); } }; From f42420bc199322ebcc6f7ff3ba3ce06fac96ebbe Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 12 Dec 2014 01:24:35 +0100 Subject: [PATCH 041/404] Use the Original Creature field for SoundGen lookups --- apps/openmw/mwclass/creature.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 8076e0619c..d3c216c2b2 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -675,13 +675,12 @@ namespace MWClass std::vector sounds; sounds.reserve(8); - std::string ptrid = Creature::getId(ptr); + MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::Store::iterator sound = store.begin(); while(sound != store.end()) { - if(type == sound->mType && !sound->mCreature.empty() && - Misc::StringUtils::ciEqual(ptrid.substr(0, sound->mCreature.size()), - sound->mCreature)) + if (type == sound->mType && !sound->mCreature.empty() && Misc::StringUtils::ciEqual(ref->mBase->mOriginal, sound->mCreature)) sounds.push_back(&*sound); ++sound; } From cf5fc60e861d946fe2283bd7ac3369952381bf5c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 12 Dec 2014 01:42:13 +0100 Subject: [PATCH 042/404] Make ToggleMenus affect tooltips (Fixes #1989) --- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 48f28d300c..99a43fbffd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -444,6 +444,7 @@ namespace MWGui mVideoBackground->setVisible(false); mHud->setVisible(mHudEnabled && mGuiEnabled); + mToolTips->setVisible(mGuiEnabled); bool gameMode = !isGuiMode(); From e69cf110292c5a71525cebc446e97a8c2f5d58c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 12 Dec 2014 02:05:26 +0100 Subject: [PATCH 043/404] Hide tooltips during loading screens --- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 99a43fbffd..cbcef3bbd8 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -589,6 +589,7 @@ namespace MWGui break; case GM_LoadingWallpaper: mHud->setVisible(false); + mToolTips->setVisible(false); setCursorVisible(false); break; case GM_Loading: @@ -597,7 +598,7 @@ namespace MWGui mStatsWindow->setVisible(mStatsWindow->pinned() && !(mForceHidden & GW_Stats)); mInventoryWindow->setVisible(mInventoryWindow->pinned() && !(mForceHidden & GW_Inventory)); mSpellWindow->setVisible(mSpellWindow->pinned() && !(mForceHidden & GW_Magic)); - + mToolTips->setVisible(false); setCursorVisible(false); break; default: From 03da21f088da16c322f5046354141082a6d5dfc0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 12 Dec 2014 02:12:49 +0100 Subject: [PATCH 044/404] Remove redundant GUI element showing during loading screens --- apps/openmw/mwgui/windowmanagerimp.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index cbcef3bbd8..acb8b2eb75 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -588,16 +588,10 @@ namespace MWGui mJournal->setVisible(true); break; case GM_LoadingWallpaper: - mHud->setVisible(false); - mToolTips->setVisible(false); - setCursorVisible(false); - break; case GM_Loading: - // Show the pinned windows - mMap->setVisible(mMap->pinned() && !(mForceHidden & GW_Map)); - mStatsWindow->setVisible(mStatsWindow->pinned() && !(mForceHidden & GW_Stats)); - mInventoryWindow->setVisible(mInventoryWindow->pinned() && !(mForceHidden & GW_Inventory)); - mSpellWindow->setVisible(mSpellWindow->pinned() && !(mForceHidden & GW_Magic)); + // Don't need to show anything here - GM_LoadingWallpaper covers everything else anyway, + // GM_Loading uses a texture of the last rendered frame so everything previously visible will be rendered. + mHud->setVisible(false); mToolTips->setVisible(false); setCursorVisible(false); break; From bc85bb32c21cf4a1e932991eebe7e0f3d2dbd0eb Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 12 Dec 2014 02:39:59 +0100 Subject: [PATCH 045/404] Fix vampirism magic effect not applying immediately (Fixes #1984) --- apps/openmw/mwmechanics/character.cpp | 8 +++++--- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwrender/animation.hpp | 1 + apps/openmw/mwrender/npcanimation.cpp | 10 ++++++++++ apps/openmw/mwrender/npcanimation.hpp | 2 ++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 87d9a5b679..4d74133aa3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1279,7 +1279,7 @@ void CharacterController::update(float duration) const MWWorld::Class &cls = mPtr.getClass(); Ogre::Vector3 movement(0.0f); - updateVisibility(); + updateMagicEffects(); if(!cls.isActor()) { @@ -1777,7 +1777,7 @@ void CharacterController::updateContinuousVfx() } } -void CharacterController::updateVisibility() +void CharacterController::updateMagicEffects() { if (!mPtr.getClass().isActor()) return; @@ -1794,9 +1794,11 @@ void CharacterController::updateVisibility() { alpha *= std::max(0.2f, (100.f - chameleon)/100.f); } - mAnimation->setAlpha(alpha); + bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0.0f; + mAnimation->setVampire(vampire); + float light = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Light).getMagnitude(); mAnimation->setLightEffect(light); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 2a14c53b91..3409efedfd 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -190,7 +190,7 @@ class CharacterController void castSpell(const std::string& spellid); - void updateVisibility(); + void updateMagicEffects(); void playDeath(float startpoint, CharacterState death); void playRandomDeath(float startpoint = 0.0f); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index a8a9ee11e6..d25d4f0b0e 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -228,6 +228,7 @@ public: virtual void preRender (Ogre::Camera* camera); virtual void setAlpha(float alpha) {} + virtual void setVampire(bool vampire) {} public: void updatePtr(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 4b5ddcbec3..b93b37aeac 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -983,4 +983,14 @@ void NpcAnimation::equipmentChanged() updateParts(); } +void NpcAnimation::setVampire(bool vampire) +{ + if (mNpcType == Type_Werewolf) // we can't have werewolf vampires, can we + return; + if ((mNpcType == Type_Vampire) != vampire) + { + rebuild(); + } +} + } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index ee62fce9cd..6631c8f412 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -168,6 +168,8 @@ public: /// Make the NPC only partially visible virtual void setAlpha(float alpha); + virtual void setVampire(bool vampire); + /// Prepare this animation for being rendered with \a camera (rotates billboard nodes) virtual void preRender (Ogre::Camera* camera); }; From 018f4e6895177c8290d52ca3b49c0319a4ecdfd2 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Sun, 30 Nov 2014 18:59:05 -0500 Subject: [PATCH 046/404] Fail early if trying to read a string larger than the nif file size. This is much better than failing after a few minutes with an out of memory error. --- components/nif/nifstream.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index a6fd5ef5ae..1b5f715bc7 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -83,6 +83,11 @@ Transformation NIFStream::getTrafo() std::string NIFStream::getString(size_t length) { + //Make sure we're not reading in too large of a string + unsigned int fileSize = inp->size(); + if(fileSize != 0 && fileSize < length) + file->fail("Attempted to read a string with " + Ogre::StringConverter::toString(length) + "characters , but file is only "+Ogre::StringConverter::toString(fileSize)+ "bytes!"); + std::vector str (length+1, 0); if(inp->read(&str[0], length) != length) From cd835152e109dfc5bcbd41812c6ce80f11ba6c4e Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Fri, 12 Dec 2014 01:36:10 -0500 Subject: [PATCH 047/404] Fix spacing issue for NIF file errors. --- components/nif/niffile.hpp | 4 ++-- components/nif/nifstream.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 2ef2a6fda2..ceb9984fb8 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -46,14 +46,14 @@ public: /// Used if file parsing fails void fail(const std::string &msg) { - std::string err = "NIFFile Error: " + msg; + std::string err = " NIFFile Error: " + msg; err += "\nFile: " + filename; throw std::runtime_error(err); } /// Used when something goes wrong, but not catastrophically so void warn(const std::string &msg) { - std::cerr << "NIFFile Warning: " << msg <size(); if(fileSize != 0 && fileSize < length) - file->fail("Attempted to read a string with " + Ogre::StringConverter::toString(length) + "characters , but file is only "+Ogre::StringConverter::toString(fileSize)+ "bytes!"); + file->fail("Attempted to read a string with " + Ogre::StringConverter::toString(length) + " characters , but file is only "+Ogre::StringConverter::toString(fileSize)+ " bytes!"); std::vector str (length+1, 0); From b8edd9bac33ad0f0c8f0701ebc8b020e5368ab51 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Fri, 12 Dec 2014 01:48:57 -0500 Subject: [PATCH 048/404] Get a nif file's version string regardless of its length. --- components/nif/niffile.cpp | 4 ++-- components/nif/nifstream.cpp | 4 ++++ components/nif/nifstream.hpp | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index c689e27b3a..9a544f3c4e 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -131,9 +131,9 @@ void NIFFile::parse() NIFStream nif (this, Ogre::ResourceGroupManager::getSingleton().openResource(filename)); // Check the header string - std::string head = nif.getString(40); + std::string head = nif.getVersionString(); if(head.compare(0, 22, "NetImmerse File Format") != 0) - fail("Invalid NIF header"); + fail("Invalid NIF header: " + head); // Get BCD version ver = nif.getUInt(); diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 878b6b75f7..e5699db7b9 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -101,6 +101,10 @@ std::string NIFStream::getString() size_t size = read_le32(); return getString(size); } +std::string NIFStream::getVersionString() +{ + return inp->getLine(); +} void NIFStream::getShorts(std::vector &vec, size_t size) { diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 3d6a1319a8..cc14971fd5 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -81,6 +81,8 @@ public: std::string getString(size_t length); ///Read in a string of the length specified in the file std::string getString(); + ///This is special since the version string doesn't start with a number, and ends with "\n" + std::string getVersionString(); void getShorts(std::vector &vec, size_t size); void getFloats(std::vector &vec, size_t size); From 74e341b2bcc3ec516d5ab14e715fc928a057c995 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 12 Dec 2014 10:09:09 +0100 Subject: [PATCH 049/404] updated changelog again --- readme.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.txt b/readme.txt index 83f139a1d8..f5e3e0e59b 100644 --- a/readme.txt +++ b/readme.txt @@ -101,11 +101,13 @@ CHANGELOG 0.34.0 Bug #904: omwlauncher doesn't allow installing Tribunal and Bloodmoon if only MW is installed +Bug #986: Launcher: renaming profile names is broken Bug #1061: "Browse to CD..." launcher crash Bug #1135: Launcher crashes if user does not have write permission Bug #1231: Current installer in launcher does not correctly import russian Morrowind.ini settings from setup.inx Bug #1288: Fix the Alignment of the Resolution Combobox Bug #1343: BIK videos occasionally out of sync with audio +Bug #1684: Morrowind Grass Mod graphical glitches Bug #1734: NPC in fight with invisible/sneaking player Bug #1982: Long class names are cut off in the UI Bug #2012: Editor: OpenCS script compiler sometimes fails to find IDs From d034a079e60b8014802f92522b0dab46be47f3a9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 12 Dec 2014 16:49:22 +0100 Subject: [PATCH 050/404] Allow equipping twohanded weapon and shield at the same time (Fixes #1785) The shield can be equipped, meaning armor rating and item enchantments apply, but can not be blocked with. --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 9 ++++++ apps/openmw/mwmechanics/actors.hpp | 2 ++ apps/openmw/mwmechanics/character.cpp | 32 ++++++++++++++++--- apps/openmw/mwmechanics/character.hpp | 4 +++ apps/openmw/mwmechanics/combat.cpp | 9 +----- .../mwmechanics/mechanicsmanagerimp.cpp | 5 +++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ apps/openmw/mwrender/characterpreview.cpp | 7 +++- apps/openmw/mwrender/npcanimation.hpp | 2 +- apps/openmw/mwworld/actionequip.cpp | 6 +--- apps/openmw/mwworld/inventorystore.cpp | 6 +--- 12 files changed, 61 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index b7af1cbf72..c92459183d 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -205,6 +205,8 @@ namespace MWBase /// Resurrects the player if necessary virtual void keepPlayerAlive() = 0; + + virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 899ab95088..a64b6c57d4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1554,4 +1554,13 @@ namespace MWMechanics if (ptr.getClass().isNpc()) calculateNpcStatModifiers(ptr, 0.f); } + + bool Actors::isReadyToBlock(const MWWorld::Ptr &ptr) const + { + PtrControllerMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return false; + + return it->second->isReadyToBlock(); + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index a24095c702..a30a9dcf01 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -125,6 +125,8 @@ namespace MWMechanics void clear(); // Clear death counter + bool isReadyToBlock(const MWWorld::Ptr& ptr) const; + private: PtrControllerMap mActors; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4d74133aa3..06450ddb3c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -648,7 +648,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mAnimation->showWeapons(true); mAnimation->setWeaponGroup(mCurrentWeapon); } - mAnimation->showCarriedLeft(mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand); + + mAnimation->showCarriedLeft(updateCarriedLeftVisible(mWeaponType)); } if(!cls.getCreatureStats(mPtr).isDead()) @@ -836,6 +837,25 @@ bool CharacterController::updateCreatureState() return false; } +bool CharacterController::updateCarriedLeftVisible(WeaponType weaptype) const +{ + // Shields/torches shouldn't be visible during any operation involving two hands + // There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop", + // but they are also present in weapon drawing animation. + switch (weaptype) + { + case WeapType_Spell: + case WeapType_BowAndArrow: + case WeapType_Crossbow: + case WeapType_HandToHand: + case WeapType_TwoHand: + case WeapType_TwoWide: + return false; + default: + return true; + } +} + bool CharacterController::updateWeaponState() { const MWWorld::Class &cls = mPtr.getClass(); @@ -850,10 +870,7 @@ bool CharacterController::updateWeaponState() { forcestateupdate = true; - // Shields/torches shouldn't be visible during spellcasting or hand-to-hand - // There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop", - // but they are also present in weapon drawing animation. - mAnimation->showCarriedLeft(weaptype != WeapType_Spell && weaptype != WeapType_HandToHand); + mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype)); std::string weapgroup; if(weaptype == WeapType_None) @@ -1818,4 +1835,9 @@ void CharacterController::determineAttackType() } } +bool CharacterController::isReadyToBlock() const +{ + return updateCarriedLeftVisible(mWeaponType); +} + } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 3409efedfd..075db37beb 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -199,6 +199,8 @@ class CharacterController /// @param num if non-NULL, the chosen animation number will be written here std::string chooseRandomGroup (const std::string& prefix, int* num = NULL); + bool updateCarriedLeftVisible(WeaponType weaptype) const; + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); @@ -224,6 +226,8 @@ public: void forceStateUpdate(); AiState& getAiState() { return mAiState; } + + bool isReadyToBlock() const; }; void getWeaponGroup(WeaponType weaptype, std::string &group); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9225a57999..4d484469c2 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -62,17 +62,10 @@ namespace MWMechanics || blockerStats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) return false; - // Don't block when in spellcasting state (shield is equipped, but not visible) - if (blockerStats.getDrawState() == DrawState_Spell) + if (!MWBase::Environment::get().getMechanicsManager()->isReadyToBlock(blocker)) return false; MWWorld::InventoryStore& inv = blocker.getClass().getInventoryStore(blocker); - - // Don't block when in hand-to-hand combat (shield is equipped, but not visible) - if (blockerStats.getDrawState() == DrawState_Weapon && - inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight) == inv.end()) - return false; - MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name()) return false; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1dbe6f950e..cd9f0d0f76 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1352,4 +1352,9 @@ namespace MWMechanics stats.resurrect(); } } + + bool MechanicsManager::isReadyToBlock(const MWWorld::Ptr &ptr) const + { + return mActors.isReadyToBlock(ptr); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 1eec26c8ac..489da75417 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -169,6 +169,8 @@ namespace MWMechanics virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false); virtual void keepPlayerAlive(); + + virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 92d0bcd557..66052a96ec 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -195,6 +195,7 @@ namespace MWRender MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); std::string groupname; + bool showCarriedLeft = true; if(iter == inv.end()) groupname = "inventoryhandtohand"; else @@ -224,11 +225,15 @@ namespace MWRender groupname = "inventoryweapontwowide"; else groupname = "inventoryhandtohand"; - } + + showCarriedLeft = (iter->getClass().canBeEquipped(*iter, mCharacter).first != 2); + } else groupname = "inventoryhandtohand"; } + mAnimation->showCarriedLeft(showCarriedLeft); + mCurrentAnimGroup = groupname; mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 6631c8f412..aba01bcfaa 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -143,7 +143,7 @@ public: virtual void setPitchFactor(float factor) { mPitchFactor = factor; } virtual void showWeapons(bool showWeapon); - virtual void showCarriedLeft(bool showa); + virtual void showCarriedLeft(bool show); virtual void attachArrow(); virtual void releaseArrow(); diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 50da1e5e5d..87a4c63084 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -31,11 +31,7 @@ namespace MWWorld { case 0: return; - case 2: - invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, actor); - break; - case 3: - invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor); + default: break; } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 9032b04e11..c577d4b0d2 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -255,11 +255,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { case 0: continue; - case 2: - slots_[MWWorld::InventoryStore::Slot_CarriedLeft] = end(); - break; - case 3: - // Prefer keeping twohanded weapon + default: break; } From 60aa20914411c96ea13d8949dda041a269d70036 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 12 Dec 2014 17:39:00 +0100 Subject: [PATCH 051/404] Implement drowning when knocked out underwater (Fixes #1228) --- apps/openmw/mwmechanics/actors.cpp | 14 ++++++++++---- apps/openmw/mwmechanics/character.cpp | 5 +++++ apps/openmw/mwmechanics/character.hpp | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a64b6c57d4..0730a4660f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -869,13 +869,19 @@ namespace MWMechanics void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) { - MWBase::World *world = MWBase::Environment::get().getWorld(); + PtrControllerMap::iterator it = mActors.find(ptr); + if (it == mActors.end()) + return; + CharacterController* ctrl = it->second; + NpcStats &stats = ptr.getClass().getNpcStats(ptr); - if(world->isSubmerged(ptr) && - stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0) + MWBase::World *world = MWBase::Environment::get().getWorld(); + bool knockedOutUnderwater = (ctrl->isKnockedOut() && world->isUnderwater(ptr.getCell(), Ogre::Vector3(ptr.getRefData().getPosition().pos))); + if((world->isSubmerged(ptr) || knockedOutUnderwater) + && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0) { float timeLeft = 0.0f; - if(stats.getFatigue().getCurrent() == 0) + if(knockedOutUnderwater) stats.setTimeToStartDrowning(0); else { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 06450ddb3c..d8693fbcdf 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1840,4 +1840,9 @@ bool CharacterController::isReadyToBlock() const return updateCarriedLeftVisible(mWeaponType); } +bool CharacterController::isKnockedOut() const +{ + return mHitState == CharState_KnockOut; +} + } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 075db37beb..d7028a8441 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -228,6 +228,7 @@ public: AiState& getAiState() { return mAiState; } bool isReadyToBlock() const; + bool isKnockedOut() const; }; void getWeaponGroup(WeaponType weaptype, std::string &group); From ed6face4aa8cf6f1ae4ffc5aa18f0932199ff373 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 12 Dec 2014 22:21:57 +0100 Subject: [PATCH 052/404] Disable activation scripts for actors in combat --- apps/openmw/engine.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d7b23c0966..15d63eb4b4 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -505,6 +505,9 @@ void OMW::Engine::activate() if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated return; + if (ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat()) + return; + MWBase::Environment::get().getWorld()->activate(ptr, MWBase::Environment::get().getWorld()->getPlayerPtr()); } From 2ebf328dec5b7748a0dd92478c851fb85803cbf9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 13 Dec 2014 00:39:24 +0100 Subject: [PATCH 053/404] Always print the failing dialogue script These aren't usually very long, so printing them shouldn't spam the console by too much. --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index eff54fbc01..4aab5003fd 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -227,7 +227,7 @@ namespace MWDialogue success = false; } - if (!success && mScriptVerbose) + if (!success) { std::cerr << "compiling failed (dialogue script)" << std::endl From 0ca11eab1c50dfa7a85c4aad46d213a10e0cc739 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 13 Dec 2014 02:39:56 +0100 Subject: [PATCH 054/404] Ignore extra argument for removeItem (Fixes #2208) --- components/compiler/extensions0.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 1e2aebd991..690e589f73 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -116,7 +116,7 @@ namespace Compiler extensions.registerInstruction ("additem", "clX", opcodeAddItem, opcodeAddItemExplicit); extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount, opcodeGetItemCountExplicit); - extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem, + extensions.registerInstruction ("removeitem", "clX", opcodeRemoveItem, opcodeRemoveItemExplicit); extensions.registerInstruction ("equip", "cX", opcodeEquip, opcodeEquipExplicit); extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); From ba65c6cc7f19ed480a6dfe847b81593fc64d11b3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 13 Dec 2014 02:47:04 +0100 Subject: [PATCH 055/404] Add --script-all-dialogue switch to compile all dialogue scripts (Fixes #1659) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 18 ++- apps/openmw/engine.hpp | 4 + apps/openmw/main.cpp | 4 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- apps/openmw/mwdialogue/filter.cpp | 11 ++ apps/openmw/mwdialogue/filter.hpp | 6 +- apps/openmw/mwdialogue/scripttest.cpp | 124 ++++++++++++++++++ apps/openmw/mwdialogue/scripttest.hpp | 20 +++ apps/openmw/mwscript/compilercontext.hpp | 2 +- 10 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 apps/openmw/mwdialogue/scripttest.cpp create mode 100644 apps/openmw/mwdialogue/scripttest.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 803c743259..06a142f0a1 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -44,7 +44,7 @@ add_openmw_dir (mwgui ) add_openmw_dir (mwdialogue - dialoguemanagerimp journalimp journalentry quest topic filter selectwrapper hypertextparser keywordsearch + dialoguemanagerimp journalimp journalentry quest topic filter selectwrapper hypertextparser keywordsearch scripttest ) add_openmw_dir (mwscript diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d7b23c0966..1b350d7522 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -40,6 +40,7 @@ #include "mwdialogue/dialoguemanagerimp.hpp" #include "mwdialogue/journalimp.hpp" +#include "mwdialogue/scripttest.hpp" #include "mwmechanics/mechanicsmanagerimp.hpp" @@ -174,6 +175,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) + , mCompileAllDialogue (false) , mWarningsMode (1) , mScriptContext (0) , mFSStrict (false) @@ -425,7 +427,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) if (mCompileAll) { std::pair result = MWBase::Environment::get().getScriptManager()->compileAll(); - if (result.first) std::cout << "compiled " << result.second << " of " << result.first << " scripts (" @@ -433,6 +434,16 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) << "%)" << std::endl; } + if (mCompileAllDialogue) + { + std::pair result = MWDialogue::ScriptTest::compileAll(&mExtensions); + if (result.first) + std::cout + << "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a(" + << 100*static_cast (result.second)/result.first + << "%)" + << std::endl; + } } // Initialise and enter main loop. @@ -535,6 +546,11 @@ void OMW::Engine::setCompileAll (bool all) mCompileAll = all; } +void OMW::Engine::setCompileAllDialogue (bool all) +{ + mCompileAllDialogue = all; +} + void OMW::Engine::setSoundUsage(bool soundUsage) { mUseSound = soundUsage; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 0ee5a09c5b..6cf31cba89 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -76,6 +76,7 @@ namespace OMW bool mSkipMenu; bool mUseSound; bool mCompileAll; + bool mCompileAllDialogue; int mWarningsMode; std::string mFocusName; std::map mFallbackMap; @@ -178,6 +179,9 @@ namespace OMW /// Compile all scripts (excludign dialogue scripts) at startup? void setCompileAll (bool all); + /// Compile all dialogue scripts at startup? + void setCompileAllDialogue (bool all); + /// Font encoding void setEncoding(const ToUTF8::FromType& encoding); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 744780b258..9382e2516b 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -130,6 +130,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-all", bpo::value()->implicit_value(true) ->default_value(false), "compile all scripts (excluding dialogue scripts) at startup") + ("script-all-dialogue", bpo::value()->implicit_value(true) + ->default_value(false), "compile all dialogue scripts at startup") + ("script-console", bpo::value()->implicit_value(true) ->default_value(false), "enable console-only script functionality") @@ -264,6 +267,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // scripts engine.setCompileAll(variables["script-all"].as()); + engine.setCompileAllDialogue(variables["script-all-dialogue"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index eff54fbc01..196df00393 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -47,7 +47,7 @@ namespace MWDialogue { DialogueManager::DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage) : - mCompilerContext (MWScript::CompilerContext::Type_Dialgoue), + mCompilerContext (MWScript::CompilerContext::Type_Dialogue), mErrorStream(std::cout.rdbuf()),mErrorHandler(mErrorStream) , mTemporaryDispositionChange(0.f) , mPermanentDispositionChange(0.f), mScriptVerbose (scriptVerbose) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 629d99cc2a..af8a5754ff 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -603,6 +603,17 @@ const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, return suitableInfos[0]; } +std::vector MWDialogue::Filter::listAll (const ESM::Dialogue& dialogue) const +{ + std::vector infos; + for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) + { + if (testActor (*iter)) + infos.push_back(&*iter); + } + return infos; +} + std::vector MWDialogue::Filter::list (const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const { diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index 7e7f2b6f54..d41b7aa1ef 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -55,7 +55,11 @@ namespace MWDialogue std::vector list (const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition=false) const; - ///< \note If fallbackToInfoRefusal is used, the returned DialInfo might not be from the supplied ESM::Dialogue. + ///< List all infos that could be used on the given actor, using the current runtime state of the actor. + /// \note If fallbackToInfoRefusal is used, the returned DialInfo might not be from the supplied ESM::Dialogue. + + std::vector listAll (const ESM::Dialogue& dialogue) const; + ///< List all infos that could possibly be used on the given actor, ignoring runtime state filters and ignoring player filters. const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; ///< Get a matching response for the requested dialogue. diff --git a/apps/openmw/mwdialogue/scripttest.cpp b/apps/openmw/mwdialogue/scripttest.cpp new file mode 100644 index 0000000000..a8de21ef97 --- /dev/null +++ b/apps/openmw/mwdialogue/scripttest.cpp @@ -0,0 +1,124 @@ +#include "scripttest.hpp" + +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/scriptmanager.hpp" + +#include "../mwscript/compilercontext.hpp" + +#include +#include +#include +#include +#include +#include + +#include "filter.hpp" + +namespace +{ + +void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler::Extensions* extensions) +{ + MWDialogue::Filter filter(actor, 0, false); + + MWScript::CompilerContext compilerContext(MWScript::CompilerContext::Type_Dialogue); + compilerContext.setExtensions(extensions); + std::ostream errorStream(std::cout.rdbuf()); + Compiler::StreamErrorHandler errorHandler(errorStream); + + const MWWorld::Store& dialogues = MWBase::Environment::get().getWorld()->getStore().get(); + for (MWWorld::Store::iterator it = dialogues.begin(); it != dialogues.end(); ++it) + { + std::vector infos = filter.listAll(*it); + + for (std::vector::iterator it = infos.begin(); it != infos.end(); ++it) + { + const ESM::DialInfo* info = *it; + if (!info->mResultScript.empty()) + { + bool success = true; + ++total; + try + { + errorHandler.reset(); + + std::istringstream input (info->mResultScript + "\n"); + + Compiler::Scanner scanner (errorHandler, input, extensions); + + Compiler::Locals locals; + + std::string actorScript = actor.getClass().getScript(actor); + + if (!actorScript.empty()) + { + // grab local variables from actor's script, if available. + locals = MWBase::Environment::get().getScriptManager()->getLocals (actorScript); + } + + Compiler::ScriptParser parser(errorHandler, compilerContext, locals, false); + + scanner.scan (parser); + + if (!errorHandler.isGood()) + success = false; + + ++compiled; + } + catch (const Compiler::SourceException& /* error */) + { + // error has already been reported via error handler + success = false; + } + catch (const std::exception& error) + { + std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; + success = false; + } + + if (!success) + { + std::cerr + << "compiling failed (dialogue script)" << std::endl + << info->mResultScript + << std::endl << std::endl; + } + } + } + } +} + +} + +namespace MWDialogue +{ + +namespace ScriptTest +{ + + std::pair compileAll(const Compiler::Extensions *extensions) + { + int compiled = 0, total = 0; + const MWWorld::Store& npcs = MWBase::Environment::get().getWorld()->getStore().get(); + for (MWWorld::Store::iterator it = npcs.begin(); it != npcs.end(); ++it) + { + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->mId); + test(ref.getPtr(), compiled, total, extensions); + } + + const MWWorld::Store& creatures = MWBase::Environment::get().getWorld()->getStore().get(); + for (MWWorld::Store::iterator it = creatures.begin(); it != creatures.end(); ++it) + { + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->mId); + test(ref.getPtr(), compiled, total, extensions); + } + return std::make_pair(total, compiled); + } + +} + +} diff --git a/apps/openmw/mwdialogue/scripttest.hpp b/apps/openmw/mwdialogue/scripttest.hpp new file mode 100644 index 0000000000..1ed94c76a5 --- /dev/null +++ b/apps/openmw/mwdialogue/scripttest.hpp @@ -0,0 +1,20 @@ +#ifndef OPENMW_MWDIALOGUE_SCRIPTTEST_H +#define OPENMW_MWDIALOGUE_SCRIPTTEST_H + +#include + +namespace MWDialogue +{ + +namespace ScriptTest +{ + +/// Attempt to compile all dialogue scripts, use for verification purposes +/// @return A pair containing +std::pair compileAll(const Compiler::Extensions* extensions); + +} + +} + +#endif diff --git a/apps/openmw/mwscript/compilercontext.hpp b/apps/openmw/mwscript/compilercontext.hpp index 95719ab692..010926f451 100644 --- a/apps/openmw/mwscript/compilercontext.hpp +++ b/apps/openmw/mwscript/compilercontext.hpp @@ -12,7 +12,7 @@ namespace MWScript enum Type { Type_Full, // global, local, targetted - Type_Dialgoue, + Type_Dialogue, Type_Console }; From e4f75267d0475b2f7e2b19c698d9b70e85f58e4e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 13 Dec 2014 15:40:24 +0100 Subject: [PATCH 056/404] in case of arguments not separated with comma the fist token of the next argument was put back incorrectly --- components/compiler/exprparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 6dcca08df9..f7c3d3ecb5 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -660,7 +660,7 @@ namespace Compiler else { // no comma was used between arguments - scanner.putbackKeyword (code, loc); + scanner.putbackSpecial (code, loc); return false; } } From ed5387fb8c9e21aa115655e6d874b2461bc4f472 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 13 Dec 2014 15:43:40 +0100 Subject: [PATCH 057/404] replaced stay [ ignoring implementation with one that does not interfere with other workarounds (Fixes #2205) --- components/compiler/lineparser.cpp | 7 +++++++ components/compiler/scanner.cpp | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index dc19b9a4b0..37641b014b 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -489,6 +489,13 @@ namespace Compiler bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { + if (mState==EndState && code==Scanner::S_open) + { + getErrorHandler().warning ("stray '[' or '(' at the end of the line (ignoring it)", + loc); + return true; + } + if (code==Scanner::S_newline && (mState==EndState || mState==BeginState || mState==PotentialEndState)) return false; diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 203f27e6e8..59fae3ccdc 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -528,8 +528,7 @@ namespace Compiler bool Scanner::isWhitespace (char c) { - return c==' ' || c=='\t' - || c=='['; ///< \todo disable this when doing more strict compiling + return c==' ' || c=='\t'; } // constructor From 8f29f2667e4081dd15a4449a700b2dc33cec8747 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 13 Dec 2014 15:49:32 +0100 Subject: [PATCH 058/404] Fix rotation order for XYZ rotation keys (Fixes #1067) --- components/nifogre/ogrenifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 1d1e1a22d0..cdbc4d8447 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -483,7 +483,7 @@ public: Ogre::Quaternion xr(Ogre::Radian(xrot), Ogre::Vector3::UNIT_X); Ogre::Quaternion yr(Ogre::Radian(yrot), Ogre::Vector3::UNIT_Y); Ogre::Quaternion zr(Ogre::Radian(zrot), Ogre::Vector3::UNIT_Z); - return (xr*yr*zr); + return (zr*yr*xr); } public: From 80c92789c23d899dc78ef4fef850d54763854889 Mon Sep 17 00:00:00 2001 From: Nik Dyonin Date: Sun, 14 Dec 2014 02:58:42 +0300 Subject: [PATCH 059/404] Fix issue when killed NPC cannot be looted if it was in combat mode before killing. --- apps/openmw/engine.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 885d2bb4f1..d8546a2490 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -516,8 +516,13 @@ void OMW::Engine::activate() if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated return; - if (ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat()) - return; + if (ptr.getClass().isActor()) + { + MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); + + if (stats.getAiSequence().isInCombat() && !stats.isDead()) + return; + } MWBase::Environment::get().getWorld()->activate(ptr, MWBase::Environment::get().getWorld()->getPlayerPtr()); } From 88a2e4c04383dfe7c2c3652f6995456564b4f1cd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Dec 2014 01:53:15 +0100 Subject: [PATCH 060/404] Graceful error handling for missing spells/factions (Fixes #1825, Bug #2176, Bug #2203) --- apps/openmw/mwclass/creature.cpp | 8 ++++++- apps/openmw/mwclass/npc.cpp | 34 +++++++++++++++++++++++------- apps/openmw/mwmechanics/spells.cpp | 16 ++++++++------ apps/openmw/mwmechanics/spells.hpp | 3 +++ 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d3c216c2b2..25808c991a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -119,7 +119,13 @@ namespace MWClass // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); iter!=ref->mBase->mSpells.mList.end(); ++iter) - data->mCreatureStats.getSpells().add (*iter); + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter); + if (spell) + data->mCreatureStats.getSpells().add (spell); + else /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility + std::cerr << "Warning: ignoring nonexistent spell '" << spell->mId << "' on creature '" << ref->mBase->mId << "'" << std::endl; + } // inventory if (ref->mBase->mFlags & ESM::Creature::Weapon) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 6806558833..3da7446918 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -300,15 +300,20 @@ namespace MWClass if (!ref->mBase->mFaction.empty()) { std::string faction = ref->mBase->mFaction; - Misc::StringUtils::toLower(faction); - if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) + const ESM::Faction* fact = MWBase::Environment::get().getWorld()->getStore().get().search(faction); + if (fact) { - data->mNpcStats.setFactionRank(faction, (int)ref->mBase->mNpdt52.mRank); + if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) + { + data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt52.mRank); + } + else + { + data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt12.mRank); + } } else - { - data->mNpcStats.setFactionRank(faction, (int)ref->mBase->mNpdt12.mRank); - } + std::cerr << "Warning: ignoring nonexistent faction '" << fact->mId << "' on NPC '" << ref->mBase->mId << "'" << std::endl; } // creature stats @@ -361,7 +366,11 @@ namespace MWClass for (std::vector::const_iterator iter (race->mPowers.mList.begin()); iter!=race->mPowers.mList.end(); ++iter) { - data->mNpcStats.getSpells().add (*iter); + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter); + if (spell) + data->mNpcStats.getSpells().add (spell); + else + std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; } if (data->mNpcStats.getFactionRanks().size()) @@ -385,7 +394,16 @@ namespace MWClass // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); iter!=ref->mBase->mSpells.mList.end(); ++iter) - data->mNpcStats.getSpells().add (*iter); + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter); + if (spell) + data->mNpcStats.getSpells().add (spell); + else + { + /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility + std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; + } + } // inventory data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index a1b73bc47c..5953be523b 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -25,12 +25,10 @@ namespace MWMechanics return mSpells.end(); } - void Spells::add (const std::string& spellId) + void Spells::add (const ESM::Spell* spell) { - if (mSpells.find (spellId)==mSpells.end()) + if (mSpells.find (spell->mId)==mSpells.end()) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - std::map random; // Determine the random magnitudes (unless this is a castable spell, in which case @@ -50,13 +48,19 @@ namespace MWMechanics corprus.mWorsenings = 0; corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; - mCorprusSpells[spellId] = corprus; + mCorprusSpells[spell->mId] = corprus; } - mSpells.insert (std::make_pair (Misc::StringUtils::lowerCase(spellId), random)); + mSpells.insert (std::make_pair (spell->mId, random)); } } + void Spells::add (const std::string& spellId) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + add(spell); + } + void Spells::remove (const std::string& spellId) { std::string lower = Misc::StringUtils::lowerCase(spellId); diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index ab799ffe12..064b2c1e5d 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -79,6 +79,9 @@ namespace MWMechanics void add (const std::string& spell); ///< Adding a spell that is already listed in *this is a no-op. + void add (const ESM::Spell* spell); + ///< Adding a spell that is already listed in *this is a no-op. + void remove (const std::string& spell); ///< If the spell to be removed is the selected spell, the selected spell will be changed to /// no spell (empty string). From 97a4b30b59c232199a87c8b97dd00fb824fd4f20 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 14 Dec 2014 14:59:27 +0100 Subject: [PATCH 061/404] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 76efb2760a..c3eab721fe 100644 --- a/credits.txt +++ b/credits.txt @@ -71,6 +71,7 @@ Michael Papageorgiou (werdanith) Michał Bień (Glorf) Miroslav Puda (pakanek) MiroslavR +Narmo Nathan Jeffords (blunted2night) Nikolay Kasyanov (corristo) nobrakal From c5a604453ec71b3d2d366202ff02ec93f7ce304b Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sun, 14 Dec 2014 16:25:27 +0100 Subject: [PATCH 062/404] Fix several book formatting issues (Fixes #2204) --- apps/openmw/mwgui/formatting.cpp | 33 ++++++++++++++++++++++++++++---- apps/openmw/mwgui/formatting.hpp | 6 +++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index c55650c453..5831160034 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -193,6 +193,9 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(parent->getChildAt(0)); } + mTextStyle = TextStyle(); + mBlockStyle = BlockStyle(); + MyGUI::Widget * paper = parent->createWidget("Widget", MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top); paper->setNeedMouseFocus(false); @@ -207,8 +210,25 @@ namespace MWGui continue; std::string plainText = parser.getReadyText(); + + // for cases when linebreaks are used to cause a shift to the next page + // if the split text block ends in an empty line, proceeding text block(s) should have leading empty lines removed + if (pag.getIgnoreLeadingEmptyLines()) + { + while (!plainText.empty()) + { + if (plainText[0] == '\n') + plainText.erase(plainText.begin()); + else + { + pag.setIgnoreLeadingEmptyLines(false); + break; + } + } + } + if (plainText.empty()) - brBeforeLastTag = false; + brBeforeLastTag = true; else { // Each block of text (between two tags / boundary and tag) will be displayed in a separate editbox widget, @@ -252,6 +272,8 @@ namespace MWGui { case BookTextParser::Event_ImgTag: { + pag.setIgnoreLeadingEmptyLines(false); + const BookTextParser::Attributes & attr = parser.getAttributes(); if (attr.find("src") == attr.end() || attr.find("width") == attr.end() || attr.find("height") == attr.end()) @@ -331,9 +353,7 @@ namespace MWGui if (attr.find("face") != attr.end()) { std::string face = attr.at("face"); - - if (face != "Magic Cards") - mTextStyle.mFont = face; + mTextStyle.mFont = face; } if (attr.find("size") != attr.end()) { @@ -408,13 +428,18 @@ namespace MWGui // first empty lines that would go to the next page should be ignored // unfortunately, getLineInfo method won't be available until 3.2.2 #if (MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3, 2, 2)) + mPaginator.setIgnoreLeadingEmptyLines(true); + const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType()->getLineInfo(); for (unsigned int i = lastLine; i < lines.size(); ++i) { if (lines[i].width == 0) ret += lineHeight; else + { + mPaginator.setIgnoreLeadingEmptyLines(false); break; + } } #endif return ret; diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 5b79250577..0d0f74b720 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -81,7 +81,8 @@ namespace MWGui Paginator(int pageWidth, int pageHeight) : mStartTop(0), mCurrentTop(0), - mPageWidth(pageWidth), mPageHeight(pageHeight) + mPageWidth(pageWidth), mPageHeight(pageHeight), + mIgnoreLeadingEmptyLines(false) { } @@ -89,10 +90,12 @@ namespace MWGui int getCurrentTop() const { return mCurrentTop; } int getPageWidth() const { return mPageWidth; } int getPageHeight() const { return mPageHeight; } + bool getIgnoreLeadingEmptyLines() const { return mIgnoreLeadingEmptyLines; } Pages getPages() const { return mPages; } void setStartTop(int top) { mStartTop = top; } void setCurrentTop(int top) { mCurrentTop = top; } + void setIgnoreLeadingEmptyLines(bool ignore) { mIgnoreLeadingEmptyLines = ignore; } Paginator & operator<<(const Page & page) { @@ -103,6 +106,7 @@ namespace MWGui private: int mStartTop, mCurrentTop; int mPageWidth, mPageHeight; + bool mIgnoreLeadingEmptyLines; Pages mPages; }; From 192626c6f55421d57f20d4c963efde17dc95b0a1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Dec 2014 17:44:03 +0100 Subject: [PATCH 063/404] SoundGen fix: use Original Creature field only if non-empty --- apps/openmw/mwclass/creature.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 25808c991a..b87dfda98b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -679,14 +679,15 @@ namespace MWClass if(type >= 0) { std::vector sounds; - sounds.reserve(8); MWWorld::LiveCellRef* ref = ptr.get(); + const std::string& ourId = (ref->mBase->mOriginal.empty()) ? getId(ptr) : ref->mBase->mOriginal; + MWWorld::Store::iterator sound = store.begin(); while(sound != store.end()) { - if (type == sound->mType && !sound->mCreature.empty() && Misc::StringUtils::ciEqual(ref->mBase->mOriginal, sound->mCreature)) + if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(ourId, sound->mCreature))) sounds.push_back(&*sound); ++sound; } From 4acc25f59c65101257ad3144e8935f2e345881b7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Dec 2014 17:52:06 +0100 Subject: [PATCH 064/404] Use SoundGen with no creature field as fallback This fixes the adorable "thump" sounds in the Scrib's idle animation not playing. --- apps/openmw/mwclass/creature.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index b87dfda98b..1b005270f2 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -679,6 +679,7 @@ namespace MWClass if(type >= 0) { std::vector sounds; + std::vector fallbacksounds; MWWorld::LiveCellRef* ref = ptr.get(); @@ -689,10 +690,14 @@ namespace MWClass { if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(ourId, sound->mCreature))) sounds.push_back(&*sound); + if (type == sound->mType && sound->mCreature.empty()) + fallbacksounds.push_back(&*sound); ++sound; } if(!sounds.empty()) return sounds[(int)(rand()/(RAND_MAX+1.0)*sounds.size())]->mSound; + if (!fallbacksounds.empty()) + return fallbacksounds[(int)(rand()/(RAND_MAX+1.0)*fallbacksounds.size())]->mSound; } return ""; From 4f3995a4d8f407557bedede17cbf9069fb707ee6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Dec 2014 19:15:43 +0100 Subject: [PATCH 065/404] Fix werewolf AI being able to use items --- apps/openmw/mwmechanics/aicombataction.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 6b4ede3059..73cb276262 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -454,6 +454,8 @@ namespace MWMechanics float bestActionRating = 0.f; // Default to hand-to-hand combat boost::shared_ptr bestAction (new ActionWeapon(MWWorld::Ptr())); + if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) + return bestAction; if (actor.getClass().hasInventoryStore(actor)) { From 2b78e9795d346e2d54f6833baba258d7b2a1158c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Dec 2014 19:35:34 +0100 Subject: [PATCH 066/404] Implement Calm effect removing combat packages (Fixes #1985) --- apps/openmw/mwclass/creature.cpp | 6 ++++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 7 +++++++ apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 14 ++++++++++++++ apps/openmw/mwmechanics/aicombataction.cpp | 3 +++ apps/openmw/mwmechanics/aisequence.cpp | 5 ++--- apps/openmw/mwmechanics/aisequence.hpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 5 +++++ apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 2 ++ 11 files changed, 49 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1b005270f2..519216ec9b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -891,4 +891,10 @@ namespace MWClass MWWorld::ContainerStore& store = getContainerStore(ptr); store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); } + + int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + return ref->mBase->mAiData.mFight; + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 1820d4ea43..c52e85534b 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -154,6 +154,8 @@ namespace MWClass virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr &ptr) const; + + virtual int getBaseFightRating(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3da7446918..a3ebef5821 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1266,6 +1266,7 @@ namespace MWClass // TODO: I have no idea what these are supposed to do for NPCs since they use // voiced dialog for various conditions like health loss and combat taunts. Maybe // only for biped creatures? + if(name == "moan") return ""; if(name == "roar") @@ -1380,4 +1381,10 @@ namespace MWClass MWWorld::ContainerStore& store = getContainerStore(ptr); store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); } + + int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + return ref->mBase->mAiData.mFight; + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index a92e72af59..3bc450088b 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -185,6 +185,8 @@ namespace MWClass virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr& ptr) const; + + virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0730a4660f..fecf189861 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -677,6 +677,19 @@ namespace MWMechanics // TODO: dirty flag for magic effects to avoid some unnecessary work below? + // any value of calm > 0 will stop the actor from fighting + if ((creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0 && ptr.getClass().isNpc()) + || (creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmCreature).getMagnitude() > 0 && !ptr.getClass().isNpc())) + { + for (std::list::const_iterator it = creatureStats.getAiSequence().begin(); it != creatureStats.getAiSequence().end(); ) + { + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + it = creatureStats.getAiSequence().erase(it); + else + ++it; + } + } + // Update bound effects static std::map boundItemsMap; if (boundItemsMap.empty()) @@ -1056,6 +1069,7 @@ namespace MWMechanics // Reset factors to attack creatureStats.setAttacked(false); creatureStats.setAlarmed(false); + creatureStats.setAiSetting(CreatureStats::AI_Fight, ptr.getClass().getBaseFightRating(ptr)); // Update witness crime id npcStats.setCrimeId(-1); diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 73cb276262..356955b221 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -455,7 +455,10 @@ namespace MWMechanics // Default to hand-to-hand combat boost::shared_ptr bestAction (new ActionWeapon(MWWorld::Ptr())); if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) + { + bestAction->prepare(actor); return bestAction; + } if (actor.getClass().hasInventoryStore(actor)) { diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 990145c8d5..2ee8984050 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -86,15 +86,14 @@ std::list::const_iterator AiSequence::end() const return mPackages.end(); } -void AiSequence::erase(std::list::const_iterator package) +std::list::const_iterator AiSequence::erase(std::list::const_iterator package) { // Not sure if manually terminated packages should trigger mDone, probably not? for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) { if (package == it) { - mPackages.erase(it); - return; + return mPackages.erase(it); } } throw std::runtime_error("can't find package to erase"); diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 25605ff447..460a411ba8 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -61,7 +61,7 @@ namespace MWMechanics std::list::const_iterator begin() const; std::list::const_iterator end() const; - void erase (std::list::const_iterator package); + std::list::const_iterator erase (std::list::const_iterator package); /// Returns currently executing AiPackage type /** \see enum AiPackage::TypeId **/ diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index cd9f0d0f76..520b6b8a7c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1096,6 +1096,11 @@ namespace MWMechanics { startCombat(*it, player); + // Apply aggression value to the base Fight rating, so that the actor can continue fighting + // after a Calm spell wears off + int fightBase = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase(); + it->getClass().getCreatureStats(*it).setAiSetting(CreatureStats::AI_Fight, fightBase + aggression); + // Set the crime ID, which we will use to calm down participants // once the bounty has been paid. it->getClass().getNpcStats(*it).setCrimeId(id); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 0a84862097..7c95858343 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -429,4 +429,9 @@ namespace MWWorld { return std::string(); } + + int Class::getBaseFightRating(const Ptr &ptr) const + { + throw std::runtime_error("class does not support fight rating"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index dcac16b06e..cc18d830cc 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -339,6 +339,8 @@ namespace MWWorld /// Returns sound id virtual std::string getSound(const MWWorld::Ptr& ptr) const; + + virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const; }; } From 79237d16a7502666e06d963384da759d2800e69d Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Dec 2014 13:13:25 +0100 Subject: [PATCH 067/404] Refactor spell window to use model/view and remove duplicated code in QuickKeysMenu This should also improve window resizing performance, the widgets are now just resized instead of recreated. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/quickkeysmenu.cpp | 218 +----------- apps/openmw/mwgui/quickkeysmenu.hpp | 16 +- apps/openmw/mwgui/spellmodel.cpp | 140 ++++++++ apps/openmw/mwgui/spellmodel.hpp | 56 +++ apps/openmw/mwgui/spellview.cpp | 255 ++++++++++++++ apps/openmw/mwgui/spellview.hpp | 71 ++++ apps/openmw/mwgui/spellwindow.cpp | 321 ++---------------- apps/openmw/mwgui/spellwindow.hpp | 25 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 + files/mygui/openmw_list.skin.xml | 9 + .../mygui/openmw_magicselection_dialog.layout | 6 +- files/mygui/openmw_spell_window.layout | 5 +- 13 files changed, 585 insertions(+), 541 deletions(-) create mode 100644 apps/openmw/mwgui/spellmodel.cpp create mode 100644 apps/openmw/mwgui/spellmodel.hpp create mode 100644 apps/openmw/mwgui/spellview.cpp create mode 100644 apps/openmw/mwgui/spellview.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 06a142f0a1..28eadc5172 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -40,7 +40,7 @@ add_openmw_dir (mwgui merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog - recharge mode videowidget backgroundimage itemwidget screenfader debugwindow + recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 440165e749..7ffb51b5ab 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -13,7 +13,6 @@ #include "../mwbase/world.hpp" #include "../mwmechanics/spellcasting.hpp" -#include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwgui/inventorywindow.hpp" @@ -23,7 +22,8 @@ #include "windowmanagerimp.hpp" #include "itemselection.hpp" -#include "spellwindow.hpp" +#include "spellview.hpp" + #include "itemwidget.hpp" #include "sortfilteritemmodel.hpp" @@ -495,13 +495,15 @@ namespace MWGui MagicSelectionDialog::MagicSelectionDialog(QuickKeysMenu* parent) : WindowModal("openmw_magicselection_dialog.layout") , mParent(parent) - , mWidth(0) - , mHeight(0) { getWidget(mCancelButton, "CancelButton"); getWidget(mMagicList, "MagicList"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); + mMagicList->setShowCostColumn(false); + mMagicList->setHighlightSelected(false); + mMagicList->eventSpellClicked += MyGUI::newDelegate(this, &MagicSelectionDialog::onModelIndexSelected); + center(); } @@ -519,211 +521,17 @@ namespace MWGui { WindowModal::open(); - while (mMagicList->getChildCount ()) - MyGUI::Gui::getInstance ().destroyWidget (mMagicList->getChildAt (0)); - - mHeight = 0; - - const int spellHeight = 18; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); - MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); - - /// \todo lots of copy&pasted code from SpellWindow - - // retrieve powers & spells, sort by name - std::vector spellList; - - for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) - { - spellList.push_back (it->first); - } - - const MWWorld::ESMStore &esmStore = - MWBase::Environment::get().getWorld()->getStore(); - - std::vector powers; - std::vector::iterator it = spellList.begin(); - while (it != spellList.end()) - { - const ESM::Spell* spell = esmStore.get().find(*it); - if (spell->mData.mType == ESM::Spell::ST_Power) - { - powers.push_back(*it); - it = spellList.erase(it); - } - else if (spell->mData.mType == ESM::Spell::ST_Ability - || spell->mData.mType == ESM::Spell::ST_Blight - || spell->mData.mType == ESM::Spell::ST_Curse - || spell->mData.mType == ESM::Spell::ST_Disease) - { - it = spellList.erase(it); - } - else - ++it; - } - std::sort(powers.begin(), powers.end(), sortSpells); - std::sort(spellList.begin(), spellList.end(), sortSpells); - - // retrieve usable magic items & sort - std::vector items; - for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it) - { - std::string enchantId = it->getClass().getEnchantment(*it); - if (enchantId != "") - { - // only add items with "Cast once" or "Cast on use" - const ESM::Enchantment* enchant = - esmStore.get().find(enchantId); - - int type = enchant->mData.mType; - if (type != ESM::Enchantment::CastOnce - && type != ESM::Enchantment::WhenUsed) - continue; - - items.push_back(*it); - } - } - std::sort(items.begin(), items.end(), sortItems); - - - int height = estimateHeight(items.size() + powers.size() + spellList.size()); - bool scrollVisible = height > mMagicList->getHeight(); - mWidth = mMagicList->getWidth() - scrollVisible * 18; - - - // powers - addGroup("#{sPowers}", ""); - - for (std::vector::const_iterator it = powers.begin(); it != powers.end(); ++it) - { - const ESM::Spell* spell = esmStore.get().find(*it); - MyGUI::Button* t = mMagicList->createWidget("SandTextButton", - MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); - t->setCaption(spell->mName); - t->setTextAlign(MyGUI::Align::Left); - t->setUserString("ToolTipType", "Spell"); - t->setUserString("Spell", *it); - t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); - t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onSpellSelected); - - mHeight += spellHeight; - } - - // other spells - addGroup("#{sSpells}", ""); - for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) - { - const ESM::Spell* spell = esmStore.get().find(*it); - MyGUI::Button* t = mMagicList->createWidget("SandTextButton", - MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); - t->setCaption(spell->mName); - t->setTextAlign(MyGUI::Align::Left); - t->setUserString("ToolTipType", "Spell"); - t->setUserString("Spell", *it); - t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); - t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onSpellSelected); - - mHeight += spellHeight; - } - - - // enchanted items - addGroup("#{sMagicItem}", ""); - - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) - { - MWWorld::Ptr item = *it; - - // check if the item is currently equipped (will display in a different color) - bool equipped = false; - for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) - { - if (store.getSlot(i) != store.end() && *store.getSlot(i) == item) - { - equipped = true; - break; - } - } - - MyGUI::Button* t = mMagicList->createWidget(equipped ? "SandTextButton" : "SpellTextUnequipped", - MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); - t->setCaption(item.getClass().getName(item)); - t->setTextAlign(MyGUI::Align::Left); - t->setUserData(item); - t->setUserString("ToolTipType", "ItemPtr"); - t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onEnchantedItemSelected); - t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); - - mHeight += spellHeight; - } - - // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden - mMagicList->setVisibleVScroll(false); - mMagicList->setCanvasSize (mWidth, std::max(mMagicList->getHeight(), mHeight)); - mMagicList->setVisibleVScroll(true); + mMagicList->setModel(new SpellModel(MWBase::Environment::get().getWorld()->getPlayerPtr())); + mMagicList->update(); } - void MagicSelectionDialog::addGroup(const std::string &label, const std::string& label2) + void MagicSelectionDialog::onModelIndexSelected(SpellModel::ModelIndex index) { - if (mMagicList->getChildCount() > 0) - { - MyGUI::ImageBox* separator = mMagicList->createWidget("MW_HLine", - MyGUI::IntCoord(4, mHeight, mWidth-8, 18), - MyGUI::Align::Left | MyGUI::Align::Top); - separator->setNeedMouseFocus(false); - mHeight += 18; - } - - MyGUI::TextBox* groupWidget = mMagicList->createWidget("SandBrightText", - MyGUI::IntCoord(0, mHeight, mWidth, 24), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - groupWidget->setCaptionWithReplacing(label); - groupWidget->setTextAlign(MyGUI::Align::Left); - groupWidget->setNeedMouseFocus(false); - - if (label2 != "") - { - MyGUI::TextBox* groupWidget2 = mMagicList->createWidget("SandBrightText", - MyGUI::IntCoord(0, mHeight, mWidth-4, 24), - MyGUI::Align::Left | MyGUI::Align::Top); - groupWidget2->setCaptionWithReplacing(label2); - groupWidget2->setTextAlign(MyGUI::Align::Right); - groupWidget2->setNeedMouseFocus(false); - } - - mHeight += 24; - } - - - void MagicSelectionDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) - { - if (mMagicList->getViewOffset().top + _rel*0.3 > 0) - mMagicList->setViewOffset(MyGUI::IntPoint(0, 0)); + const Spell& spell = mMagicList->getModel()->getItem(index); + if (spell.mType == Spell::Type_EnchantedItem) + mParent->onAssignMagicItem(spell.mItem); else - mMagicList->setViewOffset(MyGUI::IntPoint(0, mMagicList->getViewOffset().top + _rel*0.3)); - } - - void MagicSelectionDialog::onEnchantedItemSelected(MyGUI::Widget* _sender) - { - MWWorld::Ptr item = *_sender->getUserData(); - - mParent->onAssignMagicItem (item); - } - - void MagicSelectionDialog::onSpellSelected(MyGUI::Widget* _sender) - { - mParent->onAssignMagic (_sender->getUserString("Spell")); - } - - int MagicSelectionDialog::estimateHeight(int numSpells) const - { - int height = 0; - height += 24 * 3 + 18 * 2; // group headings - height += numSpells * 18; - return height; + mParent->onAssignMagic(spell.mId); } } diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index dc088d3c9a..30e6728911 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -5,6 +5,8 @@ #include "windowbase.hpp" +#include "spellmodel.hpp" + namespace MWGui { @@ -12,6 +14,7 @@ namespace MWGui class ItemSelectionDialog; class MagicSelectionDialog; class ItemWidget; + class SpellView; class QuickKeysMenu : public WindowBase { @@ -94,21 +97,12 @@ namespace MWGui private: MyGUI::Button* mCancelButton; - MyGUI::ScrollView* mMagicList; - - int mWidth; - int mHeight; + SpellView* mMagicList; QuickKeysMenu* mParent; void onCancelButtonClicked (MyGUI::Widget* sender); - void onMouseWheel(MyGUI::Widget* _sender, int _rel); - void onEnchantedItemSelected(MyGUI::Widget* _sender); - void onSpellSelected(MyGUI::Widget* _sender); - int estimateHeight(int numSpells) const; - - - void addGroup(const std::string& label, const std::string& label2); + void onModelIndexSelected(SpellModel::ModelIndex index); }; } diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp new file mode 100644 index 0000000000..0305258978 --- /dev/null +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -0,0 +1,140 @@ +#include "spellmodel.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellcasting.hpp" + +#include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/class.hpp" + +namespace +{ + + bool sortSpells(const MWGui::Spell& left, const MWGui::Spell& right) + { + if (left.mType != right.mType) + return left.mType < right.mType; + + int cmp = left.mName.compare(right.mName); + return cmp < 0; + } + +} + +namespace MWGui +{ + + SpellModel::SpellModel(const MWWorld::Ptr &actor) + : mActor(actor) + { + + } + + void SpellModel::update() + { + mSpells.clear(); + + MWMechanics::CreatureStats& stats = mActor.getClass().getCreatureStats(mActor); + const MWMechanics::Spells& spells = stats.getSpells(); + + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + + for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + const ESM::Spell* spell = esmStore.get().find(it->first); + if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell) + continue; + + Spell newSpell; + newSpell.mName = spell->mName; + if (spell->mData.mType == ESM::Spell::ST_Spell) + { + newSpell.mType = Spell::Type_Spell; + std::string cost = boost::lexical_cast(spell->mData.mCost); + std::string chance = boost::lexical_cast(int(MWMechanics::getSpellSuccessChance(spell, mActor))); + newSpell.mCostColumn = cost + "/" + chance; + } + else + newSpell.mType = Spell::Type_Power; + newSpell.mId = it->first; + + newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == it->first); + newSpell.mActive = true; + mSpells.push_back(newSpell); + } + + MWWorld::InventoryStore& invStore = mActor.getClass().getInventoryStore(mActor); + for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) + { + MWWorld::Ptr item = *it; + const std::string enchantId = item.getClass().getEnchantment(item); + if (enchantId.empty()) + continue; + const ESM::Enchantment* enchant = + esmStore.get().find(item.getClass().getEnchantment(item)); + if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce) + continue; + + Spell newSpell; + newSpell.mItem = item; + newSpell.mId = item.getClass().getId(item); + newSpell.mName = item.getClass().getName(item); + newSpell.mType = Spell::Type_EnchantedItem; + newSpell.mSelected = invStore.getSelectedEnchantItem() == it; + + // FIXME: move to mwmechanics + if (enchant->mData.mType == ESM::Enchantment::CastOnce) + { + newSpell.mCostColumn = "100/100"; + newSpell.mActive = false; + } + else + { + float enchantCost = enchant->mData.mCost; + int eSkill = mActor.getClass().getSkill(mActor, ESM::Skill::Enchant); + int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); + + std::string cost = boost::lexical_cast(castCost); + int currentCharge = int(item.getCellRef().getEnchantmentCharge()); + if (currentCharge == -1) + currentCharge = enchant->mData.mCharge; + std::string charge = boost::lexical_cast(currentCharge); + newSpell.mCostColumn = cost + "/" + charge; + + bool equipped = false; + for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) + { + if (invStore.getSlot(i) != invStore.end() && *invStore.getSlot(i) == item) + { + equipped = true; + break; + } + } + newSpell.mActive = equipped; + } + mSpells.push_back(newSpell); + } + + std::stable_sort(mSpells.begin(), mSpells.end(), sortSpells); + } + + size_t SpellModel::getItemCount() const + { + return mSpells.size(); + } + + Spell SpellModel::getItem(ModelIndex index) const + { + if (index < 0 || index >= int(mSpells.size())) + throw std::runtime_error("invalid spell index supplied"); + return mSpells[index]; + } + +} diff --git a/apps/openmw/mwgui/spellmodel.hpp b/apps/openmw/mwgui/spellmodel.hpp new file mode 100644 index 0000000000..1f7a0cb7c9 --- /dev/null +++ b/apps/openmw/mwgui/spellmodel.hpp @@ -0,0 +1,56 @@ +#ifndef OPENMW_GUI_SPELLMODEL_H +#define OPENMW_GUI_SPELLMODEL_H + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + + struct Spell + { + enum Type + { + Type_Power, + Type_Spell, + Type_EnchantedItem + }; + + Type mType; + std::string mName; + std::string mCostColumn; // Cost/chance or Cost/charge + std::string mId; // Item ID or spell ID + MWWorld::Ptr mItem; // Only for Type_EnchantedItem + bool mSelected; // Is this the currently selected spell/item (only one can be selected at a time) + bool mActive; // (Items only) is the item equipped? + + Spell() + : mSelected(false) + , mActive(false) + { + } + }; + + ///@brief Model that lists all usable powers, spells and enchanted items for an actor. + class SpellModel + { + public: + SpellModel(const MWWorld::Ptr& actor); + + typedef int ModelIndex; + + void update(); + + Spell getItem (ModelIndex index) const; + ///< throws for invalid index + + size_t getItemCount() const; + + private: + MWWorld::Ptr mActor; + + std::vector mSpells; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp new file mode 100644 index 0000000000..3e5a01e3a7 --- /dev/null +++ b/apps/openmw/mwgui/spellview.cpp @@ -0,0 +1,255 @@ +#include "spellview.hpp" + +#include +#include +#include +#include + +#include + +namespace MWGui +{ + + SpellView::SpellView() + : mShowCostColumn(true) + , mHighlightSelected(true) + , mScrollView(NULL) + { + } + + void SpellView::initialiseOverride() + { + Base::initialiseOverride(); + + assignWidget(mScrollView, "ScrollView"); + if (mScrollView == NULL) + throw std::runtime_error("Item view needs a scroll view"); + + mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); + } + + void SpellView::registerComponents() + { + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + } + + void SpellView::setModel(SpellModel *model) + { + mModel.reset(model); + update(); + } + + SpellModel* SpellView::getModel() + { + return mModel.get(); + } + + void SpellView::setShowCostColumn(bool show) + { + if (show != mShowCostColumn) + { + mShowCostColumn = show; + update(); + } + } + + void SpellView::setHighlightSelected(bool highlight) + { + if (highlight != mHighlightSelected) + { + mHighlightSelected = highlight; + update(); + } + } + + void SpellView::update() + { + if (!mModel.get()) + return; + + mModel->update(); + + int curType = -1; + + const int spellHeight = 18; + + mLines.clear(); + + while (mScrollView->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); + + for (SpellModel::ModelIndex i = 0; igetItemCount()); ++i) + { + const Spell& spell = mModel->getItem(i); + if (curType != spell.mType) + { + if (spell.mType == Spell::Type_Power) + addGroup("#{sPowers}", ""); + else if (spell.mType == Spell::Type_Spell) + addGroup("#{sSpells}", "#{sCostChance}"); + else + addGroup("#{sMagicItem}", "#{sCostCharge}"); + curType = spell.mType; + } + + const std::string skin = spell.mActive ? "SandTextButton" : "SpellTextUnequipped"; + + Gui::SharedStateButton* t = mScrollView->createWidget(skin, + MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + t->setCaption(spell.mName); + t->setTextAlign(MyGUI::Align::Left); + adjustSpellWidget(spell, i, t); + + if (!spell.mCostColumn.empty() && mShowCostColumn) + { + Gui::SharedStateButton* costChance = mScrollView->createWidget(skin, + MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + costChance->setCaption(spell.mCostColumn); + costChance->setTextAlign(MyGUI::Align::Right); + adjustSpellWidget(spell, i, costChance); + + Gui::ButtonGroup group; + group.push_back(t); + group.push_back(costChance); + Gui::SharedStateButton::createButtonGroup(group); + + mLines.push_back(std::make_pair(t, costChance)); + } + else + mLines.push_back(std::make_pair(t, (MyGUI::Widget*)NULL)); + + t->setStateSelected(spell.mSelected); + } + + layoutWidgets(); + } + + void SpellView::layoutWidgets() + { + int height = 0; + for (std::vector< std::pair >::iterator it = mLines.begin(); + it != mLines.end(); ++it) + { + height += (it->first)->getHeight(); + } + + bool scrollVisible = height > mScrollView->getHeight(); + int width = mScrollView->getWidth() - (scrollVisible ? 18 : 0); + + height = 0; + for (std::vector< std::pair >::iterator it = mLines.begin(); + it != mLines.end(); ++it) + { + int lineHeight = (it->first)->getHeight(); + (it->first)->setCoord(4, height, width-8, lineHeight); + if (it->second) + { + (it->second)->setCoord(4, height, width-8, lineHeight); + MyGUI::TextBox* second = (it->second)->castType(false); + if (second) + (it->first)->setSize(width-8-second->getTextSize().width, lineHeight); + } + + height += lineHeight; + } + + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mScrollView->setVisibleVScroll(false); + mScrollView->setCanvasSize(mScrollView->getWidth(), std::max(mScrollView->getHeight(), height)); + mScrollView->setVisibleVScroll(true); + } + + void SpellView::addGroup(const std::string &label, const std::string& label2) + { + if (mScrollView->getChildCount() > 0) + { + MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", + MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18), + MyGUI::Align::Left | MyGUI::Align::Top); + separator->setNeedMouseFocus(false); + mLines.push_back(std::make_pair(separator, (MyGUI::Widget*)NULL)); + } + + MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", + MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), + MyGUI::Align::Left | MyGUI::Align::Top); + groupWidget->setCaptionWithReplacing(label); + groupWidget->setTextAlign(MyGUI::Align::Left); + groupWidget->setNeedMouseFocus(false); + + if (label2 != "") + { + MyGUI::TextBox* groupWidget2 = mScrollView->createWidget("SandBrightText", + MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), + MyGUI::Align::Left | MyGUI::Align::Top); + groupWidget2->setCaptionWithReplacing(label2); + groupWidget2->setTextAlign(MyGUI::Align::Right); + groupWidget2->setNeedMouseFocus(false); + + mLines.push_back(std::make_pair(groupWidget, groupWidget2)); + } + else + mLines.push_back(std::make_pair(groupWidget, (MyGUI::Widget*)NULL)); + } + + + void SpellView::setSize(const MyGUI::IntSize &_value) + { + bool changed = (_value.width != getWidth() || _value.height != getHeight()); + Base::setSize(_value); + if (changed) + layoutWidgets(); + } + + void SpellView::setSize(int _width, int _height) + { + setSize(MyGUI::IntSize(_width, _height)); + } + + void SpellView::setCoord(const MyGUI::IntCoord &_value) + { + bool changed = (_value.width != getWidth() || _value.height != getHeight()); + Base::setCoord(_value); + if (changed) + layoutWidgets(); + } + + void SpellView::setCoord(int _left, int _top, int _width, int _height) + { + setCoord(MyGUI::IntCoord(_left, _top, _width, _height)); + } + + void SpellView::adjustSpellWidget(const Spell &spell, SpellModel::ModelIndex index, MyGUI::Widget *widget) + { + if (spell.mType == Spell::Type_EnchantedItem) + { + widget->setUserData(spell.mItem); + widget->setUserString("ToolTipType", "ItemPtr"); + } + else + { + widget->setUserString("ToolTipType", "Spell"); + widget->setUserString("Spell", spell.mId); + } + + widget->setUserString("SpellModelIndex", MyGUI::utility::toString(index)); + + widget->eventMouseWheel += MyGUI::newDelegate(this, &SpellView::onMouseWheel); + widget->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellView::onSpellSelected); + } + + void SpellView::onSpellSelected(MyGUI::Widget* _sender) + { + SpellModel::ModelIndex i = MyGUI::utility::parseInt(_sender->getUserString("SpellModelIndex")); + eventSpellClicked(i); + } + + void SpellView::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mScrollView->getViewOffset().top + _rel*0.3 > 0) + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3)); + } + +} diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp new file mode 100644 index 0000000000..30daf86711 --- /dev/null +++ b/apps/openmw/mwgui/spellview.hpp @@ -0,0 +1,71 @@ +#ifndef OPENMW_GUI_SPELLVIEW_H +#define OPENMW_GUI_SPELLVIEW_H + +#include + +#include "spellmodel.hpp" + +namespace MyGUI +{ + class ScrollView; +} + +namespace MWGui +{ + + class SpellModel; + + ///@brief Displays a SpellModel in a list widget + class SpellView : public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(SpellView) + public: + SpellView(); + + /// Register needed components with MyGUI's factory manager + static void registerComponents (); + + /// Should the cost/chance column be shown? + void setShowCostColumn(bool show); + + void setHighlightSelected(bool highlight); + + /// Takes ownership of \a model + void setModel (SpellModel* model); + + SpellModel* getModel(); + + void update(); + + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ModelIndex; + /// Fired when a spell was clicked + EventHandle_ModelIndex eventSpellClicked; + + virtual void initialiseOverride(); + + virtual void setSize(const MyGUI::IntSize& _value); + virtual void setCoord(const MyGUI::IntCoord& _value); + void setSize(int _width, int _height); + void setCoord(int _left, int _top, int _width, int _height); + + private: + MyGUI::ScrollView* mScrollView; + + std::auto_ptr mModel; + + std::vector< std::pair > mLines; + + bool mShowCostColumn; + bool mHighlightSelected; + + void layoutWidgets(); + void addGroup(const std::string& label1, const std::string& label2); + void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget); + + void onSpellSelected(MyGUI::Widget* _sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 7904c249ab..deb971e6d4 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -1,10 +1,7 @@ #include "spellwindow.hpp" -#include #include -#include - #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -19,44 +16,24 @@ #include "spellicons.hpp" #include "inventorywindow.hpp" #include "confirmationdialog.hpp" +#include "spellview.hpp" namespace MWGui { - bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) - { - int cmp = left.getClass().getName(left).compare( - right.getClass().getName(right)); - return cmp < 0; - } - - bool sortSpells(const std::string& left, const std::string& right) - { - const MWWorld::Store &spells = - MWBase::Environment::get().getWorld()->getStore().get(); - - const ESM::Spell* a = spells.find(left); - const ESM::Spell* b = spells.find(right); - - int cmp = a->mName.compare(b->mName); - return cmp < 0; - } - SpellWindow::SpellWindow(DragAndDrop* drag) : WindowPinnableBase("openmw_spell_window.layout") , NoDrop(drag, mMainWidget) - , mHeight(0) - , mWidth(0) - , mWindowSize(mMainWidget->getSize()) + , mSpellView(NULL) { mSpellIcons = new SpellIcons(); getWidget(mSpellView, "SpellView"); getWidget(mEffectBox, "EffectsBox"); - setCoord(498, 300, 302, 300); + mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected); - mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &SpellWindow::onWindowResize); + setCoord(498, 300, 302, 300); } SpellWindow::~SpellWindow() @@ -84,261 +61,14 @@ namespace MWGui { mSpellIcons->updateWidgets(mEffectBox, false); - const int spellHeight = 18; - - mHeight = 0; - while (mSpellView->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellView->getChildAt(0)); - - // retrieve all player spells, divide them into Powers and Spells and sort them - std::vector spellList; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); - MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); - - for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) - spellList.push_back (it->first); - - const MWWorld::ESMStore &esmStore = - MWBase::Environment::get().getWorld()->getStore(); - - std::vector powers; - std::vector::iterator it = spellList.begin(); - while (it != spellList.end()) - { - const ESM::Spell* spell = esmStore.get().find(*it); - - if (spell->mData.mType == ESM::Spell::ST_Power) - { - powers.push_back(*it); - it = spellList.erase(it); - } - else if (spell->mData.mType == ESM::Spell::ST_Ability - || spell->mData.mType == ESM::Spell::ST_Blight - || spell->mData.mType == ESM::Spell::ST_Curse - || spell->mData.mType == ESM::Spell::ST_Disease) - { - it = spellList.erase(it); - } - else - ++it; - } - std::sort(powers.begin(), powers.end(), sortSpells); - std::sort(spellList.begin(), spellList.end(), sortSpells); - - // retrieve player's enchanted items - std::vector items; - for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it) - { - std::string enchantId = it->getClass().getEnchantment(*it); - if (enchantId != "") - { - // only add items with "Cast once" or "Cast on use" - const ESM::Enchantment* enchant = - esmStore.get().find(enchantId); - - int type = enchant->mData.mType; - if (type != ESM::Enchantment::CastOnce - && type != ESM::Enchantment::WhenUsed) - continue; - - items.push_back(*it); - } - } - std::sort(items.begin(), items.end(), sortItems); - - - int height = estimateHeight(items.size() + powers.size() + spellList.size()); - bool scrollVisible = height > mSpellView->getHeight(); - mWidth = mSpellView->getWidth() - (scrollVisible ? 18 : 0); - - // powers - addGroup("#{sPowers}", ""); - - for (std::vector::const_iterator it = powers.begin(); it != powers.end(); ++it) - { - const ESM::Spell* spell = esmStore.get().find(*it); - MyGUI::Button* t = mSpellView->createWidget("SandTextButton", - MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); - t->setCaption(spell->mName); - t->setTextAlign(MyGUI::Align::Left); - t->setUserString("ToolTipType", "Spell"); - t->setUserString("Spell", *it); - t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); - t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); - - if (*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell()) - t->setStateSelected(true); - - mHeight += spellHeight; - } - - // other spells - addGroup("#{sSpells}", "#{sCostChance}"); - for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) - { - const ESM::Spell* spell = esmStore.get().find(*it); - Gui::SharedStateButton* t = mSpellView->createWidget("SandTextButton", - MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); - t->setCaption(spell->mName); - t->setTextAlign(MyGUI::Align::Left); - t->setUserString("ToolTipType", "Spell"); - t->setUserString("Spell", *it); - t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); - t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); - - // cost / success chance - Gui::SharedStateButton* costChance = mSpellView->createWidget("SandTextButton", - MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); - std::string cost = boost::lexical_cast(spell->mData.mCost); - std::string chance = boost::lexical_cast(int(MWMechanics::getSpellSuccessChance(*it, player))); - costChance->setCaption(cost + "/" + chance); - costChance->setTextAlign(MyGUI::Align::Right); - costChance->setUserString("ToolTipType", "Spell"); - costChance->setUserString("Spell", *it); - costChance->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); - costChance->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); - - t->setSize(mWidth-12-costChance->getTextSize().width, t->getHeight()); - - Gui::ButtonGroup group; - group.push_back(t); - group.push_back(costChance); - Gui::SharedStateButton::createButtonGroup(group); - - t->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell()); - - mHeight += spellHeight; - } - - - // enchanted items - addGroup("#{sMagicItem}", "#{sCostCharge}"); - - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) - { - MWWorld::Ptr item = *it; - - const ESM::Enchantment* enchant = - esmStore.get().find(item.getClass().getEnchantment(item)); - - // check if the item is currently equipped (will display in a different color) - bool equipped = false; - for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) - { - if (store.getSlot(i) != store.end() && *store.getSlot(i) == item) - { - equipped = true; - break; - } - } - - Gui::SharedStateButton* t = mSpellView->createWidget(equipped ? "SandTextButton" : "SpellTextUnequipped", - MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); - t->setCaption(item.getClass().getName(item)); - t->setTextAlign(MyGUI::Align::Left); - t->setUserData(item); - t->setUserString("ToolTipType", "ItemPtr"); - t->setUserString("Equipped", equipped ? "true" : "false"); - t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onEnchantedItemSelected); - t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); - - // cost / charge - Gui::SharedStateButton* costCharge = mSpellView->createWidget(equipped ? "SandTextButton" : "SpellTextUnequipped", - MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); - - float enchantCost = enchant->mData.mCost; - int eSkill = player.getClass().getSkill(player, ESM::Skill::Enchant); - int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); - - std::string cost = boost::lexical_cast(castCost); - int currentCharge = int(item.getCellRef().getEnchantmentCharge()); - if (currentCharge == -1) - currentCharge = enchant->mData.mCharge; - std::string charge = boost::lexical_cast(currentCharge); - if (enchant->mData.mType == ESM::Enchantment::CastOnce) - { - // this is Morrowind behaviour - cost = "100"; - charge = "100"; - } - - - costCharge->setUserData(item); - costCharge->setUserString("ToolTipType", "ItemPtr"); - costCharge->setUserString("Equipped", equipped ? "true" : "false"); - costCharge->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onEnchantedItemSelected); - costCharge->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); - costCharge->setCaption(cost + "/" + charge); - costCharge->setTextAlign(MyGUI::Align::Right); - - Gui::ButtonGroup group; - group.push_back(t); - group.push_back(costCharge); - Gui::SharedStateButton::createButtonGroup(group); - - if (store.getSelectedEnchantItem() != store.end()) - t->setStateSelected(item == *store.getSelectedEnchantItem()); - - t->setSize(mWidth-12-costCharge->getTextSize().width, t->getHeight()); - - mHeight += spellHeight; - } - - // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden - mSpellView->setVisibleVScroll(false); - mSpellView->setCanvasSize(mSpellView->getWidth(), std::max(mSpellView->getHeight(), mHeight)); - mSpellView->setVisibleVScroll(true); - } - - void SpellWindow::addGroup(const std::string &label, const std::string& label2) - { - if (mSpellView->getChildCount() > 0) - { - MyGUI::ImageBox* separator = mSpellView->createWidget("MW_HLine", - MyGUI::IntCoord(4, mHeight, mWidth-8, 18), - MyGUI::Align::Left | MyGUI::Align::Top); - separator->setNeedMouseFocus(false); - mHeight += 18; - } - - MyGUI::TextBox* groupWidget = mSpellView->createWidget("SandBrightText", - MyGUI::IntCoord(0, mHeight, mWidth, 24), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - groupWidget->setCaptionWithReplacing(label); - groupWidget->setTextAlign(MyGUI::Align::Left); - groupWidget->setNeedMouseFocus(false); - - if (label2 != "") - { - MyGUI::TextBox* groupWidget2 = mSpellView->createWidget("SandBrightText", - MyGUI::IntCoord(0, mHeight, mWidth-4, 24), - MyGUI::Align::Left | MyGUI::Align::Top); - groupWidget2->setCaptionWithReplacing(label2); - groupWidget2->setTextAlign(MyGUI::Align::Right); - groupWidget2->setNeedMouseFocus(false); - - groupWidget->setSize(mWidth-8-groupWidget2->getTextSize().width, groupWidget->getHeight()); - } - - mHeight += 24; + mSpellView->setModel(new SpellModel(MWBase::Environment::get().getWorld()->getPlayerPtr())); + mSpellView->update(); } - void SpellWindow::onWindowResize(MyGUI::Window* _sender) - { - if (mMainWidget->getSize() != mWindowSize) - { - mWindowSize = mMainWidget->getSize(); - updateSpells(); - } - } - - void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender) + void SpellWindow::onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); - MWWorld::Ptr item = *_sender->getUserData(); // retrieve ContainerStoreIterator to the item MWWorld::ContainerStoreIterator it = store.begin(); @@ -352,7 +82,7 @@ namespace MWGui assert(it != store.end()); // equip, if it can be equipped and is not already equipped - if (_sender->getUserString("Equipped") == "false" + if (!alreadyEquipped && !item.getClass().getEquipmentSlots(item).first.empty()) { MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); @@ -364,12 +94,21 @@ namespace MWGui updateSpells(); } - void SpellWindow::onSpellSelected(MyGUI::Widget* _sender) + void SpellWindow::onModelIndexSelected(SpellModel::ModelIndex index) { - std::string spellId = _sender->getUserString("Spell"); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); + const Spell& spell = mSpellView->getModel()->getItem(index); + if (spell.mType == Spell::Type_EnchantedItem) + { + onEnchantedItemSelected(spell.mItem, spell.mActive); + } + else + { + onSpellSelected(spell.mId); + } + } + void SpellWindow::onSpellSelected(const std::string& spellId) + { if (MyGUI::InputManager::getInstance().isShiftPressed()) { // delete spell, if allowed @@ -396,6 +135,8 @@ namespace MWGui } else { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } @@ -403,22 +144,6 @@ namespace MWGui updateSpells(); } - int SpellWindow::estimateHeight(int numSpells) const - { - int height = 0; - height += 24 * 3 + 18 * 2; // group headings - height += numSpells * 18; - return height; - } - - void SpellWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) - { - if (mSpellView->getViewOffset().top + _rel*0.3 > 0) - mSpellView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mSpellView->setViewOffset(MyGUI::IntPoint(0, mSpellView->getViewOffset().top + _rel*0.3)); - } - void SpellWindow::onDeleteSpellAccept() { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index a74847b907..2f7319cb92 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -4,13 +4,12 @@ #include "windowpinnablebase.hpp" #include "../mwworld/ptr.hpp" +#include "spellmodel.hpp" + namespace MWGui { class SpellIcons; - - bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right); - - bool sortSpells(const std::string& left, const std::string& right); + class SpellView; class SpellWindow : public WindowPinnableBase, public NoDrop { @@ -23,30 +22,20 @@ namespace MWGui void onFrame(float dt) { NoDrop::onFrame(dt); } protected: - MyGUI::ScrollView* mSpellView; MyGUI::Widget* mEffectBox; - int mHeight; - int mWidth; - - MyGUI::IntSize mWindowSize; - std::string mSpellToDelete; - void addGroup(const std::string& label, const std::string& label2); - - int estimateHeight(int numSpells) const; - - void onWindowResize(MyGUI::Window* _sender); - void onEnchantedItemSelected(MyGUI::Widget* _sender); - void onSpellSelected(MyGUI::Widget* _sender); - void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped); + void onSpellSelected(const std::string& spellId); + void onModelIndexSelected(SpellModel::ModelIndex index); void onDeleteSpellAccept(); virtual void onPinToggled(); virtual void onTitleDoubleClicked(); virtual void open(); + SpellView* mSpellView; SpellIcons* mSpellIcons; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index acb8b2eb75..26f14572f2 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -73,6 +73,7 @@ #include "itemwidget.hpp" #include "screenfader.hpp" #include "debugwindow.hpp" +#include "spellview.hpp" namespace MWGui { @@ -179,6 +180,7 @@ namespace MWGui BookPage::registerMyGUIComponents (); ItemView::registerComponents(); ItemWidget::registerComponents(); + SpellView::registerComponents(); Gui::registerAllWidgets(); MyGUI::FactoryManager::getInstance().registerFactory("Controller"); diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 21b17c8f07..ce8209e3d5 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -157,6 +157,15 @@ + + + + + + + + + diff --git a/files/mygui/openmw_magicselection_dialog.layout b/files/mygui/openmw_magicselection_dialog.layout index a89795473f..bf4cb71d48 100644 --- a/files/mygui/openmw_magicselection_dialog.layout +++ b/files/mygui/openmw_magicselection_dialog.layout @@ -3,12 +3,10 @@ - + - - - + diff --git a/files/mygui/openmw_spell_window.layout b/files/mygui/openmw_spell_window.layout index ec655ace81..21bf74267a 100644 --- a/files/mygui/openmw_spell_window.layout +++ b/files/mygui/openmw_spell_window.layout @@ -10,10 +10,7 @@ - - - - + From 4e0d16da8c4651d006f3b40e05e50e5eea82243e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Dec 2014 13:34:04 +0100 Subject: [PATCH 068/404] Take Scale field in creature record into account (Fixes #2214) --- apps/openmw/mwclass/creature.cpp | 6 ++++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwworld/physicssystem.cpp | 7 +++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 519216ec9b..9d07aecf3f 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -897,4 +897,10 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mAiData.mFight; } + + void Creature::adjustScale(const MWWorld::Ptr &ptr, float &scale) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + scale *= ref->mBase->mScale; + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index c52e85534b..4b58864489 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -156,6 +156,8 @@ namespace MWClass virtual void restock (const MWWorld::Ptr &ptr) const; virtual int getBaseFightRating(const MWWorld::Ptr &ptr) const; + + virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; }; } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index eecc4a02db..61a3b27f81 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -754,8 +754,11 @@ namespace MWWorld if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { - // NOTE: Ignoring Npc::adjustScale (race height) on purpose. This is a bug in MW and must be replicated for compatibility reasons - act->setScale(ptr.getCellRef().getScale()); + float scale = ptr.getCellRef().getScale(); + if (!ptr.getClass().isNpc()) + // NOTE: Ignoring Npc::adjustScale (race height) on purpose. This is a bug in MW and must be replicated for compatibility reasons + ptr.getClass().adjustScale(ptr, scale); + act->setScale(scale); } } From 4d5adfb5ddec13c21ebead5d405b512a9dc379e9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Dec 2014 13:47:34 +0100 Subject: [PATCH 069/404] Fix being able to use enchantments of items that failed to equip (Fixes #2215) --- apps/openmw/mwgui/inventoryitemmodel.cpp | 10 ++-------- apps/openmw/mwgui/quickkeysmenu.cpp | 4 ++++ apps/openmw/mwgui/spellmodel.cpp | 11 +---------- apps/openmw/mwgui/spellwindow.cpp | 3 +++ apps/openmw/mwgui/tradeitemmodel.cpp | 11 +---------- apps/openmw/mwmechanics/aicombataction.cpp | 4 ++++ apps/openmw/mwworld/inventorystore.cpp | 10 ++++++++++ apps/openmw/mwworld/inventorystore.hpp | 3 +++ 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index f45881770a..cee0dc6eed 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -99,14 +99,8 @@ void InventoryItemModel::update() if (mActor.getClass().hasInventoryStore(mActor)) { MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor); - for (int slot=0; slotgetInventoryWindow()->useItem(item); + + // make sure that item was successfully equipped + if (!store.isEquipped(item)) + return; } store.setSelectedEnchantItem(it); diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 0305258978..fe2f073712 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -108,16 +108,7 @@ namespace MWGui std::string charge = boost::lexical_cast(currentCharge); newSpell.mCostColumn = cost + "/" + charge; - bool equipped = false; - for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) - { - if (invStore.getSlot(i) != invStore.end() && *invStore.getSlot(i) == item) - { - equipped = true; - break; - } - } - newSpell.mActive = equipped; + newSpell.mActive = invStore.isEquipped(item); } mSpells.push_back(newSpell); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index deb971e6d4..1da9635db3 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -86,6 +86,9 @@ namespace MWGui && !item.getClass().getEquipmentSlots(item).first.empty()) { MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); + // make sure that item was successfully equipped + if (!store.isEquipped(item)) + return; } MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index 3abfac997b..df0cbcac95 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -175,17 +175,8 @@ namespace MWGui // don't show equipped items if(mMerchant.getClass().hasInventoryStore(mMerchant)) { - bool isEquipped = false; MWWorld::InventoryStore& store = mMerchant.getClass().getInventoryStore(mMerchant); - for (int slot=0; slotgetStore().get().find(ptr.getClass().getEnchantment(ptr)); + if (enchantment->mData.mType == ESM::Enchantment::CastOnce) { return rateEffects(enchantment->mEffects, actor, target); } else + { + //if (!ptr.getClass().canBeEquipped(ptr, actor)) return 0.f; + } } float rateEffect(const ESM::ENAMstruct &effect, const MWWorld::Ptr &actor, const MWWorld::Ptr &target) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index c577d4b0d2..fef34d67be 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -643,3 +643,13 @@ void MWWorld::InventoryStore::clear() initSlots (mSlots); ContainerStore::clear(); } + +bool MWWorld::InventoryStore::isEquipped(const MWWorld::Ptr &item) +{ + for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) + { + if (getSlot(i) != end() && *getSlot(i) == item) + return true; + } + return false; +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 16d965cda3..30abc2ea57 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -145,6 +145,9 @@ namespace MWWorld void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor); ///< \warning \a iterator can not be an end()-iterator, use unequip function instead + bool isEquipped(const MWWorld::Ptr& item); + ///< Utility function, returns true if the given item is equipped in any slot + void setSelectedEnchantItem(const ContainerStoreIterator& iterator); ///< set the selected magic item (for using enchantments of type "Cast once" or "Cast when used") /// \note to unset the selected item, call this method with end() iterator From 935cccf9745dc04c37a85321bed9adef557f9831 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Dec 2014 15:23:03 +0100 Subject: [PATCH 070/404] Implement weapon/spell cycling hotkeys (Fixes #1024) --- apps/openmw/mwbase/windowmanager.hpp | 5 +++ apps/openmw/mwgui/inventorywindow.cpp | 48 +++++++++++++++++++++++ apps/openmw/mwgui/inventorywindow.hpp | 3 ++ apps/openmw/mwgui/sortfilteritemmodel.cpp | 32 +++++++++------ apps/openmw/mwgui/sortfilteritemmodel.hpp | 4 ++ apps/openmw/mwgui/spellmodel.cpp | 4 ++ apps/openmw/mwgui/spellwindow.cpp | 21 ++++++++++ apps/openmw/mwgui/spellwindow.hpp | 3 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 10 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 5 +++ apps/openmw/mwinput/inputmanagerimp.cpp | 25 ++++++++++++ 11 files changed, 148 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index d4f1afa32d..fb35157e85 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -342,6 +342,11 @@ namespace MWBase virtual void setWerewolfOverlay(bool set) = 0; virtual void toggleDebugWindow() = 0; + + /// Cycle to next or previous spell + virtual void cycleSpell(bool next) = 0; + /// Cycle to next or previous weapon + virtual void cycleWeapon(bool next) = 0; }; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index d1eeba1573..4fbfc2c67a 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -27,6 +27,19 @@ #include "tradewindow.hpp" #include "container.hpp" +namespace +{ + + bool isRightHandWeapon(const MWWorld::Ptr& item) + { + if (item.getClass().getTypeName() != typeid(ESM::Weapon).name()) + return false; + std::vector equipmentSlots = item.getClass().getEquipmentSlots(item).first; + return (!equipmentSlots.empty() && equipmentSlots.front() == MWWorld::InventoryStore::Slot_CarriedRight); + } + +} + namespace MWGui { @@ -599,4 +612,39 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); } + + void InventoryWindow::cycle(bool next) + { + ItemModel::ModelIndex selected = 0; + // not using mSortFilterModel as we only need sorting, not filtering + SortFilterItemModel model(new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayerPtr())); + model.setSortByType(false); + model.update(); + if (model.getItemCount() == 0) + return; + for (ItemModel::ModelIndex i=0; imData.mCost; int eSkill = mActor.getClass().getSkill(mActor, ESM::Skill::Enchant); int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 1da9635db3..37747f957d 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -160,4 +160,25 @@ namespace MWGui updateSpells(); } + + void SpellWindow::cycle(bool next) + { + mSpellView->setModel(new SpellModel(MWBase::Environment::get().getWorld()->getPlayerPtr())); + mSpellView->getModel()->update(); + + SpellModel::ModelIndex selected = 0; + for (SpellModel::ModelIndex i = 0; igetModel()->getItemCount()); ++i) + { + if (mSpellView->getModel()->getItem(i).mSelected) + selected = i; + } + + selected += next ? 1 : -1; + int itemcount = mSpellView->getModel()->getItemCount(); + if (itemcount == 0) + return; + selected = (selected + itemcount) % itemcount; + + onModelIndexSelected(selected); + } } diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 2f7319cb92..650218d307 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -21,6 +21,9 @@ namespace MWGui void onFrame(float dt) { NoDrop::onFrame(dt); } + /// Cycle to next/previous spell + void cycle(bool next); + protected: MyGUI::Widget* mEffectBox; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 26f14572f2..9d24fb19bd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1820,4 +1820,14 @@ namespace MWGui mDebugWindow->setVisible(!mDebugWindow->isVisible()); } + void WindowManager::cycleSpell(bool next) + { + mSpellWindow->cycle(next); + } + + void WindowManager::cycleWeapon(bool next) + { + mInventoryWindow->cycle(next); + } + } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 94d8a93db2..015f200d58 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -338,6 +338,11 @@ namespace MWGui virtual void toggleDebugWindow(); + /// Cycle to next or previous spell + virtual void cycleSpell(bool next); + /// Cycle to next or previous weapon + virtual void cycleWeapon(bool next); + private: bool mConsoleOnlyScripts; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b0d2bfefff..2f6149f091 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -286,6 +286,18 @@ namespace MWInput case A_QuickLoad: quickLoad(); break; + case A_CycleSpellLeft: + MWBase::Environment::get().getWindowManager()->cycleSpell(false); + break; + case A_CycleSpellRight: + MWBase::Environment::get().getWindowManager()->cycleSpell(true); + break; + case A_CycleWeaponLeft: + MWBase::Environment::get().getWindowManager()->cycleWeapon(false); + break; + case A_CycleWeaponRight: + MWBase::Environment::get().getWindowManager()->cycleWeapon(true); + break; } } } @@ -894,6 +906,11 @@ namespace MWInput defaultKeyBindings[A_MoveRight] = SDL_SCANCODE_D; defaultKeyBindings[A_ToggleWeapon] = SDL_SCANCODE_F; defaultKeyBindings[A_ToggleSpell] = SDL_SCANCODE_R; + defaultKeyBindings[A_CycleSpellLeft] = SDL_SCANCODE_MINUS; + defaultKeyBindings[A_CycleSpellRight] = SDL_SCANCODE_EQUALS; + defaultKeyBindings[A_CycleWeaponLeft] = SDL_SCANCODE_LEFTBRACKET; + defaultKeyBindings[A_CycleWeaponRight] = SDL_SCANCODE_RIGHTBRACKET; + defaultKeyBindings[A_QuickKeysMenu] = SDL_SCANCODE_F1; defaultKeyBindings[A_Console] = SDL_SCANCODE_GRAVE; defaultKeyBindings[A_Run] = SDL_SCANCODE_LSHIFT; @@ -972,6 +989,10 @@ namespace MWInput descriptions[A_MoveRight] = "sRight"; descriptions[A_ToggleWeapon] = "sReady_Weapon"; descriptions[A_ToggleSpell] = "sReady_Magic"; + descriptions[A_CycleSpellLeft] = "sPrevSpell"; + descriptions[A_CycleSpellRight] = "sNextSpell"; + descriptions[A_CycleWeaponLeft] = "sPrevWeapon"; + descriptions[A_CycleWeaponRight] = "sNextWeapon"; descriptions[A_Console] = "sConsoleTitle"; descriptions[A_Run] = "sRun"; descriptions[A_Sneak] = "sCrouch_Sneak"; @@ -1032,6 +1053,10 @@ namespace MWInput ret.push_back(A_Use); ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleSpell); + ret.push_back(A_CycleSpellLeft); + ret.push_back(A_CycleSpellRight); + ret.push_back(A_CycleWeaponLeft); + ret.push_back(A_CycleWeaponRight); ret.push_back(A_AutoMove); ret.push_back(A_Jump); ret.push_back(A_Inventory); From c7e1c0b595dd88c4fc61134a00bb3266ba156455 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Dec 2014 17:38:32 +0100 Subject: [PATCH 071/404] Fix weapon cycle getting stuck for same item IDs --- apps/openmw/mwgui/inventorywindow.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 4fbfc2c67a..d3c6073f47 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -615,13 +615,14 @@ namespace MWGui void InventoryWindow::cycle(bool next) { - ItemModel::ModelIndex selected = 0; + ItemModel::ModelIndex selected = -1; // not using mSortFilterModel as we only need sorting, not filtering SortFilterItemModel model(new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayerPtr())); model.setSortByType(false); model.update(); if (model.getItemCount() == 0) return; + for (ItemModel::ModelIndex i=0; i Date: Mon, 15 Dec 2014 18:19:05 +0100 Subject: [PATCH 072/404] Work around particles not being rendered in the first frame --- components/nifogre/ogrenifloader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index cdbc4d8447..22685f5489 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1033,7 +1033,7 @@ class NIFObjectLoader static void createParticleInitialState(Ogre::ParticleSystem* partsys, const Nif::NiAutoNormalParticlesData* particledata, const Nif::NiParticleSystemController* partctrl) { - partsys->_update(0.f); // seems to be required to allocate mFreeParticles + partsys->_update(0.f); // seems to be required to allocate mFreeParticles. TODO: patch Ogre to handle this better int i=0; for (std::vector::const_iterator it = partctrl->particles.begin(); iactiveCount && it != partctrl->particles.end(); ++it, ++i) @@ -1073,6 +1073,7 @@ class NIFObjectLoader totalTimeToLive = std::max(0.f, particle.lifespan); timeToLive = std::max(0.f, particle.lifespan - particle.lifetime); } + partsys->_update(0.f); // now apparently needs another update, otherwise it won't render in the first frame. TODO: patch Ogre to handle this better } static void createNodeControllers(const Nif::NIFFilePtr& nif, const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags) From e4127aa491fa39eac68b43fc5fd4c4301d085200 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Dec 2014 19:04:53 +0100 Subject: [PATCH 073/404] Use space in ItemView more efficiently --- apps/openmw/mwgui/itemview.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index a51ada2754..6af6a3b3f6 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -53,9 +53,14 @@ void ItemView::layoutWidgets() int x = 0; int y = 0; - int maxHeight = mScrollView->getSize().height - 58; - MyGUI::Widget* dragArea = mScrollView->getChildAt(0); + int maxHeight = dragArea->getHeight(); + + int rows = maxHeight/42; + rows = std::max(rows, 1); + bool showScrollbar = std::ceil(dragArea->getChildCount()/float(rows)) > mScrollView->getWidth()/42; + if (showScrollbar) + maxHeight -= 18; for (unsigned int i=0; igetChildCount(); ++i) { @@ -64,7 +69,8 @@ void ItemView::layoutWidgets() w->setPosition(x, y); y += 42; - if (y > maxHeight) + + if (y > maxHeight-42 && i < dragArea->getChildCount()-1) { x += 42; y = 0; From ec00c830e5a1ae8afc195a8149ae4ba0e62fb6c2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Dec 2014 19:18:14 +0100 Subject: [PATCH 074/404] Fix missing armor rating label update --- apps/openmw/mwgui/inventorywindow.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index d3c6073f47..c1a5ab94a5 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -527,6 +527,14 @@ namespace MWGui void InventoryWindow::doRenderUpdate () { mPreview->onFrame(); + + if (mPreviewResize || mPreviewDirty) + { + mArmorRating->setCaptionWithReplacing ("#{sArmor}: " + + boost::lexical_cast(static_cast(mPtr.getClass().getArmorRating(mPtr)))); + if (mArmorRating->getTextSize().width > mArmorRating->getSize().width) + mArmorRating->setCaptionWithReplacing (boost::lexical_cast(static_cast(mPtr.getClass().getArmorRating(mPtr)))); + } if (mPreviewResize) { mPreviewResize = false; @@ -543,11 +551,6 @@ namespace MWGui mPreview->update (); mAvatarImage->setImageTexture("CharacterPreview"); - - mArmorRating->setCaptionWithReplacing ("#{sArmor}: " - + boost::lexical_cast(static_cast(mPtr.getClass().getArmorRating(mPtr)))); - if (mArmorRating->getTextSize().width > mArmorRating->getSize().width) - mArmorRating->setCaptionWithReplacing (boost::lexical_cast(static_cast(mPtr.getClass().getArmorRating(mPtr)))); } } From 0dc94012698a2713fc89ae2526a1652d6a51d633 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Dec 2014 20:20:17 +0100 Subject: [PATCH 075/404] Fix GUI crash due to outdated spells list --- apps/openmw/mwgui/container.cpp | 2 ++ apps/openmw/mwgui/inventorywindow.cpp | 6 ++++++ apps/openmw/mwgui/spellwindow.cpp | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 58f23c175b..6df8a3f448 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -124,6 +124,8 @@ namespace MWGui if (targetView) targetView->update(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + // We need to update the view since an other item could be auto-equipped. mSourceView->update(); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index c1a5ab94a5..60f40e6fb3 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -309,6 +309,9 @@ namespace MWGui void InventoryWindow::updateItemView() { + if (MWBase::Environment::get().getWindowManager()->getSpellWindow()) + MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells(); + mItemView->update(); mPreviewDirty = true; } @@ -614,6 +617,9 @@ namespace MWGui mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count); MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); + + if (MWBase::Environment::get().getWindowManager()->getSpellWindow()) + MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells(); } void InventoryWindow::cycle(bool next) diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 37747f957d..240d0419e3 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -79,7 +79,8 @@ namespace MWGui break; } } - assert(it != store.end()); + if (it == store.end()) + throw std::runtime_error("can't find selected item"); // equip, if it can be equipped and is not already equipped if (!alreadyEquipped From 830ecfc70b9bfa7804addf94eb5211bee91e06c5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 16 Dec 2014 11:27:13 +0100 Subject: [PATCH 076/404] updated changelog once more --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index f5e3e0e59b..5d344c0b50 100644 --- a/readme.txt +++ b/readme.txt @@ -163,6 +163,7 @@ Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly. Bug #2170: Mods using conversations to update PC inconsistant Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources +Bug #2212: Crash or unexpected behavior while closing OpenCS cell render window on OS X Feature #238: Add UI to run INI-importer from the launcher Feature #854: Editor: Add user setting to show status bar Feature #987: Launcher: first launch instructions for CD need to be more explicit From b9e5aa9db677a4ab0dd3913c6213c1a5cad39d97 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 16 Dec 2014 20:44:42 +0100 Subject: [PATCH 077/404] Movement controller: Don't allow stepping up other actors This seems to fix issues with NPCs inadvertently being placed on top of a small creature while fighting it. Note that jumping on top of actors is still possible (Bug #1192) --- apps/openmw/mwworld/physicssystem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 61a3b27f81..aeaa07a925 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -195,6 +195,9 @@ namespace MWWorld stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-Ogre::Vector3(0.0f,0.0f,sStepSize), engine); if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) { + // don't allow stepping up other actors + if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Actor) + return false; // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing // NOTE: caller's variables 'position' & 'remainingTime' are modified here From d962f0918dee426a7dce21cf7719de0c6fe0afae Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 16 Dec 2014 20:47:45 +0100 Subject: [PATCH 078/404] Implement NPC head tracking (Fixes #1720) --- apps/openmw/mwmechanics/actors.cpp | 51 ++++++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 3 ++ apps/openmw/mwmechanics/character.cpp | 62 +++++++++++++++++++++++++++ apps/openmw/mwmechanics/character.hpp | 7 +++ apps/openmw/mwrender/animation.hpp | 4 ++ apps/openmw/mwrender/npcanimation.cpp | 31 +++++++++++++- apps/openmw/mwrender/npcanimation.hpp | 8 ++++ 7 files changed, 165 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index fecf189861..3a9ba56184 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -273,6 +274,40 @@ namespace MWMechanics calculateRestoration(ptr, duration); } + void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, + MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance) + { + static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get() + .find("fMaxHeadTrackDistance")->getFloat(); + static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get() + .find("fInteriorHeadTrackMult")->getFloat(); + float maxDistance = fMaxHeadTrackDistance; + const ESM::Cell* currentCell = actor.getCell()->getCell(); + if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx)) + maxDistance *= fInteriorHeadTrackMult; + + const ESM::Position& actor1Pos = actor.getRefData().getPosition(); + const ESM::Position& actor2Pos = targetActor.getRefData().getPosition(); + float sqrDist = Ogre::Vector3(actor1Pos.pos).squaredDistance(Ogre::Vector3(actor2Pos.pos)); + + if (sqrDist > maxDistance*maxDistance) + return; + + // stop tracking when target is behind the actor + Ogre::Vector3 actorDirection (actor.getRefData().getBaseNode()->getOrientation().yAxis()); + Ogre::Vector3 targetDirection (Ogre::Vector3(actor2Pos.pos) - Ogre::Vector3(actor1Pos.pos)); + actorDirection.z = 0; + targetDirection.z = 0; + if (actorDirection.angleBetween(targetDirection) < Ogre::Degree(90) + && sqrDist <= sqrHeadTrackDistance + && MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor)) + { + sqrHeadTrackDistance = sqrDist; + headTrackTarget = targetActor; + } + } + void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) { CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1); @@ -1138,9 +1173,11 @@ namespace MWMechanics if(!paused) { static float timerUpdateAITargets = 0; + static float timerUpdateHeadTrack = 0; // target lists get updated once every 1.0 sec if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0; + if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -1174,6 +1211,19 @@ namespace MWMechanics engageCombat(iter->first, it->first, it->first == player); } } + if (timerUpdateHeadTrack == 0) + { + float sqrHeadTrackDistance = std::numeric_limits::max(); + MWWorld::Ptr headTrackTarget; + + for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it) + { + if (it->first == iter->first) + continue; + updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance); + } + iter->second->setHeadTrackTarget(headTrackTarget); + } if (iter->first.getClass().isNpc() && iter->first != player) updateCrimePersuit(iter->first, duration); @@ -1194,6 +1244,7 @@ namespace MWMechanics } timerUpdateAITargets += duration; + timerUpdateHeadTrack += duration; // Looping magic VFX update // Note: we need to do this before any of the animations are updated. diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index a30a9dcf01..321229571f 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -89,6 +89,9 @@ namespace MWMechanics */ void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer); + void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, + MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); + void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d8693fbcdf..d675b0157e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -42,6 +42,15 @@ namespace { +// Wraps a value to (-PI, PI] +void wrap(Ogre::Radian& rad) +{ + if (rad.valueRadians()>0) + rad = Ogre::Radian(std::fmod(rad.valueRadians()+Ogre::Math::PI, 2.0f*Ogre::Math::PI)-Ogre::Math::PI); + else + rad = Ogre::Radian(std::fmod(rad.valueRadians()-Ogre::Math::PI, 2.0f*Ogre::Math::PI)+Ogre::Math::PI); +} + std::string getBestAttack (const ESM::Weapon* weapon) { int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; @@ -1627,6 +1636,8 @@ void CharacterController::update(float duration) cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0; // Can't reset jump state (mPosition[2]) here; we don't know for sure whether the PhysicSystem will actually handle it in this frame // due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled. + + updateHeadTracking(duration); } else if(cls.getCreatureStats(mPtr).isDead()) { @@ -1845,4 +1856,55 @@ bool CharacterController::isKnockedOut() const return mHitState == CharState_KnockOut; } +void CharacterController::setHeadTrackTarget(const MWWorld::Ptr &target) +{ + mHeadTrackTarget = target; +} + +void CharacterController::updateHeadTracking(float duration) +{ + Ogre::Node* head = mAnimation->getNode("Bip01 Head"); + if (!head) + return; + Ogre::Radian zAngle (0.f); + Ogre::Radian xAngle (0.f); + if (!mHeadTrackTarget.isEmpty()) + { + Ogre::Vector3 headPos = mPtr.getRefData().getBaseNode()->convertLocalToWorldPosition(head->_getDerivedPosition()); + Ogre::Vector3 targetPos (mHeadTrackTarget.getRefData().getPosition().pos); + if (MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) + { + Ogre::Node* targetHead = anim->getNode("Head"); + if (!targetHead) + targetHead = anim->getNode("Bip01 Head"); + if (targetHead) + targetPos = mHeadTrackTarget.getRefData().getBaseNode()->convertLocalToWorldPosition( + targetHead->_getDerivedPosition()); + } + + Ogre::Vector3 direction = targetPos - headPos; + direction.normalise(); + + const Ogre::Vector3 actorDirection = mPtr.getRefData().getBaseNode()->getOrientation().yAxis(); + + zAngle = Ogre::Math::ATan2(direction.x,direction.y) - + Ogre::Math::ATan2(actorDirection.x, actorDirection.y); + xAngle = -Ogre::Math::ASin(direction.z); + wrap(zAngle); + wrap(xAngle); + xAngle = Ogre::Degree(std::min(xAngle.valueDegrees(), 40.f)); + xAngle = Ogre::Degree(std::max(xAngle.valueDegrees(), -40.f)); + zAngle = Ogre::Degree(std::min(zAngle.valueDegrees(), 30.f)); + zAngle = Ogre::Degree(std::max(zAngle.valueDegrees(), -30.f)); + + } + float factor = duration*5; + factor = std::min(factor, 1.f); + xAngle = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngle); + zAngle = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngle); + + mAnimation->setHeadPitch(xAngle); + mAnimation->setHeadYaw(zAngle); +} + } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index d7028a8441..5b2c57b0a9 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -175,6 +175,8 @@ class CharacterController float mSecondsOfSwimming; float mSecondsOfRunning; + MWWorld::Ptr mHeadTrackTarget; + float mTurnAnimationThreshold; // how long to continue playing turning animation after actor stopped turning std::string mAttackType; // slash, chop or thrust @@ -188,6 +190,8 @@ class CharacterController bool updateCreatureState(); void updateIdleStormState(); + void updateHeadTracking(float duration); + void castSpell(const std::string& spellid); void updateMagicEffects(); @@ -229,6 +233,9 @@ public: bool isReadyToBlock() const; bool isKnockedOut() const; + + /// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr. + void setHeadTrackTarget(const MWWorld::Ptr& target); }; void getWeaponGroup(WeaponType weaptype, std::string &group); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index d25d4f0b0e..1a420582cd 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -306,6 +306,10 @@ public: /// A relative factor (0-1) that decides if and how much the skeleton should be pitched /// to indicate the facing orientation of the character. virtual void setPitchFactor(float factor) {} + virtual void setHeadPitch(Ogre::Radian factor) {} + virtual void setHeadYaw(Ogre::Radian factor) {} + virtual Ogre::Radian getHeadPitch() const { return Ogre::Radian(0.f); } + virtual Ogre::Radian getHeadYaw() const { return Ogre::Radian(0.f); } virtual Ogre::Vector3 runAnimation(float duration); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b93b37aeac..7c68a45380 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -207,7 +207,9 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f), mNpcType(Type_Normal), - mSoundsDisabled(disableSounds) + mSoundsDisabled(disableSounds), + mHeadPitch(0.f), + mHeadYaw(0.f) { mNpc = mPtr.get()->mBase; @@ -621,6 +623,13 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) { // In third person mode we may still need pitch for ranged weapon targeting pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst); + + Ogre::Node* node = baseinst->getBone("Bip01 Head"); + if (node) + { + node->rotate(Ogre::Vector3::UNIT_Z, mHeadYaw, Ogre::Node::TS_WORLD); + node->rotate(Ogre::Vector3::UNIT_X, mHeadPitch, Ogre::Node::TS_WORLD); + } } mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. @@ -993,4 +1002,24 @@ void NpcAnimation::setVampire(bool vampire) } } +void NpcAnimation::setHeadPitch(Ogre::Radian pitch) +{ + mHeadPitch = pitch; +} + +void NpcAnimation::setHeadYaw(Ogre::Radian yaw) +{ + mHeadYaw = yaw; +} + +Ogre::Radian NpcAnimation::getHeadPitch() const +{ + return mHeadPitch; +} + +Ogre::Radian NpcAnimation::getHeadYaw() const +{ + return mHeadYaw; +} + } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index aba01bcfaa..f3603fe14b 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -100,6 +100,9 @@ private: float mAlpha; bool mSoundsDisabled; + Ogre::Radian mHeadYaw; + Ogre::Radian mHeadPitch; + void updateNpcBase(); NifOgre::ObjectScenePtr insertBoundedPart(const std::string &model, int group, const std::string &bonename, @@ -142,6 +145,11 @@ public: /// to indicate the facing orientation of the character. virtual void setPitchFactor(float factor) { mPitchFactor = factor; } + virtual void setHeadPitch(Ogre::Radian pitch); + virtual void setHeadYaw(Ogre::Radian yaw); + virtual Ogre::Radian getHeadPitch() const; + virtual Ogre::Radian getHeadYaw() const; + virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool show); From 88c5e1991c13fe3f877335256139fc2040613352 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 16 Dec 2014 23:18:41 +0100 Subject: [PATCH 079/404] Fix being able to stand on top of actors (Fixes #1192) --- apps/openmw/mwworld/physicssystem.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index aeaa07a925..d9941bafdc 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -452,7 +452,8 @@ namespace MWWorld Ogre::Vector3 to = newPosition - (physicActor->getOnGround() ? Ogre::Vector3(0,0,sStepSize+2.f) : Ogre::Vector3(0,0,2.f)); tracer.doTrace(colobj, from, to, engine); - if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) + if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope + && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != OEngine::Physic::CollisionType_Actor) { const btCollisionObject* standingOn = tracer.mHitObject; if (const OEngine::Physic::RigidBody* body = dynamic_cast(standingOn)) @@ -468,7 +469,25 @@ namespace MWWorld isOnGround = true; } else + { + // standing on actors is not allowed (see above). + // in addition to that, apply a sliding effect away from the center of the actor, + // so that we do not stay suspended in air indefinitely. + if (tracer.mFraction < 1.0f && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Actor) + { + if (Ogre::Vector3(inertia.x, inertia.y, 0).squaredLength() < 100.f*100.f) + { + btVector3 aabbMin, aabbMax; + tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax); + btVector3 center = (aabbMin + aabbMax) / 2.f; + inertia = Ogre::Vector3(position.x - center.x(), position.y - center.y(), 0); + inertia.normalise(); + inertia *= 100; + } + } + isOnGround = false; + } } if(isOnGround || newPosition.z < waterlevel || isFlying) From d642512f71698ff91c297d32a5412bf01f4ffbd6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Dec 2014 00:57:04 +0100 Subject: [PATCH 080/404] Error message fix --- apps/openmw/mwclass/creature.cpp | 5 ++--- apps/openmw/mwclass/npc.cpp | 11 ++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9d07aecf3f..5fd5f4dde0 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -120,11 +120,10 @@ namespace MWClass for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); iter!=ref->mBase->mSpells.mList.end(); ++iter) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter); - if (spell) + if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mCreatureStats.getSpells().add (spell); else /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility - std::cerr << "Warning: ignoring nonexistent spell '" << spell->mId << "' on creature '" << ref->mBase->mId << "'" << std::endl; + std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'" << std::endl; } // inventory diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a3ebef5821..4814879acd 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -300,8 +300,7 @@ namespace MWClass if (!ref->mBase->mFaction.empty()) { std::string faction = ref->mBase->mFaction; - const ESM::Faction* fact = MWBase::Environment::get().getWorld()->getStore().get().search(faction); - if (fact) + if (const ESM::Faction* fact = MWBase::Environment::get().getWorld()->getStore().get().search(faction)) { if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { @@ -313,7 +312,7 @@ namespace MWClass } } else - std::cerr << "Warning: ignoring nonexistent faction '" << fact->mId << "' on NPC '" << ref->mBase->mId << "'" << std::endl; + std::cerr << "Warning: ignoring nonexistent faction '" << faction << "' on NPC '" << ref->mBase->mId << "'" << std::endl; } // creature stats @@ -366,8 +365,7 @@ namespace MWClass for (std::vector::const_iterator iter (race->mPowers.mList.begin()); iter!=race->mPowers.mList.end(); ++iter) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter); - if (spell) + if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mNpcStats.getSpells().add (spell); else std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; @@ -395,8 +393,7 @@ namespace MWClass for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); iter!=ref->mBase->mSpells.mList.end(); ++iter) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter); - if (spell) + if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mNpcStats.getSpells().add (spell); else { From 31d28e727f004d944c40c58e5f3822415192a265 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Dec 2014 01:05:32 +0100 Subject: [PATCH 081/404] Implement leveled list script functions (Fixes #1546) --- apps/openmw/mwbase/world.hpp | 10 +++ apps/openmw/mwscript/docs/vmformat.txt | 6 +- apps/openmw/mwscript/miscextensions.cpp | 112 ++++++++++++++++++++++++ apps/openmw/mwstate/statemanagerimp.cpp | 2 + apps/openmw/mwworld/esmstore.cpp | 8 +- apps/openmw/mwworld/esmstore.hpp | 15 ++++ apps/openmw/mwworld/store.hpp | 10 +-- apps/openmw/mwworld/worldimp.cpp | 10 +++ apps/openmw/mwworld/worldimp.hpp | 8 ++ components/compiler/extensions0.cpp | 4 + components/compiler/opcodes.hpp | 4 + 11 files changed, 182 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 34be298d3a..32beadf18a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -40,6 +40,8 @@ namespace ESM struct Enchantment; struct Book; struct EffectList; + struct CreatureLevList; + struct ItemLevList; } namespace MWRender @@ -359,6 +361,14 @@ namespace MWBase ///< Create a new record (of type book) in the ESM store. /// \return pointer to created record + virtual const ESM::CreatureLevList *createOverrideRecord (const ESM::CreatureLevList& record) = 0; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + + virtual const ESM::ItemLevList *createOverrideRecord (const ESM::ItemLevList& record) = 0; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + virtual void update (float duration, bool paused) = 0; virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 800c6e2c7b..172e1b528a 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -438,5 +438,9 @@ op 0x20002f7: PCForce3rdPerson op 0x20002f8: PCGet3rdPerson op 0x20002f9: HitAttemptOnMe op 0x20002fa: HitAttemptOnMe, explicit +op 0x20002fb: AddToLevCreature +op 0x20002fc: RemoveFromLevCreature +op 0x20002fd: AddToLevItem +op 0x20002fe: RemoveFromLevItem -opcodes 0x20002fb-0x3ffffff unused +opcodes 0x20002ff-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index c92acff820..f20c9967df 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -31,6 +31,42 @@ #include "interpretercontext.hpp" #include "ref.hpp" +namespace +{ + + void addToLevList(ESM::LeveledListBase* list, const std::string& itemId, int level) + { + for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) + { + if (it->mLevel == level && itemId == it->mId) + return; + } + + ESM::LeveledListBase::LevelItem item; + item.mId = itemId; + item.mLevel = level; + list->mList.push_back(item); + } + + void removeFromLevList(ESM::LeveledListBase* list, const std::string& itemId, int level) + { + // level of -1 removes all items with that itemId + for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) + { + if (level != -1 && it->mLevel != level) + { + ++it; + continue; + } + if (Misc::StringUtils::ciEqual(itemId, it->mId)) + it = list->mList.erase(it); + else + ++it; + } + } + +} + namespace MWScript { namespace Misc @@ -1032,6 +1068,78 @@ namespace MWScript } }; + class OpAddToLevCreature : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + const std::string& creatureId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + int level = runtime[0].mInteger; + runtime.pop(); + + ESM::CreatureLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); + addToLevList(&listCopy, creatureId, level); + MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); + } + }; + + class OpRemoveFromLevCreature : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + const std::string& creatureId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + int level = runtime[0].mInteger; + runtime.pop(); + + ESM::CreatureLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); + removeFromLevList(&listCopy, creatureId, level); + MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); + } + }; + + class OpAddToLevItem : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + const std::string& itemId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + int level = runtime[0].mInteger; + runtime.pop(); + + ESM::ItemLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); + addToLevList(&listCopy, itemId, level); + MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); + } + }; + + class OpRemoveFromLevItem : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + const std::string& itemId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + int level = runtime[0].mInteger; + runtime.pop(); + + ESM::ItemLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); + removeFromLevList(&listCopy, itemId, level); + MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -1121,6 +1229,10 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling); interpreter.installSegment5 (Compiler::Misc::opcodeBetaComment, new OpBetaComment); interpreter.installSegment5 (Compiler::Misc::opcodeBetaCommentExplicit, new OpBetaComment); + interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevCreature, new OpAddToLevCreature); + interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevCreature, new OpRemoveFromLevCreature); + interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevItem, new OpAddToLevItem); + interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevItem, new OpRemoveFromLevItem); } } } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 18ebe11cee..9746202dc3 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -352,6 +352,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_PROJ: case ESM::REC_MPRJ: case ESM::REC_ENAB: + case ESM::REC_LEVC: + case ESM::REC_LEVI: MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 56cb05c646..2a3fd9179e 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -149,7 +149,9 @@ void ESMStore::setUp() +mEnchants.getDynamicSize() +mNpcs.getDynamicSize() +mSpells.getDynamicSize() - +mWeapons.getDynamicSize(); + +mWeapons.getDynamicSize() + +mCreatureLists.getDynamicSize() + +mItemLists.getDynamicSize(); } void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -170,6 +172,8 @@ void ESMStore::setUp() mSpells.write (writer, progress); mWeapons.write (writer, progress); mNpcs.write (writer, progress); + mItemLists.write (writer, progress); + mCreatureLists.write (writer, progress); } bool ESMStore::readRecord (ESM::ESMReader& reader, int32_t type) @@ -185,6 +189,8 @@ void ESMStore::setUp() case ESM::REC_SPEL: case ESM::REC_WEAP: case ESM::REC_NPC_: + case ESM::REC_LEVI: + case ESM::REC_LEVC: mStores[type]->read (reader); diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 83e911174d..5d794db895 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -167,6 +167,7 @@ namespace MWWorld throw std::runtime_error("Storage for this type not exist"); } + /// Insert a custom record (i.e. with a generated ID that will not clash will pre-existing records) template const T *insert(const T &x) { std::ostringstream id; @@ -191,6 +192,20 @@ namespace MWWorld return ptr; } + /// Insert a record with set ID, and allow it to override a pre-existing static record. + template + const T *overrideRecord(const T &x) { + Store &store = const_cast &>(get()); + + T *ptr = store.insert(x); + for (iterator it = mStores.begin(); it != mStores.end(); ++it) { + if (it->second == &store) { + mIds[ptr->mId] = it->first; + } + } + return ptr; + } + template const T *insertStatic(const T &x) { std::ostringstream id; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index c4070f0327..a0d34b228d 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -145,17 +145,17 @@ namespace MWWorld T item; item.mId = Misc::StringUtils::lowerCase(id); + typename Dynamic::const_iterator dit = mDynamic.find(item.mId); + if (dit != mDynamic.end()) { + return &dit->second; + } + typename std::map::const_iterator it = mStatic.find(item.mId); if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { return &(it->second); } - typename Dynamic::const_iterator dit = mDynamic.find(item.mId); - if (dit != mDynamic.end()) { - return &dit->second; - } - return 0; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e24e99c305..17a45f9f12 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1490,6 +1490,16 @@ namespace MWWorld return mStore.insert(record); } + const ESM::CreatureLevList *World::createOverrideRecord(const ESM::CreatureLevList &record) + { + return mStore.overrideRecord(record); + } + + const ESM::ItemLevList *World::createOverrideRecord(const ESM::ItemLevList &record) + { + return mStore.overrideRecord(record); + } + const ESM::NPC *World::createRecord(const ESM::NPC &record) { bool update = false; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index c8e254b2f9..2a0da917b9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -420,6 +420,14 @@ namespace MWWorld ///< Create a new record (of type book) in the ESM store. /// \return pointer to created record + virtual const ESM::CreatureLevList *createOverrideRecord (const ESM::CreatureLevList& record); + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + + virtual const ESM::ItemLevList *createOverrideRecord (const ESM::ItemLevList& record); + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + virtual void update (float duration, bool paused); virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 690e589f73..d08a2bb320 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -308,6 +308,10 @@ namespace Compiler extensions.registerFunction ("getpctraveling", 'l', "", opcodeGetPcTraveling); extensions.registerInstruction ("betacomment", "S", opcodeBetaComment, opcodeBetaCommentExplicit); extensions.registerInstruction ("bc", "S", opcodeBetaComment, opcodeBetaCommentExplicit); + extensions.registerInstruction ("addtolevcreature", "ccl", opcodeAddToLevCreature); + extensions.registerInstruction ("removefromlevcreature", "ccl", opcodeRemoveFromLevCreature); + extensions.registerInstruction ("addtolevitem", "ccl", opcodeAddToLevItem); + extensions.registerInstruction ("removefromlevitem", "ccl", opcodeRemoveFromLevItem); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index da79555e22..bbafd6b13b 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -284,6 +284,10 @@ namespace Compiler const int opcodeExplodeSpellExplicit = 0x200022a; const int opcodeGetPcInJail = 0x200023e; const int opcodeGetPcTraveling = 0x200023f; + const int opcodeAddToLevCreature = 0x20002fb; + const int opcodeRemoveFromLevCreature = 0x20002fc; + const int opcodeAddToLevItem = 0x20002fd; + const int opcodeRemoveFromLevItem = 0x20002fe; } namespace Sky From c2771bc8abf2024779d31afd998bf7f0b797b783 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Dec 2014 02:15:40 +0100 Subject: [PATCH 082/404] Head tracking fix --- apps/openmw/mwrender/npcanimation.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 7c68a45380..3b0b4e08b2 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -626,10 +626,7 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) Ogre::Node* node = baseinst->getBone("Bip01 Head"); if (node) - { - node->rotate(Ogre::Vector3::UNIT_Z, mHeadYaw, Ogre::Node::TS_WORLD); - node->rotate(Ogre::Vector3::UNIT_X, mHeadPitch, Ogre::Node::TS_WORLD); - } + node->rotate(Ogre::Quaternion(mHeadYaw, Ogre::Vector3::UNIT_Z) * Ogre::Quaternion(mHeadPitch, Ogre::Vector3::UNIT_X), Ogre::Node::TS_WORLD); } mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. From 899ae763e667bbe98be1ce459c1e169eb51a3764 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 17 Dec 2014 09:33:40 +0100 Subject: [PATCH 083/404] fixing a travis build problem --- apps/openmw/mwgui/itemview.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 6af6a3b3f6..ed2002d72c 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -1,5 +1,7 @@ #include "itemview.hpp" +#include + #include #include From 5cb94da9c5a8491d724c9d4eb950e38e3b9c44fa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 17 Dec 2014 11:56:54 +0100 Subject: [PATCH 084/404] compensate for incorrect minus character in translated dialogue script (Fixes #2207) --- components/compiler/scanner.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 203f27e6e8..488906d8ff 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -445,6 +445,32 @@ namespace Compiler else special = S_minus; } + else if (static_cast (c)==0xe2) + { + /// Workaround for some translator who apparently can't keep his minus in order + /// \todo disable for later script formats + if (get (c) && static_cast (c)==0x80 && + get (c) && static_cast (c)==0x93) + { + if (get (c)) + { + if (c=='>') + special = S_ref; + else + { + putback (c); + special = S_minus; + } + } + else + special = S_minus; + } + else + { + mErrorHandler.error ("Invalid character", mLoc); + return false; + } + } else if (c=='<') { if (get (c)) From b951251572b1cf0f1c473615422d66a6f640aa35 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 17 Dec 2014 15:03:05 +0100 Subject: [PATCH 085/404] handle junk in argument lists (Fixes #2206) --- components/CMakeLists.txt | 4 +-- components/compiler/exprparser.cpp | 11 ++++++- components/compiler/exprparser.hpp | 3 +- components/compiler/extensions.hpp | 1 + components/compiler/extensions0.cpp | 2 +- components/compiler/junkparser.cpp | 48 +++++++++++++++++++++++++++++ components/compiler/junkparser.hpp | 41 ++++++++++++++++++++++++ components/compiler/lineparser.cpp | 2 +- 8 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 components/compiler/junkparser.cpp create mode 100644 components/compiler/junkparser.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 234325718d..6918b87a7a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -70,7 +70,7 @@ add_component_dir (compiler context controlparser errorhandler exception exprparser extensions fileparser generator lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser - quickfileparser discardparser + quickfileparser discardparser junkparser ) add_component_dir (interpreter @@ -123,7 +123,7 @@ if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) launchersettings settingsbase ) - + add_component_qt_dir (process processinvoker ) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 6dcca08df9..18d9e6dc7a 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -17,6 +17,7 @@ #include "extensions.hpp" #include "context.hpp" #include "discardparser.hpp" +#include "junkparser.hpp" namespace Compiler { @@ -752,7 +753,7 @@ namespace Compiler } int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner, - std::vector& code) + std::vector& code, int ignoreKeyword) { bool optional = false; int optionalCount = 0; @@ -760,6 +761,7 @@ namespace Compiler ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true); StringParser stringParser (getErrorHandler(), getContext(), mLiterals); DiscardParser discardParser (getErrorHandler(), getContext()); + JunkParser junkParser (getErrorHandler(), getContext(), ignoreKeyword); std::stack > stack; @@ -815,6 +817,13 @@ namespace Compiler if (discardParser.isEmpty()) break; } + else if (*iter=='j') + { + /// \todo disable this when operating in strict mode + junkParser.reset(); + + scanner.scan (junkParser); + } else { parser.reset(); diff --git a/components/compiler/exprparser.hpp b/components/compiler/exprparser.hpp index e4e385ff0f..639ca65aab 100644 --- a/components/compiler/exprparser.hpp +++ b/components/compiler/exprparser.hpp @@ -96,11 +96,12 @@ namespace Compiler /// \return Type ('l': integer, 'f': float) int parseArguments (const std::string& arguments, Scanner& scanner, - std::vector& code); + std::vector& code, int ignoreKeyword = -1); ///< Parse sequence of arguments specified by \a arguments. /// \param arguments Uses ScriptArgs typedef /// \see Compiler::ScriptArgs /// \param invert Store arguments in reverted order. + /// \param ignoreKeyword A keyword that is seen as junk /// \return number of optional arguments }; } diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index a15218d99f..9fb9bdb95a 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -23,6 +23,7 @@ namespace Compiler x - Optional, ignored string argument X - Optional, ignored numeric expression z - Optional, ignored string or numeric argument + j - A piece of junk (either . or a specific keyword) **/ typedef std::string ScriptArgs; diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index d08a2bb320..234c5b12d4 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -179,7 +179,7 @@ namespace Compiler extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex); extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex); extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic); - extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice); + extensions.registerInstruction ("choice", "j/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice); extensions.registerInstruction("forcegreeting","",opcodeForceGreeting, opcodeForceGreetingExplicit); extensions.registerInstruction("goodbye", "", opcodeGoodbye); diff --git a/components/compiler/junkparser.cpp b/components/compiler/junkparser.cpp new file mode 100644 index 0000000000..cfa94044e7 --- /dev/null +++ b/components/compiler/junkparser.cpp @@ -0,0 +1,48 @@ + +#include "junkparser.hpp" + +#include "scanner.hpp" + +Compiler::JunkParser::JunkParser (ErrorHandler& errorHandler, const Context& context, + int ignoreKeyword) +: Parser (errorHandler, context), mIgnoreKeyword (ignoreKeyword) +{} + +bool Compiler::JunkParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) +{ + scanner.putbackInt (value, loc); + return false; +} + +bool Compiler::JunkParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) +{ + scanner.putbackFloat (value, loc); + return false; +} + +bool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) +{ + scanner.putbackName (name, loc); + return false; +} + +bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) +{ + if (keyword==mIgnoreKeyword) + reportWarning ("found junk (ignoring it)", loc); + else + scanner.putbackKeyword (keyword, loc); + + return false; +} + +bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) +{ + if (code==Scanner::S_member) + reportWarning ("found junk (ignoring it)", loc); + else + scanner.putbackSpecial (code, loc); + + return false; +} diff --git a/components/compiler/junkparser.hpp b/components/compiler/junkparser.hpp new file mode 100644 index 0000000000..6dfbd97af4 --- /dev/null +++ b/components/compiler/junkparser.hpp @@ -0,0 +1,41 @@ +#ifndef COMPILER_JUNKPARSER_H_INCLUDED +#define COMPILER_JUNKPARSER_H_INCLUDED + +#include "parser.hpp" + +namespace Compiler +{ + /// \brief Parse an optional single junk token + class JunkParser : public Parser + { + int mIgnoreKeyword; + + public: + + JunkParser (ErrorHandler& errorHandler, const Context& context, + int ignoreKeyword = -1); + + virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); + ///< Handle an int token. + /// \return fetch another token? + + virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); + ///< Handle a float token. + /// \return fetch another token? + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + + virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); + ///< Handle a keyword token. + /// \return fetch another token? + + virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + }; +} + +#endif diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index dc19b9a4b0..2d08ed3fe7 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -304,7 +304,7 @@ namespace Compiler errorDowngrade.reset (new ErrorDowngrade (getErrorHandler())); std::vector code; - int optionals = mExprParser.parseArguments (argumentType, scanner, code); + int optionals = mExprParser.parseArguments (argumentType, scanner, code, keyword); mCode.insert (mCode.end(), code.begin(), code.end()); extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals); From fc1d42a7d2d9b904092cfec7627c96f54a429022 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 18 Dec 2014 09:55:26 +0100 Subject: [PATCH 086/404] fixed exclusion for certain characters at the start of names --- components/compiler/scanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 59fae3ccdc..9acba861a9 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -314,7 +314,7 @@ namespace Compiler bool Scanner::scanName (char c, std::string& name) { - bool first = false; + bool first = true; bool error = false; name.clear(); From a6d30bc2e3014c77b883de9ceab40d141078f78f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 18 Dec 2014 10:20:15 +0100 Subject: [PATCH 087/404] consider --script-warn when running with --script-all-dialogue --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwdialogue/scripttest.cpp | 9 +++++---- apps/openmw/mwdialogue/scripttest.hpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 885d2bb4f1..6ea09d4c9d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -436,7 +436,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) } if (mCompileAllDialogue) { - std::pair result = MWDialogue::ScriptTest::compileAll(&mExtensions); + std::pair result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode); if (result.first) std::cout << "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a(" diff --git a/apps/openmw/mwdialogue/scripttest.cpp b/apps/openmw/mwdialogue/scripttest.cpp index a8de21ef97..b2c8f536a2 100644 --- a/apps/openmw/mwdialogue/scripttest.cpp +++ b/apps/openmw/mwdialogue/scripttest.cpp @@ -21,7 +21,7 @@ namespace { -void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler::Extensions* extensions) +void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler::Extensions* extensions, int warningsMode) { MWDialogue::Filter filter(actor, 0, false); @@ -29,6 +29,7 @@ void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler:: compilerContext.setExtensions(extensions); std::ostream errorStream(std::cout.rdbuf()); Compiler::StreamErrorHandler errorHandler(errorStream); + errorHandler.setWarningsMode (warningsMode); const MWWorld::Store& dialogues = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator it = dialogues.begin(); it != dialogues.end(); ++it) @@ -100,21 +101,21 @@ namespace MWDialogue namespace ScriptTest { - std::pair compileAll(const Compiler::Extensions *extensions) + std::pair compileAll(const Compiler::Extensions *extensions, int warningsMode) { int compiled = 0, total = 0; const MWWorld::Store& npcs = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator it = npcs.begin(); it != npcs.end(); ++it) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->mId); - test(ref.getPtr(), compiled, total, extensions); + test(ref.getPtr(), compiled, total, extensions, warningsMode); } const MWWorld::Store& creatures = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator it = creatures.begin(); it != creatures.end(); ++it) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->mId); - test(ref.getPtr(), compiled, total, extensions); + test(ref.getPtr(), compiled, total, extensions, warningsMode); } return std::make_pair(total, compiled); } diff --git a/apps/openmw/mwdialogue/scripttest.hpp b/apps/openmw/mwdialogue/scripttest.hpp index 1ed94c76a5..0ac2597256 100644 --- a/apps/openmw/mwdialogue/scripttest.hpp +++ b/apps/openmw/mwdialogue/scripttest.hpp @@ -11,7 +11,7 @@ namespace ScriptTest /// Attempt to compile all dialogue scripts, use for verification purposes /// @return A pair containing -std::pair compileAll(const Compiler::Extensions* extensions); +std::pair compileAll(const Compiler::Extensions* extensions, int warningsMode); } From 120873a66d7607446c56d7338f81ae0536844d31 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 18 Dec 2014 10:40:51 +0100 Subject: [PATCH 088/404] another workaround for script translation messup --- components/compiler/scanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 2aeaffd9c3..3fdbdb9f01 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -545,7 +545,7 @@ namespace Compiler { return std::isalpha (c) || std::isdigit (c) || c=='_' || /// \todo disable this when doing more stricter compiling - c=='`' || + c=='`' || c=='\'' || /// \todo disable this when doing more stricter compiling. Also, find out who is /// responsible for allowing it in the first place and meet up with that person in /// a dark alley. From 0af5c7b3798efdf18870cae513e72ae4ae864a48 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Fri, 19 Dec 2014 09:23:16 +0100 Subject: [PATCH 089/404] Starting to clean up some heavy includes --- apps/openmw/mwgui/widgets.cpp | 1 + apps/openmw/mwgui/widgets.hpp | 4 +++- apps/openmw/mwmechanics/aipackage.hpp | 1 - apps/openmw/mwscript/interpretercontext.hpp | 2 -- apps/openmw/mwworld/cellstore.hpp | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index d7bc2c96e9..052407c581 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -1,4 +1,5 @@ #include "widgets.hpp" +#include "../mwworld/esmstore.hpp" #include diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index aae28a6862..6ce114131e 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -1,10 +1,12 @@ #ifndef MWGUI_WIDGETS_H #define MWGUI_WIDGETS_H -#include "../mwworld/esmstore.hpp" #include "../mwmechanics/stat.hpp" #include "controllers.hpp" +#include +#include + #include #include #include diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index f1c9ec7d25..ca08de072c 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -3,7 +3,6 @@ #include "pathfinding.hpp" #include -#include "../mwbase/world.hpp" #include "obstacle.hpp" #include "aistate.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 354df00bdf..698df62c2a 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -5,8 +5,6 @@ #include -#include "../mwbase/world.hpp" - #include "../mwworld/ptr.hpp" #include "../mwworld/action.hpp" diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index eba627b3ee..f6f2a3b489 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -6,10 +6,10 @@ #include #include "livecellref.hpp" -#include "esmstore.hpp" #include "cellreflist.hpp" #include +#include #include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld @@ -24,7 +24,7 @@ namespace ESM namespace MWWorld { class Ptr; - + class ESMStore; /// \brief Mutable state of a cell From 462b41a3a8a6bd63fcf35781aa0e59e6754c988e Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Fri, 19 Dec 2014 11:26:54 +0100 Subject: [PATCH 090/404] Missing files, aka; Why you shouldn't stresscommit --- apps/openmw/mwclass/activator.cpp | 3 ++- apps/openmw/mwclass/armor.cpp | 1 + apps/openmw/mwclass/book.cpp | 1 + apps/openmw/mwclass/clothing.cpp | 1 + apps/openmw/mwclass/container.cpp | 1 + apps/openmw/mwclass/door.cpp | 1 + apps/openmw/mwclass/ingredient.cpp | 1 + apps/openmw/mwclass/potion.cpp | 1 + apps/openmw/mwclass/weapon.cpp | 1 + apps/openmw/mwgui/alchemywindow.cpp | 3 +++ apps/openmw/mwgui/birth.cpp | 2 ++ apps/openmw/mwgui/charactercreation.cpp | 1 + apps/openmw/mwgui/class.cpp | 1 + apps/openmw/mwgui/class.hpp | 3 ++- apps/openmw/mwgui/console.cpp | 1 + apps/openmw/mwgui/dialogue.cpp | 1 + apps/openmw/mwgui/enchantingdialog.cpp | 2 ++ apps/openmw/mwgui/hud.cpp | 2 ++ apps/openmw/mwgui/mapwindow.cpp | 1 + apps/openmw/mwgui/quickkeysmenu.cpp | 1 + apps/openmw/mwgui/race.cpp | 1 + apps/openmw/mwgui/recharge.cpp | 3 +++ apps/openmw/mwgui/review.cpp | 1 + apps/openmw/mwgui/review.hpp | 2 ++ apps/openmw/mwgui/savegamedialog.cpp | 1 + apps/openmw/mwgui/spellcreationdialog.cpp | 2 ++ apps/openmw/mwgui/spellcreationdialog.hpp | 2 ++ apps/openmw/mwgui/spellicons.cpp | 2 ++ apps/openmw/mwgui/spellwindow.cpp | 1 + apps/openmw/mwgui/tooltips.cpp | 1 + apps/openmw/mwgui/tooltips.hpp | 6 ++++++ apps/openmw/mwgui/trainingwindow.cpp | 1 + apps/openmw/mwgui/waitdialog.cpp | 1 + apps/openmw/mwmechanics/pathgrid.cpp | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 1 + apps/openmw/mwscript/skyextensions.cpp | 1 + apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/class.cpp | 1 + 38 files changed, 56 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index bf02b4d05e..46b23e942d 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -8,7 +8,8 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" -#include "../mwworld//cellstore.hpp" +#include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/action.hpp" diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 97f9211d9a..2fa6602c4d 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -14,6 +14,7 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/containerstore.hpp" diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 51d47e7216..b99d71a06f 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -11,6 +11,7 @@ #include "../mwworld/actionread.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 0098783505..eb2dec0ab2 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -12,6 +12,7 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 59e51e4613..3430dcb075 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -15,6 +15,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/actionopen.hpp" #include "../mwworld/actiontrap.hpp" #include "../mwworld/physicssystem.hpp" diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index fa9db9e160..8dc135ec84 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -15,6 +15,7 @@ #include "../mwworld/actionteleport.hpp" #include "../mwworld/actiondoor.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actiontrap.hpp" diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index fa03f23ff1..610a0b478c 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -10,6 +10,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/actioneat.hpp" #include "../mwworld/nullaction.hpp" diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 2da213c8d6..814d903ffb 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -11,6 +11,7 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/actionapply.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index d2f88efef3..f1f0386c64 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -12,6 +12,7 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index b9e0044cef..bb201e2dd6 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -11,6 +11,9 @@ #include "../mwmechanics/magiceffects.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include #include "inventoryitemmodel.hpp" #include "sortfilteritemmodel.hpp" diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index a7f90c00ba..949452a913 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -2,11 +2,13 @@ #include +#include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/esmstore.hpp" #include "widgets.hpp" diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 85f57a1a8c..33d0c4907d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -13,6 +13,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" +#include "../mwworld/esmstore.hpp" namespace { diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 4e45f1a7c5..89c7346be1 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -3,6 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/esmstore.hpp" #include "tooltips.hpp" diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 40056ca5e9..9d529ece01 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -1,7 +1,8 @@ #ifndef MWGUI_CLASS_H #define MWGUI_CLASS_H - +#include +#include #include "widgets.hpp" #include "windowbase.hpp" diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 5a7193d650..c922b625d7 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index eb548d596d..787e4e36eb 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -15,6 +15,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwdialogue/dialoguemanagerimp.hpp" diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 56caa6513b..59bcd96475 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -11,6 +12,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" #include "itemselection.hpp" #include "container.hpp" diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 2593e6e77c..d2d2fe51bb 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -7,8 +7,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 0262c4d0e4..db2a2dd75f 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -11,6 +11,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwrender/globalmap.hpp" diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 7f56b046e2..d59b29f0fc 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -8,6 +8,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index bcb766f8f3..e60c667478 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -3,6 +3,7 @@ #include #include +#include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index c45c2566ea..7458a6effb 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -3,12 +3,15 @@ #include #include +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index d1f8a76a62..ca1b6ed5d7 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/esmstore.hpp" #include "tooltips.hpp" diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 01b106d907..1419925b59 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -1,6 +1,8 @@ #ifndef MWGUI_REVIEW_H #define MWGUI_REVIEW_H +#include +#include #include "windowbase.hpp" #include "widgets.hpp" diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 66c7a92386..6c8c7d737d 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -12,6 +12,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwstate/character.hpp" diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 5da33c67ae..fd0c4ad323 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" @@ -12,6 +13,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spells.hpp" diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index ecccf3baf9..a94289bfdc 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -1,6 +1,8 @@ #ifndef MWGUI_SPELLCREATION_H #define MWGUI_SPELLCREATION_H +#include +#include #include #include "windowbase.hpp" diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index dbd91ab753..9d55c9e0e0 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include "../mwbase/world.hpp" @@ -12,6 +13,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/creaturestats.hpp" diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 240d0419e3..98ee588b19 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -8,6 +8,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spells.hpp" diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 396c8fa489..aafea11780 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -11,6 +11,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/spellcasting.hpp" #include "mapwindow.hpp" diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index ffbb35e047..21b5527ccb 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -7,6 +7,12 @@ #include "widgets.hpp" +namespace ESM +{ + class Class; + struct Race; +} + namespace MWGui { // Info about tooltip that is supplied by the MWWorld::Class object diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 6ff5ae35f6..56c9dff98d 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -10,6 +10,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index f95ec56751..f2f4a1f91f 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -12,6 +12,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 848d2c7a03..9e50af2b8c 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -4,6 +4,7 @@ #include "../mwbase/environment.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" namespace { diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d33bb6ad15..6984d4c783 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -15,6 +15,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwrender/animation.hpp" diff --git a/apps/openmw/mwscript/skyextensions.cpp b/apps/openmw/mwscript/skyextensions.cpp index 8b9efd74e1..0ccd0ce311 100644 --- a/apps/openmw/mwscript/skyextensions.cpp +++ b/apps/openmw/mwscript/skyextensions.cpp @@ -9,6 +9,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9746202dc3..f77a90d8ee 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -27,6 +27,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 7c95858343..61c597517a 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" #include "ptr.hpp" #include "refdata.hpp" From c7965894205f8e3e12a92dee22927af817ccc209 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Mon, 22 Dec 2014 01:54:24 +0100 Subject: [PATCH 091/404] Allow adding multiple Attribute/Skill effects in spell making (Fixes #2224) --- apps/openmw/mwgui/spellcreationdialog.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 5da33c67ae..3c9de90211 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -592,14 +592,6 @@ namespace MWGui int buttonId = *sender->getUserData(); mSelectedKnownEffectId = mButtonMapping[buttonId]; - for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) - { - if (it->mEffectID == mSelectedKnownEffectId) - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}"); - return; - } - } const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(mSelectedKnownEffectId); @@ -622,6 +614,15 @@ namespace MWGui } else { + for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) + { + if (it->mEffectID == mSelectedKnownEffectId) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}"); + return; + } + } + mAddEffectDialog.newEffect(effect); } } From ec18a2cfa0e309cf517218183da9e35262795a2d Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 22 Dec 2014 02:16:30 +0100 Subject: [PATCH 092/404] add support for borderless windows --- files/settings-default.cfg | 1 + libs/openengine/ogre/renderer.cpp | 4 +++- libs/openengine/ogre/renderer.hpp | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 7566994e29..25959546ed 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -6,6 +6,7 @@ resolution x = 800 resolution y = 600 fullscreen = false +borderless = true screen = 0 # Minimize the window if it loses key focus? diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index eb33d5876c..fadfc11a64 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -128,7 +128,9 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings.window_x, // width, in pixels settings.window_y, // height, in pixels SDL_WINDOW_SHOWN - | (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) | SDL_WINDOW_RESIZABLE + | SDL_WINDOW_RESIZABLE + | (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) + | (settings.borderless ? SDL_WINDOW_BORDERLESS : 0) ); SFO::SDLWindowHelper helper(mSDLWindow, settings.window_x, settings.window_y, title, settings.fullscreen, params); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index e56f5f8165..5f1f6a4bb2 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -37,6 +37,7 @@ namespace OEngine { bool vsync; bool fullscreen; + bool borderless; int window_x, window_y; int screen; std::string fsaa; From 639fbfad0b55d5b7a606b6ed847a414dd33dfe34 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 22 Dec 2014 02:44:20 +0100 Subject: [PATCH 093/404] make borderless setting available to the UI --- apps/launcher/graphicspage.cpp | 6 ++++++ apps/openmw/engine.cpp | 1 + apps/openmw/mwgui/settingswindow.cpp | 1 + apps/openmw/mwgui/settingswindow.hpp | 1 + files/mygui/openmw_settings_window.layout | 10 ++++++++++ files/ui/graphicspage.ui | 17 ++++++++++++----- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index ec7f5a04d7..11132f0f47 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -159,6 +159,9 @@ bool Launcher::GraphicsPage::loadSettings() if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true")) fullScreenCheckBox->setCheckState(Qt::Checked); + if (mGraphicsSettings.value(QString("Video/borderless")) == QLatin1String("true")) + borderlessCheckBox->setCheckState(Qt::Checked); + int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing"))); if (aaIndex != -1) antiAliasingComboBox->setCurrentIndex(aaIndex); @@ -193,6 +196,9 @@ void Launcher::GraphicsPage::saveSettings() fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true")) : mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false")); + borderlessCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/borderless"), QString("true")) + : mGraphicsSettings.setValue(QString("Video/borderless"), QString("false")); + mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText()); mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText()); diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3d8aa5530b..85127db284 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -350,6 +350,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) OEngine::Render::WindowSettings windowSettings; windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); + windowSettings.borderless = settings.getBool("borderless", "Video"); windowSettings.window_x = settings.getInt("resolution x", "Video"); windowSettings.window_y = settings.getInt("resolution y", "Video"); windowSettings.screen = settings.getInt("screen", "Video"); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 8ef0a331a4..78a46cb6aa 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -166,6 +166,7 @@ namespace MWGui getWidget(mResolutionList, "ResolutionList"); getWidget(mFullscreenButton, "FullscreenButton"); getWidget(mVSyncButton, "VSyncButton"); + getWidget(mBorderlessButton, "BorderlessButton"); getWidget(mFPSButton, "FPSButton"); getWidget(mFOVSlider, "FOVSlider"); getWidget(mAnisotropySlider, "AnisotropySlider"); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 2619943f98..9214fbd6f9 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -28,6 +28,7 @@ namespace MWGui MyGUI::ListBox* mResolutionList; MyGUI::Button* mFullscreenButton; MyGUI::Button* mVSyncButton; + MyGUI::Button* mBorderlessButton; MyGUI::Button* mFPSButton; MyGUI::ScrollBar* mFOVSlider; MyGUI::ScrollBar* mDifficultySlider; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index fd84be3e9e..5be873d587 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -239,6 +239,16 @@ + + + + + + + + + + diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index 7e9fe00d9e..753d65d033 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -51,20 +51,27 @@ + + + Borderless + + + + Anti-aliasing: - + Screen: - + Resolution: @@ -74,13 +81,13 @@ - + - + - + From ebd384455f6a3f295b5dbf85db7bb3703a2a4c5b Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 22 Dec 2014 02:56:07 +0100 Subject: [PATCH 094/404] disable borderless window by default fix positioning of the in-game graphics settings --- files/mygui/openmw_settings_window.layout | 8 ++++---- files/settings-default.cfg | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 5be873d587..6e5d8b9e38 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -239,7 +239,7 @@ - + @@ -249,13 +249,13 @@ - + - + @@ -265,7 +265,7 @@ - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 25959546ed..69a9b2db5c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -6,7 +6,7 @@ resolution x = 800 resolution y = 600 fullscreen = false -borderless = true +borderless = false screen = 0 # Minimize the window if it loses key focus? From dcfadeb51a695afe0041b78eff814719c40ecf66 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 22 Dec 2014 10:45:34 +0100 Subject: [PATCH 095/404] fix typo and annoying gcc/clang unused return values in crash catcher --- apps/openmw/crashcatcher.cpp | 6 +++--- apps/wizard/installationpage.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/crashcatcher.cpp b/apps/openmw/crashcatcher.cpp index b9d78540e0..2426714d33 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -11,7 +11,6 @@ #include #include - #include #include #include @@ -29,6 +28,7 @@ #include #endif +#define UNUSED(x) (void)(x) static const char crash_switch[] = "--cc-handle-crash"; @@ -160,7 +160,7 @@ static void gdb_info(pid_t pid) printf("Executing: %s\n", cmd_buf); fflush(stdout); - system(cmd_buf); + int unused = system(cmd_buf); UNUSED(unused); /* Clean up */ remove(respfile); } @@ -406,7 +406,7 @@ int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, co snprintf(argv0, sizeof(argv0), "%s", argv[0]); else { - getcwd(argv0, sizeof(argv0)); + char * unused = getcwd(argv0, sizeof(argv0)); UNUSED(unused); retval = strlen(argv0); snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); } diff --git a/apps/wizard/installationpage.cpp b/apps/wizard/installationpage.cpp index 09e5773173..dc2674680e 100644 --- a/apps/wizard/installationpage.cpp +++ b/apps/wizard/installationpage.cpp @@ -185,7 +185,7 @@ void Wizard::InstallationPage::installationFinished() msgBox.setWindowTitle(tr("Installation finished")); msgBox.setIcon(QMessageBox::Information); msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("Installation completed sucessfully!")); + msgBox.setText(tr("Installation completed successfully!")); msgBox.exec(); From 45299abe99f82f27033afd2b9f309d9611ee38ac Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 23 Dec 2014 17:13:11 +0100 Subject: [PATCH 096/404] make it C98 compat --- apps/openmw/crashcatcher.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/crashcatcher.cpp b/apps/openmw/crashcatcher.cpp index 2426714d33..8f25d041cb 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -160,7 +160,11 @@ static void gdb_info(pid_t pid) printf("Executing: %s\n", cmd_buf); fflush(stdout); - int unused = system(cmd_buf); UNUSED(unused); + { /* another special exception for "ignoring return value..." */ + int unused; + unused = system(cmd_buf); + UNUSED(unused); + } /* Clean up */ remove(respfile); } @@ -406,7 +410,13 @@ int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, co snprintf(argv0, sizeof(argv0), "%s", argv[0]); else { - char * unused = getcwd(argv0, sizeof(argv0)); UNUSED(unused); + { + /* we don't want to disable "ignoring return value" warnings, so we make + * a special exception here. */ + char * unused; + unused = getcwd(argv0, sizeof(argv0)); + UNUSED(unused); + } retval = strlen(argv0); snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); } From 3cc32b641ae6ff1ba06599b84940aaad1c3f529a Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Tue, 23 Dec 2014 20:44:25 +0100 Subject: [PATCH 097/404] Fix some memory leaks --- apps/launcher/utils/profilescombobox.cpp | 6 +++--- apps/launcher/utils/textinputdialog.cpp | 6 +++--- .../contentselector/model/contentmodel.cpp | 16 ++++++++++++---- .../contentselector/model/contentmodel.hpp | 1 + .../contentselector/view/contentselector.cpp | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/apps/launcher/utils/profilescombobox.cpp b/apps/launcher/utils/profilescombobox.cpp index c143307249..7df89098e2 100644 --- a/apps/launcher/utils/profilescombobox.cpp +++ b/apps/launcher/utils/profilescombobox.cpp @@ -47,13 +47,13 @@ void ProfilesComboBox::setEditEnabled(bool editable) void ProfilesComboBox::slotTextChanged(const QString &text) { - QPalette *palette = new QPalette(); - palette->setColor(QPalette::Text,Qt::red); + QPalette palette; + palette.setColor(QPalette::Text,Qt::red); int index = findText(text); if (text.isEmpty() || (index != -1 && index != currentIndex())) { - lineEdit()->setPalette(*palette); + lineEdit()->setPalette(palette); } else { lineEdit()->setPalette(QApplication::palette()); } diff --git a/apps/launcher/utils/textinputdialog.cpp b/apps/launcher/utils/textinputdialog.cpp index b47fdb6e62..385d086fd0 100644 --- a/apps/launcher/utils/textinputdialog.cpp +++ b/apps/launcher/utils/textinputdialog.cpp @@ -59,13 +59,13 @@ void Launcher::TextInputDialog::setOkButtonEnabled(bool enabled) QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(enabled); - QPalette *palette = new QPalette(); - palette->setColor(QPalette::Text, Qt::red); + QPalette palette; + palette.setColor(QPalette::Text, Qt::red); if (enabled) { mLineEdit->setPalette(QApplication::palette()); } else { // Existing profile name, make the text red - mLineEdit->setPalette(*palette); + mLineEdit->setPalette(palette); } } diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 0d4f2365a6..7af8b8cefa 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -21,6 +21,12 @@ ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : uncheckAll(); } +ContentSelectorModel::ContentModel::~ContentModel() +{ + qDeleteAll(mFiles); + mFiles.clear(); +} + void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) { mEncoding = encoding; @@ -444,7 +450,9 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) foreach (const QString &path, dir.entryList()) { QFileInfo info(dir.absoluteFilePath(path)); - EsmFile *file = new EsmFile(path); + + if (item(info.absoluteFilePath()) != 0) + continue; try { ESM::ESMReader fileReader; @@ -453,6 +461,8 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); + EsmFile *file = new EsmFile(path); + foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles()) file->addGameFile(QString::fromStdString(item.name)); @@ -462,10 +472,8 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) file->setFilePath (info.absoluteFilePath()); file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); - // Put the file in the table - if (item(file->filePath()) == 0) - addFile(file); + addFile(file); } catch(std::runtime_error &e) { // An error occurred while reading the .esp diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 7b2000b510..80b5094890 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -21,6 +21,7 @@ namespace ContentSelectorModel Q_OBJECT public: explicit ContentModel(QObject *parent = 0); + ~ContentModel(); void setEncoding(const QString &encoding); diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index e9599de498..c8085937cf 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -24,7 +24,7 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : void ContentSelectorView::ContentSelector::buildContentModel() { - mContentModel = new ContentSelectorModel::ContentModel(); + mContentModel = new ContentSelectorModel::ContentModel(this); } void ContentSelectorView::ContentSelector::buildGameFileView() From ad5d88476113e1ac4a4166b6150401c38451e569 Mon Sep 17 00:00:00 2001 From: dteviot Date: Wed, 24 Dec 2014 09:53:49 +1300 Subject: [PATCH 098/404] omwlauncher crash when can't read file in active profile (Fixes #1069) --- .../contentselector/view/contentselector.cpp | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index e9599de498..b12d4147a0 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -64,29 +64,17 @@ void ContentSelectorView::ContentSelector::buildAddonView() void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList) { clearCheckStates(); - bool foundGamefile = false; foreach (const QString &filepath, fileList) { - if (!foundGamefile) + const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath); + if (file && file->isGameFile()) { - const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath); - - foundGamefile = (file->isGameFile()); - - if (foundGamefile) - { - setGameFile (filepath); - break; - } + setGameFile (filepath); + break; } } -/* if (!foundGameFile) - { - //throw gamefile error here. - }*/ - setCheckStates (fileList); } From e85df001587cb0278bc548880c6a07b15f8fac7b Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 24 Dec 2014 15:09:50 +0100 Subject: [PATCH 099/404] change setting "borderless" to "window border" set window border on setting changes disable window border checkbox in the launcher if fullscreen is enabled --- apps/launcher/graphicspage.cpp | 10 ++++++---- apps/openmw/engine.cpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 2 +- apps/openmw/mwgui/settingswindow.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 6 ++++++ files/mygui/openmw_settings_window.layout | 6 +++--- files/settings-default.cfg | 2 +- files/ui/graphicspage.ui | 4 ++-- libs/openengine/ogre/renderer.cpp | 2 +- libs/openengine/ogre/renderer.hpp | 2 +- 10 files changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 11132f0f47..da707b0056 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -159,8 +159,8 @@ bool Launcher::GraphicsPage::loadSettings() if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true")) fullScreenCheckBox->setCheckState(Qt::Checked); - if (mGraphicsSettings.value(QString("Video/borderless")) == QLatin1String("true")) - borderlessCheckBox->setCheckState(Qt::Checked); + if (mGraphicsSettings.value(QString("Video/window border")) == QLatin1String("true")) + windowBorderCheckBox->setCheckState(Qt::Checked); int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing"))); if (aaIndex != -1) @@ -196,8 +196,8 @@ void Launcher::GraphicsPage::saveSettings() fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true")) : mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false")); - borderlessCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/borderless"), QString("true")) - : mGraphicsSettings.setValue(QString("Video/borderless"), QString("false")); + windowBorderCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/window border"), QString("true")) + : mGraphicsSettings.setValue(QString("Video/window border"), QString("false")); mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText()); mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText()); @@ -337,10 +337,12 @@ void Launcher::GraphicsPage::slotFullScreenChanged(int state) customRadioButton->setEnabled(false); customWidthSpinBox->setEnabled(false); customHeightSpinBox->setEnabled(false); + windowBorderCheckBox->setEnabled(false); } else { customRadioButton->setEnabled(true); customWidthSpinBox->setEnabled(true); customHeightSpinBox->setEnabled(true); + windowBorderCheckBox->setEnabled(true); } } diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 85127db284..99a642454e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -350,7 +350,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) OEngine::Render::WindowSettings windowSettings; windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); - windowSettings.borderless = settings.getBool("borderless", "Video"); + windowSettings.window_border = settings.getBool("window border", "Video"); windowSettings.window_x = settings.getInt("resolution x", "Video"); windowSettings.window_y = settings.getInt("resolution y", "Video"); windowSettings.screen = settings.getInt("screen", "Video"); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 78a46cb6aa..804f023fab 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -166,7 +166,7 @@ namespace MWGui getWidget(mResolutionList, "ResolutionList"); getWidget(mFullscreenButton, "FullscreenButton"); getWidget(mVSyncButton, "VSyncButton"); - getWidget(mBorderlessButton, "BorderlessButton"); + getWidget(mWindowBorderButton, "WindowBorderButton"); getWidget(mFPSButton, "FPSButton"); getWidget(mFOVSlider, "FOVSlider"); getWidget(mAnisotropySlider, "AnisotropySlider"); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 9214fbd6f9..8dcc8dd077 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -28,7 +28,7 @@ namespace MWGui MyGUI::ListBox* mResolutionList; MyGUI::Button* mFullscreenButton; MyGUI::Button* mVSyncButton; - MyGUI::Button* mBorderlessButton; + MyGUI::Button* mWindowBorderButton; MyGUI::Button* mFPSButton; MyGUI::ScrollBar* mFOVSlider; MyGUI::ScrollBar* mDifficultySlider; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ed25e70a69..2cddbce75c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -753,6 +753,8 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec || it->second == "resolution y" || it->second == "fullscreen")) changeRes = true; + else if (it->first == "Video" && it->second == "window border") + changeRes = true; else if (it->second == "field of view" && it->first == "General") mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); else if ((it->second == "texture filtering" && it->first == "General") @@ -810,6 +812,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec unsigned int x = Settings::Manager::getInt("resolution x", "Video"); unsigned int y = Settings::Manager::getInt("resolution y", "Video"); bool fullscreen = Settings::Manager::getBool("fullscreen", "Video"); + bool windowBorder = Settings::Manager::getBool("window border", "Video"); SDL_Window* window = mRendering.getSDLWindow(); @@ -828,7 +831,10 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec SDL_SetWindowFullscreen(window, fullscreen); } else + { SDL_SetWindowSize(window, x, y); + SDL_SetWindowBordered(window, windowBorder ? SDL_TRUE : SDL_FALSE); + } } mWater->processChangedSettings(settings); diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 6e5d8b9e38..e2f46f2d16 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -240,13 +240,13 @@ - + - + - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 69a9b2db5c..df8266f7af 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -6,7 +6,7 @@ resolution x = 800 resolution y = 600 fullscreen = false -borderless = false +window border = true screen = 0 # Minimize the window if it loses key focus? diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index 753d65d033..f9ea63efe6 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -51,9 +51,9 @@ - + - Borderless + Window border diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index fadfc11a64..404602c304 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -130,7 +130,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) - | (settings.borderless ? SDL_WINDOW_BORDERLESS : 0) + | (settings.window_border ? 0 : SDL_WINDOW_BORDERLESS) ); SFO::SDLWindowHelper helper(mSDLWindow, settings.window_x, settings.window_y, title, settings.fullscreen, params); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 5f1f6a4bb2..70cc3db60f 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -37,7 +37,7 @@ namespace OEngine { bool vsync; bool fullscreen; - bool borderless; + bool window_border; int window_x, window_y; int screen; std::string fsaa; From 1b9209df4a2ad0030faeed309b7b740a38d3c5a9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Dec 2014 16:07:50 +0100 Subject: [PATCH 100/404] Allow blocking of hand-to-hand attacks --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwmechanics/combat.cpp | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 5fd5f4dde0..323fafbb6c 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -324,7 +324,7 @@ namespace MWClass MWMechanics::applyElementalShields(ptr, victim); - if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) + if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) damage = 0; if (damage > 0) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4814879acd..2c68b4c72c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -641,7 +641,7 @@ namespace MWClass MWMechanics::applyElementalShields(ptr, victim); - if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) + if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) damage = 0; if (healthdmg && damage > 0) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 4d484469c2..e52dbdde71 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -91,7 +91,11 @@ namespace MWMechanics blockerTerm *= gmst.find("fBlockStillBonus")->getFloat(); blockerTerm *= blockerStats.getFatigueTerm(); - float attackerSkill = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon)); + float attackerSkill = 0.f; + if (weapon.isEmpty()) + attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand); + else + attackerSkill = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon)); float attackerTerm = attackerSkill + 0.2 * attackerStats.getAttribute(ESM::Attribute::Agility).getModified() + 0.1 * attackerStats.getAttribute(ESM::Attribute::Luck).getModified(); attackerTerm *= attackerStats.getFatigueTerm(); @@ -120,7 +124,8 @@ namespace MWMechanics float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker); normalizedEncumbrance = std::min(1.f, normalizedEncumbrance); float fatigueLoss = fFatigueBlockBase + normalizedEncumbrance * fFatigueBlockMult; - fatigueLoss += weapon.getClass().getWeight(weapon) * attackerStats.getAttackStrength() * fWeaponFatigueBlockMult; + if (!weapon.isEmpty()) + fatigueLoss += weapon.getClass().getWeight(weapon) * attackerStats.getAttackStrength() * fWeaponFatigueBlockMult; fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); blockerStats.setFatigue(fatigue); From f931ba2efc64af5914136e2ea320cf7d2d92e4f8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 03:24:10 +0100 Subject: [PATCH 101/404] Fix some static analysis issues (coverity) --- apps/bsatool/bsatool.cpp | 43 +++--- apps/esmtool/esmtool.cpp | 34 +++-- apps/launcher/main.cpp | 80 ++++++----- apps/mwiniimporter/main.cpp | 130 +++++++++--------- apps/opencs/main.cpp | 66 +++++---- apps/opencs/view/doc/filedialog.cpp | 2 +- apps/opencs/view/render/cell.cpp | 16 +-- apps/opencs/view/render/mousestate.cpp | 2 +- apps/openmw/mwgui/bookpage.cpp | 3 +- apps/openmw/mwgui/class.cpp | 1 + apps/openmw/mwgui/itemview.cpp | 2 +- apps/openmw/mwgui/mainmenu.cpp | 13 +- apps/openmw/mwgui/savegamedialog.cpp | 2 +- apps/openmw/mwgui/spellmodel.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 13 +- apps/openmw/mwmechanics/alchemy.cpp | 3 +- apps/openmw/mwmechanics/character.cpp | 7 +- apps/openmw/mwmechanics/spellcasting.cpp | 7 +- apps/openmw/mwrender/characterpreview.cpp | 5 +- apps/openmw/mwrender/localmap.cpp | 5 +- apps/openmw/mwrender/sky.cpp | 3 +- apps/openmw/mwworld/physicssystem.cpp | 5 +- apps/openmw/mwworld/store.hpp | 24 ++-- apps/openmw/mwworld/worldimp.cpp | 4 +- .../contentselector/model/contentmodel.cpp | 7 - components/contentselector/view/combobox.cpp | 2 +- components/esm/loadcell.cpp | 2 + components/nifogre/ogrenifloader.cpp | 2 + components/to_utf8/to_utf8.cpp | 2 + .../oics/ICSInputControlSystem_joystick.cpp | 2 +- extern/sdl4ogre/sdlinputwrapper.cpp | 6 +- 31 files changed, 268 insertions(+), 226 deletions(-) diff --git a/apps/bsatool/bsatool.cpp b/apps/bsatool/bsatool.cpp index 7e47d0b8fa..5b1f7d6bba 100644 --- a/apps/bsatool/bsatool.cpp +++ b/apps/bsatool/bsatool.cpp @@ -150,34 +150,33 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info); int main(int argc, char** argv) { - Arguments info; - if(!parseOptions (argc, argv, info)) - return 1; - - // Open file - Bsa::BSAFile bsa; try { + Arguments info; + if(!parseOptions (argc, argv, info)) + return 1; + + // Open file + Bsa::BSAFile bsa; bsa.open(info.filename); + + if (info.mode == "list") + return list(bsa, info); + else if (info.mode == "extract") + return extract(bsa, info); + else if (info.mode == "extractall") + return extractAll(bsa, info); + else + { + std::cout << "Unsupported mode. That is not supposed to happen." << std::endl; + return 1; + } } - catch(std::exception &e) + catch (std::exception& e) { - std::cout << "ERROR reading BSA archive '" << info.filename - << "'\nDetails:\n" << e.what() << std::endl; + std::cerr << "ERROR reading BSA archive\nDetails:\n" << e.what() << std::endl; return 2; } - - if (info.mode == "list") - return list(bsa, info); - else if (info.mode == "extract") - return extract(bsa, info); - else if (info.mode == "extractall") - return extractAll(bsa, info); - else - { - std::cout << "Unsupported mode. That is not supposed to happen." << std::endl; - return 1; - } } int list(Bsa::BSAFile& bsa, Arguments& info) @@ -189,9 +188,11 @@ int list(Bsa::BSAFile& bsa, Arguments& info) if(info.longformat) { // Long format + std::ios::fmtflags f(std::cout.flags()); std::cout << std::setw(50) << std::left << files[i].name; std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize; std::cout << "@ 0x" << std::hex << files[i].offset << std::endl; + std::cout.flags(f); } else std::cout << files[i].name << std::endl; diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index ea908590a4..a18736bf26 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -203,19 +203,27 @@ int comp(Arguments& info); int main(int argc, char**argv) { - Arguments info; - if(!parseOptions (argc, argv, info)) - return 1; - - if (info.mode == "dump") - return load(info); - else if (info.mode == "clone") - return clone(info); - else if (info.mode == "comp") - return comp(info); - else + try + { + Arguments info; + if(!parseOptions (argc, argv, info)) + return 1; + + if (info.mode == "dump") + return load(info); + else if (info.mode == "clone") + return clone(info); + else if (info.mode == "comp") + return comp(info); + else + { + std::cout << "Invalid or no mode specified, dying horribly. Have a nice day." << std::endl; + return 1; + } + } + catch (std::exception& e) { - std::cout << "Invalid or no mode specified, dying horribly. Have a nice day." << std::endl; + std::cerr << "ERROR: " << e.what() << std::endl; return 1; } @@ -273,8 +281,10 @@ void printRaw(ESM::ESMReader &esm) esm.getSubName(); esm.skipHSub(); n = esm.retSubName(); + std::ios::fmtflags f(std::cout.flags()); std::cout << " " << n.toString() << " - " << esm.getSubSize() << " bytes @ 0x" << std::hex << offs << "\n"; + std::cout.flags(f); } } } diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 562f5c7795..3dc82bc54d 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -15,53 +15,61 @@ int main(int argc, char *argv[]) { - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); - SDL_SetMainReady(); - if (SDL_Init(SDL_INIT_VIDEO) != 0) + try { - qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError()); - return 0; - } + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + SDL_SetMainReady(); + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError()); + return 0; + } - QApplication app(argc, argv); + QApplication app(argc, argv); - // Now we make sure the current dir is set to application path - QDir dir(QCoreApplication::applicationDirPath()); + // Now we make sure the current dir is set to application path + QDir dir(QCoreApplication::applicationDirPath()); - #ifdef Q_OS_MAC - if (dir.dirName() == "MacOS") { - dir.cdUp(); - dir.cdUp(); - dir.cdUp(); - } + #ifdef Q_OS_MAC + if (dir.dirName() == "MacOS") { + dir.cdUp(); + dir.cdUp(); + dir.cdUp(); + } - // force Qt to load only LOCAL plugins, don't touch system Qt installation - QDir pluginsPath(QCoreApplication::applicationDirPath()); - pluginsPath.cdUp(); - pluginsPath.cd("Plugins"); + // force Qt to load only LOCAL plugins, don't touch system Qt installation + QDir pluginsPath(QCoreApplication::applicationDirPath()); + pluginsPath.cdUp(); + pluginsPath.cd("Plugins"); - QStringList libraryPaths; - libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); - app.setLibraryPaths(libraryPaths); - #endif + QStringList libraryPaths; + libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); + app.setLibraryPaths(libraryPaths); + #endif - QDir::setCurrent(dir.absolutePath()); + QDir::setCurrent(dir.absolutePath()); - // Support non-latin characters - QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); + // Support non-latin characters + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); - Launcher::MainDialog mainWin; + Launcher::MainDialog mainWin; - if (!mainWin.showFirstRunDialog()) - return 0; + if (!mainWin.showFirstRunDialog()) + return 0; -// if (!mainWin.setup()) { -// return 0; -// } + // if (!mainWin.setup()) { + // return 0; + // } - mainWin.show(); + mainWin.show(); - int returnValue = app.exec(); - SDL_Quit(); - return returnValue; + int returnValue = app.exec(); + SDL_Quit(); + return returnValue; + } + catch (std::exception& e) + { + std::cerr << "ERROR: " << e.what() << std::endl; + return 0; + } } diff --git a/apps/mwiniimporter/main.cpp b/apps/mwiniimporter/main.cpp index fdf6db804c..316737c1dc 100644 --- a/apps/mwiniimporter/main.cpp +++ b/apps/mwiniimporter/main.cpp @@ -56,93 +56,87 @@ int wmain(int argc, wchar_t *wargv[]) { char **argv = converter.get(); boost::filesystem::path::imbue(boost::locale::generator().generate("")); #endif - bpo::options_description desc("Syntax: mwiniimporter inifile configfile\nAllowed options"); - bpo::positional_options_description p_desc; - desc.add_options() - ("help,h", "produce help message") - ("verbose,v", "verbose output") - ("ini,i", bpo::value(), "morrowind.ini file") - ("cfg,c", bpo::value(), "openmw.cfg file") - ("output,o", bpo::value()->default_value(""), "openmw.cfg file") - ("game-files,g", "import esm and esp files") - ("no-archives,A", "disable bsa archives import") - ("encoding,e", bpo::value()-> default_value("win1252"), - "Character encoding used in OpenMW game messages:\n" - "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" - "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" - "\n\twin1252 - Western European (Latin) alphabet, used by default") - ; - p_desc.add("ini", 1).add("cfg", 1); - - bpo::variables_map vm; - + try { + bpo::options_description desc("Syntax: mwiniimporter inifile configfile\nAllowed options"); + bpo::positional_options_description p_desc; + desc.add_options() + ("help,h", "produce help message") + ("verbose,v", "verbose output") + ("ini,i", bpo::value(), "morrowind.ini file") + ("cfg,c", bpo::value(), "openmw.cfg file") + ("output,o", bpo::value()->default_value(""), "openmw.cfg file") + ("game-files,g", "import esm and esp files") + ("no-archives,A", "disable bsa archives import") + ("encoding,e", bpo::value()-> default_value("win1252"), + "Character encoding used in OpenMW game messages:\n" + "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" + "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" + "\n\twin1252 - Western European (Latin) alphabet, used by default") + ; + p_desc.add("ini", 1).add("cfg", 1); + + bpo::variables_map vm; + bpo::parsed_options parsed = bpo::command_line_parser(argc, argv) .options(desc) .positional(p_desc) .run(); bpo::store(parsed, vm); - } - catch(boost::program_options::unknown_option & x) - { - std::cerr << "ERROR: " << x.what() << std::endl; - return false; - } - catch(boost::program_options::invalid_command_line_syntax & x) - { - std::cerr << "ERROR: " << x.what() << std::endl; - return false; - } - - if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) { - std::cout << desc; - return 0; - } - bpo::notify(vm); + if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) { + std::cout << desc; + return 0; + } - std::string iniFile = vm["ini"].as(); - std::string cfgFile = vm["cfg"].as(); + bpo::notify(vm); - // if no output is given, write back to cfg file - std::string outputFile(vm["output"].as()); - if(vm["output"].defaulted()) { - outputFile = vm["cfg"].as(); - } + std::string iniFile = vm["ini"].as(); + std::string cfgFile = vm["cfg"].as(); - if(!boost::filesystem::exists(iniFile)) { - std::cerr << "ini file does not exist" << std::endl; - return -3; - } - if(!boost::filesystem::exists(cfgFile)) - std::cerr << "cfg file does not exist" << std::endl; + // if no output is given, write back to cfg file + std::string outputFile(vm["output"].as()); + if(vm["output"].defaulted()) { + outputFile = vm["cfg"].as(); + } - MwIniImporter importer; - importer.setVerbose(vm.count("verbose")); + if(!boost::filesystem::exists(iniFile)) { + std::cerr << "ini file does not exist" << std::endl; + return -3; + } + if(!boost::filesystem::exists(cfgFile)) + std::cerr << "cfg file does not exist" << std::endl; - // Font encoding settings - std::string encoding(vm["encoding"].as()); - importer.setInputEncoding(ToUTF8::calculateEncoding(encoding)); + MwIniImporter importer; + importer.setVerbose(vm.count("verbose")); - MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile); - MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile); + // Font encoding settings + std::string encoding(vm["encoding"].as()); + importer.setInputEncoding(ToUTF8::calculateEncoding(encoding)); - importer.merge(cfg, ini); - importer.mergeFallback(cfg, ini); + MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile); + MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile); - if(vm.count("game-files")) { - importer.importGameFiles(cfg, ini); - } + importer.merge(cfg, ini); + importer.mergeFallback(cfg, ini); - if(!vm.count("no-archives")) { - importer.importArchives(cfg, ini); - } + if(vm.count("game-files")) { + importer.importGameFiles(cfg, ini); + } - std::cout << "write to: " << outputFile << std::endl; - bfs::ofstream file((bfs::path(outputFile))); - importer.writeToFile(file, cfg); + if(!vm.count("no-archives")) { + importer.importArchives(cfg, ini); + } + std::cout << "write to: " << outputFile << std::endl; + bfs::ofstream file((bfs::path(outputFile))); + importer.writeToFile(file, cfg); + } + catch (std::exception& e) + { + std::cerr << "ERROR: " << e.what() << std::endl; + } return 0; } diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index dd20324d12..0b8da61efe 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -46,47 +46,55 @@ class Application : public QApplication int main(int argc, char *argv[]) { - Q_INIT_RESOURCE (resources); + try + { + Q_INIT_RESOURCE (resources); - qRegisterMetaType ("std::string"); - qRegisterMetaType ("CSMWorld::UniversalId"); + qRegisterMetaType ("std::string"); + qRegisterMetaType ("CSMWorld::UniversalId"); - OgreInit::OgreInit ogreInit; + OgreInit::OgreInit ogreInit; - std::auto_ptr shinyFactory; + std::auto_ptr shinyFactory; - Application application (argc, argv); + Application application (argc, argv); -#ifdef Q_OS_MAC - QDir dir(QCoreApplication::applicationDirPath()); - if (dir.dirName() == "MacOS") { - dir.cdUp(); - dir.cdUp(); - dir.cdUp(); - } - QDir::setCurrent(dir.absolutePath()); + #ifdef Q_OS_MAC + QDir dir(QCoreApplication::applicationDirPath()); + if (dir.dirName() == "MacOS") { + dir.cdUp(); + dir.cdUp(); + dir.cdUp(); + } + QDir::setCurrent(dir.absolutePath()); - // force Qt to load only LOCAL plugins, don't touch system Qt installation - QDir pluginsPath(QCoreApplication::applicationDirPath()); - pluginsPath.cdUp(); - pluginsPath.cd("Plugins"); + // force Qt to load only LOCAL plugins, don't touch system Qt installation + QDir pluginsPath(QCoreApplication::applicationDirPath()); + pluginsPath.cdUp(); + pluginsPath.cd("Plugins"); - QStringList libraryPaths; - libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); - application.setLibraryPaths(libraryPaths); -#endif + QStringList libraryPaths; + libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); + application.setLibraryPaths(libraryPaths); + #endif - application.setWindowIcon (QIcon (":./opencs.png")); + application.setWindowIcon (QIcon (":./opencs.png")); - CS::Editor editor (ogreInit); + CS::Editor editor (ogreInit); - if(!editor.makeIPCServer()) + if(!editor.makeIPCServer()) + { + editor.connectToIPCServer(); + return 0; + } + + shinyFactory = editor.setupGraphics(); + return editor.run(); + } + catch (std::exception& e) { - editor.connectToIPCServer(); + std::cerr << "ERROR: " << e.what() << std::endl; return 0; } - shinyFactory = editor.setupGraphics(); - - return editor.run(); } diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 300656f334..c6f76cddfe 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -18,7 +18,7 @@ #include "adjusterwidget.hpp" CSVDoc::FileDialog::FileDialog(QWidget *parent) : - QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false) + QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false), mAction(ContentAction_Undefined) { ui.setupUi (this); resize(400, 400); diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 1fb7809be1..a0d0f6e71b 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -61,7 +61,7 @@ bool CSVRender::Cell::addObjects (int start, int end) CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, const std::string& id, boost::shared_ptr physics, const Ogre::Vector3& origin) -: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics) +: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics), mX(0), mY(0) { mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode(); mCellNode->setPosition (origin); @@ -77,15 +77,14 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, int landIndex = land.searchId(mId); if (landIndex != -1) { - mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true, - Terrain::Align_XY)); - const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get(); - mTerrain->loadCell(esmLand->mX, - esmLand->mY); - if(esmLand) { + mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true, + Terrain::Align_XY)); + mTerrain->loadCell(esmLand->mX, + esmLand->mY); + float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; mX = esmLand->mX; @@ -98,7 +97,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, CSVRender::Cell::~Cell() { - mPhysics->removeHeightField(mSceneMgr, mX, mY); + if (mTerrain.get()) + mPhysics->removeHeightField(mSceneMgr, mX, mY); for (std::map::iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp index 988819fcb1..a94f4f8ab1 100644 --- a/apps/opencs/view/render/mousestate.cpp +++ b/apps/opencs/view/render/mousestate.cpp @@ -346,7 +346,7 @@ namespace CSVRender //plane X, upvector Y, mOffset x : x-z plane, wheel closer/further std::pair MouseState::planeAxis() { - bool screenCoord = true; + const bool screenCoord = true; Ogre::Vector3 dir = getCamera()->getDerivedDirection(); QString wheelDir = "Closer/Further"; diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 57cb3e7a9c..0f2df494a2 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -639,7 +639,8 @@ namespace MyGUI::Vertex* vertices, RenderXform const & renderXform) : mZ(Z), mOrigin (left, top), mFont (font), mVertices (vertices), - mRenderXform (renderXform) + mRenderXform (renderXform), + mC(0) { mVertexColourType = MyGUI::RenderManager::getInstance().getVertexFormat(); } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 4e45f1a7c5..62167142fa 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -770,6 +770,7 @@ namespace MWGui SelectSkillDialog::SelectSkillDialog() : WindowModal("openmw_chargen_select_skill.layout") + , mSkillId(ESM::Skill::Block) { // Centre dialog center(); diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index ed2002d72c..b4ce97924c 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -60,7 +60,7 @@ void ItemView::layoutWidgets() int rows = maxHeight/42; rows = std::max(rows, 1); - bool showScrollbar = std::ceil(dragArea->getChildCount()/float(rows)) > mScrollView->getWidth()/42; + bool showScrollbar = int(std::ceil(dragArea->getChildCount()/float(rows))) > mScrollView->getWidth()/42; if (showScrollbar) maxHeight -= 18; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index bb003c4818..f1e7b4fc5a 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -176,13 +176,16 @@ namespace MWGui int screenHeight = viewSize.height; mVideoBackground->setSize(screenWidth, screenHeight); - double imageaspect = static_cast(mVideo->getVideoWidth())/mVideo->getVideoHeight(); + if (mVideo->getVideoHeight() > 0) + { + double imageaspect = static_cast(mVideo->getVideoWidth())/mVideo->getVideoHeight(); - int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2); - int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2); + int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2); + int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2); - mVideo->setCoord(leftPadding, topPadding, - screenWidth - leftPadding*2, screenHeight - topPadding*2); + mVideo->setCoord(leftPadding, topPadding, + screenWidth - leftPadding*2, screenHeight - topPadding*2); + } mVideo->setVisible(true); } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 66c7a92386..72f242bdbf 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -304,7 +304,7 @@ namespace MWGui mOkButton->setEnabled(pos != MyGUI::ITEM_NONE || mSaving); mDeleteButton->setEnabled(pos != MyGUI::ITEM_NONE); - if (pos == MyGUI::ITEM_NONE) + if (pos == MyGUI::ITEM_NONE || !mCurrentCharacter) { mCurrentSlot = NULL; mInfoText->setCaption(""); diff --git a/apps/openmw/mwgui/spellmodel.hpp b/apps/openmw/mwgui/spellmodel.hpp index 1f7a0cb7c9..7859c8a7b6 100644 --- a/apps/openmw/mwgui/spellmodel.hpp +++ b/apps/openmw/mwgui/spellmodel.hpp @@ -26,6 +26,7 @@ namespace MWGui Spell() : mSelected(false) , mActive(false) + , mType(Type_Spell) { } }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9d24fb19bd..72805dd31e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1691,13 +1691,16 @@ namespace MWGui // Use black bars to correct aspect ratio mVideoBackground->setSize(screenWidth, screenHeight); - double imageaspect = static_cast(mVideoWidget->getVideoWidth())/mVideoWidget->getVideoHeight(); + if (mVideoWidget->getVideoHeight() > 0) + { + double imageaspect = static_cast(mVideoWidget->getVideoWidth())/mVideoWidget->getVideoHeight(); - int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2); - int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2); + int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2); + int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2); - mVideoWidget->setCoord(leftPadding, topPadding, - screenWidth - leftPadding*2, screenHeight - topPadding*2); + mVideoWidget->setCoord(leftPadding, topPadding, + screenWidth - leftPadding*2, screenHeight - topPadding*2); + } } WindowModal* WindowManager::getCurrentModal() const diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index da2492a771..f3d376a700 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -286,7 +286,8 @@ void MWMechanics::Alchemy::addPotion (const std::string& name) if (!iter->isEmpty()) newRecord.mData.mWeight += iter->get()->mBase->mData.mWeight; - newRecord.mData.mWeight /= countIngredients(); + if (countIngredients() > 0) + newRecord.mData.mWeight /= countIngredients(); newRecord.mData.mValue = mValue; newRecord.mData.mAutoCalc = 0; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d675b0157e..6f092f5a49 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1644,7 +1644,7 @@ void CharacterController::update(float duration) world->queueMovement(mPtr, Ogre::Vector3(0.0f)); } - if(mAnimation && !mSkipAnim) + if(!mSkipAnim) { Ogre::Vector3 moved = mAnimation->runAnimation(duration); if(duration > 0.0f) @@ -1762,10 +1762,7 @@ bool CharacterController::kill() playRandomDeath(); - if(mAnimation) - { - mAnimation->disable(mCurrentIdle); - } + mAnimation->disable(mCurrentIdle); mIdleState = CharState_None; mCurrentIdle.clear(); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d33bb6ad15..a831cc8bbc 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -687,12 +687,11 @@ namespace MWMechanics // Failure sound int school = 0; - for (std::vector::const_iterator effectIt (enchantment->mEffects.mList.begin()); - effectIt!=enchantment->mEffects.mList.end(); ++effectIt) + if (!enchantment->mEffects.mList.empty()) { - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); + short effectId = enchantment->mEffects.mList.front().mEffectID; + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectId); school = magicEffect->mData.mSchool; - break; } static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 66052a96ec..8d693e9668 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -330,7 +330,10 @@ namespace MWRender void RaceSelectionPreview::updateCamera() { Ogre::Vector3 scale = mNode->getScale(); - Ogre::Vector3 headOffset = mAnimation->getNode("Bip01 Head")->_getDerivedPosition(); + Ogre::Node* headNode = mAnimation->getNode("Bip01 Head"); + if (!headNode) + return; + Ogre::Vector3 headOffset = headNode->_getDerivedPosition(); headOffset = mNode->convertLocalToWorldPosition(headOffset); mCamera->setPosition(headOffset + mPosition * scale); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index f4388eec54..f8c3a64ef1 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -24,8 +24,9 @@ using namespace MWRender; using namespace Ogre; -LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManager* rendering) : - mInterior(false) +LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManager* rendering) + : mInterior(false) + , mAngle(0.f) { mRendering = rend; mRenderingManager = rendering; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 1841021279..131e12a5c7 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -104,7 +104,7 @@ BillboardObject::BillboardObject( const String& textureName, } BillboardObject::BillboardObject() -: mNode(NULL), mMaterial(NULL), mEntity(NULL) +: mNode(NULL), mMaterial(NULL), mEntity(NULL), mVisibility(1.f) { } @@ -186,6 +186,7 @@ Moon::Moon( const String& textureName, SceneNode* rootNode, const std::string& material) : BillboardObject(textureName, initialSize, position, rootNode, material) + , mType(Type_Masser) { setVisibility(1.0); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index d9941bafdc..3879e0cd0d 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -283,10 +283,13 @@ namespace MWWorld return position; OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + if (!physicActor) + return position; + // Reset per-frame data physicActor->setWalkingOnWater(false); /* Anything to collide with? */ - if(!physicActor || !physicActor->getCollisionMode()) + if(!physicActor->getCollisionMode()) { return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index a0d34b228d..4fa8b7f543 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -630,9 +630,6 @@ namespace MWWorld } const ESM::Cell *searchOrCreate(int x, int y) { - ESM::Cell cell; - cell.mData.mX = x, cell.mData.mY = y; - std::pair key(x, y); DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { @@ -644,13 +641,15 @@ namespace MWWorld return &dit->second; } - ESM::Cell *newCell = new ESM::Cell; - newCell->mData.mX = x; - newCell->mData.mY = y; - mExt[std::make_pair(x, y)] = *newCell; - delete newCell; - - return &mExt[std::make_pair(x, y)]; + ESM::Cell newCell; + newCell.mData.mX = x; + newCell.mData.mY = y; + newCell.mData.mFlags = ESM::Cell::HasWater; + newCell.mAmbi.mAmbient = 0; + newCell.mAmbi.mSunlight = 0; + newCell.mAmbi.mFog = 0; + newCell.mAmbi.mFogDensity = 0; + return &mExt.insert(std::make_pair(key, newCell)).first->second; } const ESM::Cell *find(const std::string &id) const { @@ -853,6 +852,11 @@ namespace MWWorld public: + Store() + : mCells(NULL) + { + } + void setCells(Store& cells) { mCells = &cells; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 17a45f9f12..a68d72dd4a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2554,7 +2554,7 @@ namespace MWWorld if (!selectedSpell.empty()) { - const ESM::Spell* spell = getStore().get().search(selectedSpell); + const ESM::Spell* spell = getStore().get().find(selectedSpell); // Check mana MWMechanics::DynamicStat magicka = stats.getMagicka(); @@ -2641,7 +2641,7 @@ namespace MWWorld if (!selectedSpell.empty()) { - const ESM::Spell* spell = getStore().get().search(selectedSpell); + const ESM::Spell* spell = getStore().get().find(selectedSpell); // A power can be used once per 24h if (spell->mData.mType == ESM::Spell::ST_Power) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 0d4f2365a6..d72ba53c08 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -177,7 +177,6 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int return file->fileProperty(static_cast(column)); return QVariant(); - break; } case Qt::TextAlignmentRole: @@ -193,8 +192,6 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int default: return Qt::AlignLeft + Qt::AlignVCenter; } - return QVariant(); - break; } case Qt::ToolTipRole: @@ -203,7 +200,6 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int return QVariant(); return file->toolTip(); - break; } case Qt::CheckStateRole: @@ -212,8 +208,6 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int return QVariant(); return mCheckStates[file->filePath()]; - - break; } case Qt::UserRole: @@ -229,7 +223,6 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int case Qt::UserRole + 1: return isChecked(file->filePath()); - break; } return QVariant(); } diff --git a/components/contentselector/view/combobox.cpp b/components/contentselector/view/combobox.cpp index 1d773b62dd..18cafc2dc1 100644 --- a/components/contentselector/view/combobox.cpp +++ b/components/contentselector/view/combobox.cpp @@ -30,7 +30,7 @@ void ContentSelectorView::ComboBox::paintEvent(QPaintEvent *) // draw the icon and text if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected opt.currentText = mPlaceholderText; - painter.drawControl(QStyle::CE_ComboBoxLabel, opt); + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); } void ContentSelectorView::ComboBox::setPlaceholderText(const QString &text) diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 347f3fde4a..e4f847dec2 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -242,6 +242,8 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) else { id.mWorldspace = Misc::StringUtils::lowerCase (mName); + id.mIndex.mX = 0; + id.mIndex.mY = 0; } return id; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 22685f5489..ef4b9e9853 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -928,6 +928,8 @@ class NIFObjectLoader particledata = static_cast(partnode)->data.getPtr(); else if(partnode->recType == Nif::RC_NiRotatingParticles) particledata = static_cast(partnode)->data.getPtr(); + else + throw std::runtime_error("Unexpected particle node type"); std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); if(partnode->name.length() > 0) diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index c53cf62b51..cb9680441c 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -319,7 +319,9 @@ void Utf8Encoder::copyFromArray2(const char*& chp, char* &out) } } + std::ios::fmtflags f(std::cout.flags()); std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; + std::cout.flags(f); *(out++) = ch; // Could not find glyph, just put whatever } diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp index 8bf9317880..0fcd36bbd4 100644 --- a/extern/oics/ICSInputControlSystem_joystick.cpp +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -299,7 +299,7 @@ namespace ICS { if(it->second.find(axis) != it->second.end()) { - mControlsJoystickPOVBinderMap[deviceId].find(index)->second.erase( it->second.find(axis) ); + it->second.erase( it->second.find(axis) ); } } } diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index db46a4af97..0c29be9395 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -28,7 +28,9 @@ namespace SFO mWantGrab(false), mWantRelative(false), mWantMouseVisible(false), - mAllowGrab(grab) + mAllowGrab(grab), + mWarpX(0), + mWarpY(0) { _setupOISKeys(); } @@ -117,7 +119,9 @@ namespace SFO case SDL_CLIPBOARDUPDATE: break; // We don't need this event, clipboard is retrieved on demand default: + std::ios::fmtflags f(std::cerr.flags()); std::cerr << "Unhandled SDL event of type 0x" << std::hex << evt.type << std::endl; + std::cerr.flags(f); break; } } From a29abb85f19e56175359ea96c29c4d6c668ef155 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 17:05:24 +0100 Subject: [PATCH 102/404] Fix ItemView sizing bug --- apps/openmw/mwgui/itemview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index b4ce97924c..bf8bf1adf0 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -56,7 +56,7 @@ void ItemView::layoutWidgets() int x = 0; int y = 0; MyGUI::Widget* dragArea = mScrollView->getChildAt(0); - int maxHeight = dragArea->getHeight(); + int maxHeight = mScrollView->getHeight(); int rows = maxHeight/42; rows = std::max(rows, 1); From 8a210c49e995f8fdb89ee99a6e1e9cb536423b36 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 17:12:41 +0100 Subject: [PATCH 103/404] Improve AI prioritising health potions --- apps/openmw/mwmechanics/aicombataction.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 9bb495842c..175b980011 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -294,7 +294,10 @@ namespace MWMechanics // Effect doesn't heal more than we need, *or* we are below 1/2 health if (current.getModified() - current.getCurrent() > toHeal || current.getCurrent() < current.getModified()*0.5) - return 10000.f * priority; + { + return 10000.f * priority + - (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion + } else return -10000.f * priority; // Save for later } From 105f0f87169baf5f54f65b56f9793eea299ecf2a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 17:36:38 +0100 Subject: [PATCH 104/404] Head tracking: don't look at dead actors --- apps/openmw/mwmechanics/actors.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 3a9ba56184..abab1f9e5a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -293,6 +293,9 @@ namespace MWMechanics if (sqrDist > maxDistance*maxDistance) return; + if (targetActor.getClass().getCreatureStats(targetActor).isDead()) + return; + // stop tracking when target is behind the actor Ogre::Vector3 actorDirection (actor.getRefData().getBaseNode()->getOrientation().yAxis()); Ogre::Vector3 targetDirection (Ogre::Vector3(actor2Pos.pos) - Ogre::Vector3(actor1Pos.pos)); From 4aed5158cc1fba40a6eb807ae895d0ba4e4295c6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 17:48:46 +0100 Subject: [PATCH 105/404] Support region names in cell dialogue filter (Fixes #2113) --- apps/openmw/mwdialogue/filter.cpp | 5 +++-- apps/openmw/mwscript/cellextensions.cpp | 13 ++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index af8a5754ff..2d55069e98 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -126,7 +126,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const if (!info.mCell.empty()) { // supports partial matches, just like getPcCell - const std::string& playerCell = player.getCell()->getCell()->mName; + const std::string& playerCell = MWBase::Environment::get().getWorld()->getCellName(player.getCell()); bool match = playerCell.length()>=info.mCell.length() && Misc::StringUtils::ciEqual(playerCell.substr (0, info.mCell.length()), info.mCell); if (!match) @@ -451,7 +451,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_NotCell: - return !Misc::StringUtils::ciEqual(mActor.getCell()->getCell()->mName, select.getName()); + return !Misc::StringUtils::ciEqual(MWBase::Environment::get().getWorld()->getCellName(mActor.getCell()) + , select.getName()); case SelectWrapper::Function_NotLocal: { diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index a568b79438..43d213c5af 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -117,18 +117,9 @@ namespace MWScript runtime.push(0); return; } - const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->getCell(); + const MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); - std::string current = cell->mName; - - if (!(cell->mData.mFlags & ESM::Cell::Interior) && current.empty() - && !cell->mRegion.empty()) - { - const ESM::Region *region = - MWBase::Environment::get().getWorld()->getStore().get().find (cell->mRegion); - - current = region->mName; - } + std::string current = MWBase::Environment::get().getWorld()->getCellName(cell); Misc::StringUtils::toLower(current); bool match = current.length()>=name.length() && From 35d2bfabcaee786257fd83a2573e11ec94b5f674 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 18:02:09 +0100 Subject: [PATCH 106/404] Adjust NPC response to pickpocket attempts (Fixes #2219) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 520b6b8a7c..d43ca61f18 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1053,7 +1053,7 @@ namespace MWMechanics if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) fight = esmStore.get().find("iFightTrespass")->getInt(); else if (type == OT_Pickpocket) - fight = esmStore.get().find("iFightPickpocket")->getInt(); + fight = esmStore.get().find("iFightPickpocket")->getInt() * 4; // *4 according to research wiki else if (type == OT_Assault) // Note: iFightAttack is for the victim, iFightAttacking for witnesses? fight = esmStore.get().find("iFightAttack")->getInt(); else if (type == OT_Murder) From efa9ff3a76bbe319055822d233be8f7cd087716d Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 18:08:51 +0100 Subject: [PATCH 107/404] Fix incorrect implementation of iWerewolfBounty --- apps/openmw/mwmechanics/npcstats.cpp | 8 ++------ apps/openmw/mwworld/worldimp.cpp | 14 ++++++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 13fc14318c..ec7b232a20 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -336,16 +336,12 @@ bool MWMechanics::NpcStats::hasBeenUsed (const std::string& id) const int MWMechanics::NpcStats::getBounty() const { - if (mIsWerewolf) - return MWBase::Environment::get().getWorld()->getStore().get().find("iWereWolfBounty")->getInt(); - else - return mBounty; + return mBounty; } void MWMechanics::NpcStats::setBounty (int bounty) { - if (!mIsWerewolf) - mBounty = bounty; + mBounty = bounty; } int MWMechanics::NpcStats::getFactionReputation (const std::string& faction) const diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a68d72dd4a..d370ad4530 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2476,7 +2476,7 @@ namespace MWWorld getStore().get().search("fAlarmRadius")->getFloat(), closeActors); - bool detected = false; + bool detected = false, reported = false; for (std::vector::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it) { if (*it == actor) @@ -2486,16 +2486,22 @@ namespace MWWorld continue; if (getLOS(*it, actor) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, *it)) - { detected = true; - break; - } + if (it->getClass().getCreatureStats(*it).getAiSetting(MWMechanics::CreatureStats::AI_Alarm).getModified() > 0) + reported = true; } if (detected) { windowManager->messageBox("#{sWerewolfAlarmMessage}"); setGlobalInt("pcknownwerewolf", 1); + + if (reported) + { + npcStats.setBounty(npcStats.getBounty()+ + MWBase::Environment::get().getWorld()->getStore().get().find("iWereWolfBounty")->getInt()); + windowManager->messageBox("#{sCrimeMessage}"); + } } } } From cc9af9562b7157943968d396d18c57d5348915f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 18:33:14 +0100 Subject: [PATCH 108/404] Function_CreatureTargetted should return '2' for werewolfs This makes NPCs say the correct attack voice files when fighting a werewolf. --- apps/openmw/mwdialogue/filter.cpp | 19 +++++++++++++++---- apps/openmw/mwdialogue/selectwrapper.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 10 ---------- apps/openmw/mwmechanics/creaturestats.hpp | 2 -- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 2d55069e98..7c67cdc5c7 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -417,6 +417,21 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con return value; } + case SelectWrapper::Function_CreatureTargetted: + + { + MWWorld::Ptr target; + mActor.getClass().getCreatureStats(mActor).getAiSequence().getCombatTarget(target); + if (target) + { + if (target.getClass().isNpc() && target.getClass().getNpcStats(target).isWerewolf()) + return 2; + if (target.getTypeName() == typeid(ESM::Creature).name()) + return 1; + } + } + return 0; + default: throw std::runtime_error ("unknown integer select function"); @@ -532,10 +547,6 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor, MWBase::Environment::get().getWorld()->getPlayerPtr()); - case SelectWrapper::Function_CreatureTargetted: - - return mActor.getClass().getCreatureStats (mActor).getCreatureTargetted(); - case SelectWrapper::Function_Werewolf: return mActor.getClass().getNpcStats (mActor).isWerewolf(); diff --git a/apps/openmw/mwdialogue/selectwrapper.cpp b/apps/openmw/mwdialogue/selectwrapper.cpp index 3f22998f0e..fa0fbfe136 100644 --- a/apps/openmw/mwdialogue/selectwrapper.cpp +++ b/apps/openmw/mwdialogue/selectwrapper.cpp @@ -205,6 +205,7 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const Function_Reputation, Function_FactionRankDiff, Function_WerewolfKills, Function_RankLow, Function_RankHigh, + Function_CreatureTargetted, Function_None // end marker }; @@ -225,7 +226,6 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const Function_PcVampire, Function_TalkedToPc, Function_Alarmed, Function_Detected, Function_Attacked, Function_ShouldAttack, - Function_CreatureTargetted, Function_Werewolf, Function_None // end marker }; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 72a710c656..ac6f88d449 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -329,16 +329,6 @@ namespace MWMechanics mAttacked = attacked; } - bool CreatureStats::getCreatureTargetted() const - { - MWWorld::Ptr targetPtr; - if (mAiSequence.getCombatTarget(targetPtr)) - { - return targetPtr.getTypeName() == typeid(ESM::Creature).name(); - } - return false; - } - float CreatureStats::getEvasion() const { float evasion = (getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index d13ced3b3a..9a08b58c9c 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -199,8 +199,6 @@ namespace MWMechanics bool getAttacked() const; void setAttacked (bool attacked); - bool getCreatureTargetted() const; - float getEvasion() const; void setKnockedDown(bool value); From 191032746934a3a1df309eecda181f216cc0a554 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Dec 2014 22:27:09 +0100 Subject: [PATCH 109/404] Implement disposition changes due to crimes --- .../mwmechanics/mechanicsmanagerimp.cpp | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d43ca61f18..720f0c4f5c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1001,16 +1001,31 @@ namespace MWMechanics victim.getClass().getCreatureStats(victim).notifyMurder(); // Bounty for each type of crime + float dispTerm = 0.f, dispTermVictim = 0.f; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + { arg = store.find("iCrimeTresspass")->getInt(); + dispTerm = dispTermVictim = store.find("iDispTresspass")->getInt(); + } else if (type == OT_Pickpocket) + { arg = store.find("iCrimePickPocket")->getInt(); + dispTerm = dispTermVictim = store.find("fDispPickPocketMod")->getFloat(); + } else if (type == OT_Assault) + { arg = store.find("iCrimeAttack")->getInt(); + dispTerm = store.find("fDispAttacking")->getFloat(); + dispTermVictim = store.find("iDispAttackMod")->getInt(); + } else if (type == OT_Murder) + { arg = store.find("iCrimeKilling")->getInt(); + dispTerm = dispTermVictim = store.find("iDispKilling")->getInt(); + } else if (type == OT_Theft) { + dispTerm = dispTermVictim = store.find("fDispStealing")->getFloat() * arg; arg *= store.find("fCrimeStealing")->getFloat(); arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen } @@ -1049,19 +1064,23 @@ namespace MWMechanics // What amount of provocation did this crime generate? // Controls whether witnesses will engage combat with the criminal. - int fight = 0; + int fight = 0, fightVictim = 0; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) - fight = esmStore.get().find("iFightTrespass")->getInt(); + fight = fightVictim = esmStore.get().find("iFightTrespass")->getInt(); else if (type == OT_Pickpocket) - fight = esmStore.get().find("iFightPickpocket")->getInt() * 4; // *4 according to research wiki - else if (type == OT_Assault) // Note: iFightAttack is for the victim, iFightAttacking for witnesses? + { + fight = esmStore.get().find("iFightPickpocket")->getInt(); + fightVictim = esmStore.get().find("iFightPickpocket")->getInt() * 4; // *4 according to research wiki + } + else if (type == OT_Assault) + { fight = esmStore.get().find("iFightAttack")->getInt(); + fightVictim = esmStore.get().find("iFightAttacking")->getInt(); + } else if (type == OT_Murder) - fight = esmStore.get().find("iFightKilling")->getInt(); + fight = fightVictim = esmStore.get().find("iFightKilling")->getInt(); else if (type == OT_Theft) - fight = esmStore.get().find("fFightStealing")->getFloat(); - - const int iFightAttacking = esmStore.get().find("iFightAttacking")->getInt(); + fight = fightVictim = esmStore.get().find("fFightStealing")->getFloat(); // Tell everyone (including the original reporter) in alarm range for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) @@ -1069,11 +1088,7 @@ namespace MWMechanics if ( *it == player || !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue; - int aggression = fight; - - // Note: iFightAttack is used for the victim, iFightAttacking for witnesses? - if (*it != victim && type == OT_Assault) - aggression = iFightAttacking; + int aggression = (*it == victim) ? fightVictim : fight; if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) continue; @@ -1091,6 +1106,11 @@ namespace MWMechanics } else { + int dispChange = (*it == victim) ? dispTermVictim : dispTerm; + NpcStats& observerStats = it->getClass().getNpcStats(*it); + int originalDisposition = observerStats.getBaseDisposition(); + observerStats.setBaseDisposition(originalDisposition+dispChange); + bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression, true); if (aggressive) { @@ -1108,6 +1128,8 @@ namespace MWMechanics // Mark as Alarmed for dialogue it->getClass().getCreatureStats(*it).setAlarmed(true); } + else + observerStats.setBaseDisposition(originalDisposition); } } } From 307b84e9f6447cd9b5b317ab5a31e778ccc81749 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 19 Dec 2014 19:51:36 +0100 Subject: [PATCH 110/404] Add enemy health bar fading and use relevant GMSTs --- apps/openmw/mwgui/hud.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 2593e6e77c..a5703682f3 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -618,6 +618,11 @@ namespace MWGui // Health is usually cast to int before displaying. Actors die whenever they are < 1 health. // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :) mEnemyHealth->setProgressPosition(int(stats.getHealth().getCurrent()) / stats.getHealth().getModified() * 100); + + static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarFade")->getFloat(); + if (fNPCHealthBarFade > 0.f) + mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade))); + } void HUD::update() @@ -639,7 +644,7 @@ namespace MWGui void HUD::setEnemy(const MWWorld::Ptr &enemy) { mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); - mEnemyHealthTimer = 5; + mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarTime")->getFloat(); if (!mEnemyHealth->getVisible()) mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); mEnemyHealth->setVisible(true); From 866fdfe8bdcde3cf07d22872f55a2a617201c0f0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 19 Dec 2014 21:45:26 +0100 Subject: [PATCH 111/404] Crime system improvements - If someone saw the crime, they will notify everyone else in range, even if the Alarm rating of the witness is 0. - Pickpocket and selling stolen items now works properly, i.e. honors the victim's Alarm rating instead of always being reported. --- apps/openmw/mwbase/mechanicsmanager.hpp | 10 +-- apps/openmw/mwgui/container.cpp | 10 +-- apps/openmw/mwgui/enchantingdialog.cpp | 5 +- apps/openmw/mwgui/tradewindow.cpp | 5 +- .../mwmechanics/mechanicsmanagerimp.cpp | 82 +++++++++---------- .../mwmechanics/mechanicsmanagerimp.hpp | 14 ++-- 6 files changed, 57 insertions(+), 69 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index c92459183d..fe4b5fb63a 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -118,16 +118,14 @@ namespace MWBase OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft) }; /** - * @brief Commit a crime. If any actors witness the crime and report it, - * reportCrime will be called automatically. * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. - * @return was the crime reported? + * @param victimAware Is the victim already aware of the crime? + * If this parameter is false, it will be determined by a line-of-sight and awareness check. + * @return was the crime seen? */ virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, - OffenseType type, int arg=0) = 0; - virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, - OffenseType type, int arg=0) = 0; + OffenseType type, int arg=0, bool victimAware=false) = 0; /// @return false if the attack was considered a "friendly hit" and forgiven virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0; /// Utility to check if taking this item is illegal and calling commitCrime if so diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 6df8a3f448..ee1d285925 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -301,10 +301,9 @@ namespace MWGui MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.finish()) { - MWBase::Environment::get().getMechanicsManager()->reportCrime( - player, mPtr, MWBase::MechanicsManager::OT_Pickpocket); + MWBase::Environment::get().getMechanicsManager()->commitCrime( + player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; return; } @@ -384,10 +383,9 @@ namespace MWGui if (pickpocket.pick(item.mBase, count)) { int value = item.mBase.getClass().getValue(item.mBase) * count; - MWBase::Environment::get().getMechanicsManager()->reportCrime( - player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value); + MWBase::Environment::get().getMechanicsManager()->commitCrime( + player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; return false; } diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 56caa6513b..d4c10d568c 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -338,9 +338,8 @@ namespace MWGui if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); MWBase::Environment::get().getWindowManager()->messageBox(msg); - MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); - MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, - item.getClass().getValue(item)); + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, + item.getClass().getValue(item), true); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); return; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0f98598fd8..6499a66d5f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -290,10 +290,9 @@ namespace MWGui if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase)); MWBase::Environment::get().getWindowManager()->messageBox(msg); - MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); - MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, it->mBase.getClass().getValue(it->mBase) - * it->mCount); + * it->mCount, true); onCancelButtonClicked(mCancelButton); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); return; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 720f0c4f5c..88a0b2640e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -917,24 +917,19 @@ namespace MWMechanics commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } - bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) + bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg, bool victimAware) { - // NOTE: int arg can be from itemTaken() so DON'T modify it, since it is - // passed to reportCrime later on in this function. - // NOTE: victim may be empty // Only player can commit crime if (player.getRefData().getHandle() != "player") return false; - const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - - // Find all the actors within the alarm radius std::vector neighbors; Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos); + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); float radius = esmStore.get().find("fAlarmRadius")->getFloat(); mActors.getObjectsInRange(from, radius, neighbors); @@ -943,11 +938,8 @@ namespace MWMechanics if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) neighbors.push_back(victim); - bool victimAware = false; - - // Find actors who directly witnessed the crime + // Did anyone see it? bool crimeSeen = false; - bool reported = false; for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if (*it == player) @@ -955,17 +947,13 @@ namespace MWMechanics if (it->getClass().getCreatureStats(*it).isDead()) continue; - // Was the crime seen? - if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) + if ((*it == victim && victimAware) + || (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) // Murder crime can be reported even if no one saw it (hearing is enough, I guess). // TODO: Add mod support for stealth executions! || (type == OT_Murder && *it != victim)) { - if (*it == victim) - victimAware = true; - - // TODO: are there other messages? - if (type == OT_Theft) + if (type == OT_Theft || type == OT_Pickpocket) MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); // Crime reporting only applies to NPCs @@ -977,20 +965,13 @@ namespace MWMechanics crimeSeen = true; } - - // Will the witness report the crime? - if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) - { - reported = true; - } } - if (crimeSeen && reported) + if (crimeSeen) reportCrime(player, victim, type, arg); - else if (victimAware && !victim.isEmpty() && type == OT_Assault) - startCombat(victim, player); - - return reported; + else if (type == OT_Assault && !victim.isEmpty()) + startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee? + return crimeSeen; } void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) @@ -1030,22 +1011,6 @@ namespace MWMechanics arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen } - MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); - player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty() - + arg); - - // If committing a crime against a faction member, expell from the faction - if (!victim.isEmpty() && victim.getClass().isNpc()) - { - std::string factionID; - if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty()) - factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first; - if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim))) - { - player.getClass().getNpcStats(player).expell(factionID); - } - } - // Make surrounding actors within alarm distance respond to the crime std::vector neighbors; @@ -1082,6 +1047,8 @@ namespace MWMechanics else if (type == OT_Theft) fight = fightVictim = esmStore.get().find("fFightStealing")->getFloat(); + bool reported = false; + // Tell everyone (including the original reporter) in alarm range for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { @@ -1093,6 +1060,12 @@ namespace MWMechanics if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) continue; + // Will the witness report the crime? + if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) + { + reported = true; + } + if (it->getClass().isClass(*it, "guard")) { // Mark as Alarmed for dialogue @@ -1132,6 +1105,25 @@ namespace MWMechanics observerStats.setBaseDisposition(originalDisposition); } } + + if (reported) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); + player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty() + + arg); + + // If committing a crime against a faction member, expell from the faction + if (!victim.isEmpty() && victim.getClass().isNpc()) + { + std::string factionID; + if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty()) + factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first; + if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim))) + { + player.getClass().getNpcStats(player).expell(factionID); + } + } + } } bool MechanicsManager::actorAttacked(const MWWorld::Ptr &ptr, const MWWorld::Ptr &attacker) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 489da75417..74b4247faf 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -110,16 +110,14 @@ namespace MWMechanics virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); /** - * @brief Commit a crime. If any actors witness the crime and report it, - * reportCrime will be called automatically. * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. - * @return was the crime reported? + * @param victimAware Is the victim already aware of the crime? + * If this parameter is false, it will be determined by a line-of-sight and awareness check. + * @return was the crime seen? */ virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, - OffenseType type, int arg=0); - virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, - OffenseType type, int arg=0); + OffenseType type, int arg=0, bool victimAware=false); /// @return false if the attack was considered a "friendly hit" and forgiven virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); /// Utility to check if taking this item is illegal and calling commitCrime if so @@ -171,6 +169,10 @@ namespace MWMechanics virtual void keepPlayerAlive(); virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const; + + private: + void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0); }; } From 37e11b7272b227902a26f3cdbbc05397e4536780 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 00:04:24 +0100 Subject: [PATCH 112/404] Adjust loading box size for large captions --- apps/openmw/mwgui/loadingscreen.cpp | 6 ++++++ apps/openmw/mwgui/loadingscreen.hpp | 2 ++ files/mygui/openmw_loading_screen.layout | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index a446266d9b..4ff6b0c89b 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -34,6 +34,7 @@ namespace MWGui getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); + getWidget(mLoadingBox, "LoadingBox"); mProgressBar->setScrollViewPage(1); @@ -46,6 +47,11 @@ namespace MWGui void LoadingScreen::setLabel(const std::string &label) { mLoadingText->setCaptionWithReplacing(label); + int padding = mLoadingBox->getWidth() - mLoadingText->getWidth(); + MyGUI::IntSize size(mLoadingText->getTextSize().width+padding, mLoadingBox->getHeight()); + size.width = std::max(300, size.width); + mLoadingBox->setSize(size); + mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mLoadingBox->getTop()); } LoadingScreen::~LoadingScreen() diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 310a6df3c2..892710433f 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -47,6 +47,8 @@ namespace MWGui size_t mProgress; + MyGUI::Widget* mLoadingBox; + MyGUI::TextBox* mLoadingText; MyGUI::ScrollBar* mProgressBar; BackgroundImage* mBackgroundImage; diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout index 49ee0a1de5..8a1514f844 100644 --- a/files/mygui/openmw_loading_screen.layout +++ b/files/mygui/openmw_loading_screen.layout @@ -4,9 +4,9 @@ - + - + From c7be8501625c23633f150d2feac6339c72656882 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 00:06:14 +0100 Subject: [PATCH 113/404] Add messagebox escape characters for spell/weapon cycling hotkeys --- components/interpreter/defines.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 94916ee851..4881274f03 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -47,10 +47,10 @@ namespace Interpreter{ retval << context.getActionBinding("#{sReady_Magic}"); } else if((found = check(temp, "actionprevweapon", &i, &start))){ - retval << "PLACEHOLDER_ACTION_PREV_WEAPON"; + retval << context.getActionBinding("#{sPrevWeapon}"); } else if((found = check(temp, "actionnextweapon", &i, &start))){ - retval << "PLACEHOLDER_ACTION_PREV_WEAPON"; + retval << context.getActionBinding("#{sNextWeapon}"); } else if((found = check(temp, "actiontogglerun", &i, &start))){ retval << context.getActionBinding("#{sAuto_Run}"); @@ -62,10 +62,10 @@ namespace Interpreter{ retval << context.getActionBinding("#{sReady_Weapon}"); } else if((found = check(temp, "actionprevspell", &i, &start))){ - retval << "PLACEHOLDER_ACTION_PREV_SPELL"; + retval << context.getActionBinding("#{sPrevSpell}"); } else if((found = check(temp, "actionnextspell", &i, &start))){ - retval << "PLACEHOLDER_ACTION_NEXT_SPELL"; + retval << context.getActionBinding("#{sNextSpell}"); } else if((found = check(temp, "actionrestmenu", &i, &start))){ retval << context.getActionBinding("#{sRestKey}"); From f3738e9a98f89ba5e9276ce1d723f5f23a29a8da Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 00:18:58 +0100 Subject: [PATCH 114/404] Say an "intruder" voice dialogue for trespassing crimes (Fixes #1082) Seems to be broken in the original engine, but according to the TES-CS help this is how the intruder voices should be used. There are legitimate entries for "intruder" in the game's files, so we might as well use them. --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index fe4b5fb63a..41d6c63102 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -113,7 +113,7 @@ namespace MWBase OT_Theft, // Taking items owned by an NPC or a faction you are not a member of OT_Assault, // Attacking a peaceful NPC OT_Murder, // Murdering a peaceful NPC - OT_Trespassing, // Staying in a cell you are not allowed in (where is this defined?) + OT_Trespassing, // Picking the lock of an owned door/chest OT_SleepingInOwnedBed, // Sleeping in a bed owned by an NPC or a faction you are not a member of OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft) }; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 88a0b2640e..82b0d556db 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -955,6 +955,8 @@ namespace MWMechanics { if (type == OT_Theft || type == OT_Pickpocket) MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); + else if (type == OT_Trespassing) + MWBase::Environment::get().getDialogueManager()->say(*it, "intruder"); // Crime reporting only applies to NPCs if (!it->getClass().isNpc()) From 9ed71765a92e0fe59bb5b441e141145a12df3286 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 19:53:38 +0100 Subject: [PATCH 115/404] Fix deleted containers showing in merchant inventories --- apps/openmw/mwworld/worldimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d370ad4530..1867cf84cf 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2259,6 +2259,8 @@ namespace MWWorld for (CellRefList::List::iterator container = refList.begin(); container != refList.end(); ++container) { MWWorld::Ptr ptr (&*container, *cellIt); + if (ptr.getRefData().isDeleted()) + continue; if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), npc.getCellRef().getRefId())) out.push_back(ptr); } From 1bcc4430e0c9df3c3519f413c7032e18496acd4b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 20:13:27 +0100 Subject: [PATCH 116/404] Fix owner not getting set on restocked items --- apps/openmw/mwworld/containerstore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 45728371bf..7479268776 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -474,7 +474,7 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW { int currentCount = count(item); if (currentCount < std::abs(it->mCount)) - add (item, std::abs(it->mCount) - currentCount, ptr); + addInitialItem(item, owner, faction, std::abs(it->mCount) - currentCount, true); } } flagAsModified(); From fb542a64ec0fa022cd42790b9a9ddee5ead61d4e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 20:17:14 +0100 Subject: [PATCH 117/404] Merchant items should be restocked instantly --- apps/openmw/mwgui/dialogue.cpp | 11 +---------- apps/openmw/mwgui/tradewindow.cpp | 16 ++++++++++++++++ apps/openmw/mwgui/tradewindow.hpp | 2 ++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index eb548d596d..3b4ac8deae 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -415,20 +415,11 @@ namespace MWGui MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getFloat(); + // Gold is restocked every 24h if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) { sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr)); - mPtr.getClass().restock(mPtr); - - // Also restock any containers owned by this merchant, which are also available to buy in the trade window - std::vector itemSources; - MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources); - for (std::vector::iterator it = itemSources.begin(); it != itemSources.end(); ++it) - { - it->getClass().restock(*it); - } - sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 6499a66d5f..6f0074e7e5 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -94,6 +94,20 @@ namespace MWGui setCoord(400, 0, 400, 300); } + void TradeWindow::restock() + { + // Restock items on the actor inventory + mPtr.getClass().restock(mPtr); + + // Also restock any containers owned by this merchant, which are also available to buy in the trade window + std::vector itemSources; + MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources); + for (std::vector::iterator it = itemSources.begin(); it != itemSources.end(); ++it) + { + it->getClass().restock(*it); + } + } + void TradeWindow::startTrade(const MWWorld::Ptr& actor) { mPtr = actor; @@ -101,6 +115,8 @@ namespace MWGui mCurrentBalance = 0; mCurrentMerchantOffer = 0; + restock(); + std::vector itemSources; MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 47de9215a2..b8bdfe6480 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -98,6 +98,8 @@ namespace MWGui virtual void onReferenceUnavailable(); int getMerchantGold(); + + void restock(); }; } From 703b9c59e9b535d8ee8b396c8c146a05e858628a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 20:39:13 +0100 Subject: [PATCH 118/404] Add missing tooltips to spell creation dialog --- files/mygui/openmw_enchanting_dialog.layout | 6 ++--- .../mygui/openmw_spellcreation_dialog.layout | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/files/mygui/openmw_enchanting_dialog.layout b/files/mygui/openmw_enchanting_dialog.layout index 811a214e3e..4fdb916026 100644 --- a/files/mygui/openmw_enchanting_dialog.layout +++ b/files/mygui/openmw_enchanting_dialog.layout @@ -133,13 +133,13 @@ - - - + + + diff --git a/files/mygui/openmw_spellcreation_dialog.layout b/files/mygui/openmw_spellcreation_dialog.layout index 78d3f3de73..fac0497db5 100644 --- a/files/mygui/openmw_spellcreation_dialog.layout +++ b/files/mygui/openmw_spellcreation_dialog.layout @@ -7,6 +7,9 @@ + + + @@ -20,23 +23,36 @@ + + + + + + + + + + + + + @@ -44,6 +60,9 @@ + + + @@ -60,6 +79,9 @@ + + + From 877e07823d4e2a82616744ec580015c510faf409 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 20:48:22 +0100 Subject: [PATCH 119/404] Fix incorrect sound for spell creation success --- apps/openmw/mwgui/spellcreationdialog.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 5da33c67ae..c8510c0e1b 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -371,7 +371,7 @@ namespace MWGui MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); npcStats.setGoldPool(npcStats.getGoldPool() + price); - MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); + MWBase::Environment::get().getSoundManager()->playSound ("Mysticism Hit", 1.0, 1.0); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->createRecord(mSpell); @@ -379,8 +379,6 @@ namespace MWGui MWMechanics::Spells& spells = stats.getSpells(); spells.add (spell->mId); - MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); - MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation); } From 0081a683762d12a268cfd36208c67a948121ccfa Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 20:56:44 +0100 Subject: [PATCH 120/404] Use fMagicStartIconBlink for spell effect indicator fading --- apps/openmw/mwgui/spellicons.cpp | 17 +++++++++++++---- apps/openmw/mwgui/spellicons.hpp | 6 ++++-- apps/openmw/mwmechanics/activespells.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 4 ++-- apps/openmw/mwmechanics/magiceffects.hpp | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index dbd91ab753..4199695747 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -24,7 +24,7 @@ namespace MWGui void EffectSourceVisitor::visit (MWMechanics::EffectKey key, const std::string& sourceName, int casterActorId, - float magnitude, float remainingTime) + float magnitude, float remainingTime, float totalTime) { MagicEffectInfo newEffectSource; newEffectSource.mKey = key; @@ -32,6 +32,7 @@ namespace MWGui newEffectSource.mPermanent = mIsPermanent; newEffectSource.mRemainingTime = remainingTime; newEffectSource.mSource = sourceName; + newEffectSource.mTotalTime = totalTime; mEffectSources[key.mId].push_back(newEffectSource); } @@ -67,10 +68,11 @@ namespace MWGui MWBase::Environment::get().getWorld ()->getStore ().get().find(it->first); float remainingDuration = 0; + float totalDuration = 0; std::string sourcesDescription; - const float fadeTime = 5.f; + static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get().find("fMagicStartIconBlink")->getFloat(); for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) @@ -80,9 +82,15 @@ namespace MWGui // if at least one of the effect sources is permanent, the effect will never wear off if (effectIt->mPermanent) + { remainingDuration = fadeTime; + totalDuration = fadeTime; + } else + { remainingDuration = std::max(remainingDuration, effectIt->mRemainingTime); + totalDuration = std::max(totalDuration, effectIt->mTotalTime); + } sourcesDescription += effectIt->mSource; @@ -158,8 +166,9 @@ namespace MWGui ToolTipInfo* tooltipInfo = image->getUserData(); tooltipInfo->text = sourcesDescription; - // Fade out during the last 5 seconds - image->setAlpha(std::min(remainingDuration/fadeTime, 1.f)); + // Fade out + if (totalDuration >= fadeTime && fadeTime > 0.f) + image->setAlpha(std::min(remainingDuration/fadeTime, 1.f)); } else if (mWidgetMap.find(it->first) != mWidgetMap.end()) { diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index 7df9ad8b91..e9d9967ead 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -26,12 +26,14 @@ namespace MWGui MagicEffectInfo() : mPermanent(false) , mMagnitude(0) - , mRemainingTime(0) + , mRemainingTime(0.f) + , mTotalTime(0.f) {} std::string mSource; // display name for effect source (e.g. potion name) MWMechanics::EffectKey mKey; int mMagnitude; float mRemainingTime; + float mTotalTime; bool mPermanent; // the effect is permanent }; @@ -46,7 +48,7 @@ namespace MWGui virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, int casterActorId, - float magnitude, float remainingTime = -1); + float magnitude, float remainingTime = -1, float totalTime = -1); }; class SpellIcons diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index d87e225434..717a63be8f 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -195,7 +195,7 @@ namespace MWMechanics float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime); + visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); } } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index abab1f9e5a..f824fd6f4a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -102,7 +102,7 @@ public: virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, int casterActorId, - float magnitude, float remainingTime = -1) + float magnitude, float remainingTime = -1, float totalTime = -1) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if ( ((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc()) @@ -200,7 +200,7 @@ namespace MWMechanics virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, int casterActorId, - float magnitude, float remainingTime = -1) + float magnitude, float remainingTime = -1, float totalTime = -1) { if (key.mId != ESM::MagicEffect::Soultrap) return; diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 0a8392dabd..88d8d988f7 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -74,7 +74,7 @@ namespace MWMechanics { virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, int casterActorId, - float magnitude, float remainingTime = -1) = 0; + float magnitude, float remainingTime = -1, float totalTime = -1) = 0; }; /// \brief Effects currently affecting a NPC or creature From 3912ee2b1d0a958f2d8d785608f2b22aa8298282 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 20 Dec 2014 22:00:45 +0100 Subject: [PATCH 121/404] Fix faction rank not being set on items in faction-owned containers --- apps/openmw/mwclass/container.cpp | 4 ++-- apps/openmw/mwclass/creature.cpp | 4 ++-- apps/openmw/mwclass/npc.cpp | 4 ++-- apps/openmw/mwworld/cellref.cpp | 9 +++++++++ apps/openmw/mwworld/cellref.hpp | 1 + apps/openmw/mwworld/containerstore.cpp | 17 +++++++++-------- apps/openmw/mwworld/containerstore.hpp | 6 +++--- 7 files changed, 28 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 59e51e4613..72a9802e8e 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -59,7 +59,7 @@ namespace MWClass ptr.get(); data->mContainerStore.fill( - ref->mBase->mInventory, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), MWBase::Environment::get().getWorld()->getStore()); + ref->mBase->mInventory, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank(), MWBase::Environment::get().getWorld()->getStore()); // store ptr.getRefData().setCustomData (data.release()); @@ -81,7 +81,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); - store.restock(list, ptr, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction()); + store.restock(list, ptr, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank()); } void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 323fafbb6c..c69081c2e0 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -139,7 +139,7 @@ namespace MWClass // store ptr.getRefData().setCustomData(data.release()); - getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", + getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", -1, MWBase::Environment::get().getWorld()->getStore()); if (ref->mBase->mFlags & ESM::Creature::Weapon) @@ -888,7 +888,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); - store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); + store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1); } int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 2c68b4c72c..3fe23772d6 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -403,7 +403,7 @@ namespace MWClass } // inventory - data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", + data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", -1, MWBase::Environment::get().getWorld()->getStore()); data->mNpcStats.setGoldPool(gold); @@ -1376,7 +1376,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); - store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); + store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1); } int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index c0b3bb6afc..3ea3ed8bf7 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -99,6 +99,15 @@ namespace MWWorld return mCellRef.mGlobalVariable; } + void CellRef::setFactionRank(int factionRank) + { + if (factionRank != mCellRef.mFactionRank) + { + mChanged = true; + mCellRef.mFactionRank = factionRank; + } + } + int CellRef::getFactionRank() const { return mCellRef.mFactionRank; diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index d2e4fdf1ce..d7c0ce2219 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -79,6 +79,7 @@ namespace MWWorld void setFaction (const std::string& faction); // PC faction rank required to use the item. Sometimes is -1, which means "any rank". + void setFactionRank(int factionRank); int getFactionRank() const; // Lock level for doors and containers diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 7479268776..e9c968a07c 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -396,19 +396,19 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor return count - toRemove; } -void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, const MWWorld::ESMStore& store) +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, int factionRank, const MWWorld::ESMStore& store) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); ++iter) { std::string id = Misc::StringUtils::lowerCase(iter->mItem.toString()); - addInitialItem(id, owner, faction, iter->mCount); + addInitialItem(id, owner, faction, factionRank, iter->mCount); } flagAsModified(); } -void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, +void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int factionRank, int count, bool topLevel, const std::string& levItem) { ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); @@ -420,7 +420,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: if (topLevel && std::abs(count) > 1 && levItem->mFlags & ESM::ItemLevList::Each) { for (int i=0; i 0 ? 1 : -1, true, levItem->mId); + addInitialItem(id, owner, faction, factionRank, count > 0 ? 1 : -1, true, levItem->mId); return; } else @@ -428,7 +428,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: std::string id = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase, false); if (id.empty()) return; - addInitialItem(id, owner, faction, count, false, levItem->mId); + addInitialItem(id, owner, faction, factionRank, count, false, levItem->mId); } } else @@ -445,11 +445,12 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: ref.getPtr().getCellRef().setOwner(owner); ref.getPtr().getCellRef().setFaction(faction); + ref.getPtr().getCellRef().setFactionRank(factionRank); addImp (ref.getPtr(), count); } } -void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction) +void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction, int factionRank) { // Remove the items already spawned by levelled items that will restock for (std::map::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) @@ -468,13 +469,13 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mItem.toString())) { - addInitialItem(item, owner, faction, it->mCount, true); + addInitialItem(item, owner, faction, factionRank, it->mCount, true); } else { int currentCount = count(item); if (currentCount < std::abs(it->mCount)) - addInitialItem(item, owner, faction, std::abs(it->mCount) - currentCount, true); + addInitialItem(item, owner, faction, factionRank, std::abs(it->mCount) - currentCount, true); } } flagAsModified(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 6d9d7a6bb1..33255138a6 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -74,7 +74,7 @@ namespace MWWorld mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); - void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true, const std::string& levItem = ""); + void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int factionRank, int count, bool topLevel=true, const std::string& levItem = ""); template ContainerStoreIterator getState (CellRefList& collection, @@ -153,10 +153,10 @@ namespace MWWorld virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); ///< @return true if the two specified objects can stack with each other - void fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, const MWWorld::ESMStore& store); + void fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, int factionRank, const MWWorld::ESMStore& store); ///< Insert items into *this. - void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction); + void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction, int factionRank); virtual void clear(); ///< Empty container. From e73e97529112e46ab9222ef22da9e2f52c1eb568 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 00:09:50 +0100 Subject: [PATCH 122/404] Fix player being able to activate objects when knocked out --- apps/openmw/engine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3d8aa5530b..e89bcfa1d3 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -508,6 +508,10 @@ void OMW::Engine::activate() if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (player.getClass().getCreatureStats(player).getKnockedDown()) + return; + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject(); if (ptr.isEmpty()) From b35f87ae7e4ff247079669064b1bff379021205a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 01:28:06 +0100 Subject: [PATCH 123/404] Improve font file error handling --- components/fontloader/fontloader.cpp | 48 ++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index e01e4b7bc2..17c630fd9f 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -1,5 +1,7 @@ #include "fontloader.hpp" +#include + #include #include @@ -121,6 +123,15 @@ namespace return encoder.getUtf8(std::string(1, c)); } + void fail (Ogre::DataStreamPtr file, const std::string& fileName, const std::string& message) + { + std::stringstream error; + error << "Font loading error: " << message; + error << "\n File: " << fileName; + error << "\n Offset: 0x" << std::hex << file->tell(); + throw std::runtime_error(error.str()); + } + } namespace Gui @@ -173,20 +184,30 @@ namespace Gui Ogre::DataStreamPtr file = Ogre::ResourceGroupManager::getSingleton().openResource(fileName); float fontSize; + if (file->read(&fontSize, sizeof(fontSize)) < sizeof(fontSize)) + fail(file, fileName, "File too small to be a valid font"); + int one; - file->read(&fontSize, sizeof(fontSize)); + if (file->read(&one, sizeof(int)) < sizeof(int)) + fail(file, fileName, "File too small to be a valid font"); - file->read(&one, sizeof(int)); - assert(one == 1); - file->read(&one, sizeof(int)); - assert(one == 1); + if (one != 1) + fail(file, fileName, "Unexpected value"); + + if (file->read(&one, sizeof(int)) < sizeof(int)) + fail(file, fileName, "File too small to be a valid font"); + + if (one != 1) + fail(file, fileName, "Unexpected value"); char name_[284]; - file->read(name_, sizeof(name_)); + if (file->read(name_, sizeof(name_)) < sizeof(name_)) + fail(file, fileName, "File too small to be a valid font"); std::string name(name_); GlyphInfo data[256]; - file->read(data, sizeof(data)); + if (file->read(data, sizeof(data)) < sizeof(data)) + fail(file, fileName, "File too small to be a valid font"); file->close(); // Create the font texture @@ -194,12 +215,19 @@ namespace Gui Ogre::DataStreamPtr bitmapFile = Ogre::ResourceGroupManager::getSingleton().openResource(bitmapFilename); int width, height; - bitmapFile->read(&width, sizeof(int)); - bitmapFile->read(&height, sizeof(int)); + if (bitmapFile->read(&width, sizeof(int)) < sizeof(int)) + fail(bitmapFile, bitmapFilename, "File too small to be a valid bitmap"); + + if (bitmapFile->read(&height, sizeof(int)) < sizeof(int)) + fail(bitmapFile, bitmapFilename, "File too small to be a valid bitmap"); + + if (width <= 0 || height <= 0) + fail(bitmapFile, bitmapFilename, "Width and height must be positive"); std::vector textureData; textureData.resize(width*height*4); - bitmapFile->read(&textureData[0], width*height*4); + if (bitmapFile->read(&textureData[0], width*height*4) < (size_t)(width*height*4)) + fail(bitmapFile, bitmapFilename, "Bitmap does not contain the specified number of pixels"); bitmapFile->close(); std::string resourceName; From 2410d7941081a5b396aa2ca69d4442d78bd8a2a0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 01:53:32 +0100 Subject: [PATCH 124/404] Fix iFightAttack and iFightAttacking being swapped Looks like the research wiki page was incorrect, the higher value (iFightAttack) being for the victim makes more sense, is consistent with iDispAttackMod/fDispAttacking, and seems to be how the original game behaves as well. --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 82b0d556db..f885385074 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1041,8 +1041,8 @@ namespace MWMechanics } else if (type == OT_Assault) { - fight = esmStore.get().find("iFightAttack")->getInt(); - fightVictim = esmStore.get().find("iFightAttacking")->getInt(); + fight = esmStore.get().find("iFightAttacking")->getInt(); + fightVictim = esmStore.get().find("iFightAttack")->getInt(); } else if (type == OT_Murder) fight = fightVictim = esmStore.get().find("iFightKilling")->getInt(); From 9e5dfb6e98d0e99cbdeef0a259edcde6c2117c84 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 02:45:37 +0100 Subject: [PATCH 125/404] Update crime system according to research wiki for more accurate attack responses --- apps/openmw/mwbase/mechanicsmanager.hpp | 4 +- .../mwmechanics/mechanicsmanagerimp.cpp | 101 +++++++++++------- .../mwmechanics/mechanicsmanagerimp.hpp | 4 +- 3 files changed, 63 insertions(+), 46 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 41d6c63102..a51e3a301c 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -197,9 +197,7 @@ namespace MWBase virtual void clear() = 0; - /// @param bias Can be used to add an additional aggression bias towards the target, - /// making it more likely for the function to return true. - virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0; + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; /// Resurrects the player if necessary virtual void keepPlayerAlive() = 0; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index f885385074..54d53512a8 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -23,6 +23,33 @@ #include +namespace +{ + + float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2) + { + Ogre::Vector3 pos1 (actor1.getRefData().getPosition().pos); + Ogre::Vector3 pos2 (actor2.getRefData().getPosition().pos); + + float d = pos1.distance(pos2); + + static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( + "iFightDistanceBase")->getInt(); + static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find( + "fFightDistanceMultiplier")->getFloat(); + + return (iFightDistanceBase - fFightDistanceMultiplier * d); + } + + float getFightDispositionBias(float disposition) + { + static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get().find( + "fFightDispMult")->getFloat(); + return ((50.f - disposition) * fFightDispMult); + } + +} + namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -983,32 +1010,32 @@ namespace MWMechanics if (type == OT_Murder && !victim.isEmpty()) victim.getClass().getCreatureStats(victim).notifyMurder(); - // Bounty for each type of crime - float dispTerm = 0.f, dispTermVictim = 0.f; + // Bounty and disposition penalty for each type of crime + float disp = 0.f, dispVictim = 0.f; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) { arg = store.find("iCrimeTresspass")->getInt(); - dispTerm = dispTermVictim = store.find("iDispTresspass")->getInt(); + disp = dispVictim = store.find("iDispTresspass")->getInt(); } else if (type == OT_Pickpocket) { arg = store.find("iCrimePickPocket")->getInt(); - dispTerm = dispTermVictim = store.find("fDispPickPocketMod")->getFloat(); + disp = dispVictim = store.find("fDispPickPocketMod")->getFloat(); } else if (type == OT_Assault) { arg = store.find("iCrimeAttack")->getInt(); - dispTerm = store.find("fDispAttacking")->getFloat(); - dispTermVictim = store.find("iDispAttackMod")->getInt(); + disp = store.find("fDispAttacking")->getFloat(); + dispVictim = store.find("iDispAttackMod")->getInt(); } else if (type == OT_Murder) { arg = store.find("iCrimeKilling")->getInt(); - dispTerm = dispTermVictim = store.find("iDispKilling")->getInt(); + disp = dispVictim = store.find("iDispKilling")->getInt(); } else if (type == OT_Theft) { - dispTerm = dispTermVictim = store.find("fDispStealing")->getFloat() * arg; + disp = dispVictim = store.find("fDispStealing")->getFloat() * arg; arg *= store.find("fCrimeStealing")->getFloat(); arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen } @@ -1057,8 +1084,6 @@ namespace MWMechanics if ( *it == player || !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue; - int aggression = (*it == victim) ? fightVictim : fight; - if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) continue; @@ -1081,30 +1106,41 @@ namespace MWMechanics } else { - int dispChange = (*it == victim) ? dispTermVictim : dispTerm; - NpcStats& observerStats = it->getClass().getNpcStats(*it); - int originalDisposition = observerStats.getBaseDisposition(); - observerStats.setBaseDisposition(originalDisposition+dispChange); + float dispTerm = (*it == victim) ? dispVictim : disp; + + float alarmTerm = 0.01 * it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase(); + if (*it != victim) + dispTerm *= alarmTerm; + + float fightTerm = (*it == victim) ? fightVictim : fight; + fightTerm += getFightDispositionBias(dispTerm); + fightTerm += getFightDistanceBias(*it, player); + if (type != OT_Pickpocket) // type check not in the wiki, but this seems to be needed for MW behaviour + fightTerm *= alarmTerm; - bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression, true); - if (aggressive) + int observerFightRating = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase(); + if (observerFightRating + fightTerm > 100) + fightTerm = 100 - observerFightRating; + fightTerm = std::max(0.f, fightTerm); + + if (observerFightRating + fightTerm >= 100) { startCombat(*it, player); + NpcStats& observerStats = it->getClass().getNpcStats(*it); // Apply aggression value to the base Fight rating, so that the actor can continue fighting // after a Calm spell wears off - int fightBase = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase(); - it->getClass().getCreatureStats(*it).setAiSetting(CreatureStats::AI_Fight, fightBase + aggression); + observerStats.setAiSetting(CreatureStats::AI_Fight, observerFightRating + fightTerm); + + observerStats.setBaseDisposition(observerStats.getBaseDisposition()+dispTerm); // Set the crime ID, which we will use to calm down participants // once the bounty has been paid. - it->getClass().getNpcStats(*it).setCrimeId(id); + observerStats.setCrimeId(id); // Mark as Alarmed for dialogue - it->getClass().getCreatureStats(*it).setAlarmed(true); + observerStats.setAlarmed(true); } - else - observerStats.setBaseDisposition(originalDisposition); } } @@ -1318,30 +1354,15 @@ namespace MWMechanics mActors.clear(); } - bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias, bool ignoreDistance) + bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) { - Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); - Ogre::Vector3 pos2 (target.getRefData().getPosition().pos); - - float d = 0; - if (!ignoreDistance) - d = pos1.distance(pos2); - - static int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( - "iFightDistanceBase")->getInt(); - static float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find( - "fFightDistanceMultiplier")->getFloat(); - static float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fFightDispMult")->getFloat(); - int disposition = 50; if (ptr.getClass().isNpc()) disposition = getDerivedDisposition(ptr); int fight = std::max(0.f, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() - + (iFightDistanceBase - fFightDistanceMultiplier * d) - + ((50 - disposition) * fFightDispMult)) - + bias; + + getFightDistanceBias(ptr, target) + + getFightDispositionBias(disposition)); if (ptr.getClass().isNpc() && target.getClass().isNpc()) { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 74b4247faf..fc9c589746 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -162,9 +162,7 @@ namespace MWMechanics virtual void clear(); - /// @param bias Can be used to add an additional aggression bias towards the target, - /// making it more likely for the function to return true. - virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false); + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); virtual void keepPlayerAlive(); From 8bc7eb5530b0df34d691061440cfafc434490e76 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 15:29:20 +0100 Subject: [PATCH 126/404] PlayGroup: Don't loop animations with no loop keys (Fixes #2223) --- apps/openmw/mwmechanics/character.cpp | 4 ++-- apps/openmw/mwrender/animation.cpp | 18 +++++++++++++----- apps/openmw/mwrender/animation.hpp | 9 ++++++--- apps/openmw/mwrender/characterpreview.cpp | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6f092f5a49..e553b6cdcd 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -325,7 +325,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mCurrentIdle = idle; if(!mCurrentIdle.empty()) mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false, - 1.0f, "start", "stop", 0.0f, ~0ul); + 1.0f, "start", "stop", 0.0f, ~0ul, true); } updateIdleStormState(); @@ -1289,7 +1289,7 @@ bool CharacterController::updateWeaponState() { mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0), true); } else if (mAnimation->isPlaying("torch")) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ecaaba0b9d..213fd5f614 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -613,7 +613,7 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi mAccumRoot->setPosition(-off); } -bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) +bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback) { // Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two // separate walkforward keys, and the last one is supposed to be used. @@ -654,8 +654,16 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s return false; state.mStartTime = startkey->first; - state.mLoopStartTime = startkey->first; - state.mLoopStopTime = stopkey->first; + if (loopfallback) + { + state.mLoopStartTime = startkey->first; + state.mLoopStopTime = stopkey->first; + } + else + { + state.mLoopStartTime = startkey->first; + state.mLoopStopTime = std::numeric_limits::max(); + } state.mStopTime = stopkey->first; state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint); @@ -850,7 +858,7 @@ void Animation::stopLooping(const std::string& groupname) } } -void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops) +void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback) { if(!mSkelBase || mAnimSources.empty()) return; @@ -886,7 +894,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo for(;iter != mAnimSources.rend();++iter) { const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys; - if(reset(state, textkeys, groupname, start, stop, startpoint)) + if(reset(state, textkeys, groupname, start, stop, startpoint, loopfallback)) { state.mSource = *iter; state.mSpeedMult = speedmult; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 1a420582cd..7120690362 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -173,7 +173,7 @@ protected: */ bool reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, - float startpoint); + float startpoint, bool loopfallback); void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key, const NifOgre::TextKeyMap& map); @@ -255,11 +255,14 @@ public: * at the start marker, 1 starts at the stop marker. * \param loops How many times to loop the animation. This will use the * "loop start" and "loop stop" markers if they exist, - * otherwise it will use "start" and "stop". + * otherwise it may fall back to "start" and "stop", but only if + * the \a loopFallback parameter is true. + * \param loopFallback Allow looping an animation that has no loop keys, i.e. fall back to use + * the "start" and "stop" keys for looping? */ void play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, - float startpoint, size_t loops); + float startpoint, size_t loops, bool loopfallback=false); /** If the given animation group is currently playing, set its remaining loop count to '0'. */ diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 8d693e9668..57b4c4f68c 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -242,7 +242,7 @@ namespace MWRender { if(!mAnimation->getInfo("torch")) mAnimation->play("torch", 2, MWRender::Animation::Group_LeftArm, false, - 1.0f, "start", "stop", 0.0f, ~0ul); + 1.0f, "start", "stop", 0.0f, ~0ul, true); } else if(mAnimation->getInfo("torch")) mAnimation->disable("torch"); From 42d63a4eb233065427cf4450a15d6080116d98ad Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 15:33:37 +0100 Subject: [PATCH 127/404] Fix position flicker after an animation ends --- apps/openmw/mwrender/animation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 213fd5f614..4894378c7e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1002,6 +1002,9 @@ void Animation::resetActiveGroups() break; } } + + if (mAccumRoot && mNonAccumCtrl) + mAccumRoot->setPosition(-mNonAccumCtrl->getTranslation(state->second.mTime)*mAccumulate); } From edc128572dfb0bca5681d575588f76e07636fb7a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 16:45:30 +0100 Subject: [PATCH 128/404] Add MWMechanics::Actor class for temporary actor state, move AiState there --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/actor.cpp | 28 ++++++ apps/openmw/mwmechanics/actor.hpp | 42 +++++++++ apps/openmw/mwmechanics/actors.cpp | 88 ++++++++++--------- apps/openmw/mwmechanics/actors.hpp | 11 +-- apps/openmw/mwmechanics/character.hpp | 6 -- .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- 7 files changed, 124 insertions(+), 55 deletions(-) create mode 100644 apps/openmw/mwmechanics/actor.cpp create mode 100644 apps/openmw/mwmechanics/actor.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 28eadc5172..c645e1a0a2 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -76,7 +76,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting - disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction + disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwmechanics/actor.cpp b/apps/openmw/mwmechanics/actor.cpp new file mode 100644 index 0000000000..675bd160a3 --- /dev/null +++ b/apps/openmw/mwmechanics/actor.cpp @@ -0,0 +1,28 @@ +#include "actor.hpp" + +#include "character.hpp" + +namespace MWMechanics +{ + + Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation) + { + mCharacterController.reset(new CharacterController(ptr, animation)); + } + + void Actor::updatePtr(const MWWorld::Ptr &newPtr) + { + mCharacterController->updatePtr(newPtr); + } + + CharacterController* Actor::getCharacterController() + { + return mCharacterController.get(); + } + + AiState& Actor::getAiState() + { + return mAiState; + } + +} diff --git a/apps/openmw/mwmechanics/actor.hpp b/apps/openmw/mwmechanics/actor.hpp new file mode 100644 index 0000000000..846af14671 --- /dev/null +++ b/apps/openmw/mwmechanics/actor.hpp @@ -0,0 +1,42 @@ +#ifndef OPENMW_MECHANICS_ACTOR_H +#define OPENMW_MECHANICS_ACTOR_H + +#include + +#include "aistate.hpp" + +namespace MWRender +{ + class Animation; +} +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + class CharacterController; + + /// @brief Holds temporary state for an actor that will be discarded when the actor leaves the scene. + class Actor + { + public: + Actor(const MWWorld::Ptr& ptr, MWRender::Animation* animation); + + /// Notify this actor of its new base object Ptr, use when the object changed cells + void updatePtr(const MWWorld::Ptr& newPtr); + + CharacterController* getCharacterController(); + + AiState& getAiState(); + + private: + std::auto_ptr mCharacterController; + + AiState mAiState; + }; + +} + +#endif diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f824fd6f4a..1a1d8c1547 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -35,6 +35,8 @@ #include "aifollow.hpp" #include "aipursue.hpp" +#include "actor.hpp" + namespace { @@ -920,10 +922,10 @@ namespace MWMechanics void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) { - PtrControllerMap::iterator it = mActors.find(ptr); + PtrActorMap::iterator it = mActors.find(ptr); if (it == mActors.end()) return; - CharacterController* ctrl = it->second; + CharacterController* ctrl = it->second->getCharacterController(); NpcStats &stats = ptr.getClass().getNpcStats(ptr); MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -1128,14 +1130,14 @@ namespace MWMechanics removeActor(ptr); MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); - mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); + mActors.insert(std::make_pair(ptr, new Actor(ptr, anim))); if (updateImmediately) - mActors[ptr]->update(0); + mActors[ptr]->getCharacterController()->update(0); } void Actors::removeActor (const MWWorld::Ptr& ptr) { - PtrControllerMap::iterator iter = mActors.find(ptr); + PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) { delete iter->second; @@ -1145,20 +1147,20 @@ namespace MWMechanics void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) { - PtrControllerMap::iterator iter = mActors.find(old); + PtrActorMap::iterator iter = mActors.find(old); if(iter != mActors.end()) { - CharacterController *ctrl = iter->second; + Actor *actor = iter->second; mActors.erase(iter); - ctrl->updatePtr(ptr); - mActors.insert(std::make_pair(ptr, ctrl)); + actor->updatePtr(ptr); + mActors.insert(std::make_pair(ptr, actor)); } } void Actors::dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore) { - PtrControllerMap::iterator iter = mActors.begin(); + PtrActorMap::iterator iter = mActors.begin(); while(iter != mActors.end()) { if(iter->first.getCell()==cellStore && iter->first != ignore) @@ -1192,8 +1194,10 @@ namespace MWMechanics // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) const float sqrProcessingDistance = 7168*7168; + /// \todo move update logic to Actor class where appropriate + // AI and magic effects update - for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { @@ -1207,7 +1211,7 @@ namespace MWMechanics if (iter->first != player) adjustCommandedActor(iter->first); - for(PtrControllerMap::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 continue; @@ -1219,13 +1223,13 @@ namespace MWMechanics float sqrHeadTrackDistance = std::numeric_limits::max(); MWWorld::Ptr headTrackTarget; - for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it) + for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { if (it->first == iter->first) continue; updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance); } - iter->second->setHeadTrackTarget(headTrackTarget); + iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); } if (iter->first.getClass().isNpc() && iter->first != player) @@ -1254,12 +1258,12 @@ namespace MWMechanics // Reaching the text keys may trigger Hit / Spellcast (and as such, particles), // so updating VFX immediately after that would just remove the particle effects instantly. // There needs to be a magic effect update in between. - for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) - iter->second->updateContinuousVfx(); + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + iter->second->getCharacterController()->updateContinuousVfx(); // Animation/movement update CharacterController* playerCharacter = NULL; - for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (iter->first != player && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(iter->first.getRefData().getPosition().pos)) @@ -1268,22 +1272,22 @@ namespace MWMechanics if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( ESM::MagicEffect::Paralyze).getMagnitude() > 0) - iter->second->skipAnim(); + iter->second->getCharacterController()->skipAnim(); // Handle player last, in case a cell transition occurs by casting a teleportation spell // (would invalidate the iterator) if (iter->first.getCellRef().getRefId() == "player") { - playerCharacter = iter->second; + playerCharacter = iter->second->getCharacterController(); continue; } - iter->second->update(duration); + iter->second->getCharacterController()->update(duration); } if (playerCharacter) playerCharacter->update(duration); - for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); @@ -1341,7 +1345,7 @@ namespace MWMechanics bool detected = false; - for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + for (PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (iter->first == player) // not the player continue; @@ -1384,32 +1388,32 @@ namespace MWMechanics void Actors::killDeadActors() { - for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); if(!stats.isDead()) { - if(iter->second->isDead()) + if(iter->second->getCharacterController()->isDead()) { // Actor has been resurrected. Notify the CharacterController and re-enable collision. MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true); - iter->second->resurrect(); + iter->second->getCharacterController()->resurrect(); } if(!stats.isDead()) continue; } - if (iter->second->kill()) + if (iter->second->getCharacterController()->kill()) { iter->first.getClass().getCreatureStats(iter->first).notifyDied(); ++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())]; // Make sure spell effects with CasterLinked flag are removed - for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) + for (PtrActorMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) { MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); spells.purge(stats.getActorId()); @@ -1438,7 +1442,7 @@ namespace MWMechanics void Actors::restoreDynamicStats(bool sleep) { - for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) restoreDynamicStats(iter->first, sleep); } @@ -1470,35 +1474,35 @@ namespace MWMechanics void Actors::forceStateUpdate(const MWWorld::Ptr & ptr) { - PtrControllerMap::iterator iter = mActors.find(ptr); + PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second->forceStateUpdate(); + iter->second->getCharacterController()->forceStateUpdate(); } void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { - PtrControllerMap::iterator iter = mActors.find(ptr); + PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second->playGroup(groupName, mode, number); + iter->second->getCharacterController()->playGroup(groupName, mode, number); } void Actors::skipAnimation(const MWWorld::Ptr& ptr) { - PtrControllerMap::iterator iter = mActors.find(ptr); + PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second->skipAnim(); + iter->second->getCharacterController()->skipAnim(); } bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) { - PtrControllerMap::iterator iter = mActors.find(ptr); + PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - return iter->second->isAnimPlaying(groupName); + return iter->second->getCharacterController()->isAnimPlaying(groupName); return false; } void Actors::getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out) { - for (PtrControllerMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) + for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(position) <= radius*radius) out.push_back(iter->first); @@ -1508,7 +1512,7 @@ namespace MWMechanics std::list Actors::getActorsFollowing(const MWWorld::Ptr& actor) { std::list list; - for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); @@ -1538,7 +1542,7 @@ namespace MWMechanics std::list Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor) { std::list list; - for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); @@ -1611,7 +1615,7 @@ namespace MWMechanics void Actors::clear() { - PtrControllerMap::iterator it(mActors.begin()); + PtrActorMap::iterator it(mActors.begin()); for (; it != mActors.end(); ++it) { delete it->second; @@ -1631,10 +1635,10 @@ namespace MWMechanics bool Actors::isReadyToBlock(const MWWorld::Ptr &ptr) const { - PtrControllerMap::const_iterator it = mActors.find(ptr); + PtrActorMap::const_iterator it = mActors.find(ptr); if (it == mActors.end()) return false; - return it->second->isReadyToBlock(); + return it->second->getCharacterController()->isReadyToBlock(); } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 321229571f..91bfad1708 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -6,7 +6,6 @@ #include #include -#include "character.hpp" #include "movement.hpp" #include "../mwbase/world.hpp" @@ -23,6 +22,8 @@ namespace MWWorld namespace MWMechanics { + class Actor; + class Actors { std::map mDeathCount; @@ -51,10 +52,10 @@ namespace MWMechanics Actors(); ~Actors(); - typedef std::map PtrControllerMap; + typedef std::map PtrActorMap; - PtrControllerMap::const_iterator begin() { return mActors.begin(); } - PtrControllerMap::const_iterator end() { return mActors.end(); } + PtrActorMap::const_iterator begin() { return mActors.begin(); } + PtrActorMap::const_iterator end() { return mActors.end(); } /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) @@ -131,7 +132,7 @@ namespace MWMechanics bool isReadyToBlock(const MWWorld::Ptr& ptr) const; private: - PtrControllerMap mActors; + PtrActorMap mActors; }; } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 5b2c57b0a9..8a77494b97 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -6,7 +6,6 @@ #include #include "../mwworld/ptr.hpp" -#include "aistate.hpp" namespace MWWorld { @@ -140,9 +139,6 @@ class CharacterController MWWorld::Ptr mPtr; MWRender::Animation *mAnimation; - // - AiState mAiState; - typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; @@ -229,8 +225,6 @@ public: void forceStateUpdate(); - AiState& getAiState() { return mAiState; } - bool isReadyToBlock() const; bool isKnockedOut() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 54d53512a8..20c52e7b4e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1289,7 +1289,7 @@ namespace MWMechanics // if guard starts combat with player, guards pursuing player should do the same if (ptr.getClass().isClass(ptr, "Guard")) { - for (Actors::PtrControllerMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter) + for (Actors::PtrActorMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { if (iter->first.getClass().isClass(iter->first, "Guard")) { From e5de253169f529c9e81ccabc3690f5ca4f05647a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 16:56:14 +0100 Subject: [PATCH 129/404] Use maximum step size of 62 units for stepping down (Fixes #1809) --- apps/openmw/mwworld/physicssystem.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 3879e0cd0d..af1191ad98 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -93,7 +93,9 @@ namespace MWWorld { static const float sMaxSlope = 49.0f; - static const float sStepSize = 32.0f; + static const float sStepSizeUp = 34.0f; + static const float sStepSizeDown = 62.0f; + // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. static const int sMaxIterations = 8; @@ -155,7 +157,7 @@ namespace MWWorld */ OEngine::Physic::ActorTracer tracer, stepper; - stepper.doTrace(colobj, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), engine); + stepper.doTrace(colobj, position, position+Ogre::Vector3(0.0f,0.0f,sStepSizeUp), engine); if(stepper.mFraction < std::numeric_limits::epsilon()) return false; // didn't even move the smallest representable amount // (TODO: shouldn't this be larger? Why bother with such a small amount?) @@ -178,7 +180,7 @@ namespace MWWorld return false; // didn't even move the smallest representable amount /* - * Try moving back down sStepSize using stepper. + * Try moving back down sStepSizeDown using stepper. * NOTE: if there is an obstacle below (e.g. stairs), we'll be "stepping up". * Below diagram is the case where we "stepped over" an obstacle in front. * @@ -192,7 +194,7 @@ namespace MWWorld * +--+ +--+ * ============================================== */ - stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-Ogre::Vector3(0.0f,0.0f,sStepSize), engine); + stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-Ogre::Vector3(0.0f,0.0f,sStepSizeDown), engine); if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) { // don't allow stepping up other actors @@ -453,7 +455,7 @@ namespace MWWorld { Ogre::Vector3 from = newPosition; Ogre::Vector3 to = newPosition - (physicActor->getOnGround() ? - Ogre::Vector3(0,0,sStepSize+2.f) : Ogre::Vector3(0,0,2.f)); + Ogre::Vector3(0,0,sStepSizeDown+2.f) : Ogre::Vector3(0,0,2.f)); tracer.doTrace(colobj, from, to, engine); if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != OEngine::Physic::CollisionType_Actor) From 019cd96719b4338415d1913aa936215b5b80d227 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Dec 2014 17:34:34 +0100 Subject: [PATCH 130/404] Stop AiPursue when target has invisibility or chameleon>=75 --- apps/openmw/mwmechanics/aipursue.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 0c3de96432..8c31a10db6 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -43,6 +43,10 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, AiState& state, float duratio ) return true; //Target doesn't exist + if (target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0 + || target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 75) + return true; + if(target.getClass().getCreatureStats(target).isDead()) return true; From d55fe43fc95bfaec681c65b57aff378e6703f894 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Dec 2014 18:50:24 +0100 Subject: [PATCH 131/404] Support animation groups for Light and Door objects (Fixes #2039) --- apps/openmw/mwclass/door.cpp | 8 +++++-- apps/openmw/mwclass/light.cpp | 9 +++++--- apps/openmw/mwrender/activatoranimation.cpp | 25 +++++++++++++++++++++ apps/openmw/mwrender/activatoranimation.hpp | 3 +++ apps/openmw/mwrender/actors.cpp | 11 ++++++++- apps/openmw/mwrender/actors.hpp | 2 +- apps/openmw/mwrender/animation.cpp | 17 -------------- apps/openmw/mwrender/animation.hpp | 3 --- apps/openmw/mwrender/objects.cpp | 10 +-------- apps/openmw/mwrender/objects.hpp | 2 +- 10 files changed, 53 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index fa9db9e160..41f9f36862 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -8,6 +8,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" @@ -22,7 +23,7 @@ #include "../mwgui/tooltips.hpp" -#include "../mwrender/objects.hpp" +#include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" namespace @@ -51,7 +52,8 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - renderingInterface.getObjects().insertModel(ptr, model); + MWRender::Actors& actors = renderingInterface.getActors(); + actors.insertActivator(ptr); } } @@ -70,6 +72,8 @@ namespace MWClass MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState); } } + + MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Door::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index e6d266de25..7ad81232d5 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -8,6 +8,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -22,6 +23,7 @@ #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" namespace @@ -54,13 +56,12 @@ namespace MWClass void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); - MWWorld::LiveCellRef *ref = ptr.get(); // Insert even if model is empty, so that the light is added - renderingInterface.getObjects().insertModel(ptr, model, false, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)); + MWRender::Actors& actors = renderingInterface.getActors(); + actors.insertActivator(ptr, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)); } void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const @@ -78,6 +79,8 @@ namespace MWClass MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + + MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Light::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 540876a3ec..4c63e2cf29 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -1,5 +1,8 @@ #include "activatoranimation.hpp" +#include +#include + #include #include "../mwbase/world.hpp" @@ -27,6 +30,28 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) addAnimSource(model); } + else + { + // No model given. Create an object root anyway, so that lights can be added to it if needed. + mObjectRoot = NifOgre::ObjectScenePtr (new NifOgre::ObjectScene(mInsert->getCreator())); + } +} + +void ActivatorAnimation::addLight(const ESM::Light *light) +{ + addExtraLight(mInsert->getCreator(), mObjectRoot, light); +} + +void ActivatorAnimation::removeParticles() +{ + for (unsigned int i=0; imParticles.size(); ++i) + { + // Don't destroyParticleSystem, the ParticleSystemController is still holding a pointer to it. + // Don't setVisible, this could conflict with a VisController. + // The following will remove all spawned particles, then set the speed factor to zero so that no new ones will be spawned. + mObjectRoot->mParticles[i]->setSpeedFactor(0.f); + mObjectRoot->mParticles[i]->clear(); + } } } diff --git a/apps/openmw/mwrender/activatoranimation.hpp b/apps/openmw/mwrender/activatoranimation.hpp index eb3e5815e7..ec0114ccd2 100644 --- a/apps/openmw/mwrender/activatoranimation.hpp +++ b/apps/openmw/mwrender/activatoranimation.hpp @@ -15,6 +15,9 @@ namespace MWRender public: ActivatorAnimation(const MWWorld::Ptr& ptr); virtual ~ActivatorAnimation(); + + void addLight(const ESM::Light *light); + void removeParticles(); }; } diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index b7e9f5730c..06acf4b3c5 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -83,10 +83,19 @@ void Actors::insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields) mAllActors[ptr] = anim; mRendering->addWaterRippleEmitter (ptr); } -void Actors::insertActivator (const MWWorld::Ptr& ptr) +void Actors::insertActivator (const MWWorld::Ptr& ptr, bool addLight) { insertBegin(ptr); ActivatorAnimation* anim = new ActivatorAnimation(ptr); + + if(ptr.getTypeName() == typeid(ESM::Light).name()) + { + if (addLight) + anim->addLight(ptr.get()->mBase); + else + anim->removeParticles(); + } + delete mAllActors[ptr]; mAllActors[ptr] = anim; } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index d5d6c52bb0..177c8a732a 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -41,7 +41,7 @@ namespace MWRender void insertNPC(const MWWorld::Ptr& ptr); void insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields); - void insertActivator (const MWWorld::Ptr& ptr); + void insertActivator (const MWWorld::Ptr& ptr, bool addLight=false); bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 4894378c7e..af9fdf8531 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1507,23 +1507,6 @@ ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &mod } } -void ObjectAnimation::addLight(const ESM::Light *light) -{ - addExtraLight(mInsert->getCreator(), mObjectRoot, light); -} - -void ObjectAnimation::removeParticles() -{ - for (unsigned int i=0; imParticles.size(); ++i) - { - // Don't destroyParticleSystem, the ParticleSystemController is still holding a pointer to it. - // Don't setVisible, this could conflict with a VisController. - // The following will remove all spawned particles, then set the speed factor to zero so that no new ones will be spawned. - mObjectRoot->mParticles[i]->setSpeedFactor(0.f); - mObjectRoot->mParticles[i]->clear(); - } -} - class FindEntityTransparency { public: diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 7120690362..73d10fd06c 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -346,9 +346,6 @@ class ObjectAnimation : public Animation { public: ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model); - void addLight(const ESM::Light *light); - void removeParticles(); - bool canBatch() const; void fillBatch(Ogre::StaticGeometry *sg); }; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 96decbb360..96cce178e8 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -73,20 +73,12 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr) ptr.getRefData().setBaseNode(insert); } -void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch, bool addLight) +void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch) { insertBegin(ptr); std::auto_ptr anim(new ObjectAnimation(ptr, mesh)); - if(ptr.getTypeName() == typeid(ESM::Light).name()) - { - if (addLight) - anim->addLight(ptr.get()->mBase); - else - anim->removeParticles(); - } - if (!mesh.empty()) { Ogre::AxisAlignedBox bounds = anim->getWorldBounds(); diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 7f740dbab0..02e974e2d8 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -41,7 +41,7 @@ public: , mRootNode(NULL) {} ~Objects(){} - void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false, bool addLight=false); + void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false); ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr); From 59f21c61058e6966566440649aa3dc9f16d4e2bd Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Dec 2014 19:58:18 +0100 Subject: [PATCH 132/404] Use "hair" as filter for PRT_Hair parts (Fixes #2218) --- apps/openmw/mwrender/animation.cpp | 2 +- apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 11 +++++++---- apps/openmw/mwrender/npcanimation.hpp | 1 + apps/openmw/mwrender/weaponanimation.cpp | 7 +++++-- components/nifogre/ogrenifloader.cpp | 9 ++++----- components/nifogre/ogrenifloader.hpp | 1 + 7 files changed, 20 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index af9fdf8531..a922fa170e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1270,7 +1270,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con if (bonename.empty()) params.mObjects = NifOgre::Loader::createObjects(mInsert, model); else - params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, "", mInsert, model); // TODO: turn off shadow casting setRenderProperties(params.mObjects, RV_Misc, diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index fef9fa644f..c1957d7a8f 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -106,7 +106,7 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo else bonename = "Shield Bone"; - scene = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, item.getClass().getModel(item)); + scene = NifOgre::Loader::createObjects(mSkelBase, bonename, bonename, mInsert, item.getClass().getModel(item)); Ogre::Vector3 glowColor = getEnchantmentColor(item); setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0, diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 3b0b4e08b2..1cc9ad2325 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -160,7 +160,7 @@ static NpcAnimation::PartBoneMap createPartListMap() { NpcAnimation::PartBoneMap result; result.insert(std::make_pair(ESM::PRT_Head, "Head")); - result.insert(std::make_pair(ESM::PRT_Hair, "Head")); + result.insert(std::make_pair(ESM::PRT_Hair, "Head")); // note it uses "Head" as attach bone, but "Hair" as filter result.insert(std::make_pair(ESM::PRT_Neck, "Neck")); result.insert(std::make_pair(ESM::PRT_Cuirass, "Chest")); result.insert(std::make_pair(ESM::PRT_Groin, "Groin")); @@ -574,9 +574,9 @@ public: } }; -NifOgre::ObjectScenePtr NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename, bool enchantedGlow, Ogre::Vector3* glowColor) +NifOgre::ObjectScenePtr NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename, const std::string &bonefilter, bool enchantedGlow, Ogre::Vector3* glowColor) { - NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(mSkelBase, bonename, bonefilter, mInsert, model); setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, enchantedGlow, glowColor); @@ -690,7 +690,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g mPartPriorities[type] = priority; try { - mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type), enchantedGlow, glowColor); + const std::string& bonename = sPartList.at(type); + // PRT_Hair seems to be the only type that breaks consistency and uses a filter that's different from the attachment bone + const std::string bonefilter = (type == ESM::PRT_Hair) ? "hair" : bonename; + mObjectParts[type] = insertBoundedPart(mesh, group, bonename, bonefilter, enchantedGlow, glowColor); } catch (std::exception& e) { diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index f3603fe14b..90b1c269bf 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -106,6 +106,7 @@ private: void updateNpcBase(); NifOgre::ObjectScenePtr insertBoundedPart(const std::string &model, int group, const std::string &bonename, + const std::string &bonefilter, bool enchantedGlow, Ogre::Vector3* glowColor=NULL); void removeIndividualPart(ESM::PartReferenceType type); diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index c59a93feba..7a52ce7ead 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -55,8 +55,11 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor) return; std::string model = ammo->getClass().getModel(*ammo); - assert(weapon->mSkelBase && "Need a skeleton to attach the arrow to"); - mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", weapon->mSkelBase->getParentSceneNode(), model); + if (!weapon->mSkelBase) + throw std::runtime_error("Need a skeleton to attach the arrow to"); + + const std::string bonename = "ArrowBone"; + mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, bonename, bonename, weapon->mSkelBase->getParentSceneNode(), model); configureAddedObject(mAmmunition, *ammo, MWWorld::InventoryStore::Slot_Ammunition); } } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index ef4b9e9853..b552487841 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1383,6 +1383,7 @@ ObjectScenePtr Loader::createObjects(Ogre::SceneNode *parentNode, std::string na } ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bonename, + const std::string& bonefilter, Ogre::SceneNode *parentNode, std::string name, const std::string &group) { @@ -1408,11 +1409,9 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo if(isskinned) { - // Apparently both are allowed. Sigh. - // This could also mean that filters are supposed to work on the actual node - // hierarchy, rather than just trishapes, and the 'tri ' should be omitted? - std::string filter = "@shape=tri "+bonename; - std::string filter2 = "@shape="+bonename; + // accepts anything named "filter*" or "tri filter*" + std::string filter = "@shape=tri "+bonefilter; + std::string filter2 = "@shape="+bonefilter; Misc::StringUtils::toLower(filter); Misc::StringUtils::toLower(filter2); for(size_t i = 0;i < scene->mEntities.size();i++) diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 485495a388..fd5ddca7d0 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -97,6 +97,7 @@ class Loader { public: static ObjectScenePtr createObjects(Ogre::Entity *parent, const std::string &bonename, + const std::string& filter, Ogre::SceneNode *parentNode, std::string name, const std::string &group="General"); From 7f0d71f8f49c77cf3bccde32b2e8fd2600157de7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Dec 2014 23:58:16 +0100 Subject: [PATCH 133/404] Swap use of iDispAttackMod/fDispAttacking (thanks Hrnchamd) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 20c52e7b4e..5c4422fa14 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1025,8 +1025,8 @@ namespace MWMechanics else if (type == OT_Assault) { arg = store.find("iCrimeAttack")->getInt(); - disp = store.find("fDispAttacking")->getFloat(); - dispVictim = store.find("iDispAttackMod")->getInt(); + disp = store.find("iDispAttackMod")->getInt(); + dispVictim = store.find("fDispAttacking")->getFloat(); } else if (type == OT_Murder) { From a47de06492b2c671ec3b344f53be97b39d6aa107 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Dec 2014 01:54:37 +0100 Subject: [PATCH 134/404] Make local map resolution configurable and use lower default value This seems to be the resolution the original engine is using. The change also significantly reduces cell loading time. --- apps/openmw/mwrender/globalmap.cpp | 6 ++++-- apps/openmw/mwrender/localmap.cpp | 5 +++-- apps/openmw/mwrender/localmap.hpp | 2 +- files/settings-default.cfg | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index fd8b919367..5546c401ba 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -209,8 +209,10 @@ namespace MWRender if (!localMapTexture.isNull()) { + int mapWidth = localMapTexture->getWidth(); + int mapHeight = localMapTexture->getHeight(); mOverlayTexture->load(); - mOverlayTexture->getBuffer()->blit(localMapTexture->getBuffer(), Ogre::Image::Box(0,0,512,512), + mOverlayTexture->getBuffer()->blit(localMapTexture->getBuffer(), Ogre::Image::Box(0,0,mapWidth,mapHeight), Ogre::Image::Box(originX,originY,originX+mCellSize,originY+mCellSize)); Ogre::Image backup; @@ -218,7 +220,7 @@ namespace MWRender data.resize(mCellSize*mCellSize*4, 0); backup.loadDynamicImage(&data[0], mCellSize, mCellSize, Ogre::PF_A8B8G8R8); - localMapTexture->getBuffer()->blitToMemory(Ogre::Image::Box(0,0,512,512), backup.getPixelBox()); + localMapTexture->getBuffer()->blitToMemory(Ogre::Image::Box(0,0,mapWidth,mapHeight), backup.getPixelBox()); for (int x=0; xgetBuffer()->blit(mRenderTexture->getBuffer()); diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 4c60cbb11a..7cfa388146 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -87,7 +87,7 @@ namespace MWRender OEngine::Render::OgreRenderer* mRendering; MWRender::RenderingManager* mRenderingManager; - static const int sMapResolution = 512; + int mMapResolution; // the dynamic texture is a bottleneck, so don't set this too high static const int sFogOfWarResolution = 32; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 7566994e29..658c2da6dd 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -115,6 +115,8 @@ use static geometry = true # Adjusts the scale of the global map global map cell size = 18 +local map resolution = 256 + [Viewing distance] # Limit the rendering distance of small objects limit small object distance = false From 5d7dcafa53ef12f80c9249787ddfb72401dafd3d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Dec 2014 02:33:14 +0100 Subject: [PATCH 135/404] Make local map widget size configurable --- apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 38 ++++++++++++++-------------- apps/openmw/mwgui/mapwindow.hpp | 4 ++- files/mygui/openmw_hud.layout | 2 -- files/mygui/openmw_map_window.layout | 2 -- files/settings-default.cfg | 3 +++ 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index a5703682f3..2fd6dd6b94 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -162,7 +162,7 @@ namespace MWGui getWidget(mTriangleCounter, "TriangleCounter"); getWidget(mBatchCounter, "BatchCounter"); - LocalMapBase::init(mMinimap, mCompass); + LocalMapBase::init(mMinimap, mCompass, Settings::Manager::getInt("local map hud widget size", "Map")); mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 0262c4d0e4..cc87cdd7b8 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -22,8 +22,6 @@ namespace { - const int widgetSize = 512; - const int cellSize = 8192; enum LocalMapWidgetDepth @@ -164,6 +162,7 @@ namespace MWGui , mCompass(NULL) , mMarkerUpdateTimer(0.0f) , mCustomMarkers(markers) + , mMapWidgetSize(0) { mCustomMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } @@ -173,26 +172,28 @@ namespace MWGui mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } - void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass) + void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize) { mLocalMap = widget; mCompass = compass; + mMapWidgetSize = mapWidgetSize; + + mLocalMap->setCanvasSize(mMapWidgetSize*3, mMapWidgetSize*3); mCompass->setDepth(Local_CompassLayer); mCompass->setNeedMouseFocus(false); - // create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) { MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", - MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), + MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left); map->setDepth(Local_MapLayer); MyGUI::ImageBox* fog = mLocalMap->createWidget("ImageBox", - MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), + MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left); fog->setDepth(Local_FogLayer); @@ -258,8 +259,8 @@ namespace MWGui markerPos.cellX = cellX; markerPos.cellY = cellY; - widgetPos = MyGUI::IntPoint(nX * widgetSize + (1+cellDx) * widgetSize, - nY * widgetSize - (cellDy-1) * widgetSize); + widgetPos = MyGUI::IntPoint(nX * mMapWidgetSize + (1+cellDx) * mMapWidgetSize, + nY * mMapWidgetSize - (cellDy-1) * mMapWidgetSize); } else { @@ -271,8 +272,8 @@ namespace MWGui markerPos.cellY = cellY; // Image space is -Y up, cells are Y up - widgetPos = MyGUI::IntPoint(nX * widgetSize + (1+(cellX-mCurX)) * widgetSize, - nY * widgetSize + (1-(cellY-mCurY)) * widgetSize); + widgetPos = MyGUI::IntPoint(nX * mMapWidgetSize + (1+(cellX-mCurX)) * mMapWidgetSize, + nY * mMapWidgetSize + (1-(cellY-mCurY)) * mMapWidgetSize); } markerPos.nX = nX; @@ -425,9 +426,9 @@ namespace MWGui void LocalMapBase::setPlayerPos(int cellX, int cellY, const float nx, const float ny) { - MyGUI::IntPoint pos(widgetSize+nx*widgetSize-16, widgetSize+ny*widgetSize-16); - pos.left += (cellX - mCurX) * widgetSize; - pos.top -= (cellY - mCurY) * widgetSize; + MyGUI::IntPoint pos(mMapWidgetSize+nx*mMapWidgetSize-16, mMapWidgetSize+ny*mMapWidgetSize-16); + pos.left += (cellX - mCurX) * mMapWidgetSize; + pos.top -= (cellY - mCurY) * mMapWidgetSize; if (pos != mCompass->getPosition()) { @@ -612,8 +613,7 @@ namespace MWGui mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked); - LocalMapBase::init(mLocalMap, mPlayerArrowLocal); - } + LocalMapBase::init(mLocalMap, mPlayerArrowLocal, Settings::Manager::getInt("local map widget size", "Map")); } void MapWindow::onNoteEditOk() { @@ -657,10 +657,10 @@ namespace MWGui MyGUI::IntPoint clickedPos = MyGUI::InputManager::getInstance().getMousePosition(); MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition(); - int x = int(widgetPos.left/float(widgetSize))-1; - int y = (int(widgetPos.top/float(widgetSize))-1)*-1; - float nX = widgetPos.left/float(widgetSize) - int(widgetPos.left/float(widgetSize)); - float nY = widgetPos.top/float(widgetSize) - int(widgetPos.top/float(widgetSize)); + int x = int(widgetPos.left/float(mMapWidgetSize))-1; + int y = (int(widgetPos.top/float(mMapWidgetSize))-1)*-1; + float nX = widgetPos.left/float(mMapWidgetSize) - int(widgetPos.left/float(mMapWidgetSize)); + float nY = widgetPos.top/float(mMapWidgetSize) - int(widgetPos.top/float(mMapWidgetSize)); x += mCurX; y += mCurY; diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index a8aaa8e45f..118fee0f6c 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -70,7 +70,7 @@ namespace MWGui public: LocalMapBase(CustomMarkerCollection& markers); virtual ~LocalMapBase(); - void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass); + void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize); void setCellPrefix(const std::string& prefix); void setActiveCell(const int x, const int y, bool interior=false); @@ -99,6 +99,8 @@ namespace MWGui bool mChanged; bool mFogOfWar; + int mMapWidgetSize; + // Stores markers that were placed by a player. May be shared between multiple map views. CustomMarkerCollection& mCustomMarkers; diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index 640e5867f4..84fd9d247b 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -108,8 +108,6 @@ - - diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index 605c3d6fff..b38097dce5 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -6,8 +6,6 @@ - - diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 658c2da6dd..530366e89e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -117,6 +117,9 @@ global map cell size = 18 local map resolution = 256 +local map widget size = 512 +local map hud widget size = 512 + [Viewing distance] # Limit the rendering distance of small objects limit small object distance = false From 9b33eca36879ad9cc4ae6dc17430510b14c399b4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Dec 2014 02:33:29 +0100 Subject: [PATCH 136/404] Use scaling factor for HUD map to match original MW --- files/settings-default.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 530366e89e..37e3d45fb2 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -118,7 +118,7 @@ global map cell size = 18 local map resolution = 256 local map widget size = 512 -local map hud widget size = 512 +local map hud widget size = 256 [Viewing distance] # Limit the rendering distance of small objects From b8fa73dfa9c27733238f0f71800a018ac7c53cff Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Dec 2014 15:28:02 +0100 Subject: [PATCH 137/404] Preserve record ordering in Store This fixes the default head/hair used for some races in the chargen UI. --- apps/openmw/mwgui/race.cpp | 13 +++++-- apps/openmw/mwworld/store.hpp | 69 ++++++++++++----------------------- 2 files changed, 32 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index bcb766f8f3..925480334d 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -345,8 +345,7 @@ namespace MWGui const MWWorld::Store &races = MWBase::Environment::get().getWorld()->getStore().get(); - - int index = 0; + std::map items; // ID, name MWWorld::Store::iterator it = races.begin(); for (; it != races.end(); ++it) { @@ -354,8 +353,14 @@ namespace MWGui if (!playable) // Only display playable races continue; - mRaceList->addItem(it->mName, it->mId); - if (Misc::StringUtils::ciEqual(it->mId, mCurrentRaceId)) + items[it->mId] = it->mName; + } + + int index = 0; + for (std::map::const_iterator it = items.begin(); it != items.end(); ++it) + { + mRaceList->addItem(it->second, it->first); + if (Misc::StringUtils::ciEqual(it->first, mCurrentRaceId)) mRaceList->setIndexSelected(index); ++index; } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 4fa8b7f543..dcfbb4eb17 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -99,7 +99,9 @@ namespace MWWorld class Store : public StoreBase { std::map mStatic; - std::vector mShared; + std::vector mShared; // Preserves the record order as it came from the content files (this + // is relevant for the spell autocalc code and selection order + // for heads/hairs in the character creation) std::map mDynamic; typedef std::map Dynamic; @@ -137,8 +139,9 @@ namespace MWWorld // setUp needs to be called again after virtual void clearDynamic() { + // remove the dynamic part of mShared + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); mDynamic.clear(); - mShared.clear(); } const T *search(const std::string &id) const { @@ -203,18 +206,18 @@ namespace MWWorld void load(ESM::ESMReader &esm, const std::string &id) { std::string idLower = Misc::StringUtils::lowerCase(id); - mStatic[idLower] = T(); - mStatic[idLower].mId = idLower; - mStatic[idLower].load(esm); + + std::pair inserted = mStatic.insert(std::make_pair(idLower, T())); + if (inserted.second) + mShared.push_back(&inserted.first->second); + + inserted.first->second.mId = idLower; + inserted.first->second.load(esm); } void setUp() { - mShared.clear(); - mShared.reserve(mStatic.size()); - typename std::map::iterator it = mStatic.begin(); - for (; it != mStatic.end(); ++it) { - mShared.push_back(&(it->second)); - } + // remove the dynamic part of mShared + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); } iterator begin() const { @@ -356,10 +359,9 @@ namespace MWWorld std::map::iterator it = mStatic.find(idLower); if (it == mStatic.end()) { it = mStatic.insert( std::make_pair( idLower, ESM::Dialogue() ) ).first; - it->second.mId = id; // don't smash case here, as this line is printed... I think + it->second.mId = id; // don't smash case here, as this line is printed } - //I am not sure is it need to load the dialog from a plugin if it was already loaded from prevois plugins it->second.load(esm); } @@ -368,7 +370,10 @@ namespace MWWorld ESM::Script scpt; scpt.load(esm); Misc::StringUtils::toLower(scpt.mId); - mStatic[scpt.mId] = scpt; + + std::pair inserted = mStatic.insert(std::make_pair(scpt.mId, scpt)); + if (inserted.second) + mShared.push_back(&inserted.first->second); } template <> @@ -376,7 +381,10 @@ namespace MWWorld ESM::StartScript s; s.load(esm); s.mId = Misc::StringUtils::toLower(s.mScript); - mStatic[s.mId] = s; + + std::pair inserted = mStatic.insert(std::make_pair(s.mId, s)); + if (inserted.second) + mShared.push_back(&inserted.first->second); } template <> @@ -1067,37 +1075,6 @@ namespace MWWorld } }; - - // Specialisation for ESM::Spell to preserve record order as it was in the content files. - // The NPC spell autocalc code heavily depends on this order. - // We could also do this in the base class, but it's usually not a good idea to depend on record order. - template<> - inline void Store::clearDynamic() - { - // remove the dynamic part of mShared - mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); - mDynamic.clear(); - } - - template<> - inline void Store::load(ESM::ESMReader &esm, const std::string &id) { - std::string idLower = Misc::StringUtils::lowerCase(id); - - std::pair inserted = mStatic.insert(std::make_pair(idLower, ESM::Spell())); - if (inserted.second) - mShared.push_back(&mStatic[idLower]); - - inserted.first->second.mId = idLower; - inserted.first->second.load(esm); - } - - template<> - inline void Store::setUp() - { - // remove the dynamic part of mShared - mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); - } - template<> inline void Store::setUp() { From 768c4a57577455f1810fd6284d98495a97d57cc5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Dec 2014 16:38:30 +0100 Subject: [PATCH 138/404] Update crime response to pickpocket attempts (thanks Hrnchamd) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5c4422fa14..c0c16eaf9a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1109,14 +1109,16 @@ namespace MWMechanics float dispTerm = (*it == victim) ? dispVictim : disp; float alarmTerm = 0.01 * it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase(); + if (type == OT_Pickpocket && alarmTerm <= 0) + alarmTerm = 1.0; + if (*it != victim) dispTerm *= alarmTerm; float fightTerm = (*it == victim) ? fightVictim : fight; fightTerm += getFightDispositionBias(dispTerm); fightTerm += getFightDistanceBias(*it, player); - if (type != OT_Pickpocket) // type check not in the wiki, but this seems to be needed for MW behaviour - fightTerm *= alarmTerm; + fightTerm *= alarmTerm; int observerFightRating = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase(); if (observerFightRating + fightTerm > 100) From 9a1bde684f16253ec6f274e2d9fa28bf21f896ab Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Dec 2014 19:31:39 +0100 Subject: [PATCH 139/404] Sort class list in select class dialog --- apps/openmw/mwgui/class.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 62167142fa..84474d4a7a 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -9,6 +9,16 @@ #undef min #undef max +namespace +{ + + bool sortClasses(const std::pair& left, const std::pair& right) + { + return left.second.compare(right.second) < 0; + } + +} + namespace MWGui { @@ -129,8 +139,6 @@ namespace MWGui if (Misc::StringUtils::ciEqual(*mClassList->getItemDataAt(i), classId)) { mClassList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); break; } } @@ -165,9 +173,6 @@ namespace MWGui if (_index == MyGUI::ITEM_NONE) return; - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - const std::string *classId = mClassList->getItemDataAt(_index); if (Misc::StringUtils::ciEqual(mCurrentClassId, *classId)) return; @@ -184,7 +189,7 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int index = 0; + std::vector > items; // class id, class name MWWorld::Store::iterator it = store.get().begin(); for (; it != store.get().end(); ++it) { @@ -192,8 +197,15 @@ namespace MWGui if (!playable) // Only display playable classes continue; - const std::string &id = it->mId; - mClassList->addItem(it->mName, id); + items.push_back(std::make_pair(it->mId, it->mName)); + } + std::sort(items.begin(), items.end(), sortClasses); + + int index = 0; + for (std::vector >::const_iterator it = items.begin(); it != items.end(); ++it) + { + const std::string &id = it->first; + mClassList->addItem(it->second, id); if (mCurrentClassId.empty()) { mCurrentClassId = id; From 2e5e7370ba3afbf134a474d16c95e8e11d3a3f3b Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Dec 2014 19:51:17 +0100 Subject: [PATCH 140/404] Adjust default angle, FOV and viewport of character preview (Fixes #2220) --- apps/openmw/mwgui/birth.cpp | 5 ---- apps/openmw/mwgui/race.cpp | 29 +++++++++++++---------- apps/openmw/mwgui/textinput.cpp | 8 +------ apps/openmw/mwrender/characterpreview.cpp | 19 +++++++++------ apps/openmw/mwrender/characterpreview.hpp | 4 ++++ 5 files changed, 34 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index a7f90c00ba..4df95c3bc2 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -80,8 +80,6 @@ namespace MWGui if (Misc::StringUtils::ciEqual(*mBirthList->getItemDataAt(i), birthId)) { mBirthList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); break; } } @@ -116,9 +114,6 @@ namespace MWGui if (_index == MyGUI::ITEM_NONE) return; - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - const std::string *birthId = mBirthList->getItemDataAt(_index); if (Misc::StringUtils::ciEqual(mCurrentBirthId, *birthId)) return; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 925480334d..9c01a39ef5 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -20,6 +20,12 @@ namespace else return index; } + + bool sortRaces(const std::pair& left, const std::pair& right) + { + return left.second.compare(right.second) < 0; + } + } namespace MWGui @@ -122,7 +128,7 @@ namespace MWGui mPreview.reset(new MWRender::RaceSelectionPreview()); mPreview->setup(); - mPreview->update (0); + mPreview->update (mCurrentAngle); const ESM::NPC proto = mPreview->getPrototype(); setRaceId(proto.mRace); @@ -143,8 +149,11 @@ namespace MWGui mPreviewImage->setImageTexture (textureName); mPreviewDirty = true; - } + size_t initialPos = mHeadRotate->getScrollRange()/2+mHeadRotate->getScrollRange()/10; + mHeadRotate->setScrollPosition(initialPos); + onHeadRotate(mHeadRotate, initialPos); + } void RaceDialog::setRaceId(const std::string &raceId) { @@ -156,8 +165,6 @@ namespace MWGui if (Misc::StringUtils::ciEqual(*mRaceList->getItemDataAt(i), raceId)) { mRaceList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); break; } } @@ -191,10 +198,9 @@ namespace MWGui void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position) { float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5) * 3.14 * 2; - float diff = angle - mCurrentAngle; - mPreview->update (diff); + mPreview->update (angle); mPreviewDirty = true; - mCurrentAngle += diff; + mCurrentAngle = angle; } void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) @@ -242,8 +248,6 @@ namespace MWGui if (_index == MyGUI::ITEM_NONE) return; - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); const std::string *raceId = mRaceList->getItemDataAt(_index); if (Misc::StringUtils::ciEqual(mCurrentRaceId, *raceId)) return; @@ -345,7 +349,7 @@ namespace MWGui const MWWorld::Store &races = MWBase::Environment::get().getWorld()->getStore().get(); - std::map items; // ID, name + std::vector > items; // ID, name MWWorld::Store::iterator it = races.begin(); for (; it != races.end(); ++it) { @@ -353,11 +357,12 @@ namespace MWGui if (!playable) // Only display playable races continue; - items[it->mId] = it->mName; + items.push_back(std::make_pair(it->mId, it->mName)); } + std::sort(items.begin(), items.end(), sortRaces); int index = 0; - for (std::map::const_iterator it = items.begin(); it != items.end(); ++it) + for (std::vector >::const_iterator it = items.begin(); it != items.end(); ++it) { mRaceList->addItem(it->second, it->first); if (Misc::StringUtils::ciEqual(it->first, mCurrentRaceId)) diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 954bc41abb..80652b4305 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -61,13 +61,7 @@ namespace MWGui void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) { - if (mTextEdit->getCaption() == "") - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget (mTextEdit); - } - else - eventDone(this); + onOkClicked(_sender); } } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 57b4c4f68c..831efce4fb 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -72,14 +72,15 @@ namespace MWRender l->setDirection (Ogre::Vector3(0.3, -0.7, 0.3)); l->setDiffuseColour (Ogre::ColourValue(1,1,1)); - mSceneMgr->setAmbientLight (Ogre::ColourValue(0.5, 0.5, 0.5)); + mSceneMgr->setAmbientLight (Ogre::ColourValue(0.25, 0.25, 0.25)); mCamera = mSceneMgr->createCamera (mName); + mCamera->setFOVy(Ogre::Degree(12.3)); mCamera->setAspectRatio (float(mSizeX) / float(mSizeY)); Ogre::SceneNode* renderRoot = mSceneMgr->getRootSceneNode()->createChildSceneNode("renderRoot"); - //we do this with mwRoot in renderingManager, do it here too. + // leftover of old coordinate system. TODO: remove this and adjust positions/orientations to match renderRoot->pitch(Ogre::Degree(-90)); mNode = renderRoot->createChildSceneNode(); @@ -91,7 +92,7 @@ namespace MWRender mCamera->setPosition(mPosition * scale); mCamera->lookAt(mLookAt * scale); - mCamera->setNearClipDistance (0.01); + mCamera->setNearClipDistance (1); mCamera->setFarClipDistance (1000); mTexture = Ogre::TextureManager::getSingleton().createManual(mName, @@ -159,7 +160,7 @@ namespace MWRender InventoryPreview::InventoryPreview(MWWorld::Ptr character) - : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) + : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 71, -700), Ogre::Vector3(0,71,0)) , mSelectionBuffer(NULL) , mSizeX(0) , mSizeY(0) @@ -288,9 +289,10 @@ namespace MWRender RaceSelectionPreview::RaceSelectionPreview() : CharacterPreview(MWBase::Environment::get().getWorld()->getPlayerPtr(), - 512, 512, "CharacterHeadPreview", Ogre::Vector3(0, 6, -35), Ogre::Vector3(0,125,0)) + 512, 512, "CharacterHeadPreview", Ogre::Vector3(0, 8, -125), Ogre::Vector3(0,127,0)) , mBase (*mCharacter.get()->mBase) , mRef(&mBase) + , mPitch(Ogre::Degree(6)) { mCharacter = MWWorld::Ptr(&mRef, NULL); } @@ -298,7 +300,9 @@ namespace MWRender void RaceSelectionPreview::update(float angle) { mAnimation->runAnimation(0.0f); - mNode->roll(Ogre::Radian(angle), Ogre::SceneNode::TS_LOCAL); + + mNode->setOrientation(Ogre::Quaternion(Ogre::Radian(angle), Ogre::Vector3::UNIT_Z) + * Ogre::Quaternion(mPitch, Ogre::Vector3::UNIT_X)); updateCamera(); } @@ -317,7 +321,8 @@ namespace MWRender mBase = proto; mBase.mId = "player"; rebuild(); - update(0); + mAnimation->runAnimation(0.0f); + updateCamera(); } void RaceSelectionPreview::onSetup () diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index 711de0d154..80dbe18b4a 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -120,6 +120,10 @@ namespace MWRender } void setPrototype(const ESM::NPC &proto); + + private: + + Ogre::Radian mPitch; }; } From 6f72989cb1afe8cd9dd6cc82ad1f49c2ac0b3ce4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 24 Dec 2014 15:41:48 +0100 Subject: [PATCH 141/404] SpellModel, SortFilterItemModel: case insensitive sorting --- apps/openmw/mwgui/sortfilteritemmodel.cpp | 9 ++++++--- apps/openmw/mwgui/spellmodel.cpp | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 3bb5991615..6c164df888 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -1,5 +1,7 @@ #include "sortfilteritemmodel.hpp" +#include + #include #include #include @@ -52,9 +54,10 @@ namespace if (left.mBase.getTypeName() == right.mBase.getTypeName()) { - int cmp = left.mBase.getClass().getName(left.mBase).compare( - right.mBase.getClass().getName(right.mBase)); - return cmp < 0; + std::string leftName = Misc::StringUtils::lowerCase(left.mBase.getClass().getName(left.mBase)); + std::string rightName = Misc::StringUtils::lowerCase(right.mBase.getClass().getName(right.mBase)); + + return leftName.compare(rightName) < 0; } else return compareType(left.mBase.getTypeName(), right.mBase.getTypeName()); diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 37f0fa5be4..ad9a913fa0 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -21,8 +21,10 @@ namespace if (left.mType != right.mType) return left.mType < right.mType; - int cmp = left.mName.compare(right.mName); - return cmp < 0; + std::string leftName = Misc::StringUtils::lowerCase(left.mName); + std::string rightName = Misc::StringUtils::lowerCase(right.mName); + + return leftName.compare(rightName) < 0; } } From 764cd9ca163746ac5aa4b00a3125b4efe73f6070 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 24 Dec 2014 16:31:23 +0100 Subject: [PATCH 142/404] disable "window border" setting in the ingame settings UI if fullscreen is enabled --- apps/openmw/mwgui/settingswindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 804f023fab..ce2a20d8ba 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -240,6 +240,8 @@ namespace MWGui MyGUI::TextBox* diffText; getWidget(diffText, "DifficultyText"); diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast(int(Settings::Manager::getInt("difficulty", "Game"))) + ")"); + + mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video")); } void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) @@ -355,6 +357,8 @@ namespace MWGui _sender->castType()->setCaption(off); return; } + + mWindowBorderButton->setEnabled(!newState); } if (getSettingType(_sender) == checkButtonType) From 2fa39c45816a7f4dff9834bde170898472991a44 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 25 Dec 2014 14:22:27 +0300 Subject: [PATCH 143/404] Do not install formulae that are already present on Travis instances, let's check if that would make Travis happy --- CI/before_install.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index b1d4f991ba..165763efa6 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -6,4 +6,4 @@ export CC=clang brew tap openmw/openmw brew update brew unlink boost -brew install cmake openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg pkg-config qt unshield +brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg qt unshield From 49e2b14d0554c8cb49c5bc1061231bc33fd1da07 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 26 Dec 2014 16:30:20 +0100 Subject: [PATCH 144/404] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index c3eab721fe..ebb2f8f26b 100644 --- a/credits.txt +++ b/credits.txt @@ -31,6 +31,7 @@ Dmitry Shkurskiy (endorph) Douglas Diniz (Dgdiniz) Douglas Mencken (dougmencken) dreamer-dead +dteviot Edmondo Tommasina (edmondo) Eduard Cot (trombonecot) Eli2 From 2f0793390f9921a43c1188c83bd858af20d5bd75 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 27 Dec 2014 01:25:26 +0100 Subject: [PATCH 145/404] Fix cut off text for some widgets in the stats window --- apps/openmw/mwgui/statswindow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index baa779c1c4..dcf85ddb73 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -342,10 +342,14 @@ namespace MWGui { MyGUI::TextBox* skillNameWidget; - skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); + skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + int textWidth = skillNameWidget->getTextSize().width; + skillNameWidget->setSize(textWidth, skillNameWidget->getHeight()); + mSkillWidgets.push_back(skillNameWidget); coord1.top += sLineHeight; From d92cd2ffad226a9b3d140d546ef549c1b52bb022 Mon Sep 17 00:00:00 2001 From: Marco Schulze Date: Sat, 27 Dec 2014 00:26:35 -0300 Subject: [PATCH 146/404] Remove GetGitRevisionDescription.cmake GetGitRevisionDescription.cmake uses a somewhat contrived method to obtain the hash of the commit pointed by the repository's HEAD. This method fails on unusual, but still valid repository layouts. This commit removes cmake/GetGitRevisionDescription.cmake{,.in}, replacing its functionality with direct use of Git's plumbing. --- CMakeLists.txt | 32 ++--- cmake/GetGitRevisionDescription.cmake | 154 ----------------------- cmake/GetGitRevisionDescription.cmake.in | 38 ------ 3 files changed, 18 insertions(+), 206 deletions(-) delete mode 100644 cmake/GetGitRevisionDescription.cmake delete mode 100644 cmake/GetGitRevisionDescription.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 9587c652c6..d7c85818e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,24 +25,28 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git) find_package(Git) if(GIT_FOUND) - include(GetGitRevisionDescription) - get_git_tag_revision(TAGHASH --tags --max-count=1) - get_git_head_revision(REFSPEC COMMITHASH) - git_describe(VERSION --tags ${TAGHASH}) - - string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") - if(MATCH) - string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" GIT_VERSION_MAJOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_MINOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_RELEASE "${VERSION}") - + execute_process ( + COMMAND "${GIT_EXECUTABLE}" rev-list --tags --max-count=1 + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE EXITCODE1 + OUTPUT_VARIABLE TAGHASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process ( + COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE EXITCODE2 + OUTPUT_VARIABLE COMMITHASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + + string (COMPARE EQUAL "${EXITCODE1}:${EXITCODE2}" "0:0" SUCCESS) + if (SUCCESS) set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") set(OPENMW_VERSION_TAGHASH "${TAGHASH}") - message(STATUS "OpenMW version ${OPENMW_VERSION}") - else(MATCH) + else (SUCCESS) message(WARNING "Failed to get valid version information from Git") - endif(MATCH) + endif (SUCCESS) else(GIT_FOUND) message(WARNING "Git executable not found") endif(GIT_FOUND) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake deleted file mode 100644 index 56ff1d5459..0000000000 --- a/cmake/GetGitRevisionDescription.cmake +++ /dev/null @@ -1,154 +0,0 @@ -# - Returns a version string from Git -# -# These functions force a re-configure on each git commit so that you can -# trust the values of the variables in your build system. -# -# get_git_head_revision( [ ...]) -# -# Returns the refspec and sha hash of the current head revision -# -# git_describe( [ ...]) -# -# Returns the results of git describe on the source tree, and adjusting -# the output so that it tests false if an error occurs. -# -# git_get_exact_tag( [ ...]) -# -# Returns the results of git describe --exact-match on the source tree, -# and adjusting the output so that it tests false if there was no exact -# matching tag. -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2009-2010. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -if(__get_git_revision_description) - return() -endif() -set(__get_git_revision_description YES) - -# We must run the following at "include" time, not at function call time, -# to find the path to this module rather than the path to a calling list file -get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) - -function(get_git_head_revision _refspecvar _hashvar) - set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories - set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") - get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) - if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) - # We have reached the root directory, we are not in git - set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - return() - endif() - - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - endwhile() - - # check if this is a submodule - if(NOT IS_DIRECTORY ${GIT_DIR}) - file(READ ${GIT_DIR} submodule) - string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) - get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) - get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) - endif() - - set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") - - if(NOT EXISTS "${GIT_DATA}") - file(MAKE_DIRECTORY "${GIT_DATA}") - endif() - - if(NOT EXISTS "${GIT_DIR}/HEAD") - return() - endif() - - set(HEAD_FILE "${GIT_DATA}/HEAD") - configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) - - configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" - "${GIT_DATA}/grabRef.cmake" @ONLY) - include("${GIT_DATA}/grabRef.cmake") - - set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) - set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) -endfunction() - -function(git_describe _var) - #get_git_head_revision(refspec hash) - - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - - #if(NOT hash) - # set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) - # return() - #endif() - - # TODO sanitize - #if((${ARGN}" MATCHES "&&") OR - # (ARGN MATCHES "||") OR - # (ARGN MATCHES "\\;")) - # message("Please report the following error to the project!") - # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") - #endif() - - #message(STATUS "Arguments to execute_process: ${ARGN}") - - execute_process(COMMAND - "${GIT_EXECUTABLE}" - describe - #${hash} - ${ARGN} - WORKING_DIRECTORY - "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() - - set(${_var} "${out}" PARENT_SCOPE) -endfunction() - -function(get_git_tag_revision _var) - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - - execute_process(COMMAND - "${GIT_EXECUTABLE}" - rev-list - ${ARGN} - WORKING_DIRECTORY - "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() - - set(${_var} "${out}" PARENT_SCOPE) -endfunction() - - diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in deleted file mode 100644 index 888ce13aab..0000000000 --- a/cmake/GetGitRevisionDescription.cmake.in +++ /dev/null @@ -1,38 +0,0 @@ -# -# Internal file for GetGitRevisionDescription.cmake -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2009-2010. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -set(HEAD_HASH) - -file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) - -string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) -if(HEAD_CONTENTS MATCHES "ref") - # named branch - string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") - if(EXISTS "@GIT_DIR@/${HEAD_REF}") - configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) - elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") - configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) - set(HEAD_HASH "${HEAD_REF}") - endif() -else() - # detached HEAD - configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) -endif() - -if(NOT HEAD_HASH) - file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) - string(STRIP "${HEAD_HASH}" HEAD_HASH) -endif() From a87fe71ddf643fc6231f3cb90a831ed76942187c Mon Sep 17 00:00:00 2001 From: Internecine Date: Sat, 27 Dec 2014 19:46:54 +1300 Subject: [PATCH 147/404] Added a helper function to handle dynamic stat changes --- apps/openmw/mwmechanics/spellcasting.cpp | 28 +++++++++--------------- apps/openmw/mwmechanics/spellcasting.hpp | 2 ++ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index fd4c9406cb..3ec52cf46a 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -580,30 +580,15 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::DamageHealth || effectId == ESM::MagicEffect::RestoreHealth) { - MWMechanics::DynamicStat health = target.getClass().getCreatureStats(target).getHealth(); - if (effectId == ESM::MagicEffect::DamageHealth) - health.setCurrent(health.getCurrent() - magnitude); - else - health.setCurrent(health.getCurrent() + magnitude); - target.getClass().getCreatureStats(target).setHealth(health); + applyDynamicStatsEffect(0, target, magnitude); } else if (effectId == ESM::MagicEffect::DamageFatigue || effectId == ESM::MagicEffect::RestoreFatigue) { - MWMechanics::DynamicStat fatigue = target.getClass().getCreatureStats(target).getFatigue(); - if (effectId == ESM::MagicEffect::DamageFatigue) - fatigue.setCurrent(fatigue.getCurrent() - magnitude); - else - fatigue.setCurrent(fatigue.getCurrent() + magnitude); - target.getClass().getCreatureStats(target).setHealth(fatigue); + applyDynamicStatsEffect(1, target, magnitude); } else if (effectId == ESM::MagicEffect::DamageMagicka || effectId == ESM::MagicEffect::RestoreMagicka) { - MWMechanics::DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); - if (effectId == ESM::MagicEffect::DamageMagicka) - magicka.setCurrent(magicka.getCurrent() - magnitude); - else - magicka.setCurrent(magicka.getCurrent() + magnitude); - target.getClass().getCreatureStats(target).setHealth(magicka); + applyDynamicStatsEffect(2, target, magnitude); } else if (effectId == ESM::MagicEffect::DamageSkill || effectId == ESM::MagicEffect::RestoreSkill) { @@ -666,6 +651,13 @@ namespace MWMechanics } } } + + void CastSpell::applyDynamicStatsEffect(int attribute, const MWWorld::Ptr& target, float magnitude) + { + DynamicStat value = target.getClass().getCreatureStats(target).getDynamic(attribute); + value.modify(magnitude); + target.getClass().getCreatureStats(target).setDynamic(attribute, value); + } bool CastSpell::cast(const std::string &id) { diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 395ae043b4..d76478146b 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -93,6 +93,8 @@ namespace MWMechanics /// @note \a caster can be any type of object, or even an empty object. void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); + + void applyDynamicStatsEffect (int attribute, const MWWorld::Ptr& target, float magnitude); }; } From 5f9540318a99dc3c551abf6c66a26a6428a1c0a9 Mon Sep 17 00:00:00 2001 From: Internecine Date: Sat, 27 Dec 2014 19:49:14 +1300 Subject: [PATCH 148/404] Fixed incorrect indexes --- apps/openmw/mwmechanics/spellcasting.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 3ec52cf46a..352db88b4c 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -584,11 +584,11 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::DamageFatigue || effectId == ESM::MagicEffect::RestoreFatigue) { - applyDynamicStatsEffect(1, target, magnitude); + applyDynamicStatsEffect(2, target, magnitude); } else if (effectId == ESM::MagicEffect::DamageMagicka || effectId == ESM::MagicEffect::RestoreMagicka) { - applyDynamicStatsEffect(2, target, magnitude); + applyDynamicStatsEffect(1, target, magnitude); } else if (effectId == ESM::MagicEffect::DamageSkill || effectId == ESM::MagicEffect::RestoreSkill) { From 82ef145e795bbc7b8ad302d11d8cee7149e13fcb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 27 Dec 2014 13:13:01 +0100 Subject: [PATCH 149/404] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index ebb2f8f26b..dfdf875653 100644 --- a/credits.txt +++ b/credits.txt @@ -64,6 +64,7 @@ Marc Bouvier (CramitDeFrog) Marcin Hulist (Gohan) Mark Siewert (mark76) Marco Melletti (mellotanica) +Marco Schulze Mateusz Kołaczek (PL_kolek) megaton Michael Hogan (Xethik) From 50e31877ab67f5f4a5f996d3fc22c83ec54acbf4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 27 Dec 2014 14:43:07 +0100 Subject: [PATCH 150/404] Fix crash when northmarker has been disabled (Bug #2230) --- apps/openmw/mwworld/worldimp.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1867cf84cf..41f399471b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1699,8 +1699,9 @@ namespace MWWorld MWWorld::LiveCellRef* ref = statics.find("northmarker"); if (!ref) return Vector2(0, 1); - Ogre::SceneNode* node = ref->mData.getBaseNode(); - Vector3 dir = node->_getDerivedOrientation() * Ogre::Vector3(0,1,0); + + Ogre::Quaternion orient (Ogre::Radian(-ref->mData.getPosition().rot[2]), Ogre::Vector3::UNIT_Z); + Vector3 dir = orient * Ogre::Vector3(0,1,0); Vector2 d = Vector2(dir.x, dir.y); return d; } From a62fe38a1b1892d9d3ca814fe9a1deab5fd6d138 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 27 Dec 2014 15:02:05 +0100 Subject: [PATCH 151/404] Fix unsafe use of BaseNode --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c0c16eaf9a..8c68334974 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1214,7 +1214,7 @@ namespace MWMechanics bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) { - if (observer.getClass().getCreatureStats(observer).isDead()) + if (observer.getClass().getCreatureStats(observer).isDead() || !observer.getRefData().isEnabled()) return false; const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); From 0a2dd4c6cbdb85069bac3842479fbb4253c7663e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 27 Dec 2014 17:20:37 +0100 Subject: [PATCH 152/404] Fix unsafe use of BaseNode in Move script instruction --- apps/openmw/mwscript/transformationextensions.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index b39b3507ac..7568f604dd 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -689,7 +689,11 @@ namespace MWScript else throw std::runtime_error ("invalid movement axis: " + axis); + if (!ptr.getRefData().getBaseNode()) + return; + Ogre::Vector3 worldPos = ptr.getRefData().getBaseNode()->convertLocalToWorldPosition(posChange); + dynamic_cast(runtime.getContext()).updatePtr( MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z)); } From 25954a80f57fb6f98de3068a2b4c54af6fddc5cf Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 28 Dec 2014 01:06:49 +0100 Subject: [PATCH 153/404] Fix recharging of items in player inventory --- apps/openmw/mwworld/inventorystore.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index fef34d67be..de35377595 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -418,6 +418,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) void MWWorld::InventoryStore::flagAsModified() { ContainerStore::flagAsModified(); + mRechargingItemsUpToDate = false; } bool MWWorld::InventoryStore::stacks(const Ptr& ptr1, const Ptr& ptr2) From de9d3470189550b5aa14d41191325e7f30a113fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 28 Dec 2014 01:51:12 +0100 Subject: [PATCH 154/404] Fix on touch area effect spells (Fixes #2233) --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- apps/openmw/mwworld/projectilemanager.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 5 ++--- apps/openmw/mwworld/worldimp.hpp | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 32beadf18a..cdfdfc358a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -546,7 +546,7 @@ namespace MWBase virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0; virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, - const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; + const MWWorld::Ptr& caster, int rangeType, const std::string& id, const std::string& sourceName) = 0; virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a831cc8bbc..ee1e07dadd 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -514,7 +514,7 @@ namespace MWMechanics } if (!exploded) - MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, mId, mSourceName); + MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, range, mId, mSourceName); if (!reflectedEffects.mList.empty()) inflict(caster, target, reflectedEffects, range, true, exploded); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index afda6fe606..100ff0ba9b 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -206,7 +206,7 @@ namespace MWWorld if (hit) { MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); - MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mSpellId, it->mSourceName); + MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, ESM::RT_Target, it->mSpellId, it->mSourceName); MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 41f399471b..fdfa19af42 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3094,7 +3094,7 @@ namespace MWWorld mRendering->spawnEffect(model, textureOverride, worldPos); } - void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, + void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, int rangeType, const std::string& id, const std::string& sourceName) { std::map > toApply; @@ -3130,7 +3130,6 @@ namespace MWWorld std::vector objects; MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( origin, feetToGameUnits(effectIt->mArea), objects); - for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) toApply[*affected].push_back(*effectIt); } @@ -3154,7 +3153,7 @@ namespace MWWorld cast.mStack = false; ESM::EffectList effects; effects.mList = apply->second; - cast.inflict(apply->first, caster, effects, ESM::RT_Target, false, true); + cast.inflict(apply->first, caster, effects, (ESM::RangeType)rangeType, false, true); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2a0da917b9..5810fe42fa 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -622,7 +622,7 @@ namespace MWWorld virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos); virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, - const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); + const MWWorld::Ptr& caster, int rangeType, const std::string& id, const std::string& sourceName); virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor); From 377b79d5baecf4e6ebe0f11d86f8bca62f0f0dee Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 28 Dec 2014 02:39:54 +0100 Subject: [PATCH 155/404] Use SoundGen fallback for type Land only (Fixes #2228) --- apps/openmw/mwclass/creature.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c69081c2e0..59ab9dc902 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -678,7 +678,6 @@ namespace MWClass if(type >= 0) { std::vector sounds; - std::vector fallbacksounds; MWWorld::LiveCellRef* ref = ptr.get(); @@ -689,16 +688,15 @@ namespace MWClass { if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(ourId, sound->mCreature))) sounds.push_back(&*sound); - if (type == sound->mType && sound->mCreature.empty()) - fallbacksounds.push_back(&*sound); ++sound; } if(!sounds.empty()) return sounds[(int)(rand()/(RAND_MAX+1.0)*sounds.size())]->mSound; - if (!fallbacksounds.empty()) - return fallbacksounds[(int)(rand()/(RAND_MAX+1.0)*fallbacksounds.size())]->mSound; } + if (type == ESM::SoundGenerator::Land) + return "Body Fall Large"; + return ""; } From 1bd3ab8a2d8d71c0d4d4561aff0d4feda172d9a9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 28 Dec 2014 14:09:27 +0100 Subject: [PATCH 156/404] Fix torch animation playing when torch is hidden (Fixes #2236) --- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwrender/characterpreview.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index e553b6cdcd..633573ad98 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1285,7 +1285,7 @@ bool CharacterController::updateWeaponState() MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() - && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) + && updateCarriedLeftVisible(mWeaponType)) { mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 831efce4fb..756c79ad83 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -239,7 +239,7 @@ namespace MWRender mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && showCarriedLeft) { if(!mAnimation->getInfo("torch")) mAnimation->play("torch", 2, MWRender::Animation::Group_LeftArm, false, From a58bc9f2f7d07c792ee3fb65a101055117c35a38 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 28 Dec 2014 14:45:20 +0100 Subject: [PATCH 157/404] Remove sneaking effect on combat AI (Fixes #2237) --- apps/openmw/mwmechanics/aicombat.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6249606322..a23634ea39 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -300,6 +300,14 @@ namespace MWMechanics //Update with period = tReaction + // Stop attacking if target is not seen + if (target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0 + || target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 75) + { + movement.mPosition[1] = movement.mPosition[0] = 0; + return false; // TODO: run away instead of doing nothing + } + timerReact = 0; const MWWorld::CellStore*& currentCell = storage.mCell; bool cellChange = currentCell && (actor.getCell() != currentCell); @@ -326,10 +334,6 @@ namespace MWMechanics actionCooldown = currentAction->getActionCooldown(); } - // Stop attacking if target is not seen - if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(target, actor)) - return true; - if (currentAction.get()) currentAction->getCombatRange(rangeAttack, rangeFollow); From 6c9875969a5e0a12bb427b79cad7d2dc65e0256e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 28 Dec 2014 15:34:47 +0100 Subject: [PATCH 158/404] Stop idle animations for non-biped creatures when attacking/moving The idle animation wouldn't be visible anyway, since these creatures don't have animation layers. However sounds tagged in the animation would still play. --- apps/openmw/mwclass/npc.cpp | 5 +++++ apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwmechanics/character.cpp | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3fe23772d6..30445612ac 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1384,4 +1384,9 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mAiData.mFight; } + + bool Npc::isBipedal(const MWWorld::Ptr &ptr) const + { + return true; + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 3bc450088b..8c89686af3 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -182,6 +182,8 @@ namespace MWClass return true; } + virtual bool isBipedal (const MWWorld::Ptr &ptr) const; + virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 633573ad98..23ff9afff2 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1586,7 +1586,14 @@ void CharacterController::update(float duration) clearAnimQueue(); if(mAnimQueue.empty()) + { idlestate = (inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)); + if ((mUpperBodyState != UpperCharState_Nothing + || mMovementState != CharState_None + || mHitState != CharState_None) + && !mPtr.getClass().isBipedal(mPtr)) + idlestate = CharState_None; + } else if(mAnimQueue.size() > 1) { if(mAnimation->isPlaying(mAnimQueue.front().first) == false) From cee72d021d114b96500a9f7e17775df5fba3d915 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 29 Dec 2014 19:51:19 +0300 Subject: [PATCH 159/404] contrast and gamma post-processing effect added initial values are set to approximate vanilla --- apps/openmw/engine.cpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 11 ++++++ .../brightness_contrast_gamma.compositor | 24 +++++++++++++ files/materials/brightness_contrast_gamma.mat | 14 ++++++++ .../brightness_contrast_gamma.shader | 20 +++++++++++ .../brightness_contrast_gamma.shaderset | 7 ++++ files/mygui/openmw_settings_window.layout | 34 ++++++++++++++++--- files/settings-default.cfg | 2 ++ 8 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 files/materials/brightness_contrast_gamma.compositor create mode 100644 files/materials/brightness_contrast_gamma.mat create mode 100644 files/materials/brightness_contrast_gamma.shader create mode 100644 files/materials/brightness_contrast_gamma.shaderset diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 99a642454e..8f695ed8dd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -347,6 +347,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "water"); addResourcesDirectory(mResDir / "shadows"); + addResourcesDirectory(mResDir / "materials"); OEngine::Render::WindowSettings windowSettings; windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2cddbce75c..36aba70fb0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -13,6 +13,7 @@ #include #include #include +#include // for post-processing effects #include @@ -136,6 +137,9 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); + Ogre::CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "brightness_contrast_gamma"); + Ogre::CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "brightness_contrast_gamma", true); + // disable unsupported effects if (!Settings::Manager::getBool("shaders", "Objects")) Settings::Manager::setBool("enabled", "Shadows", false); @@ -155,6 +159,8 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b sh::Factory::getInstance ().setGlobalSetting ("refraction", Settings::Manager::getBool("refraction", "Water") ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("viewproj_fix", "false"); sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty (new sh::Vector4(0,0,0,0))); + sh::Factory::getInstance ().setSharedParameter ("contrast_invGamma", sh::makeProperty(new sh::Vector2( + Settings::Manager::getFloat("contrast", "General"), 1.0f/Settings::Manager::getFloat("gamma", "General")))); mRootNode = mRendering.getScene()->getRootSceneNode(); mRootNode->createChildSceneNode("player"); @@ -757,6 +763,11 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec changeRes = true; else if (it->second == "field of view" && it->first == "General") mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); + else if ((it->second == "gamma" || it->second == "contrast") && it->first == "General") + { + sh::Factory::getInstance ().setSharedParameter ("contrast_invGamma", sh::makeProperty(new sh::Vector2( + Settings::Manager::getFloat("contrast", "General"), 1.0f/Settings::Manager::getFloat("gamma", "General")))); + } else if ((it->second == "texture filtering" && it->first == "General") || (it->second == "anisotropy" && it->first == "General")) { diff --git a/files/materials/brightness_contrast_gamma.compositor b/files/materials/brightness_contrast_gamma.compositor new file mode 100644 index 0000000000..c5c189dd16 --- /dev/null +++ b/files/materials/brightness_contrast_gamma.compositor @@ -0,0 +1,24 @@ +compositor brightness_contrast_gamma +{ + technique + { + // render scene into texture + texture SceneBuffer target_width target_height PF_A8R8G8B8 + + target SceneBuffer + { + input previous + } + + target_output + { + input none + + pass render_quad + { + material mat_brightness_contrast_gamma + input 0 SceneBuffer + } + } + } +} diff --git a/files/materials/brightness_contrast_gamma.mat b/files/materials/brightness_contrast_gamma.mat new file mode 100644 index 0000000000..0a85a1a9c9 --- /dev/null +++ b/files/materials/brightness_contrast_gamma.mat @@ -0,0 +1,14 @@ +material mat_brightness_contrast_gamma +{ + pass + { + vertex_program transform_vertex + fragment_program openmw_brightness_contrast_gamma_fragment + + depth_check off + + texture_unit SceneBuffer + { + } + } +} diff --git a/files/materials/brightness_contrast_gamma.shader b/files/materials/brightness_contrast_gamma.shader new file mode 100644 index 0000000000..7a8f6a82a1 --- /dev/null +++ b/files/materials/brightness_contrast_gamma.shader @@ -0,0 +1,20 @@ +#include "core.h" + +#ifdef SH_FRAGMENT_SHADER + + SH_BEGIN_PROGRAM + shInput(float2, UV) + shSampler2D(SceneBuffer) + shUniform(float2, contrast_invGamma) @shSharedParameter(contrast_invGamma) + SH_START_PROGRAM + { + shOutputColour(0) = shSample(SceneBuffer, UV); + + // contrast + shOutputColour(0).xyz = (shOutputColour(0).xyz - float3(0.5,0.5,0.5)) * contrast_invGamma.x + float3(0.5,0.5,0.5); + shOutputColour(0).xyz = shSaturate(shOutputColour(0).xyz); + // gamma + shOutputColour(0).xyz = pow(shOutputColour(0).xyz, contrast_invGamma.yyy); + } + +#endif diff --git a/files/materials/brightness_contrast_gamma.shaderset b/files/materials/brightness_contrast_gamma.shaderset new file mode 100644 index 0000000000..2dc4ff6981 --- /dev/null +++ b/files/materials/brightness_contrast_gamma.shaderset @@ -0,0 +1,7 @@ +shader_set openmw_brightness_contrast_gamma_fragment +{ + source brightness_contrast_gamma.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index e2f46f2d16..b3da0b9f64 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -1,8 +1,8 @@  - + - + @@ -214,7 +214,7 @@ - + @@ -292,6 +292,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -470,7 +496,7 @@ - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index df8266f7af..bd4eb208bf 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -48,6 +48,8 @@ werewolf overlay = true [General] # Camera field of view field of view = 55 +gamma = 0.88 +contrast = 0.86 # Texture filtering mode. valid values: # none From c47b337fb0b205faede7132e15db91b276a970ec Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 29 Dec 2014 23:10:22 +0300 Subject: [PATCH 160/404] fix tabulation --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8f695ed8dd..105d5ee05a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -347,7 +347,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "water"); addResourcesDirectory(mResDir / "shadows"); - addResourcesDirectory(mResDir / "materials"); + addResourcesDirectory(mResDir / "materials"); OEngine::Render::WindowSettings windowSettings; windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 36aba70fb0..0bcbb9a78d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -137,8 +137,8 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); - Ogre::CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "brightness_contrast_gamma"); - Ogre::CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "brightness_contrast_gamma", true); + Ogre::CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "brightness_contrast_gamma"); + Ogre::CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "brightness_contrast_gamma", true); // disable unsupported effects if (!Settings::Manager::getBool("shaders", "Objects")) @@ -159,8 +159,8 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b sh::Factory::getInstance ().setGlobalSetting ("refraction", Settings::Manager::getBool("refraction", "Water") ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("viewproj_fix", "false"); sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty (new sh::Vector4(0,0,0,0))); - sh::Factory::getInstance ().setSharedParameter ("contrast_invGamma", sh::makeProperty(new sh::Vector2( - Settings::Manager::getFloat("contrast", "General"), 1.0f/Settings::Manager::getFloat("gamma", "General")))); + sh::Factory::getInstance ().setSharedParameter ("contrast_invGamma", sh::makeProperty(new sh::Vector2( + Settings::Manager::getFloat("contrast", "General"), 1.0f/Settings::Manager::getFloat("gamma", "General")))); mRootNode = mRendering.getScene()->getRootSceneNode(); mRootNode->createChildSceneNode("player"); @@ -763,11 +763,11 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec changeRes = true; else if (it->second == "field of view" && it->first == "General") mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); - else if ((it->second == "gamma" || it->second == "contrast") && it->first == "General") - { - sh::Factory::getInstance ().setSharedParameter ("contrast_invGamma", sh::makeProperty(new sh::Vector2( - Settings::Manager::getFloat("contrast", "General"), 1.0f/Settings::Manager::getFloat("gamma", "General")))); - } + else if ((it->second == "gamma" || it->second == "contrast") && it->first == "General") + { + sh::Factory::getInstance ().setSharedParameter ("contrast_invGamma", sh::makeProperty(new sh::Vector2( + Settings::Manager::getFloat("contrast", "General"), 1.0f/Settings::Manager::getFloat("gamma", "General")))); + } else if ((it->second == "texture filtering" && it->first == "General") || (it->second == "anisotropy" && it->first == "General")) { From e2346d7c37967f227f4f4a2ef2d36fd08093aff4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 30 Dec 2014 01:36:31 +0100 Subject: [PATCH 161/404] Store permanent magic effects in savegame (Fixes #1648) --- apps/openmw/mwworld/containerstore.hpp | 4 +-- apps/openmw/mwworld/inventorystore.cpp | 37 ++++++++++++++++++++++++++ apps/openmw/mwworld/inventorystore.hpp | 4 +++ components/esm/inventorystate.cpp | 27 +++++++++++++++++++ components/esm/inventorystate.hpp | 3 +++ 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 33255138a6..1863984b23 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -170,9 +170,9 @@ namespace MWWorld Ptr search (const std::string& id); - void writeState (ESM::InventoryState& state) const; + virtual void writeState (ESM::InventoryState& state) const; - void readState (const ESM::InventoryState& state); + virtual void readState (const ESM::InventoryState& state); friend class ContainerStoreIterator; }; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index de35377595..9c329ce720 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -654,3 +655,39 @@ bool MWWorld::InventoryStore::isEquipped(const MWWorld::Ptr &item) } return false; } + +void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const +{ + MWWorld::ContainerStore::writeState(state); + + for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it) + { + std::vector > params; + for (std::vector::const_iterator pIt = it->second.begin(); pIt != it->second.end(); ++pIt) + { + params.push_back(std::make_pair(pIt->mRandom, pIt->mMultiplier)); + } + + state.mPermanentMagicEffectMagnitudes[it->first] = params; + } +} + +void MWWorld::InventoryStore::readState(const ESM::InventoryState &state) +{ + MWWorld::ContainerStore::readState(state); + + for (ESM::InventoryState::TEffectMagnitudes::const_iterator it = state.mPermanentMagicEffectMagnitudes.begin(); + it != state.mPermanentMagicEffectMagnitudes.end(); ++it) + { + std::vector params; + for (std::vector >::const_iterator pIt = it->second.begin(); pIt != it->second.end(); ++pIt) + { + EffectParams p; + p.mRandom = pIt->first; + p.mMultiplier = pIt->second; + params.push_back(p); + } + + mPermanentMagicEffectMagnitudes[it->first] = params; + } +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 30abc2ea57..48742b557d 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -205,6 +205,10 @@ namespace MWWorld virtual void clear(); ///< Empty container. + + virtual void writeState (ESM::InventoryState& state) const; + + virtual void readState (const ESM::InventoryState& state); }; } diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index 2154faa830..a2d49b144b 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -59,6 +59,21 @@ void ESM::InventoryState::load (ESMReader &esm) esm.getHNT (count, "COUN"); mLevelledItemMap[id] = count; } + + while (esm.isNextSub("MAGI")) + { + std::string id = esm.getHString(); + + std::vector > params; + while (esm.isNextSub("RAND")) + { + float rand, multiplier; + esm.getHT (rand); + esm.getHNT (multiplier, "MULT"); + params.push_back(std::make_pair(rand, multiplier)); + } + mPermanentMagicEffectMagnitudes[id] = params; + } } void ESM::InventoryState::save (ESMWriter &esm) const @@ -75,4 +90,16 @@ void ESM::InventoryState::save (ESMWriter &esm) const esm.writeHNString ("LEVM", it->first); esm.writeHNT ("COUN", it->second); } + + for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it) + { + esm.writeHNString("MAGI", it->first); + + const std::vector >& params = it->second; + for (std::vector >::const_iterator pIt = params.begin(); pIt != params.end(); ++pIt) + { + esm.writeHNT ("RAND", pIt->first); + esm.writeHNT ("MULT", pIt->second); + } + } } diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index 4aa79f575b..bd0b46a224 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -24,6 +24,9 @@ namespace ESM std::map mLevelledItemMap; + typedef std::map > > TEffectMagnitudes; + TEffectMagnitudes mPermanentMagicEffectMagnitudes; + virtual ~InventoryState() {} virtual void load (ESMReader &esm); From a62e15d93d6d0350c9807ae71870357926eef17c Mon Sep 17 00:00:00 2001 From: dteviot Date: Tue, 30 Dec 2014 17:25:19 +1300 Subject: [PATCH 162/404] Read profile files from LauncherSettings in load order. --- apps/launcher/datafilespage.cpp | 20 ++++++++++++++------ apps/launcher/datafilespage.hpp | 2 ++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 3c4d36de77..a0ef97ff9c 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -94,20 +94,28 @@ bool Launcher::DataFilesPage::loadSettings() if (!currentProfile.isEmpty()) addProfile(currentProfile, true); - QStringList files = mLauncherSettings.values(QString("Profiles/") + currentProfile + QString("/content"), Qt::MatchExactly); + mSelector->setProfileContent(filesInProfile(currentProfile, pathIterator)); + + return true; +} + +QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName, PathIterator& pathIterator) +{ + QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/content"), Qt::MatchExactly); QStringList filepaths; - foreach (const QString &file, files) + // mLauncherSettings.values() returns the files in reverse load order + QListIterator i(files); + i.toBack(); + while (i.hasPrevious()) { - QString filepath = pathIterator.findFirstPath (file); + QString filepath = pathIterator.findFirstPath(i.previous()); if (!filepath.isEmpty()) filepaths << filepath; } - mSelector->setProfileContent (filepaths); - - return true; + return filepaths; } void Launcher::DataFilesPage::saveSettings(const QString &profile) diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 15fa00308d..c2fc224614 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -134,6 +134,8 @@ namespace Launcher } }; + + QStringList filesInProfile(const QString& profileName, PathIterator& pathIterator); }; } #endif From 18fb3f831a8a9003458dd7e44633ed38d64cce59 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 30 Dec 2014 15:46:33 +0100 Subject: [PATCH 163/404] Make the maximum horizontal stepping distance independent of movement speed (Fixes #1638) --- apps/openmw/mwworld/physicssystem.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index af1191ad98..1374dc37a9 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -428,7 +428,10 @@ namespace MWWorld Ogre::Vector3 oldPosition = newPosition; // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // NOTE: stepMove modifies newPosition if successful - if(stepMove(colobj, newPosition, velocity, remainingTime, engine)) + bool result = stepMove(colobj, newPosition, velocity, remainingTime, engine); + if (!result) + result = stepMove(colobj, newPosition, velocity.normalisedCopy()*300.f, remainingTime, engine); + if(result) { // don't let pure water creatures move out of water after stepMove if((ptr.getClass().canSwim(ptr) && !ptr.getClass().canWalk(ptr)) From 9c693d078b42e7ca0bc4e96782851abfa5b3091a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 30 Dec 2014 16:22:06 +0100 Subject: [PATCH 164/404] Fix equipment update removing ammunition (Fixes #2144) --- apps/openmw/mwrender/npcanimation.cpp | 5 +++++ apps/openmw/mwrender/weaponanimation.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 1cc9ad2325..32c1e7e059 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -361,6 +361,8 @@ void NpcAnimation::updateParts() }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); + bool wasArrowAttached = (mAmmunition.get()); + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) { @@ -555,6 +557,9 @@ void NpcAnimation::updateParts() "meshes\\"+bodypart->mModel); } } + + if (wasArrowAttached) + attachArrow(); } void NpcAnimation::addFirstPersonOffset(const Ogre::Vector3 &offset) diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 7a52ce7ead..8a9feef038 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -49,6 +49,8 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor) else { NifOgre::ObjectScenePtr weapon = getWeapon(); + if (!weapon.get()) + return; MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo == inv.end()) @@ -66,6 +68,9 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor) void WeaponAnimation::releaseArrow(MWWorld::Ptr actor) { + if (!mAmmunition.get()) + return; + MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weapon == inv.end()) From 04d95810d158148bf56d292f21d92f6978f8d23e Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 30 Dec 2014 18:33:11 +0300 Subject: [PATCH 165/404] gamma/contrast system reworked --- apps/openmw/engine.cpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 4 +++ files/mygui/openmw_settings_window.layout | 34 ++++++++++++++++++++--- files/settings-default.cfg | 2 ++ libs/openengine/ogre/renderer.cpp | 26 +++++++++++++++++ libs/openengine/ogre/renderer.hpp | 9 +++++- 6 files changed, 72 insertions(+), 5 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 99a642454e..8912cb19ec 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -358,6 +358,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) windowSettings.icon = "openmw.png"; std::string aa = settings.getString("antialiasing", "Video"); windowSettings.fsaa = (aa.substr(0, 4) == "MSAA") ? aa.substr(5, aa.size()-5) : "0"; + windowSettings.gamma = Settings::Manager::getFloat("gamma", "General"); + windowSettings.contrast = Settings::Manager::getFloat("contrast", "General"); SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, settings.getBool("minimize on focus loss", "Video") ? "1" : "0"); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2cddbce75c..44edcf03b1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -757,6 +757,10 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec changeRes = true; else if (it->second == "field of view" && it->first == "General") mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); + else if ((it->second == "gamma" || it->second == "contrast") && it->first == "General") + { + mRendering.setWindowGammaContrast(Settings::Manager::getFloat("gamma", "General"), Settings::Manager::getFloat("contrast", "General")); + } else if ((it->second == "texture filtering" && it->first == "General") || (it->second == "anisotropy" && it->first == "General")) { diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index e2f46f2d16..b3da0b9f64 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -1,8 +1,8 @@  - + - + @@ -214,7 +214,7 @@ - + @@ -292,6 +292,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -470,7 +496,7 @@ - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index df8266f7af..6bd10dc211 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -48,6 +48,8 @@ werewolf overlay = true [General] # Camera field of view field of view = 55 +gamma = 1.34 +contrast = 0.98 # Texture filtering mode. valid values: # none diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 404602c304..e20d721906 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -23,6 +23,12 @@ using namespace Ogre; using namespace OEngine::Render; +OgreRenderer::~OgreRenderer() +{ + cleanup(); + // restore system gamma ramp + SDL_SetWindowGammaRamp(mSDLWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]); +} void OgreRenderer::cleanup() { @@ -138,6 +144,8 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& helper.setWindowIcon(settings.icon); mWindow = helper.getWindow(); + SDL_GetWindowGammaRamp(mSDLWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]); + setWindowGammaContrast(settings.gamma, settings.contrast); // create the semi-transparent black background texture used by the GUI. // has to be created in code with TU_DYNAMIC_WRITE_ONLY param @@ -161,6 +169,24 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight())); } +void OgreRenderer::setWindowGammaContrast(float gamma, float contrast) +{ + Uint16 red[256], green[256], blue[256]; + for (int i = 0; i < 256; i++) + { + float k = i/256.0f; + k = (k - 0.5f) * contrast + 0.5f; + k = pow(k, 1.f/gamma); + k *= 256; + float value = k*256; + if (value > 65535) value = 65535; + else if (value < 0) value = 0; + + red[i] = green[i] = blue[i] = value; + } + SDL_SetWindowGammaRamp(mSDLWindow, red, green, blue); +} + void OgreRenderer::adjustCamera(float fov, float nearClip) { mCamera->setNearClipDistance(nearClip); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 70cc3db60f..6f2891e6a3 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -42,6 +42,8 @@ namespace OEngine int screen; std::string fsaa; std::string icon; + float gamma; + float contrast; }; class WindowSizeListener @@ -67,6 +69,9 @@ namespace OEngine int mWindowHeight; bool mOutstandingResize; + // Store system gamma ramp on window creation. Restore system gamma ramp on exit + uint16_t mOldSystemGammaRamp[256*3]; + public: OgreRenderer() : mRoot(NULL) @@ -83,7 +88,7 @@ namespace OEngine { } - ~OgreRenderer() { cleanup(); } + ~OgreRenderer(); /** Configure the renderer. This will load configuration files and set up the Root and logging classes. */ @@ -95,6 +100,8 @@ namespace OEngine /// Create a window with the given title void createWindow(const std::string &title, const WindowSettings& settings); + void setWindowGammaContrast(float gamma, float contrast); + /// Set up the scene manager, camera and viewport void adjustCamera( float fov=55, // Field of view angle From 5b2633588c0a1f6e9dcdd530726d1f9915408d48 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 30 Dec 2014 19:14:06 +0100 Subject: [PATCH 166/404] Add error handling for SDL_CreateWindow --- libs/openengine/ogre/renderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index eb33d5876c..2c81b1b9dd 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -130,6 +130,8 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& SDL_WINDOW_SHOWN | (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) | SDL_WINDOW_RESIZABLE ); + if (mSDLWindow == 0) + throw std::runtime_error("Failed to create window: " + std::string(SDL_GetError())); SFO::SDLWindowHelper helper(mSDLWindow, settings.window_x, settings.window_y, title, settings.fullscreen, params); if (settings.icon != "") From 3024c67995db4d7f2cbe5ac51ce3db5d9e40b872 Mon Sep 17 00:00:00 2001 From: Marco Schulze Date: Tue, 30 Dec 2014 18:37:33 -0300 Subject: [PATCH 167/404] Regenerate components/version/version.hpp as HEAD moves on a git checkout Pull request #416 introduced a common bug where version.hpp wouldn't be regenerated every build, poteantially leading to stale version data. This commit adds a custom build target, git-version, which updates version.hpp before the components library is built. --- CMakeLists.txt | 25 ++----------------------- cmake/GitVersion.cmake | 24 ++++++++++++++++++++++++ components/CMakeLists.txt | 23 ++++++++++++++++++++++- 3 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 cmake/GitVersion.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index d7c85818e8..323b743bc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,33 +20,13 @@ set(OPENMW_VERSION_TAGHASH "") set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") +set(GIT_CHECKOUT FALSE) if(EXISTS ${PROJECT_SOURCE_DIR}/.git) if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) find_package(Git) if(GIT_FOUND) - execute_process ( - COMMAND "${GIT_EXECUTABLE}" rev-list --tags --max-count=1 - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" - RESULT_VARIABLE EXITCODE1 - OUTPUT_VARIABLE TAGHASH - OUTPUT_STRIP_TRAILING_WHITESPACE) - - execute_process ( - COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" - RESULT_VARIABLE EXITCODE2 - OUTPUT_VARIABLE COMMITHASH - OUTPUT_STRIP_TRAILING_WHITESPACE) - - string (COMPARE EQUAL "${EXITCODE1}:${EXITCODE2}" "0:0" SUCCESS) - if (SUCCESS) - set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") - set(OPENMW_VERSION_TAGHASH "${TAGHASH}") - message(STATUS "OpenMW version ${OPENMW_VERSION}") - else (SUCCESS) - message(WARNING "Failed to get valid version information from Git") - endif (SUCCESS) + set(GIT_CHECKOUT TRUE) else(GIT_FOUND) message(WARNING "Git executable not found") endif(GIT_FOUND) @@ -874,4 +854,3 @@ if (DOXYGEN_FOUND) WORKING_DIRECTORY ${OpenMW_BINARY_DIR} COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM) endif () - diff --git a/cmake/GitVersion.cmake b/cmake/GitVersion.cmake new file mode 100644 index 0000000000..0087461a12 --- /dev/null +++ b/cmake/GitVersion.cmake @@ -0,0 +1,24 @@ +execute_process ( + COMMAND ${GIT_EXECUTABLE} rev-list --tags --max-count=1 + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE EXITCODE1 + OUTPUT_VARIABLE TAGHASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + +execute_process ( + COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE EXITCODE2 + OUTPUT_VARIABLE COMMITHASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + +string (COMPARE EQUAL "${EXITCODE1}:${EXITCODE2}" "0:0" SUCCESS) +if (SUCCESS) + set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") + set(OPENMW_VERSION_TAGHASH "${TAGHASH}") + message(STATUS "OpenMW version ${OPENMW_VERSION}") +else (SUCCESS) + message(WARNING "Failed to get valid version information from Git") +endif (SUCCESS) + +configure_file(${VERSION_HPP_IN} ${VERSION_HPP}) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 6918b87a7a..1e400f7793 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -2,7 +2,24 @@ project (Components) set (CMAKE_BUILD_TYPE DEBUG) # Version file -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp") +set (VERSION_HPP_IN ${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp.cmake) +set (VERSION_HPP ${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp) +if (GIT_CHECKOUT) + add_custom_target (git-version + COMMAND ${CMAKE_COMMAND} + -DGIT_EXECUTABLE=${GIT_EXECUTABLE} + -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} + -DVERSION_HPP_IN=${VERSION_HPP_IN} + -DVERSION_HPP=${VERSION_HPP} + -DOPENMW_VERSION_MAJOR=${OPENMW_VERSION_MAJOR} + -DOPENMW_VERSION_MINOR=${OPENMW_VERSION_MINOR} + -DOPENMW_VERSION_RELEASE=${OPENMW_VERSION_RELEASE} + -DOPENMW_VERSION=${OPENMW_VERSION} + -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/GitVersion.cmake + VERBATIM) +else (GIT_CHECKOUT) + configure_file(${VERSION_HPP_IN} ${VERSION_HPP}) +endif (GIT_CHECKOUT) # source files @@ -145,6 +162,10 @@ add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR}) target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) +if (GIT_CHECKOUT) + add_dependencies (components git-version) +endif (GIT_CHECKOUT) + # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT}) From dfbd470613045f19a7628de5ad600cf3296bc8da Mon Sep 17 00:00:00 2001 From: dteviot Date: Wed, 31 Dec 2014 19:19:54 +1300 Subject: [PATCH 168/404] Adjust plug-in order to match profile loading. Also marks plug-ins with load order problems in red and changes tool tip to describe error. --- components/CMakeLists.txt | 1 + .../contentselector/model/contentmodel.cpp | 93 ++++++++++++++++++- .../contentselector/model/contentmodel.hpp | 11 ++- .../contentselector/model/loadordererror.cpp | 22 +++++ .../contentselector/model/loadordererror.hpp | 41 ++++++++ .../contentselector/view/contentselector.cpp | 6 +- .../contentselector/view/contentselector.hpp | 2 +- 7 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 components/contentselector/model/loadordererror.cpp create mode 100644 components/contentselector/model/loadordererror.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 6918b87a7a..fbad61bfd4 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -116,6 +116,7 @@ if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (contentselector model/modelitem model/esmfile model/naturalsort model/contentmodel + model/loadordererror view/combobox view/contentselector ) add_component_qt_dir (config diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 0d4f2365a6..72bca22444 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "components/esm/esmreader.hpp" @@ -170,6 +171,16 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int switch (role) { + case Qt::ForegroundRole: + { + if (isLoadOrderError(file->filePath())) + { + QBrush redBackground(Qt::red, Qt::SolidPattern); + return redBackground; + } + break; + } + case Qt::EditRole: case Qt::DisplayRole: { @@ -202,7 +213,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int if (column != 0) return QVariant(); - return file->toolTip(); + return isLoadOrderError(file->filePath()) ? getLoadOrderError(file->filePath()).toolTip() : file->toolTip(); break; } @@ -341,6 +352,8 @@ bool ContentSelectorModel::ContentModel::removeRows(int position, int rows, cons } endRemoveRows(); + // at this point we know that drag and drop has finished. + checkForLoadOrderErrors(); return true; } @@ -530,11 +543,83 @@ bool ContentSelectorModel::ContentModel::isEnabled (QModelIndex index) const return (flags(index) & Qt::ItemIsEnabled); } -void ContentSelectorModel::ContentModel::setCheckStates (const QStringList &fileList, bool isChecked) +bool ContentSelectorModel::ContentModel::isLoadOrderError(const QString& filepath) const +{ + return !(getLoadOrderError(filepath) == LoadOrderError::sNoError); +} + +ContentSelectorModel::LoadOrderError ContentSelectorModel::ContentModel::getLoadOrderError(const QString& filepath) const +{ + return mLoadOrderErrors.contains(filepath) ? mLoadOrderErrors[filepath] : ContentSelectorModel::LoadOrderError::sNoError; +} + +void ContentSelectorModel::ContentModel::setLoadOrderError(const QString& filepath, const ContentSelectorModel::LoadOrderError& loadOrderError) +{ + mLoadOrderErrors[filepath] = loadOrderError; + int filePosition = indexFromItem(item(filepath)).row(); + emit dataChanged(index(filePosition, 0, QModelIndex()), index(filePosition, 0, QModelIndex())); +} + +void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileList, bool isChecked) { - foreach (const QString &file, fileList) + mLoadOrderErrors.clear(); + int previousPosition = -1; + foreach (const QString &filepath, fileList) { - setCheckState (file, isChecked); + if (setCheckState(filepath, isChecked)) + { + // as necessary, move plug-ins in visible list to match sequence of supplied filelist + const EsmFile* file = item(filepath); + int filePosition = indexFromItem(file).row(); + if (filePosition < previousPosition) + { + mFiles.move(filePosition, previousPosition); + emit dataChanged(index(filePosition, 0, QModelIndex()), index(previousPosition, 0, QModelIndex())); + } + else + { + previousPosition = filePosition; + } + } + } + checkForLoadOrderErrors(); +} + +void ContentSelectorModel::ContentModel::checkForLoadOrderErrors() +{ + for (int row = 0; row < mFiles.count(); ++row) + { + EsmFile* file = item(row); + bool isRowInError = isLoadOrderError(file->filePath()); + LoadOrderError::ErrorCode error = LoadOrderError::ErrorCode_None; + foreach(QString dependantfileName, file->gameFiles()) + { + const EsmFile* dependentFile = item(dependantfileName); + + if (!dependentFile) + { + error = LoadOrderError::ErrorCode_MissingDependency; + } + else if (!isChecked(dependentFile->filePath())) + { + error = LoadOrderError::ErrorCode_InactiveDependency; + } + else if (row < indexFromItem(dependentFile).row()) + { + error = LoadOrderError::ErrorCode_LoadOrder; + } + + if (!isRowInError && (error != LoadOrderError::ErrorCode_None)) + { + setLoadOrderError(file->filePath(), LoadOrderError(error, dependantfileName)); + break; + } + } + + if (isRowInError && (error == LoadOrderError::ErrorCode_None)) + { + setLoadOrderError(file->filePath(), LoadOrderError::sNoError); + } } } diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 7b2000b510..fc50eeb856 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -4,6 +4,8 @@ #include #include +#include "loadordererror.hpp" + namespace ContentSelectorModel { class EsmFile; @@ -47,10 +49,14 @@ namespace ContentSelectorModel bool isEnabled (QModelIndex index) const; bool isChecked(const QString &filepath) const; bool setCheckState(const QString &filepath, bool isChecked); - void setCheckStates (const QStringList &fileList, bool isChecked); + void setContentList(const QStringList &fileList, bool isChecked); ContentFileList checkedItems() const; void uncheckAll(); + bool isLoadOrderError(const QString& filepath) const; + ContentSelectorModel::LoadOrderError ContentSelectorModel::ContentModel::getLoadOrderError(const QString& filepath) const; + void ContentSelectorModel::ContentModel::setLoadOrderError(const QString& filepath, const ContentSelectorModel::LoadOrderError& loadOrderError); + void refreshModel(); private: @@ -60,9 +66,12 @@ namespace ContentSelectorModel EsmFile *item(int row); void sortFiles(); + void checkForLoadOrderErrors(); + ContentFileList mFiles; QHash mCheckStates; + QHash mLoadOrderErrors; QTextCodec *mCodec; QString mEncoding; diff --git a/components/contentselector/model/loadordererror.cpp b/components/contentselector/model/loadordererror.cpp new file mode 100644 index 0000000000..c8b258e983 --- /dev/null +++ b/components/contentselector/model/loadordererror.cpp @@ -0,0 +1,22 @@ +#include "loadordererror.hpp" +#include + +QString ContentSelectorModel::LoadOrderError::sErrorToolTips[ErrorCode_LoadOrder] = +{ + QString("Unable to find dependant file: %1"), + QString("Dependent file needs to be active: %1"), + QString("This file needs to load after %1"), +}; + +ContentSelectorModel::LoadOrderError ContentSelectorModel::LoadOrderError::sNoError = ContentSelectorModel::LoadOrderError(); + +QString ContentSelectorModel::LoadOrderError::toolTip() const +{ + assert(mErrorCode); + return sErrorToolTips[mErrorCode - 1].arg(mFileName); +} + +bool ContentSelectorModel::LoadOrderError::operator== (const ContentSelectorModel::LoadOrderError& rhs) const +{ + return (mErrorCode == rhs.mErrorCode) && ((mErrorCode == ErrorCode_None) || (mFileName == rhs.mFileName)); +} \ No newline at end of file diff --git a/components/contentselector/model/loadordererror.hpp b/components/contentselector/model/loadordererror.hpp new file mode 100644 index 0000000000..75ec2ad928 --- /dev/null +++ b/components/contentselector/model/loadordererror.hpp @@ -0,0 +1,41 @@ +#ifndef LOADORDERERROR_HPP +#define LOADORDERERROR_HPP + +#include + +namespace ContentSelectorModel +{ + /// \Details of a suspected Load Order problem a plug-in will have. This is basically a POD + class LoadOrderError + { + public: + enum ErrorCode + { + ErrorCode_None = 0, + ErrorCode_MissingDependency = 1, + ErrorCode_InactiveDependency = 2, + ErrorCode_LoadOrder = 3, + }; + + inline LoadOrderError() : mErrorCode(ErrorCode_None) {}; + inline LoadOrderError(ErrorCode errorCode, QString fileName) + { + mErrorCode = errorCode; + mFileName = fileName; + } + inline ErrorCode errorCode() const { return mErrorCode; } + inline QString fileName() const { return mFileName; } + bool operator==(const LoadOrderError& rhs) const; + QString toolTip() const; + + /// \Sentinal to represent a "No Load Order Error" condition + static LoadOrderError sNoError; + + private: + ErrorCode mErrorCode; + QString mFileName; + static QString sErrorToolTips[ErrorCode_LoadOrder]; + }; +} + +#endif // LOADORDERERROR_HPP diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index b12d4147a0..a5e250840b 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -75,7 +75,7 @@ void ContentSelectorView::ContentSelector::setProfileContent(const QStringList & } } - setCheckStates (fileList); + setContentList(fileList); } void ContentSelectorView::ContentSelector::setGameFile(const QString &filename) @@ -103,14 +103,14 @@ void ContentSelectorView::ContentSelector::clearCheckStates() mContentModel->uncheckAll(); } -void ContentSelectorView::ContentSelector::setCheckStates(const QStringList &list) +void ContentSelectorView::ContentSelector::setContentList(const QStringList &list) { if (list.isEmpty()) { slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex()); } else - mContentModel->setCheckStates (list, true); + mContentModel->setContentList(list, true); } ContentSelectorModel::ContentFileList diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index a25eb20ae3..a4da38727f 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -32,7 +32,7 @@ namespace ContentSelectorView void setProfileContent (const QStringList &fileList); void clearCheckStates(); - void setCheckStates (const QStringList &list); + void setContentList(const QStringList &list); ContentSelectorModel::ContentFileList selectedFiles() const; From 60a74d5eb8f5f673d6fecc626e789ecf37c31b40 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 31 Dec 2014 18:40:01 +0300 Subject: [PATCH 169/404] increase robustness for gamma to persist in the system; use GMST strings for gamma interface --- apps/openmw/engine.cpp | 5 +++-- files/mygui/openmw_settings_window.layout | 22 +++++++++++++++------- libs/openengine/ogre/renderer.cpp | 17 +++++++++++++---- libs/openengine/ogre/renderer.hpp | 3 +-- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8912cb19ec..c5ecfee2df 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -208,6 +208,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) OMW::Engine::~Engine() { + mOgre->restoreWindowGammaRamp(); mEnvironment.cleanup(); delete mScriptContext; delete mOgre; @@ -358,8 +359,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) windowSettings.icon = "openmw.png"; std::string aa = settings.getString("antialiasing", "Video"); windowSettings.fsaa = (aa.substr(0, 4) == "MSAA") ? aa.substr(5, aa.size()-5) : "0"; - windowSettings.gamma = Settings::Manager::getFloat("gamma", "General"); - windowSettings.contrast = Settings::Manager::getFloat("contrast", "General"); SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, settings.getBool("minimize on focus loss", "Video") ? "1" : "0"); @@ -384,6 +383,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); + mOgre->setWindowGammaContrast(Settings::Manager::getFloat("gamma", "General"), Settings::Manager::getFloat("contrast", "General")); + if (!mSkipMenu) { std::string logo = mFallbackMap["Movies_Company_Logo"]; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index b3da0b9f64..8fd3d8af11 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -1,8 +1,8 @@  - + - + @@ -214,7 +214,7 @@ - + @@ -293,7 +293,7 @@ - + @@ -305,10 +305,18 @@ - + + + + + + + + + - + @@ -496,7 +504,7 @@ - + diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index e20d721906..022c6473fd 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -26,8 +26,7 @@ using namespace OEngine::Render; OgreRenderer::~OgreRenderer() { cleanup(); - // restore system gamma ramp - SDL_SetWindowGammaRamp(mSDLWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]); + restoreWindowGammaRamp(); } void OgreRenderer::cleanup() @@ -145,7 +144,6 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& mWindow = helper.getWindow(); SDL_GetWindowGammaRamp(mSDLWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]); - setWindowGammaContrast(settings.gamma, settings.contrast); // create the semi-transparent black background texture used by the GUI. // has to be created in code with TU_DYNAMIC_WRITE_ONLY param @@ -171,6 +169,8 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& void OgreRenderer::setWindowGammaContrast(float gamma, float contrast) { + if (mSDLWindow == NULL) return; + Uint16 red[256], green[256], blue[256]; for (int i = 0; i < 256; i++) { @@ -184,7 +184,16 @@ void OgreRenderer::setWindowGammaContrast(float gamma, float contrast) red[i] = green[i] = blue[i] = value; } - SDL_SetWindowGammaRamp(mSDLWindow, red, green, blue); + if (SDL_SetWindowGammaRamp(mSDLWindow, red, green, blue) < 0) + std::cout << "Couldn't set gamma: " << SDL_GetError() << std::endl; +} + +void OgreRenderer::restoreWindowGammaRamp() +{ + if (mSDLWindow != NULL) + { + SDL_SetWindowGammaRamp(mSDLWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]); + } } void OgreRenderer::adjustCamera(float fov, float nearClip) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 6f2891e6a3..1b18a7b0bf 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -42,8 +42,6 @@ namespace OEngine int screen; std::string fsaa; std::string icon; - float gamma; - float contrast; }; class WindowSizeListener @@ -101,6 +99,7 @@ namespace OEngine void createWindow(const std::string &title, const WindowSettings& settings); void setWindowGammaContrast(float gamma, float contrast); + void restoreWindowGammaRamp(); /// Set up the scene manager, camera and viewport void adjustCamera( From d1a29300f0c6bad8671ce64fc10755c7af4e84c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 31 Dec 2014 16:59:21 +0100 Subject: [PATCH 170/404] Handle bipedal creatures not using weapons (Fixes #2238) --- apps/openmw/mwmechanics/character.cpp | 131 +++++++++++++++----------- 1 file changed, 78 insertions(+), 53 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 23ff9afff2..9f88d15735 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -296,7 +296,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat } const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); - if (!mPtr.getClass().hasInventoryStore(mPtr)) + if (!mPtr.getClass().isBipedal(mPtr)) weap = sWeaponTypeListEnd; if(force || idle != mIdleState) @@ -870,10 +870,26 @@ bool CharacterController::updateWeaponState() const MWWorld::Class &cls = mPtr.getClass(); CreatureStats &stats = cls.getCreatureStats(mPtr); WeaponType weaptype = WeapType_None; - MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); + if(stats.getDrawState() == DrawState_Weapon) + weaptype = WeapType_HandToHand; + else if (stats.getDrawState() == DrawState_Spell) + weaptype = WeapType_Spell; + const bool isWerewolf = cls.isNpc() && cls.getNpcStats(mPtr).isWerewolf(); + std::string soundid; + if (mPtr.getClass().hasInventoryStore(mPtr)) + { + MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); + if(weapon != inv.end() && !(weaptype == WeapType_None && mWeaponType == WeapType_Spell)) + { + soundid = (weaptype == WeapType_None) ? + weapon->getClass().getDownSoundId(*weapon) : + weapon->getClass().getUpSoundId(*weapon); + } + } + bool forcestateupdate = false; if(weaptype != mWeaponType && mHitState != CharState_KnockDown) { @@ -913,16 +929,10 @@ bool CharacterController::updateWeaponState() } } - if(weapon != inv.end() && !(weaptype == WeapType_None && mWeaponType == WeapType_Spell)) + if(!soundid.empty()) { - std::string soundid = (weaptype == WeapType_None) ? - weapon->getClass().getDownSoundId(*weapon) : - weapon->getClass().getUpSoundId(*weapon); - if(!soundid.empty()) - { - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f); - } + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f); } mWeaponType = weaptype; @@ -945,22 +955,28 @@ bool CharacterController::updateWeaponState() sndMgr->stopSound3D(mPtr, "WolfRun"); } - bool isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name()); - float weapSpeed = 1.0f; - if(isWeapon) - weapSpeed = weapon->get()->mBase->mData.mSpeed; - // Cancel attack if we no longer have ammunition bool ammunition = true; - MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); - if (mWeaponType == WeapType_Crossbow) - ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt); - else if (mWeaponType == WeapType_BowAndArrow) - ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Arrow); - if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped) + bool isWeapon = false; + float weapSpeed = 1.f; + if (mPtr.getClass().hasInventoryStore(mPtr)) { - mAnimation->disable(mCurrentWeapon); - mUpperBodyState = UpperCharState_WeapEquiped; + MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); + isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name()); + if(isWeapon) + weapSpeed = weapon->get()->mBase->mData.mSpeed; + + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (mWeaponType == WeapType_Crossbow) + ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt); + else if (mWeaponType == WeapType_BowAndArrow) + ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Arrow); + if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped) + { + mAnimation->disable(mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } } float complete; @@ -1001,15 +1017,14 @@ bool CharacterController::updateWeaponState() const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Hands"); if (mAnimation->getNode("Left Hand")) - { mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Left Hand", effect->mParticle); - mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Right Hand", effect->mParticle); - } else - { mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 L Hand", effect->mParticle); + + if (mAnimation->getNode("Right Hand")) + mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Right Hand", effect->mParticle); + else mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 R Hand", effect->mParticle); - } switch(effectentry.mRange) { @@ -1024,14 +1039,20 @@ bool CharacterController::updateWeaponState() 0.0f, 0); mUpperBodyState = UpperCharState_CastingSpell; } - if (inv.getSelectedEnchantItem() != inv.end()) + if (mPtr.getClass().hasInventoryStore(mPtr)) { - // Enchanted items cast immediately (no animation) - MWBase::Environment::get().getWorld()->castSpell(mPtr); + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + if (inv.getSelectedEnchantItem() != inv.end()) + { + // Enchanted items cast immediately (no animation) + MWBase::Environment::get().getWorld()->castSpell(mPtr); + } } + } else if(mWeaponType == WeapType_PickProbe) { + MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::Ptr item = *weapon; // TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes. MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); @@ -1063,7 +1084,10 @@ bool CharacterController::updateWeaponState() { if(isWeapon && mPtr.getRefData().getHandle() == "player" && Settings::Manager::getBool("best attack", "Game")) + { + MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight); mAttackType = getBestAttack(weapon->get()->mBase); + } else determineAttackType(); } @@ -1283,17 +1307,21 @@ bool CharacterController::updateWeaponState() } } - MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() - && updateCarriedLeftVisible(mWeaponType)) - + if (mPtr.getClass().hasInventoryStore(mPtr)) { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0), true); - } - else if (mAnimation->isPlaying("torch")) - { - mAnimation->disable("torch"); + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() + && updateCarriedLeftVisible(mWeaponType)) + + { + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0), true); + } + else if (mAnimation->isPlaying("torch")) + { + mAnimation->disable("torch"); + } } return forcestateupdate; @@ -1607,7 +1635,7 @@ void CharacterController::update(float duration) } } - if(cls.hasInventoryStore(mPtr)) + if(cls.isBipedal(mPtr)) forcestateupdate = updateWeaponState() || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; @@ -1839,15 +1867,12 @@ void CharacterController::determineAttackType() { float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition; - if(mPtr.getClass().hasInventoryStore(mPtr)) - { - if (move[1] && !move[0]) // forward-backward - mAttackType = "thrust"; - else if (move[0] && !move[1]) //sideway - mAttackType = "slash"; - else - mAttackType = "chop"; - } + if (move[1] && !move[0]) // forward-backward + mAttackType = "thrust"; + else if (move[0] && !move[1]) //sideway + mAttackType = "slash"; + else + mAttackType = "chop"; } bool CharacterController::isReadyToBlock() const From d26d5f6c26ed1d0044654fe3e0c0a3e0c1524ed6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 31 Dec 2014 17:25:06 +0100 Subject: [PATCH 171/404] Fix outdated bone locations when camera view is changed while paralyzed --- apps/openmw/mwmechanics/character.cpp | 69 ++++++++++++++------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9f88d15735..21dbc15b99 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1640,7 +1640,8 @@ void CharacterController::update(float duration) else forcestateupdate = updateCreatureState() || forcestateupdate; - refreshCurrentAnims(idlestate, movestate, forcestateupdate); + if (!mSkipAnim) + refreshCurrentAnims(idlestate, movestate, forcestateupdate); if (inJump) mMovementAnimationControlled = false; @@ -1672,47 +1673,47 @@ void CharacterController::update(float duration) // Can't reset jump state (mPosition[2]) here; we don't know for sure whether the PhysicSystem will actually handle it in this frame // due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled. - updateHeadTracking(duration); + if (!mSkipAnim) + updateHeadTracking(duration); } else if(cls.getCreatureStats(mPtr).isDead()) { world->queueMovement(mPtr, Ogre::Vector3(0.0f)); } - if(!mSkipAnim) - { - Ogre::Vector3 moved = mAnimation->runAnimation(duration); - if(duration > 0.0f) - moved /= duration; - else - moved = Ogre::Vector3(0.0f); - - // Ensure we're moving in generally the right direction... - if(mMovementSpeed > 0.f) - { - float l = moved.length(); - - if((movement.x < 0.0f && movement.x < moved.x*2.0f) || - (movement.x > 0.0f && movement.x > moved.x*2.0f)) - moved.x = movement.x; - if((movement.y < 0.0f && movement.y < moved.y*2.0f) || - (movement.y > 0.0f && movement.y > moved.y*2.0f)) - moved.y = movement.y; - if((movement.z < 0.0f && movement.z < moved.z*2.0f) || - (movement.z > 0.0f && movement.z > moved.z*2.0f)) - moved.z = movement.z; - // but keep the original speed - float newLength = moved.length(); - if (newLength > 0) - moved *= (l / newLength); - } + Ogre::Vector3 moved = mAnimation->runAnimation(mSkipAnim ? 0.f : duration); + if(duration > 0.0f) + moved /= duration; + else + moved = Ogre::Vector3(0.0f); - // Update movement - if(mMovementAnimationControlled && mPtr.getClass().isActor()) - world->queueMovement(mPtr, moved); + // Ensure we're moving in generally the right direction... + if(mMovementSpeed > 0.f) + { + float l = moved.length(); + + if((movement.x < 0.0f && movement.x < moved.x*2.0f) || + (movement.x > 0.0f && movement.x > moved.x*2.0f)) + moved.x = movement.x; + if((movement.y < 0.0f && movement.y < moved.y*2.0f) || + (movement.y > 0.0f && movement.y > moved.y*2.0f)) + moved.y = movement.y; + if((movement.z < 0.0f && movement.z < moved.z*2.0f) || + (movement.z > 0.0f && movement.z > moved.z*2.0f)) + moved.z = movement.z; + // but keep the original speed + float newLength = moved.length(); + if (newLength > 0) + moved *= (l / newLength); } - else + + if (mSkipAnim) mAnimation->updateEffects(duration); + + // Update movement + if(mMovementAnimationControlled && mPtr.getClass().isActor()) + world->queueMovement(mPtr, moved); + mSkipAnim = false; mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead()); @@ -1781,6 +1782,8 @@ void CharacterController::forceStateUpdate() { playRandomDeath(); } + + mAnimation->runAnimation(0.f); } bool CharacterController::kill() From a8ae0dec522914f801eec9869fae57506bb166f9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 31 Dec 2014 18:41:57 +0100 Subject: [PATCH 172/404] Implement AiWander fast-forward (Feature #1125) --- apps/openmw/mwmechanics/actors.cpp | 14 ++ apps/openmw/mwmechanics/actors.hpp | 3 + apps/openmw/mwmechanics/aipackage.hpp | 3 + apps/openmw/mwmechanics/aisequence.cpp | 9 + apps/openmw/mwmechanics/aisequence.hpp | 3 + apps/openmw/mwmechanics/aistate.hpp | 20 +- apps/openmw/mwmechanics/aitravel.cpp | 5 + apps/openmw/mwmechanics/aitravel.hpp | 3 + apps/openmw/mwmechanics/aiwander.cpp | 183 +++++++++--------- apps/openmw/mwmechanics/aiwander.hpp | 5 +- .../mwmechanics/mechanicsmanagerimp.cpp | 1 + 11 files changed, 141 insertions(+), 108 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1a1d8c1547..ca06b57d37 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1641,4 +1641,18 @@ namespace MWMechanics return it->second->getCharacterController()->isReadyToBlock(); } + + void Actors::fastForwardAi() + { + if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) + return; + for (PtrActorMap::iterator it = mActors.begin(); it != mActors.end(); ++it) + { + MWWorld::Ptr ptr = it->first; + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + continue; + MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); + seq.fastForward(ptr, it->second->getAiState()); + } + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 91bfad1708..39fe38208c 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -101,6 +101,9 @@ namespace MWMechanics int getHoursToRest(const MWWorld::Ptr& ptr) const; ///< Calculate how many hours the given actor needs to rest in order to be fully healed + void fastForwardAi(); + ///< Simulate the passing of time + int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index ca08de072c..80b48fc378 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -63,6 +63,9 @@ namespace MWMechanics virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {} + /// Simulates the passing of time + virtual void fastForward(const MWWorld::Ptr& actor, AiState& state) {} + protected: /// Causes the actor to attempt to walk to the specified location /** \return If the actor has arrived at his destination **/ diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 2ee8984050..ea59708c26 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -390,4 +390,13 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) } } +void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state) +{ + if (!mPackages.empty()) + { + MWMechanics::AiPackage* package = mPackages.front(); + package->fastForward(actor, state); + } +} + } // namespace MWMechanics diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 460a411ba8..e43ce72f1f 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -97,6 +97,9 @@ namespace MWMechanics /// Execute current package, switching if needed. void execute (const MWWorld::Ptr& actor, MWMechanics::AiState& state, float duration); + /// Simulate the passing of time using the currently active AI package + void fastForward(const MWWorld::Ptr &actor, AiState &state); + /// Remove all packages. void clear(); diff --git a/apps/openmw/mwmechanics/aistate.hpp b/apps/openmw/mwmechanics/aistate.hpp index 7b670ad476..581f45d078 100644 --- a/apps/openmw/mwmechanics/aistate.hpp +++ b/apps/openmw/mwmechanics/aistate.hpp @@ -76,24 +76,6 @@ namespace MWMechanics mStorage = p; } - /// \brief gives away ownership of object. Throws exception if storage does not contain Derived or is empty. - template< class Derived > - Derived* moveOut() - { - assert_derived(); - - - if(!mStorage) - throw std::runtime_error("Cant move out: empty storage."); - - Derived* result = dynamic_cast(mStorage); - - if(!mStorage) - throw std::runtime_error("Cant move out: wrong type requested."); - - return result; - } - bool empty() const { return mStorage == NULL; @@ -120,7 +102,7 @@ namespace MWMechanics /// \brief base class for the temporary storage of AiPackages. /** * Each AI package with temporary values needs a AiPackageStorage class - * which is derived from AiTemporaryBase. The CharacterController holds a container + * which is derived from AiTemporaryBase. The Actor holds a container * AiState where one of these storages can be stored at a time. * The execute(...) member function takes this container as an argument. * */ diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 9597849837..64bf8a61bb 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -113,6 +113,11 @@ namespace MWMechanics return TypeIdTravel; } + void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) + { + + } + void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr travel(new ESM::AiSequence::AiTravel()); diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index c2c33c2cf4..c2732e3aa4 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -23,6 +23,9 @@ namespace MWMechanics AiTravel(float x, float y, float z); AiTravel(const ESM::AiSequence::AiTravel* travel); + /// Simulates the passing of time + virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); + void writeState(ESM::AiSequence::AiSequence &sequence) const; virtual AiTravel *clone() const; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c8a0c85d58..20588e5c35 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -40,16 +40,9 @@ namespace MWMechanics AiWander::GreetingState mSaidGreeting; int mGreetingTimer; - - // Cached current cell location - int mCellX; - int mCellY; - // Cell location multiplied by ESM::Land::REAL_SIZE - float mXCell; - float mYCell; - + const MWWorld::CellStore* mCell; // for detecting cell change - + // AiWander states bool mChooseAction; bool mIdleNow; @@ -66,10 +59,6 @@ namespace MWMechanics mReaction(0), mSaidGreeting(AiWander::Greet_None), mGreetingTimer(0), - mCellX(std::numeric_limits::max()), - mCellY(std::numeric_limits::max()), - mXCell(0), - mYCell(0), mCell(NULL), mChooseAction(true), mIdleNow(false), @@ -183,7 +172,6 @@ namespace MWMechanics currentCell = actor.getCell(); mStoredAvailableNodes = false; // prob. not needed since mDistance = 0 } - const ESM::Cell *cell = currentCell->getCell(); cStats.setDrawState(DrawState_Nothing); cStats.setMovementFlag(CreatureStats::Flag_Run, false); @@ -371,81 +359,10 @@ namespace MWMechanics } } - - - int& cachedCellX = storage.mCellX; - int& cachedCellY = storage.mCellY; - float& cachedCellXposition = storage.mXCell; - float& cachedCellYposition = storage.mYCell; // Initialization to discover & store allowed node points for this actor. if(!mStoredAvailableNodes) { - // infrequently used, therefore no benefit in caching it as a member - const ESM::Pathgrid * - pathgrid = world->getStore().get().search(*cell); - - // cache the current cell location - cachedCellX = cell->mData.mX; - cachedCellY = cell->mData.mY; - - // If there is no path this actor doesn't go anywhere. See: - // https://forum.openmw.org/viewtopic.php?t=1556 - // http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833 - if(!pathgrid || pathgrid->mPoints.empty()) - mDistance = 0; - - // A distance value passed into the constructor indicates how far the - // actor can wander from the spawn position. AiWander assumes that - // pathgrid points are available, and uses them to randomly select wander - // destinations within the allowed set of pathgrid points (nodes). - if(mDistance) - { - cachedCellXposition = 0; - cachedCellYposition = 0; - if(cell->isExterior()) - { - cachedCellXposition = cachedCellX * ESM::Land::REAL_SIZE; - cachedCellYposition = cachedCellY * ESM::Land::REAL_SIZE; - } - - // FIXME: There might be a bug here. The allowed node points are - // based on the actor's current position rather than the actor's - // spawn point. As a result the allowed nodes for wander can change - // between saves, for example. - // - // convert npcPos to local (i.e. cell) co-ordinates - Ogre::Vector3 npcPos(pos.pos); - npcPos[0] = npcPos[0] - cachedCellXposition; - npcPos[1] = npcPos[1] - cachedCellYposition; - - // mAllowedNodes for this actor with pathgrid point indexes based on mDistance - // NOTE: mPoints and mAllowedNodes are in local co-ordinates - for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) - { - Ogre::Vector3 nodePos(pathgrid->mPoints[counter].mX, pathgrid->mPoints[counter].mY, - pathgrid->mPoints[counter].mZ); - if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) - mAllowedNodes.push_back(pathgrid->mPoints[counter]); - } - if(!mAllowedNodes.empty()) - { - Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); - float closestNode = npcPos.squaredDistance(firstNodePos); - unsigned int index = 0; - for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) - { - Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, - mAllowedNodes[counterThree].mZ); - float tempDist = npcPos.squaredDistance(nodePos); - if(tempDist < closestNode) - index = counterThree; - } - mCurrentNode = mAllowedNodes[index]; - mAllowedNodes.erase(mAllowedNodes.begin() + index); - - mStoredAvailableNodes = true; // set only if successful in finding allowed nodes - } - } + getAllowedNodes(actor, currentCell->getCell()); } // Actor becomes stationary - see above URL's for previous research @@ -581,8 +498,8 @@ namespace MWMechanics // convert dest to use world co-ordinates ESM::Pathgrid::Point dest; - dest.mX = destNodePos[0] + cachedCellXposition; - dest.mY = destNodePos[1] + cachedCellYposition; + dest.mX = destNodePos[0] + currentCell->getCell()->mData.mX * ESM::Land::REAL_SIZE; + dest.mY = destNodePos[1] + currentCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; dest.mZ = destNodePos[2]; // actor position is already in world co-ordinates @@ -732,6 +649,96 @@ namespace MWMechanics } } + void AiWander::fastForward(const MWWorld::Ptr& actor, AiState &state) + { + if (mDistance == 0) + return; + + if (!mStoredAvailableNodes) + getAllowedNodes(actor, actor.getCell()->getCell()); + + if (mAllowedNodes.empty()) + return; + + state.moveIn(new AiWanderStorage()); + + int index = std::rand() / (static_cast (RAND_MAX) + 1) * mAllowedNodes.size(); + ESM::Pathgrid::Point dest = mAllowedNodes[index]; + + // apply a slight offset to prevent overcrowding + dest.mX += Ogre::Math::RangeRandom(-64, 64); + dest.mY += Ogre::Math::RangeRandom(-64, 64); + + MWBase::Environment::get().getWorld()->moveObject(actor, dest.mX, dest.mY, dest.mZ); + actor.getClass().adjustPosition(actor, false); + } + + void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell) + { + // infrequently used, therefore no benefit in caching it as a member + const ESM::Pathgrid * + pathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); + + // If there is no path this actor doesn't go anywhere. See: + // https://forum.openmw.org/viewtopic.php?t=1556 + // http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833 + if(!pathgrid || pathgrid->mPoints.empty()) + mDistance = 0; + + // A distance value passed into the constructor indicates how far the + // actor can wander from the spawn position. AiWander assumes that + // pathgrid points are available, and uses them to randomly select wander + // destinations within the allowed set of pathgrid points (nodes). + if(mDistance) + { + float cellXOffset = 0; + float cellYOffset = 0; + if(cell->isExterior()) + { + cellXOffset = cell->mData.mX * ESM::Land::REAL_SIZE; + cellYOffset = cell->mData.mY * ESM::Land::REAL_SIZE; + } + + // FIXME: There might be a bug here. The allowed node points are + // based on the actor's current position rather than the actor's + // spawn point. As a result the allowed nodes for wander can change + // between saves, for example. + // + // convert npcPos to local (i.e. cell) co-ordinates + Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos); + npcPos[0] = npcPos[0] - cellXOffset; + npcPos[1] = npcPos[1] - cellYOffset; + + // mAllowedNodes for this actor with pathgrid point indexes based on mDistance + // NOTE: mPoints and mAllowedNodes are in local co-ordinates + for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) + { + Ogre::Vector3 nodePos(pathgrid->mPoints[counter].mX, pathgrid->mPoints[counter].mY, + pathgrid->mPoints[counter].mZ); + if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) + mAllowedNodes.push_back(pathgrid->mPoints[counter]); + } + if(!mAllowedNodes.empty()) + { + Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); + float closestNode = npcPos.squaredDistance(firstNodePos); + unsigned int index = 0; + for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) + { + Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, + mAllowedNodes[counterThree].mZ); + float tempDist = npcPos.squaredDistance(nodePos); + if(tempDist < closestNode) + index = counterThree; + } + mCurrentNode = mAllowedNodes[index]; + mAllowedNodes.erase(mAllowedNodes.begin() + index); + + mStoredAvailableNodes = true; // set only if successful in finding allowed nodes + } + } + } + void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr wander(new ESM::AiSequence::AiWander()); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index b9b394a264..cd0f6533e7 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -57,6 +57,7 @@ namespace MWMechanics virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; + virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); enum GreetingState { Greet_None, @@ -77,7 +78,6 @@ namespace MWMechanics int mTimeOfDay; std::vector mIdle; bool mRepeat; - bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position, @@ -98,6 +98,9 @@ namespace MWMechanics // allowed pathgrid nodes based on mDistance from the spawn point std::vector mAllowedNodes; + + void getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell); + ESM::Pathgrid::Point mCurrentNode; bool mTrimCurrentNode; void trimAllowedNodes(std::vector& nodes, diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 8c68334974..42728290be 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -472,6 +472,7 @@ namespace MWMechanics void MechanicsManager::rest(bool sleep) { mActors.restoreDynamicStats (sleep); + mActors.fastForwardAi(); } int MechanicsManager::getHoursToRest() const From 99ae0f901bf66729045aade887927419fcef67f0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 31 Dec 2014 18:55:07 +0100 Subject: [PATCH 173/404] Implement AiTravel fast-forward (Fixes #1125) --- apps/openmw/mwmechanics/aitravel.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 64bf8a61bb..7124a1102c 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -14,6 +14,19 @@ #include "movement.hpp" #include "creaturestats.hpp" +namespace +{ + +bool isWithinMaxRange(const Ogre::Vector3& pos1, const Ogre::Vector3& pos2) +{ + // Maximum travel distance for vanilla compatibility. + // Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well. + // We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways. + return (pos1.squaredDistance(pos2) <= 7168*7168); +} + +} + namespace MWMechanics { AiTravel::AiTravel(float x, float y, float z) @@ -71,10 +84,7 @@ namespace MWMechanics } } - // Maximum travel distance for vanilla compatibility. - // Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well. - // We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways. - if (Ogre::Vector3(mX, mY, mZ).squaredDistance(Ogre::Vector3(pos.pos)) > 7168*7168) + if (!isWithinMaxRange(Ogre::Vector3(mX, mY, mZ), Ogre::Vector3(pos.pos))) return false; bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; @@ -115,7 +125,12 @@ namespace MWMechanics void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) { - + if (!isWithinMaxRange(Ogre::Vector3(mX, mY, mZ), Ogre::Vector3(actor.getRefData().getPosition().pos))) + return; + // does not do any validation on the travel target (whether it's in air, inside collision geometry, etc), + // that is the user's responsibility + MWBase::Environment::get().getWorld()->moveObject(actor, mX, mY, mZ); + actor.getClass().adjustPosition(actor, false); } void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const From 5d7eb11596be16f9426f40535b4e422774b0e36c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 31 Dec 2014 21:04:13 +0100 Subject: [PATCH 174/404] Fix lights being rendered on the map (broken by d55fe43fc95bf) --- apps/openmw/mwrender/actors.cpp | 14 ++++++++++++++ apps/openmw/mwrender/actors.hpp | 5 ++++- apps/openmw/mwrender/objects.cpp | 14 -------------- apps/openmw/mwrender/objects.hpp | 3 --- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 06acf4b3c5..db666e890a 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -198,4 +198,18 @@ void Actors::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) mRendering->updateWaterRippleEmitterPtr (old, cur); } +void Actors::enableLights() +{ + PtrAnimationMap::const_iterator it = mAllActors.begin(); + for(;it != mAllActors.end();++it) + it->second->enableLights(true); +} + +void Actors::disableLights() +{ + PtrAnimationMap::const_iterator it = mAllActors.begin(); + for(;it != mAllActors.end();++it) + it->second->enableLights(false); +} + } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 177c8a732a..f81082e41b 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -42,9 +42,12 @@ namespace MWRender void insertNPC(const MWWorld::Ptr& ptr); void insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields); void insertActivator (const MWWorld::Ptr& ptr, bool addLight=false); - bool deleteObject (const MWWorld::Ptr& ptr); + bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? + void enableLights(); + void disableLights(); + void removeCell(MWWorld::CellStore* store); void update (Ogre::Camera* camera); diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 96cce178e8..9650830191 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -237,20 +237,6 @@ Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::CellStore* cell) return mBounds[cell]; } -void Objects::enableLights() -{ - PtrAnimationMap::const_iterator it = mObjects.begin(); - for(;it != mObjects.end();++it) - it->second->enableLights(true); -} - -void Objects::disableLights() -{ - PtrAnimationMap::const_iterator it = mObjects.begin(); - for(;it != mObjects.end();++it) - it->second->enableLights(false); -} - void Objects::update(float dt, Ogre::Camera* camera) { PtrAnimationMap::const_iterator it = mObjects.begin(); diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 02e974e2d8..adfe5ca26b 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -45,9 +45,6 @@ public: ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr); - void enableLights(); - void disableLights(); - void update (float dt, Ogre::Camera* camera); ///< per-frame update diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2cddbce75c..33649ea7cf 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -677,13 +677,13 @@ void RenderingManager::writeFog(MWWorld::CellStore* cell) void RenderingManager::disableLights(bool sun) { - mObjects->disableLights(); + mActors->disableLights(); sunDisable(sun); } void RenderingManager::enableLights(bool sun) { - mObjects->enableLights(); + mActors->enableLights(); sunEnable(sun); } From 93bbd7463ad1c0b9e97c1e9794f916b2ce1823c6 Mon Sep 17 00:00:00 2001 From: dteviot Date: Thu, 1 Jan 2015 09:40:42 +1300 Subject: [PATCH 175/404] Fixed errors and warnings from Travis CI. --- components/contentselector/model/contentmodel.cpp | 2 +- components/contentselector/model/loadordererror.cpp | 2 +- components/contentselector/model/loadordererror.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 72bca22444..b74960dc81 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "components/esm/esmreader.hpp" diff --git a/components/contentselector/model/loadordererror.cpp b/components/contentselector/model/loadordererror.cpp index c8b258e983..593c53abb3 100644 --- a/components/contentselector/model/loadordererror.cpp +++ b/components/contentselector/model/loadordererror.cpp @@ -5,7 +5,7 @@ QString ContentSelectorModel::LoadOrderError::sErrorToolTips[ErrorCode_LoadOrder { QString("Unable to find dependant file: %1"), QString("Dependent file needs to be active: %1"), - QString("This file needs to load after %1"), + QString("This file needs to load after %1") }; ContentSelectorModel::LoadOrderError ContentSelectorModel::LoadOrderError::sNoError = ContentSelectorModel::LoadOrderError(); diff --git a/components/contentselector/model/loadordererror.hpp b/components/contentselector/model/loadordererror.hpp index 75ec2ad928..f7a58c25f4 100644 --- a/components/contentselector/model/loadordererror.hpp +++ b/components/contentselector/model/loadordererror.hpp @@ -14,7 +14,7 @@ namespace ContentSelectorModel ErrorCode_None = 0, ErrorCode_MissingDependency = 1, ErrorCode_InactiveDependency = 2, - ErrorCode_LoadOrder = 3, + ErrorCode_LoadOrder = 3 }; inline LoadOrderError() : mErrorCode(ErrorCode_None) {}; From 43dd9aee94c70230181eea114e1165210fb5d510 Mon Sep 17 00:00:00 2001 From: dteviot Date: Thu, 1 Jan 2015 10:13:27 +1300 Subject: [PATCH 176/404] Fix for more errors found by Travis CI. --- components/contentselector/model/contentmodel.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index fc50eeb856..2a9f2c93bc 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -54,8 +54,8 @@ namespace ContentSelectorModel void uncheckAll(); bool isLoadOrderError(const QString& filepath) const; - ContentSelectorModel::LoadOrderError ContentSelectorModel::ContentModel::getLoadOrderError(const QString& filepath) const; - void ContentSelectorModel::ContentModel::setLoadOrderError(const QString& filepath, const ContentSelectorModel::LoadOrderError& loadOrderError); + LoadOrderError getLoadOrderError(const QString& filepath) const; + void setLoadOrderError(const QString& filepath, const LoadOrderError& loadOrderError); void refreshModel(); From 01652bbcc5d8c80868781a1e949eee5901870e8a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 31 Dec 2014 21:27:19 +0100 Subject: [PATCH 177/404] Store original actor position in AiWander package (Fixes #2200) --- apps/openmw/mwmechanics/aiwander.cpp | 18 ++++++---- apps/openmw/mwmechanics/aiwander.hpp | 3 +- components/CMakeLists.txt | 2 +- components/esm/aisequence.cpp | 8 +++++ components/esm/aisequence.hpp | 5 +++ components/esm/projectilestate.hpp | 39 ++------------------- components/esm/util.hpp | 51 ++++++++++++++++++++++++++++ 7 files changed, 81 insertions(+), 45 deletions(-) create mode 100644 components/esm/util.hpp diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 20588e5c35..95b597deff 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -70,6 +70,7 @@ namespace MWMechanics AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) + , mStoredInitialActorPosition(false) { mIdle.resize(8, 0); init(); @@ -675,6 +676,12 @@ namespace MWMechanics void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell) { + if (!mStoredInitialActorPosition) + { + mInitialActorPosition = Ogre::Vector3(actor.getRefData().getPosition().pos); + mStoredInitialActorPosition = true; + } + // infrequently used, therefore no benefit in caching it as a member const ESM::Pathgrid * pathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); @@ -699,13 +706,8 @@ namespace MWMechanics cellYOffset = cell->mData.mY * ESM::Land::REAL_SIZE; } - // FIXME: There might be a bug here. The allowed node points are - // based on the actor's current position rather than the actor's - // spawn point. As a result the allowed nodes for wander can change - // between saves, for example. - // // convert npcPos to local (i.e. cell) co-ordinates - Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos); + Ogre::Vector3 npcPos(mInitialActorPosition); npcPos[0] = npcPos[0] - cellXOffset; npcPos[1] = npcPos[1] - cellYOffset; @@ -750,6 +752,9 @@ namespace MWMechanics for (int i=0; i<8; ++i) wander->mData.mIdle[i] = mIdle[i]; wander->mData.mShouldRepeat = mRepeat; + wander->mStoredInitialActorPosition = mStoredInitialActorPosition; + if (mStoredInitialActorPosition) + wander->mInitialActorPosition = mInitialActorPosition; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Wander; @@ -763,6 +768,7 @@ namespace MWMechanics , mStartTime(MWWorld::TimeStamp(wander->mStartTime)) , mTimeOfDay(wander->mData.mTimeOfDay) , mRepeat(wander->mData.mShouldRepeat) + , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) { for (int i=0; i<8; ++i) mIdle.push_back(wander->mData.mIdle[i]); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index cd0f6533e7..975ebfb814 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -84,7 +84,8 @@ namespace MWMechanics // if we had the actor in the AiWander constructor... Ogre::Vector3 mReturnPosition; - + Ogre::Vector3 mInitialActorPosition; + bool mStoredInitialActorPosition; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 6918b87a7a..b36b898146 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -46,7 +46,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects + aisequence magiceffects util ) add_component_dir (esmterrain diff --git a/components/esm/aisequence.cpp b/components/esm/aisequence.cpp index 339b390d74..5b36f9a4cd 100644 --- a/components/esm/aisequence.cpp +++ b/components/esm/aisequence.cpp @@ -16,12 +16,20 @@ namespace AiSequence { esm.getHNT (mData, "DATA"); esm.getHNT(mStartTime, "STAR"); + mStoredInitialActorPosition = false; + if (esm.isNextSub("POS_")) + { + mStoredInitialActorPosition = true; + esm.getHT(mInitialActorPosition); + } } void AiWander::save(ESMWriter &esm) const { esm.writeHNT ("DATA", mData); esm.writeHNT ("STAR", mStartTime); + if (mStoredInitialActorPosition) + esm.writeHNT ("POS_", mInitialActorPosition); } void AiTravel::load(ESMReader &esm) diff --git a/components/esm/aisequence.hpp b/components/esm/aisequence.hpp index fbf83c2455..2560fbe7df 100644 --- a/components/esm/aisequence.hpp +++ b/components/esm/aisequence.hpp @@ -6,6 +6,8 @@ #include "defs.hpp" +#include "util.hpp" + namespace ESM { class ESMReader; @@ -61,6 +63,9 @@ namespace ESM AiWanderData mData; ESM::TimeStamp mStartTime; + bool mStoredInitialActorPosition; + ESM::Vector3 mInitialActorPosition; + /// \todo add more AiWander state void load(ESMReader &esm); diff --git a/components/esm/projectilestate.hpp b/components/esm/projectilestate.hpp index 6e36efb5b4..51cd5d8c40 100644 --- a/components/esm/projectilestate.hpp +++ b/components/esm/projectilestate.hpp @@ -8,48 +8,13 @@ #include "effectlist.hpp" +#include "util.hpp" + namespace ESM { // format 0, savegames only - struct Quaternion - { - float mValues[4]; - - Quaternion() {} - Quaternion (Ogre::Quaternion q) - { - mValues[0] = q.w; - mValues[1] = q.x; - mValues[2] = q.y; - mValues[3] = q.z; - } - - operator Ogre::Quaternion () const - { - return Ogre::Quaternion(mValues[0], mValues[1], mValues[2], mValues[3]); - } - }; - - struct Vector3 - { - float mValues[3]; - - Vector3() {} - Vector3 (Ogre::Vector3 v) - { - mValues[0] = v.x; - mValues[1] = v.y; - mValues[2] = v.z; - } - - operator Ogre::Vector3 () const - { - return Ogre::Vector3(&mValues[0]); - } - }; - struct BaseProjectileState { std::string mId; diff --git a/components/esm/util.hpp b/components/esm/util.hpp new file mode 100644 index 0000000000..bb7f3cf7cd --- /dev/null +++ b/components/esm/util.hpp @@ -0,0 +1,51 @@ +#ifndef OPENMW_ESM_UTIL_H +#define OPENMW_ESM_UTIL_H + +#include +#include + +namespace ESM +{ + +// format 0, savegames only + +struct Quaternion +{ + float mValues[4]; + + Quaternion() {} + Quaternion (Ogre::Quaternion q) + { + mValues[0] = q.w; + mValues[1] = q.x; + mValues[2] = q.y; + mValues[3] = q.z; + } + + operator Ogre::Quaternion () const + { + return Ogre::Quaternion(mValues[0], mValues[1], mValues[2], mValues[3]); + } +}; + +struct Vector3 +{ + float mValues[3]; + + Vector3() {} + Vector3 (Ogre::Vector3 v) + { + mValues[0] = v.x; + mValues[1] = v.y; + mValues[2] = v.z; + } + + operator Ogre::Vector3 () const + { + return Ogre::Vector3(&mValues[0]); + } +}; + +} + +#endif From 70d3bfc6ed02f51a126314448d14614e619354d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 31 Dec 2014 23:01:31 +0100 Subject: [PATCH 178/404] Fix idle animation not restarting immediately for creatures --- apps/openmw/mwmechanics/character.cpp | 74 ++++++++++++++------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 21dbc15b99..3136ae676a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -299,37 +299,6 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if (!mPtr.getClass().isBipedal(mPtr)) weap = sWeaponTypeListEnd; - if(force || idle != mIdleState) - { - mIdleState = idle; - - std::string idle; - // Only play "idleswim" or "idlesneak" if they exist. Otherwise, fallback to - // "idle"+weapon or "idle". - if(mIdleState == CharState_IdleSwim && mAnimation->hasAnimation("idleswim")) - idle = "idleswim"; - else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak")) - idle = "idlesneak"; - else if(mIdleState != CharState_None) - { - idle = "idle"; - if(weap != sWeaponTypeListEnd) - { - idle += weap->shortgroup; - if(!mAnimation->hasAnimation(idle)) - idle = "idle"; - } - } - - mAnimation->disable(mCurrentIdle); - mCurrentIdle = idle; - if(!mCurrentIdle.empty()) - mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false, - 1.0f, "start", "stop", 0.0f, ~0ul, true); - } - - updateIdleStormState(); - if(force && mJumpState != JumpState_None) { std::string jump; @@ -470,6 +439,44 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } } + + // idle handled last as it can depend on the other states + if ((mUpperBodyState != UpperCharState_Nothing + || mMovementState != CharState_None + || mHitState != CharState_None) + && !mPtr.getClass().isBipedal(mPtr)) + idle = CharState_None; + + if(force || idle != mIdleState) + { + mIdleState = idle; + + std::string idle; + // Only play "idleswim" or "idlesneak" if they exist. Otherwise, fallback to + // "idle"+weapon or "idle". + if(mIdleState == CharState_IdleSwim && mAnimation->hasAnimation("idleswim")) + idle = "idleswim"; + else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak")) + idle = "idlesneak"; + else if(mIdleState != CharState_None) + { + idle = "idle"; + if(weap != sWeaponTypeListEnd) + { + idle += weap->shortgroup; + if(!mAnimation->hasAnimation(idle)) + idle = "idle"; + } + } + + mAnimation->disable(mCurrentIdle); + mCurrentIdle = idle; + if(!mCurrentIdle.empty()) + mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false, + 1.0f, "start", "stop", 0.0f, ~0ul, true); + } + + updateIdleStormState(); } @@ -1616,11 +1623,6 @@ void CharacterController::update(float duration) if(mAnimQueue.empty()) { idlestate = (inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)); - if ((mUpperBodyState != UpperCharState_Nothing - || mMovementState != CharState_None - || mHitState != CharState_None) - && !mPtr.getClass().isBipedal(mPtr)) - idlestate = CharState_None; } else if(mAnimQueue.size() > 1) { From e0d083f702342f00df07d4d58cd7d43597e23ceb Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 31 Dec 2014 23:13:36 +0100 Subject: [PATCH 179/404] Add hand-to-hand combat mechanics for bipedal creatures You can now have a fistfight with vivec, if you so desire. --- apps/openmw/mwclass/creature.cpp | 8 +++++-- apps/openmw/mwclass/npc.cpp | 29 +------------------------ apps/openmw/mwmechanics/combat.cpp | 35 ++++++++++++++++++++++++++++++ apps/openmw/mwmechanics/combat.hpp | 2 ++ 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 59ab9dc902..2146fdfdea 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -288,7 +288,7 @@ namespace MWClass } float damage = min + (max - min) * stats.getAttackStrength(); - + bool healthdmg = true; if (!weapon.isEmpty()) { const unsigned char *attack = NULL; @@ -321,6 +321,10 @@ namespace MWClass } } } + else if (isBipedal(ptr)) + { + MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg); + } MWMechanics::applyElementalShields(ptr, victim); @@ -332,7 +336,7 @@ namespace MWClass MWMechanics::diseaseContact(victim, ptr); - victim.getClass().onHit(victim, damage, true, weapon, ptr, true); + victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, true); } void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 30445612ac..22263d820d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -577,34 +577,7 @@ namespace MWClass } else { - // Note: MCP contains an option to include Strength in hand-to-hand damage - // calculations. Some mods recommend using it, so we may want to include am - // option for it. - float minstrike = store.find("fMinHandToHandMult")->getFloat(); - float maxstrike = store.find("fMaxHandToHandMult")->getFloat(); - damage = stats.getSkill(weapskill).getModified(); - damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); - - healthdmg = (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) - || otherstats.getKnockedDown(); - if(stats.isWerewolf()) - { - healthdmg = true; - // GLOB instead of GMST because it gets updated during a quest - damage *= world->getGlobalFloat("werewolfclawmult"); - } - if(healthdmg) - damage *= store.find("fHandtoHandHealthPer")->getFloat(); - - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(stats.isWerewolf()) - { - const ESM::Sound *sound = world->getStore().get().searchRandom("WolfHit"); - if(sound) - sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f); - } - else - sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f); + MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg); } if(ptr.getRefData().getHandle() == "player") { diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index e52dbdde71..459ff8aefb 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -332,4 +332,39 @@ namespace MWMechanics damage *= (float(weaphealth) / weapmaxhealth); } } + + void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg) + { + // Note: MCP contains an option to include Strength in hand-to-hand damage + // calculations. Some mods recommend using it, so we may want to include an + // option for it. + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + float minstrike = store.get().find("fMinHandToHandMult")->getFloat(); + float maxstrike = store.get().find("fMaxHandToHandMult")->getFloat(); + damage = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand); + damage *= minstrike + ((maxstrike-minstrike)*attacker.getClass().getCreatureStats(attacker).getAttackStrength()); + + MWMechanics::CreatureStats& otherstats = victim.getClass().getCreatureStats(victim); + healthdmg = (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + || otherstats.getKnockedDown(); + bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()); + if(isWerewolf) + { + healthdmg = true; + // GLOB instead of GMST because it gets updated during a quest + damage *= MWBase::Environment::get().getWorld()->getGlobalFloat("werewolfclawmult"); + } + if(healthdmg) + damage *= store.get().find("fHandtoHandHealthPer")->getFloat(); + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(isWerewolf) + { + const ESM::Sound *sound = store.get().searchRandom("WolfHit"); + if(sound) + sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f); + } + else + sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f); + } } diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index 0d3009510b..c6fc9ff927 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -32,6 +32,8 @@ void reduceWeaponCondition (float damage, bool hit, MWWorld::Ptr& weapon, const /// Adjust weapon damage based on its condition. A used weapon will be less effective. void adjustWeaponDamage (float& damage, const MWWorld::Ptr& weapon); +void getHandToHandDamage (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, float& damage, bool& healthdmg); + } #endif From dc1c52bda7b850ac0905c376579efa7d03489c64 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 1 Jan 2015 02:46:18 +0100 Subject: [PATCH 180/404] Add some todo comments --- apps/openmw/mwmechanics/actors.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ca06b57d37..753c4da88a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -73,6 +73,8 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) if (charge == 0) return false; + // FIXME: charge should be a float, not int so that damage < 1 per frame can be applied. + // This was also a bug in the original engine. charge -= std::min(disintegrate, static_cast(charge)); @@ -522,6 +524,9 @@ namespace MWMechanics bool wasDead = creatureStats.isDead(); + // FIXME: effect ticks should go into separate functions so they can be used with either + // magnitude (instant effect) or magnitude*duration + // attributes for(int i = 0;i < ESM::Attribute::Length;++i) { @@ -731,6 +736,8 @@ namespace MWMechanics } // Update bound effects + // Note: in vanilla MW multiple bound items of the same type can be created by different spells. + // As these extra copies are kinda useless this may or may not be important. static std::map boundItemsMap; if (boundItemsMap.empty()) { From fb671fed20eb7e254665ddaab865690db3a761aa Mon Sep 17 00:00:00 2001 From: dteviot Date: Thu, 1 Jan 2015 15:53:35 +1300 Subject: [PATCH 181/404] Corrected issues found by Scrawl. --- components/contentselector/model/contentmodel.cpp | 6 +++--- components/contentselector/model/loadordererror.cpp | 2 +- components/contentselector/model/loadordererror.hpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index b74960dc81..6366d7f540 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -592,9 +592,9 @@ void ContentSelectorModel::ContentModel::checkForLoadOrderErrors() EsmFile* file = item(row); bool isRowInError = isLoadOrderError(file->filePath()); LoadOrderError::ErrorCode error = LoadOrderError::ErrorCode_None; - foreach(QString dependantfileName, file->gameFiles()) + foreach(QString dependentfileName, file->gameFiles()) { - const EsmFile* dependentFile = item(dependantfileName); + const EsmFile* dependentFile = item(dependentfileName); if (!dependentFile) { @@ -611,7 +611,7 @@ void ContentSelectorModel::ContentModel::checkForLoadOrderErrors() if (!isRowInError && (error != LoadOrderError::ErrorCode_None)) { - setLoadOrderError(file->filePath(), LoadOrderError(error, dependantfileName)); + setLoadOrderError(file->filePath(), LoadOrderError(error, dependentfileName)); break; } } diff --git a/components/contentselector/model/loadordererror.cpp b/components/contentselector/model/loadordererror.cpp index 593c53abb3..0d02efbebc 100644 --- a/components/contentselector/model/loadordererror.cpp +++ b/components/contentselector/model/loadordererror.cpp @@ -3,7 +3,7 @@ QString ContentSelectorModel::LoadOrderError::sErrorToolTips[ErrorCode_LoadOrder] = { - QString("Unable to find dependant file: %1"), + QString("Unable to find dependent file: %1"), QString("Dependent file needs to be active: %1"), QString("This file needs to load after %1") }; diff --git a/components/contentselector/model/loadordererror.hpp b/components/contentselector/model/loadordererror.hpp index f7a58c25f4..f3d3b0f8ae 100644 --- a/components/contentselector/model/loadordererror.hpp +++ b/components/contentselector/model/loadordererror.hpp @@ -5,7 +5,7 @@ namespace ContentSelectorModel { - /// \Details of a suspected Load Order problem a plug-in will have. This is basically a POD + /// \brief Details of a suspected Load Order problem a plug-in will have. This is basically a POD. class LoadOrderError { public: @@ -28,7 +28,7 @@ namespace ContentSelectorModel bool operator==(const LoadOrderError& rhs) const; QString toolTip() const; - /// \Sentinal to represent a "No Load Order Error" condition + /// Sentinel to represent a "No Load Order Error" condition static LoadOrderError sNoError; private: From a7a3ab0c78aef3f601e5e7006fa664a8abdf6a7e Mon Sep 17 00:00:00 2001 From: Internecine Date: Thu, 1 Jan 2015 21:26:09 +1300 Subject: [PATCH 182/404] Fixed instant negative dynamic stat changes being applied as positive --- apps/openmw/mwmechanics/spellcasting.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 352db88b4c..71064d9b0d 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -578,15 +578,27 @@ namespace MWMechanics value.restore(magnitude); target.getClass().getCreatureStats(target).setAttribute(attribute, value); } - else if (effectId == ESM::MagicEffect::DamageHealth || effectId == ESM::MagicEffect::RestoreHealth) + else if (effectId == ESM::MagicEffect::DamageHealth) { - applyDynamicStatsEffect(0, target, magnitude); + applyDynamicStatsEffect(0, target, magnitude * -1); } - else if (effectId == ESM::MagicEffect::DamageFatigue || effectId == ESM::MagicEffect::RestoreFatigue) + else if (effectId == ESM::MagicEffect::RestoreHealth) { applyDynamicStatsEffect(2, target, magnitude); } - else if (effectId == ESM::MagicEffect::DamageMagicka || effectId == ESM::MagicEffect::RestoreMagicka) + else if (effectId == ESM::MagicEffect::DamageFatigue) + { + applyDynamicStatsEffect(2, target, magnitude * -1); + } + else if (effectId == ESM::MagicEffect::RestoreFatigue) + { + applyDynamicStatsEffect(2, target, magnitude); + } + else if (effectId == ESM::MagicEffect::DamageMagicka) + { + applyDynamicStatsEffect(1, target, magnitude * -1); + } + else if (effectId == ESM::MagicEffect::RestoreMagicka) { applyDynamicStatsEffect(1, target, magnitude); } From 6cd5b78ca79212ca962fc3f69c0ea285f2aa32bd Mon Sep 17 00:00:00 2001 From: Marco Schulze Date: Thu, 1 Jan 2015 14:15:05 -0300 Subject: [PATCH 183/404] Fix missing include in apps/launcher/main.cpp --- apps/launcher/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 3dc82bc54d..e4fae74f4d 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include From 559ddbb480f1d4a322585fe15a8c57fdb50c46d5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 1 Jan 2015 18:11:37 +0100 Subject: [PATCH 184/404] Quick fix for Ai fast-forward crash in exteriors (Fixes #2241) --- apps/openmw/mwmechanics/actors.cpp | 5 ++++- apps/openmw/mwmechanics/aiwander.cpp | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 753c4da88a..1055e0f4ae 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1653,7 +1653,10 @@ namespace MWMechanics { if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) return; - for (PtrActorMap::iterator it = mActors.begin(); it != mActors.end(); ++it) + + // making a copy since fast-forward could move actor to a different cell and invalidate the mActors iterator + PtrActorMap map = mActors; + for (PtrActorMap::iterator it = map.begin(); it != map.end(); ++it) { MWWorld::Ptr ptr = it->first; if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 95b597deff..8ca78b6edd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -499,9 +499,14 @@ namespace MWMechanics // convert dest to use world co-ordinates ESM::Pathgrid::Point dest; - dest.mX = destNodePos[0] + currentCell->getCell()->mData.mX * ESM::Land::REAL_SIZE; - dest.mY = destNodePos[1] + currentCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; + dest.mX = destNodePos[0]; + dest.mY = destNodePos[1]; dest.mZ = destNodePos[2]; + if (currentCell->getCell()->isExterior()) + { + dest.mX += currentCell->getCell()->mData.mX * ESM::Land::REAL_SIZE; + dest.mY += currentCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; + } // actor position is already in world co-ordinates ESM::Pathgrid::Point start; @@ -670,6 +675,12 @@ namespace MWMechanics dest.mX += Ogre::Math::RangeRandom(-64, 64); dest.mY += Ogre::Math::RangeRandom(-64, 64); + if (actor.getCell()->isExterior()) + { + dest.mX += actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE; + dest.mY += actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE; + } + MWBase::Environment::get().getWorld()->moveObject(actor, dest.mX, dest.mY, dest.mZ); actor.getClass().adjustPosition(actor, false); } From 92e4a0669c841733e3a8ae6a3cbab707ec350b9e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 1 Jan 2015 18:58:17 +0100 Subject: [PATCH 185/404] Fix for AiWander state loading --- apps/openmw/mwmechanics/aiwander.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 8ca78b6edd..2df3762ab5 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -781,6 +781,8 @@ namespace MWMechanics , mRepeat(wander->mData.mShouldRepeat) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) { + if (mStoredInitialActorPosition) + mInitialActorPosition = wander->mInitialActorPosition; for (int i=0; i<8; ++i) mIdle.push_back(wander->mData.mIdle[i]); From 326d0d3ebf13244659dbae72dfb528e7df0a5963 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 1 Jan 2015 22:54:39 +0100 Subject: [PATCH 186/404] Add default values for fNPCHealthBarTime and fNPCHealthBarFade (Fixes #2243) These GMSTs are missing in unpatched versions of the game. --- apps/openmw/mwworld/worldimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fdfa19af42..1c9b8b9960 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -393,6 +393,8 @@ namespace MWWorld gmst["sTauntFail"] = ESM::Variant("Taunt Fail"); gmst["sBribeSuccess"] = ESM::Variant("Bribe Success"); gmst["sBribeFail"] = ESM::Variant("Bribe Fail"); + gmst["fNPCHealthBarTime"] = ESM::Variant(5.f); + gmst["fNPCHealthBarFade"] = ESM::Variant(1.f); // Werewolf (BM) gmst["fWereWolfRunMult"] = ESM::Variant(1.f); From 17fb7aa5982207f4c918fadd8b582e5d65538e2c Mon Sep 17 00:00:00 2001 From: Thoronador Date: Sun, 26 Oct 2014 17:51:08 +0100 Subject: [PATCH 187/404] uninitialized stuff --- apps/opencs/model/world/refidadapterimp.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index d9a691abdc..27406569bc 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -223,7 +223,14 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD } CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) -: ActorColumns (actorColumns) +: ActorColumns (actorColumns), + mType(NULL), + mSoul(NULL), + mScale(NULL), + mOriginal(NULL), + mCombat(NULL), + mMagic(NULL), + mStealth(NULL) {} CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) @@ -431,7 +438,15 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& InventoryRefIdAdapter::setData (column, data, index, value); } -CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns) {} +CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) +: ActorColumns (actorColumns), + mFlags(std::map()), + mRace(NULL), + mClass(NULL), + mFaction(NULL), + mHair(NULL), + mHead(NULL) +{} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) : ActorRefIdAdapter (UniversalId::Type_Npc, columns), mColumns (columns) @@ -587,4 +602,4 @@ void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData else EnchantableRefIdAdapter::setData (column, data, index, value); } -} \ No newline at end of file +} From 91ff5364602f1aedc5e7ef49e4bf91f9d8bc2b42 Mon Sep 17 00:00:00 2001 From: Thoronador Date: Sun, 26 Oct 2014 18:18:54 +0100 Subject: [PATCH 188/404] fix uninit, #2 --- apps/opencs/model/world/universalid.cpp | 1 + apps/opencs/view/doc/loader.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 190ea87d8a..d19959d44d 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -131,6 +131,7 @@ namespace } CSMWorld::UniversalId::UniversalId (const std::string& universalId) +: mIndex(0) { std::string::size_type index = universalId.find (':'); diff --git a/apps/opencs/view/doc/loader.cpp b/apps/opencs/view/doc/loader.cpp index ca7c93f9df..27dfd59e5b 100644 --- a/apps/opencs/view/doc/loader.cpp +++ b/apps/opencs/view/doc/loader.cpp @@ -18,7 +18,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event) } CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) -: mDocument (document), mAborted (false), mMessages (0) +: mDocument (document), mAborted (false), mMessages (0), mTotalRecords (0) { setWindowTitle (("Opening " + document->getSavePath().filename().string()).c_str()); @@ -199,4 +199,4 @@ void CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string& if (iter!=mDocuments.end()) iter->second->addMessage (message); -} \ No newline at end of file +} From 526fb1b37b8f19d472744927bc8a2b8ebf51b20d Mon Sep 17 00:00:00 2001 From: Thoronador Date: Fri, 2 Jan 2015 00:45:50 +0100 Subject: [PATCH 189/404] fix uninitialized value in BillboardObject --- apps/openmw/mwrender/sky.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 131e12a5c7..385e7d8c59 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -74,6 +74,7 @@ BillboardObject::BillboardObject( const String& textureName, const Vector3& position, SceneNode* rootNode, const std::string& material) +: mVisibility(1.0f) { SceneManager* sceneMgr = rootNode->getCreator(); From 87fac78823149c9fcc6c2c25e3e8be145593d254 Mon Sep 17 00:00:00 2001 From: Thoronador Date: Fri, 2 Jan 2015 01:01:37 +0100 Subject: [PATCH 190/404] fix uninitialized members in Cell and structures --- components/esm/loadcell.hpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index a24b106d40..33e4ce61e5 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -70,15 +70,27 @@ struct Cell { int mFlags; int mX, mY; + + DATAstruct() : mFlags(0), mX(0), mY(0) {} }; struct AMBIstruct { Color mAmbient, mSunlight, mFog; float mFogDensity; + + AMBIstruct() : mAmbient(0), mSunlight(0), mFog(0), mFogDensity(0) {} }; - Cell() : mWater(0) {} + Cell() : mWater(0), + mName(""), + mRegion(""), + mData(DATAstruct()), + mAmbi(AMBIstruct()), + mWaterInt(false), + mMapColor(0), + mRefNumCounter(0) + {} // Interior cells are indexed by this (it's the 'id'), for exterior // cells it is optional. From 9cc219ff762e383425f474d9a8782e84d4cc753a Mon Sep 17 00:00:00 2001 From: Thoronador Date: Fri, 2 Jan 2015 01:11:31 +0100 Subject: [PATCH 191/404] fix uninitialized members in Pathgrid::Point --- components/esm/loadpgrd.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index 7fdc9a43ca..61f56b5117 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -8,18 +8,28 @@ namespace ESM { unsigned int Pathgrid::sRecordId = REC_PGRD; - Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[3]) { + Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[3]) + { mX = rhs[0]; mY = rhs[1]; mZ = rhs[2]; + mAutogenerated = 0; + mConnectionNum = 0; + mUnknown = 0; return *this; } - Pathgrid::Point::Point(const float rhs[3]) { + Pathgrid::Point::Point(const float rhs[3]) + : mAutogenerated(0), + mConnectionNum(0), + mUnknown(0) + { mX = rhs[0]; mY = rhs[1]; mZ = rhs[2]; } - Pathgrid::Point::Point():mX(0),mY(0),mZ(0) { + Pathgrid::Point::Point():mX(0),mY(0),mZ(0),mAutogenerated(0), + mConnectionNum(0),mUnknown(0) + { } void Pathgrid::load(ESMReader &esm) From 3b00a24f3fc0bdbe927944b258acb1c518524747 Mon Sep 17 00:00:00 2001 From: Thoronador Date: Fri, 2 Jan 2015 01:33:40 +0100 Subject: [PATCH 192/404] fix some uninitialized stuff in ShaderBasedRenderManager --- libs/openengine/gui/manager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 30a5b938c9..512c7f0698 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -103,6 +103,9 @@ public: mVertexProgramOneTexture(NULL), mFragmentProgramOneTexture(NULL) { + mTextureAddressMode.u = Ogre::TextureUnitState::TAM_CLAMP; + mTextureAddressMode.v = Ogre::TextureUnitState::TAM_CLAMP; + mTextureAddressMode.w = Ogre::TextureUnitState::TAM_CLAMP; } void initialise(Ogre::RenderWindow* _window, Ogre::SceneManager* _scene) From 44e01d0eaaad984e3a8014bbe7f7b4e6b19c6d12 Mon Sep 17 00:00:00 2001 From: Thoronador Date: Fri, 2 Jan 2015 02:23:10 +0100 Subject: [PATCH 193/404] remove redundant initialization statement See https://github.com/OpenMW/openmw/pull/423#discussion_r22403388 --- apps/opencs/model/world/refidadapterimp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 27406569bc..47caa8d71d 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -440,7 +440,6 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), - mFlags(std::map()), mRace(NULL), mClass(NULL), mFaction(NULL), From 4edc4142f3b4f1cde4d99392045d5d25858e6bf7 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Tue, 28 Oct 2014 04:03:00 -0400 Subject: [PATCH 194/404] Made NIFStream getters templated --- components/nif/nifstream.cpp | 29 +++++++++++++++++++++++++++++ components/nif/nifstream.hpp | 4 ++++ 2 files changed, 33 insertions(+) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index e5699db7b9..4464c8af52 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -143,4 +143,33 @@ void NIFStream::getQuaternions(std::vector &quat, size_t size) quat[i] = getQuaternion(); } +template <> +char NIFStream::get(){ return getChar(); } +template <> +short NIFStream::get(){ return getShort(); } +template <> +unsigned short NIFStream::get(){ return getUShort(); } +template <> +int NIFStream::get(){ return getInt(); } +template <> +unsigned int NIFStream::get(){ return getUInt(); } +template <> +float NIFStream::get(){ return getFloat(); } + +template <> +Ogre::Vector2 NIFStream::get(){ return getVector2(); } +template <> +Ogre::Vector3 NIFStream::get(){ return getVector3(); } +template <> +Ogre::Vector4 NIFStream::get(){ return getVector4(); } +template <> +Ogre::Matrix3 NIFStream::get(){ return getMatrix3(); } +template <> +Ogre::Quaternion NIFStream::get(){ return getQuaternion(); } +template <> +Transformation NIFStream::get(){ return getTrafo(); } + +template <> +std::string NIFStream::get(){ return getString(); } + } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index cc14971fd5..f32d233b1b 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -84,6 +84,10 @@ public: ///This is special since the version string doesn't start with a number, and ends with "\n" std::string getVersionString(); + //Templated functions to handle reads + template + T get(){throw std::runtime_error("Can not get this type of data from a NIF File!");} + void getShorts(std::vector &vec, size_t size); void getFloats(std::vector &vec, size_t size); void getVector2s(std::vector &vec, size_t size); From ce7cef924e80ab551066e779446dcb86c25cb4c9 Mon Sep 17 00:00:00 2001 From: Scott Howard Date: Thu, 1 Jan 2015 22:27:08 -0500 Subject: [PATCH 195/404] when loading a file fails, pop-up critical window and highlight error text --- apps/opencs/view/doc/loader.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/doc/loader.cpp b/apps/opencs/view/doc/loader.cpp index ca7c93f9df..3f3163f261 100644 --- a/apps/opencs/view/doc/loader.cpp +++ b/apps/opencs/view/doc/loader.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "../../model/doc/document.hpp" @@ -104,8 +106,10 @@ void CSVDoc::LoadingDocument::nextRecord (int records) void CSVDoc::LoadingDocument::abort (const std::string& error) { mAborted = true; - mError->setText (QString::fromUtf8 (("Loading failed: " + error).c_str())); + mError->setText (QString::fromUtf8 (("Loading failed: " + error + "").c_str())); mButtons->setStandardButtons (QDialogButtonBox::Close); + QMessageBox::critical(this, tr("OpenCS Loading Failed"), + QString::fromUtf8 (("Loading failed:\n" + error).c_str())); } void CSVDoc::LoadingDocument::addMessage (const std::string& message) @@ -199,4 +203,4 @@ void CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string& if (iter!=mDocuments.end()) iter->second->addMessage (message); -} \ No newline at end of file +} From f318ee0b8c68a46d53a0fdd216ae8d6b371eedc2 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Tue, 28 Oct 2014 04:13:27 -0400 Subject: [PATCH 196/404] Add a templated option for getting vectors to NIFStream --- components/nif/nifstream.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index f32d233b1b..083147867e 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -94,6 +94,20 @@ public: void getVector3s(std::vector &vec, size_t size); void getVector4s(std::vector &vec, size_t size); void getQuaternions(std::vector &quat, size_t size); + + ///Return a vector of whatever object is needed + template + std::vector getItems(size_t number_of_items) + { + std::vector items; + items.reserve(number_of_items); + for(size_t i=0; i < number_of_items; ++i) + { + items.push_back(get()); + } + return items; + } + }; } From 2619d57bb6afc5c31bf1a90b8c033d66f29a9a58 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Tue, 28 Oct 2014 04:50:28 -0400 Subject: [PATCH 197/404] Converted most nifstream "get multiple" functions to the templated version --- components/nif/data.hpp | 16 ++++++++-------- components/nif/nifstream.cpp | 37 ------------------------------------ components/nif/nifstream.hpp | 7 ------- 3 files changed, 8 insertions(+), 52 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index d9de12fb54..e6d3370be8 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -44,16 +44,16 @@ public: int verts = nif->getUShort(); if(nif->getInt()) - nif->getVector3s(vertices, verts); + vertices = nif->getItems(verts); if(nif->getInt()) - nif->getVector3s(normals, verts); + normals = nif->getItems(verts); center = nif->getVector3(); radius = nif->getFloat(); if(nif->getInt()) - nif->getVector4s(colors, verts); + colors = nif->getItems(verts); // Only the first 6 bits are used as a count. I think the rest are // flags of some sort. @@ -64,7 +64,7 @@ public: { uvlist.resize(uvs); for(int i = 0;i < uvs;i++) - nif->getVector2s(uvlist[i], verts); + uvlist[i] = nif->getItems(verts); } } }; @@ -84,7 +84,7 @@ public: // We have three times as many vertices as triangles, so this // is always equal to tris*3. int cnt = nif->getInt(); - nif->getShorts(triangles, cnt); + triangles = nif->getItems(cnt); // Read the match list, which lists the vertices that are equal to // vertices. We don't actually need need this for anything, so @@ -123,7 +123,7 @@ public: if(nif->getInt()) { // Particle sizes - nif->getFloats(sizes, vertices.size()); + sizes = nif->getItems(vertices.size()); } } }; @@ -140,7 +140,7 @@ public: if(nif->getInt()) { // Rotation quaternions. - nif->getQuaternions(rotations, vertices.size()); + rotations = nif->getItems(vertices.size()); } } }; @@ -341,7 +341,7 @@ struct NiMorphData : public Record for(int i = 0;i < morphCount;i++) { mMorphs[i].mData.read(nif, true); - nif->getVector3s(mMorphs[i].mVertices, vertCount); + mMorphs[i].mVertices = nif->getItems(vertCount); } } }; diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 4464c8af52..9eeee6a12b 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -106,43 +106,6 @@ std::string NIFStream::getVersionString() return inp->getLine(); } -void NIFStream::getShorts(std::vector &vec, size_t size) -{ - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getShort(); -} -void NIFStream::getFloats(std::vector &vec, size_t size) -{ - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getFloat(); -} -void NIFStream::getVector2s(std::vector &vec, size_t size) -{ - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector2(); -} -void NIFStream::getVector3s(std::vector &vec, size_t size) -{ - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector3(); -} -void NIFStream::getVector4s(std::vector &vec, size_t size) -{ - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector4(); -} -void NIFStream::getQuaternions(std::vector &quat, size_t size) -{ - quat.resize(size); - for(size_t i = 0;i < quat.size();i++) - quat[i] = getQuaternion(); -} - template <> char NIFStream::get(){ return getChar(); } template <> diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 083147867e..b9caa15368 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -88,13 +88,6 @@ public: template T get(){throw std::runtime_error("Can not get this type of data from a NIF File!");} - void getShorts(std::vector &vec, size_t size); - void getFloats(std::vector &vec, size_t size); - void getVector2s(std::vector &vec, size_t size); - void getVector3s(std::vector &vec, size_t size); - void getVector4s(std::vector &vec, size_t size); - void getQuaternions(std::vector &quat, size_t size); - ///Return a vector of whatever object is needed template std::vector getItems(size_t number_of_items) From c1315ed90c87a457f17e6076c149465da3fa6c3a Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Thu, 1 Jan 2015 21:51:54 -0500 Subject: [PATCH 198/404] Build the nif file tester by default It's extremely useful in determining if a nif file is bad without having to load up openmw or opencs. Also updated the nif testing script to run at a low priority. --- CMakeLists.txt | 2 +- components/nif/tests/test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7c85818e8..fb89f4e91d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_WIZARD "build Installation Wizard" ON) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest and GMock frameworks" OFF) -option(BUILD_NIFTEST "build nif file tester" OFF) +option(BUILD_NIFTEST "build nif file tester" ON) option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON) # OS X deployment diff --git a/components/nif/tests/test.sh b/components/nif/tests/test.sh index 95ecdbfba0..424e27b07d 100755 --- a/components/nif/tests/test.sh +++ b/components/nif/tests/test.sh @@ -9,7 +9,7 @@ find "$DATAFILESDIR" -iname *nif >> nifs.txt sed -e 's/.*/\"&\"/' nifs.txt > quoted_nifs.txt -xargs --arg-file=quoted_nifs.txt ../../../niftest +nice -n 10 xargs --arg-file=quoted_nifs.txt ../../../niftest rm nifs.txt rm quoted_nifs.txt From 03b39435f8d6e4ce7d70492f8f784d8286028968 Mon Sep 17 00:00:00 2001 From: Scott Howard Date: Thu, 1 Jan 2015 22:50:35 -0500 Subject: [PATCH 199/404] place user settings window at same location as mouse pointer --- apps/opencs/editor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index f609b80b74..826619b5d0 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -236,6 +236,7 @@ void CS::Editor::showSettings() if (mSettings.isHidden()) mSettings.show(); + mSettings.move (QCursor::pos()); mSettings.raise(); mSettings.activateWindow(); } From dece4e2640f2180ae61a8280acf79ab2f8179866 Mon Sep 17 00:00:00 2001 From: Scott Howard Date: Thu, 1 Jan 2015 22:54:32 -0500 Subject: [PATCH 200/404] remove unneeded includes --- apps/opencs/view/doc/loader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/opencs/view/doc/loader.cpp b/apps/opencs/view/doc/loader.cpp index 3f3163f261..b062a42b88 100644 --- a/apps/opencs/view/doc/loader.cpp +++ b/apps/opencs/view/doc/loader.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include "../../model/doc/document.hpp" From 9909c4abadbe4c0aedc24a50155908c5e7e39b13 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Fri, 2 Jan 2015 01:16:47 -0500 Subject: [PATCH 201/404] Made incorrect nif get error message more informative. --- components/nif/nifstream.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index b9caa15368..0f9ec9085f 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include @@ -86,7 +88,7 @@ public: //Templated functions to handle reads template - T get(){throw std::runtime_error("Can not get this type of data from a NIF File!");} + T get(){throw std::runtime_error("Can not read a <"+std::string(typeid(T).name())+"> from a NIF File! The get() function was called with the wrong template!");} ///Return a vector of whatever object is needed template From ad609bff7822abffc76de9ae01b50cb9df97b093 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Fri, 2 Jan 2015 01:19:34 -0500 Subject: [PATCH 202/404] components/nif/base.hpp now uses the templated get() function --- components/nif/base.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/nif/base.hpp b/components/nif/base.hpp index 30c652b64b..031000bcc2 100644 --- a/components/nif/base.hpp +++ b/components/nif/base.hpp @@ -36,12 +36,12 @@ public: { next.read(nif); - flags = nif->getUShort(); + flags = nif->get(); - frequency = nif->getFloat(); - phase = nif->getFloat(); - timeStart = nif->getFloat(); - timeStop = nif->getFloat(); + frequency = nif->get(); + phase = nif->get(); + timeStart = nif->get(); + timeStop = nif->get(); target.read(nif); } @@ -81,7 +81,7 @@ public: void read(NIFStream *nif) { - name = nif->getString(); + name = nif->get(); Controlled::read(nif); } }; From a8621e62308abafae018e30c76f3f2feddd5edcb Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 2 Jan 2015 16:49:22 +0300 Subject: [PATCH 203/404] defaults are set to 1.0; remove contrast setting (can be changed in config though); disable gamma control for not Windows OSs --- apps/openmw/mwgui/settingswindow.cpp | 14 +++++++++++++ apps/openmw/mwrender/renderingmanager.cpp | 2 +- files/mygui/openmw_settings_window.layout | 25 ++++++----------------- files/settings-default.cfg | 4 ++-- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index ce2a20d8ba..05664386b8 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -182,6 +182,20 @@ namespace MWGui getWidget(mRefractionButton, "RefractionButton"); getWidget(mDifficultySlider, "DifficultySlider"); +#ifndef WIN32 + // hide gamma controls since it currently does not work under Linux + MyGUI::ScrollBar *gammaSlider; + getWidget(gammaSlider, "GammaSlider"); + gammaSlider->setVisible(false); + MyGUI::TextBox *textBox; + getWidget(textBox, "GammaText"); + textBox->setVisible(false); + getWidget(textBox, "GammaTextDark"); + textBox->setVisible(false); + getWidget(textBox, "GammaTextLight"); + textBox->setVisible(false); +#endif + mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &SettingsWindow::onWindowResize); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 44edcf03b1..3aff46d43e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -757,7 +757,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec changeRes = true; else if (it->second == "field of view" && it->first == "General") mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); - else if ((it->second == "gamma" || it->second == "contrast") && it->first == "General") + else if (it->second == "gamma" && it->first == "General") { mRendering.setWindowGammaContrast(Settings::Manager::getFloat("gamma", "General"), Settings::Manager::getFloat("contrast", "General")); } diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 8fd3d8af11..4a993e1406 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -1,8 +1,8 @@  - + - + @@ -214,7 +214,7 @@ - + @@ -305,27 +305,14 @@ - + - + - - - - - - - - - - - - - @@ -504,7 +491,7 @@ - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6bd10dc211..0a55a286b6 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -48,8 +48,8 @@ werewolf overlay = true [General] # Camera field of view field of view = 55 -gamma = 1.34 -contrast = 0.98 +gamma = 1.00 +contrast = 1.00 # Texture filtering mode. valid values: # none From 773669952b5baaba0cd8b1faae059a9f5a0b5314 Mon Sep 17 00:00:00 2001 From: Thoronador Date: Fri, 2 Jan 2015 16:30:14 +0100 Subject: [PATCH 204/404] remove initializing constructors from "dumb structs" As suggested by Marc Zinnschlag: https://github.com/OpenMW/openmw/pull/423#issuecomment-68526701 --- components/esm/loadcell.hpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 33e4ce61e5..e1a6eee1ab 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -70,23 +70,17 @@ struct Cell { int mFlags; int mX, mY; - - DATAstruct() : mFlags(0), mX(0), mY(0) {} }; struct AMBIstruct { Color mAmbient, mSunlight, mFog; float mFogDensity; - - AMBIstruct() : mAmbient(0), mSunlight(0), mFog(0), mFogDensity(0) {} }; Cell() : mWater(0), mName(""), mRegion(""), - mData(DATAstruct()), - mAmbi(AMBIstruct()), mWaterInt(false), mMapColor(0), mRefNumCounter(0) From f24c1845b6f754f8817ae6bc6a2564ff99ae9f29 Mon Sep 17 00:00:00 2001 From: Scott Howard Date: Fri, 2 Jan 2015 10:42:09 -0500 Subject: [PATCH 205/404] remove pop-up message on load failure --- apps/opencs/view/doc/loader.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/opencs/view/doc/loader.cpp b/apps/opencs/view/doc/loader.cpp index b062a42b88..f2b897a3ea 100644 --- a/apps/opencs/view/doc/loader.cpp +++ b/apps/opencs/view/doc/loader.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include "../../model/doc/document.hpp" @@ -107,8 +106,6 @@ void CSVDoc::LoadingDocument::abort (const std::string& error) mAborted = true; mError->setText (QString::fromUtf8 (("Loading failed: " + error + "").c_str())); mButtons->setStandardButtons (QDialogButtonBox::Close); - QMessageBox::critical(this, tr("OpenCS Loading Failed"), - QString::fromUtf8 (("Loading failed:\n" + error).c_str())); } void CSVDoc::LoadingDocument::addMessage (const std::string& message) From bbbf431ae377fcbd442592ad29c7b79548ac8346 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 3 Jan 2015 13:54:46 +0100 Subject: [PATCH 206/404] double bug in script name handling workaround (Fixes #1730) --- components/compiler/fileparser.cpp | 1 + components/compiler/quickfileparser.cpp | 6 ++++++ components/compiler/scanner.cpp | 8 +------- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp index 37ad1f2581..423841ac3e 100644 --- a/components/compiler/fileparser.cpp +++ b/components/compiler/fileparser.cpp @@ -112,6 +112,7 @@ namespace Compiler scanner.scan (mScriptParser); mState = EndNameState; + scanner.allowNameStartingwithDigit(); return true; } diff --git a/components/compiler/quickfileparser.cpp b/components/compiler/quickfileparser.cpp index f3d8063b2f..895b7ce659 100644 --- a/components/compiler/quickfileparser.cpp +++ b/components/compiler/quickfileparser.cpp @@ -19,6 +19,12 @@ bool Compiler::QuickFileParser::parseName (const std::string& name, const TokenL bool Compiler::QuickFileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { + if (keyword==Scanner::K_begin) + { + scanner.allowNameStartingwithDigit(); + return true; + } + if (keyword==Scanner::K_end) return false; diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 3fdbdb9f01..705e90eb3b 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -314,12 +314,10 @@ namespace Compiler bool Scanner::scanName (char c, std::string& name) { - bool first = true; bool error = false; name.clear(); - - putback (c); + name += c; while (get (c)) { @@ -352,13 +350,9 @@ namespace Compiler putback (c); break; } - - if (first && (std::isdigit (c) || c=='`' || c=='-')) - error = true; } name += c; - first = false; } return !error; From ac7c2a1473d931adf9bc5d66c7a6cf424b1bfd3f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 3 Jan 2015 13:59:59 +0100 Subject: [PATCH 207/404] some cleanup --- components/compiler/scanner.cpp | 11 ++++++----- components/compiler/scanner.hpp | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 705e90eb3b..14ab99c211 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -270,8 +270,9 @@ namespace Compiler bool Scanner::scanName (char c, Parser& parser, bool& cont) { std::string name; + name += c; - if (!scanName (c, name)) + if (!scanName (name)) return false; TokenLoc loc (mLoc); @@ -312,13 +313,11 @@ namespace Compiler return true; } - bool Scanner::scanName (char c, std::string& name) + bool Scanner::scanName (std::string& name) { + char c; bool error = false; - name.clear(); - name += c; - while (get (c)) { if (!name.empty() && name[0]=='"') @@ -333,12 +332,14 @@ namespace Compiler // { // if (!get (c)) // { +// error = true; // mErrorHandler.error ("incomplete escape sequence", mLoc); // break; // } // } else if (c=='\n') { + error = true; mErrorHandler.error ("incomplete string or name", mLoc); break; } diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index 349a8afb6f..ed01bbe44e 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -89,7 +89,8 @@ namespace Compiler bool scanName (char c, Parser& parser, bool& cont); - bool scanName (char c, std::string& name); + /// \param name May contain the start of the name (one or more characters) + bool scanName (std::string& name); bool scanSpecial (char c, Parser& parser, bool& cont); From 4a734f5cd3750ba3c781290d6c8fc3c945f11eb4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Jan 2015 00:17:51 +0100 Subject: [PATCH 208/404] Fall back to top-level directory when looking for resources (Fixes #2169) --- components/misc/resourcehelpers.cpp | 47 ++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/components/misc/resourcehelpers.cpp b/components/misc/resourcehelpers.cpp index 9eaf441ef0..ee911c566e 100644 --- a/components/misc/resourcehelpers.cpp +++ b/components/misc/resourcehelpers.cpp @@ -4,6 +4,29 @@ #include +namespace +{ + + + struct MatchPathSeparator + { + bool operator()( char ch ) const + { + return ch == '\\' || ch == '/'; + } + }; + + std::string + getBasename( std::string const& pathname ) + { + return std::string( + std::find_if( pathname.rbegin(), pathname.rend(), + MatchPathSeparator() ).base(), + pathname.end() ); + } + +} + bool Misc::ResourceHelpers::changeExtensionToDds(std::string &path) { Ogre::String::size_type pos = path.rfind('.'); @@ -40,14 +63,24 @@ std::string Misc::ResourceHelpers::correctResourcePath(const std::string &topLev // since we know all (GOTY edition or less) textures end // in .dds, we change the extension - if (changeExtensionToDds(correctedPath)) + bool changedToDds = changeExtensionToDds(correctedPath); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(correctedPath)) + return correctedPath; + // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods) + // verify, and revert if false (this call succeeds quickly, but fails slowly) + if (changedToDds && Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(origExt)) + return origExt; + + // fall back to a resource in the top level directory if it exists + std::string fallback = topLevelDirectory + "\\" + getBasename(correctedPath); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(fallback)) + return fallback; + + if (changedToDds) { - // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods) - // verify, and revert if false (this call succeeds quickly, but fails slowly) - if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(correctedPath)) - { - return origExt; - } + fallback = topLevelDirectory + "\\" + getBasename(origExt); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(fallback)) + return fallback; } return correctedPath; From 593ca6bd48dc2904d6bab232a4c4028ae049ae65 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Jan 2015 00:34:16 +0100 Subject: [PATCH 209/404] Fix for framerate-dependent maximum stepping distance (Bug #1638) --- apps/openmw/mwworld/physicssystem.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 1374dc37a9..3a7aa04903 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -108,7 +108,7 @@ namespace MWWorld } static bool stepMove(btCollisionObject *colobj, Ogre::Vector3 &position, - const Ogre::Vector3 &velocity, float &remainingTime, + const Ogre::Vector3 &toMove, float &remainingTime, OEngine::Physic::PhysicEngine *engine) { /* @@ -124,7 +124,7 @@ namespace MWWorld * If not successful return 'false'. May fail for these reasons: * - can't move directly up from current position * - having moved up by between epsilon() and sStepSize, can't move forward - * - having moved forward by between epsilon() and velocity*remainingTime, + * - having moved forward by between epsilon() and toMove, * = moved down between 0 and just under sStepSize but slope was too steep, or * = moved the full sStepSize down (FIXME: this could be a bug) * @@ -133,7 +133,7 @@ namespace MWWorld * Starting position. Obstacle or stairs with height upto sStepSize in front. * * +--+ +--+ |XX - * | | -------> velocity | | +--+XX + * | | -------> toMove | | +--+XX * | | | | |XXXXX * | | +--+ | | +--+XXXXX * | | |XX| | | |XXXXXXXX @@ -171,11 +171,11 @@ namespace MWWorld * | | * <------------------->| | * +--+ +--+ - * |XX| the moved amount is velocity*remainingTime*tracer.mFraction + * |XX| the moved amount is toMove*tracer.mFraction * +--+ * ============================================== */ - tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + velocity*remainingTime, engine); + tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, engine); if(tracer.mFraction < std::numeric_limits::epsilon()) return false; // didn't even move the smallest representable amount @@ -428,9 +428,9 @@ namespace MWWorld Ogre::Vector3 oldPosition = newPosition; // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // NOTE: stepMove modifies newPosition if successful - bool result = stepMove(colobj, newPosition, velocity, remainingTime, engine); - if (!result) - result = stepMove(colobj, newPosition, velocity.normalisedCopy()*300.f, remainingTime, engine); + bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, engine); + if (!result) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent + result = stepMove(colobj, newPosition, velocity.normalisedCopy()*10.f, remainingTime, engine); if(result) { // don't let pure water creatures move out of water after stepMove From 6f747df7133284ea8146b70595e82951265704e5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Jan 2015 02:10:40 +0100 Subject: [PATCH 210/404] Remove an unused constructor --- apps/openmw/mwrender/sky.cpp | 5 ----- apps/openmw/mwrender/sky.hpp | 1 - 2 files changed, 6 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 385e7d8c59..761d3fd967 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -104,11 +104,6 @@ BillboardObject::BillboardObject( const String& textureName, bodyCount++; } -BillboardObject::BillboardObject() -: mNode(NULL), mMaterial(NULL), mEntity(NULL), mVisibility(1.f) -{ -} - void BillboardObject::requestedConfiguration (sh::MaterialInstance* m, const std::string& configuration) { } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 0544f17eff..70251f4901 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -39,7 +39,6 @@ namespace MWRender Ogre::SceneNode* rootNode, const std::string& material ); - BillboardObject(); void requestedConfiguration (sh::MaterialInstance* m, const std::string& configuration); void createdConfiguration (sh::MaterialInstance* m, const std::string& configuration); From d56906acf714c8decf35336ecf104647dd954e45 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Jan 2015 02:27:05 +0100 Subject: [PATCH 211/404] Fix the creature position glitch --- apps/openmw/mwmechanics/character.cpp | 2 ++ apps/openmw/mwrender/animation.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 3136ae676a..21683d3cc1 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -441,6 +441,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat } // idle handled last as it can depend on the other states + // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), + // the idle animation should be displayed if ((mUpperBodyState != UpperCharState_Nothing || mMovementState != CharState_None || mHitState != CharState_None) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a922fa170e..1ef7a3533d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -988,7 +988,11 @@ void Animation::resetActiveGroups() AnimStateMap::const_iterator state = mStates.find(mAnimationTimePtr[0]->getAnimName()); if(state == mStates.end()) + { + if (mAccumRoot && mNonAccumRoot) + mAccumRoot->setPosition(-mNonAccumRoot->getPosition()*mAccumulate); return; + } const Ogre::SharedPtr &animsrc = state->second.mSource; const std::vector >&ctrls = animsrc->mControllers[0]; @@ -1142,9 +1146,6 @@ Ogre::Vector3 Animation::runAnimation(float duration) if(!state.mPlaying && state.mAutoDisable) { - if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) - mAccumRoot->setPosition(0.f,0.f,0.f); - mStates.erase(stateiter++); resetActiveGroups(); From 398fe6e780314bd94bee591a9d59b608b48aee56 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 4 Jan 2015 01:33:36 +0100 Subject: [PATCH 212/404] Thrown weapon fix (Fixes #2248) --- apps/openmw/mwrender/weaponanimation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 8a9feef038..8af4d637a2 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -68,9 +68,6 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor) void WeaponAnimation::releaseArrow(MWWorld::Ptr actor) { - if (!mAmmunition.get()) - return; - MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weapon == inv.end()) @@ -131,6 +128,9 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor) if (ammo == inv.end()) return; + if (!mAmmunition.get()) + return; + Ogre::Vector3 launchPos(0,0,0); if (mAmmunition->mSkelBase) { From bc686c93b50485bc6e3bb4e94d83c7b46def6dca Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 4 Jan 2015 01:36:57 +0100 Subject: [PATCH 213/404] Potential fix for thrown weapons being regarded as broken --- apps/openmw/mwclass/weapon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index f1f0386c64..8456c72e68 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -386,7 +386,7 @@ namespace MWClass std::pair Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { - if (ptr.getCellRef().getCharge() == 0) + if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0) return std::make_pair(0, "#{sInventoryMessage1}"); std::pair, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); From de12c96a460815da6acff2828ce745581e9f01ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 4 Jan 2015 19:23:31 +0100 Subject: [PATCH 214/404] Fix crash on exit if the window wasn't created (Fixes #2249) --- apps/openmw/engine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3473d0b299..24e1388d0d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -208,7 +208,8 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) OMW::Engine::~Engine() { - mOgre->restoreWindowGammaRamp(); + if (mOgre) + mOgre->restoreWindowGammaRamp(); mEnvironment.cleanup(); delete mScriptContext; delete mOgre; From 5e7e40aac90af0d55e51e7113203f3e96d517585 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 4 Jan 2015 19:50:46 +0100 Subject: [PATCH 215/404] Fix being able to switch weapons while knocked out --- apps/openmw/mwmechanics/character.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 21683d3cc1..6632f02753 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -900,7 +900,8 @@ bool CharacterController::updateWeaponState() } bool forcestateupdate = false; - if(weaptype != mWeaponType && mHitState != CharState_KnockDown) + if(weaptype != mWeaponType && mHitState != CharState_KnockDown && mHitState != CharState_KnockOut + && mHitState != CharState_Hit) { forcestateupdate = true; From d919a0186e9f50999f9d10962333f8d60536433f Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Jan 2015 18:54:52 +0100 Subject: [PATCH 216/404] Comment out unused opSkipOnZero --- components/compiler/generator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index 2efa2477e5..ead0c72901 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -150,10 +150,13 @@ namespace code.push_back (Compiler::Generator::segment0 (2, offset)); } + /* + Currently unused void opSkipOnZero (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (24)); } + */ void opSkipOnNonZero (Compiler::Generator::CodeContainer& code) { From c1955ef7fa983b9f9bf72e2cd0ee64af1f03320f Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Jan 2015 05:33:51 +0100 Subject: [PATCH 217/404] Fix enchanting dialog effect labels showing a duration for constant effects --- apps/openmw/mwgui/enchantingdialog.cpp | 9 ++++---- apps/openmw/mwgui/spellcreationdialog.cpp | 25 +++++++++++++++++------ apps/openmw/mwgui/spellcreationdialog.hpp | 9 +++++++- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 224f8a4d8f..4744fd1a16 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -120,19 +120,19 @@ namespace MWGui { case ESM::Enchantment::CastOnce: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once")); - mAddEffectDialog.constantEffect=false; + setConstantEffect(false); break; case ESM::Enchantment::WhenStrikes: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes")); - mAddEffectDialog.constantEffect=false; + setConstantEffect(false); break; case ESM::Enchantment::WhenUsed: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used")); - mAddEffectDialog.constantEffect=false; + setConstantEffect(false); break; case ESM::Enchantment::ConstantEffect: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant")); - mAddEffectDialog.constantEffect=true; + setConstantEffect(true); break; } } @@ -283,6 +283,7 @@ namespace MWGui { mEnchanting.nextCastStyle(); updateLabels(); + updateEffectsView(); } void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 6716f87da6..0db2e30ced 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -42,6 +42,7 @@ namespace MWGui : WindowModal("openmw_edit_effect.layout") , mEditing(false) , mMagicEffect(NULL) + , mConstantEffect(false) { getWidget(mCancelButton, "CancelButton"); getWidget(mOkButton, "OkButton"); @@ -71,7 +72,11 @@ namespace MWGui mMagnitudeMaxSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); - constantEffect=false; + } + + void EditEffectDialog::setConstantEffect(bool constant) + { + mConstantEffect = constant; } void EditEffectDialog::open() @@ -92,8 +97,8 @@ namespace MWGui void EditEffectDialog::newEffect (const ESM::MagicEffect *effect) { bool allowSelf = effect->mData.mFlags & ESM::MagicEffect::CastSelf; - bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !constantEffect; - bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !constantEffect; + bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; + bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; if (!allowSelf && !allowTouch && !allowTarget) return; // TODO: Show an error message popup? @@ -183,7 +188,7 @@ namespace MWGui mMagnitudeBox->setVisible (true); curY += mMagnitudeBox->getSize().height; } - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)&&constantEffect==false) + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)&&mConstantEffect==false) { mDurationBox->setPosition(mDurationBox->getPosition().left, curY); mDurationBox->setVisible (true); @@ -204,8 +209,8 @@ namespace MWGui // cycle through range types until we find something that's allowed // does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect dialog) bool allowSelf = mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf; - bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !constantEffect; - bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !constantEffect; + bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; + bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; if (mEffect.mRange == ESM::RT_Self && !allowSelf) mEffect.mRange = (mEffect.mRange+1)%3; if (mEffect.mRange == ESM::RT_Touch && !allowTouch) @@ -468,6 +473,7 @@ namespace MWGui , mSelectedEffect(0) , mSelectedKnownEffectId(0) , mType(type) + , mConstantEffect(false) { mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded); mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified); @@ -659,6 +665,7 @@ namespace MWGui params.mMagnMax = it->mMagnMax; params.mRange = it->mRange; params.mArea = it->mArea; + params.mIsConstant = mConstantEffect; MyGUI::Button* button = mUsedEffectsView->createWidget("", MyGUI::IntCoord(0, size.height, 0, 24), MyGUI::Align::Default); button->setUserData(i); @@ -703,4 +710,10 @@ namespace MWGui mAddEffectDialog.editEffect (mEffects[id]); mAddEffectDialog.setVisible (true); } + + void EffectEditorBase::setConstantEffect(bool constant) + { + mAddEffectDialog.setConstantEffect(constant); + mConstantEffect = constant; + } } diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index a94289bfdc..72e581c876 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -23,12 +23,13 @@ namespace MWGui virtual void open(); virtual void exit(); + void setConstantEffect(bool constant); + void setSkill(int skill); void setAttribute(int attribute); void newEffect (const ESM::MagicEffect* effect); void editEffect (ESM::ENAMstruct effect); - bool constantEffect; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Effect; EventHandle_Effect eventEffectAdded; @@ -82,6 +83,8 @@ namespace MWGui ESM::ENAMstruct mOldEffect; const ESM::MagicEffect* mMagicEffect; + + bool mConstantEffect; }; @@ -97,6 +100,8 @@ namespace MWGui EffectEditorBase(Type type); virtual ~EffectEditorBase(); + void setConstantEffect(bool constant); + protected: std::map mButtonMapping; // maps button ID to effect ID @@ -110,6 +115,8 @@ namespace MWGui int mSelectedEffect; short mSelectedKnownEffectId; + bool mConstantEffect; + std::vector mEffects; void onEffectAdded(ESM::ENAMstruct effect); From e6be979350102155e281a628027ea887131e74c0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Jan 2015 06:29:33 +0100 Subject: [PATCH 218/404] Fix default position for spell window --- files/settings-default.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 39232c95c3..6ec9b03e9a 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -251,10 +251,10 @@ stats y = 0 stats w = 0.375 stats h = 0.4275 -spells x = 0.3775 -spells y = 0.4275 +spells x = 0.625 +spells y = 0.5725 spells w = 0.375 -spells h = 0.5725 +spells h = 0.4275 console x = 0 console y = 0 From c343a5c803b047fdbddbc42cdfe9a15b41b43977 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Jan 2015 19:33:51 +0100 Subject: [PATCH 219/404] stopCombat fix --- apps/openmw/mwmechanics/aisequence.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index ea59708c26..13d09af7ef 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -125,19 +125,23 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const void AiSequence::stopCombat() { - while (getTypeId() == AiPackage::TypeIdCombat) + for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ) { - delete *mPackages.begin(); - mPackages.erase (mPackages.begin()); + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + it = mPackages.erase(it); + else + ++it; } } void AiSequence::stopPursuit() { - while (getTypeId() == AiPackage::TypeIdPursue) + for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ) { - delete *mPackages.begin(); - mPackages.erase (mPackages.begin()); + if ((*it)->getTypeId() == AiPackage::TypeIdPursue) + it = mPackages.erase(it); + else + ++it; } } From 708dbc2518c127ac692e18fcf80cc154b5ca8716 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Jan 2015 19:59:47 +0100 Subject: [PATCH 220/404] Crime fix --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 42728290be..3d757bde68 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1164,6 +1164,19 @@ namespace MWMechanics player.getClass().getNpcStats(player).expell(factionID); } } + + if (type == OT_Assault && !victim.isEmpty() + && !victim.getClass().getCreatureStats(victim).getAiSequence().isInCombat(player) + && victim.getClass().isNpc()) + { + // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. + // Note: accidental or collateral damage attacks are ignored. + startCombat(victim, player); + + // Set the crime ID, which we will use to calm down participants + // once the bounty has been paid. + victim.getClass().getNpcStats(victim).setCrimeId(id); + } } } From afc961d19c0618c58ae9343a194003ae13b37987 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Jan 2015 20:15:30 +0100 Subject: [PATCH 221/404] Workaround for random AABB assertion due to zero-sized particles (Fixes #1663) --- components/nifogre/ogrenifloader.cpp | 5 ++++- components/nifogre/particles.cpp | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index b552487841..9c5f4a0168 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -973,7 +973,10 @@ class NIFObjectLoader { const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); - partsys->setDefaultDimensions(partctrl->size*2, partctrl->size*2); + float size = partctrl->size*2; + // HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail + size = std::max(size, 0.00001f); + partsys->setDefaultDimensions(size, size); if(!partctrl->emitter.empty()) { diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index 316e4edc20..4fec2d29eb 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -452,6 +452,8 @@ public: { Ogre::Real scale = (life_time-particle_time) / mGrowTime; assert (scale >= 0); + // HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail + scale = std::max(scale, 0.00001f); width *= scale; height *= scale; } @@ -459,6 +461,8 @@ public: { Ogre::Real scale = particle_time / mFadeTime; assert (scale >= 0); + // HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail + scale = std::max(scale, 0.00001f); width *= scale; height *= scale; } @@ -485,6 +489,8 @@ public: { Ogre::Real scale = (life_time-particle_time) / mGrowTime; assert (scale >= 0); + // HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail + scale = std::max(scale, 0.00001f); width *= scale; height *= scale; } @@ -492,6 +498,8 @@ public: { Ogre::Real scale = particle_time / mFadeTime; assert (scale >= 0); + // HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail + scale = std::max(scale, 0.00001f); width *= scale; height *= scale; } From 464f8abb3f954a2798e8d312f938ccd6fef5ef26 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 6 Jan 2015 01:10:09 +0100 Subject: [PATCH 222/404] List exterior cell names in tab completion (Fixes #2252) --- apps/openmw/mwgui/console.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index c922b625d7..97f11f4f3f 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -102,8 +102,20 @@ namespace MWGui it->second->listIdentifier (mNames); } + // exterior cell names aren't technically identifiers, but since the COC function accepts them, + // we should list them too + for (MWWorld::Store::iterator it = store.get().extBegin(); + it != store.get().extEnd(); ++it) + { + if (!it->mName.empty()) + mNames.push_back(it->mName); + } + // sort std::sort (mNames.begin(), mNames.end()); + + // remove duplicates + mNames.erase( std::unique( mNames.begin(), mNames.end() ), mNames.end() ); } } From e1fdcb608eaa0bff2e491a4f4b063b02bf4d41b7 Mon Sep 17 00:00:00 2001 From: Internecine Date: Tue, 6 Jan 2015 15:00:24 +1300 Subject: [PATCH 223/404] Fixed incorrect index --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 71064d9b0d..8e4e80d9cf 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -584,7 +584,7 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::RestoreHealth) { - applyDynamicStatsEffect(2, target, magnitude); + applyDynamicStatsEffect(0, target, magnitude); } else if (effectId == ESM::MagicEffect::DamageFatigue) { From f267497c0316f1163c4202451babf768274a6440 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Jan 2015 18:52:37 +0100 Subject: [PATCH 224/404] Allow separate summoned creature instances for each spell ID (Fixes #2194) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/spellicons.cpp | 2 +- apps/openmw/mwgui/spellicons.hpp | 2 +- apps/openmw/mwmechanics/activespells.cpp | 18 +- apps/openmw/mwmechanics/activespells.hpp | 3 + apps/openmw/mwmechanics/actors.cpp | 159 +----------------- apps/openmw/mwmechanics/creaturestats.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 11 +- apps/openmw/mwmechanics/magiceffects.hpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 1 + apps/openmw/mwmechanics/spells.cpp | 2 +- apps/openmw/mwmechanics/summoning.cpp | 195 ++++++++++++++++++++++ apps/openmw/mwmechanics/summoning.hpp | 35 ++++ apps/openmw/mwworld/inventorystore.cpp | 49 +++++- apps/openmw/mwworld/inventorystore.hpp | 3 + components/esm/creaturestats.cpp | 8 +- components/esm/creaturestats.hpp | 2 +- 17 files changed, 329 insertions(+), 167 deletions(-) create mode 100644 apps/openmw/mwmechanics/summoning.cpp create mode 100644 apps/openmw/mwmechanics/summoning.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index c645e1a0a2..97ab130127 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -76,7 +76,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting - disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor + disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 8ea9cfd7f3..d23f1a2350 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -25,7 +25,7 @@ namespace MWGui { void EffectSourceVisitor::visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) { MagicEffectInfo newEffectSource; diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index e9d9967ead..5099fc4d63 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -47,7 +47,7 @@ namespace MWGui virtual ~EffectSourceVisitor() {} virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1); }; diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 717a63be8f..6e15449e19 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -195,7 +195,7 @@ namespace MWMechanics float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); + visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); } } } @@ -229,6 +229,22 @@ namespace MWMechanics mSpellsChanged = true; } + void ActiveSpells::purgeEffect(short effectId, const std::string& sourceId) + { + for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) + { + for (std::vector::iterator effectIt = it->second.mEffects.begin(); + effectIt != it->second.mEffects.end();) + { + if (effectIt->mEffectId == effectId && it->first == sourceId) + effectIt = it->second.mEffects.erase(effectIt); + else + ++effectIt; + } + } + mSpellsChanged = true; + } + void ActiveSpells::purge(int casterActorId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 9c1a5e613b..4f9d15d8c1 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -82,6 +82,9 @@ namespace MWMechanics /// Remove all active effects with this effect id void purgeEffect (short effectId); + /// Remove all active effects with this effect id and source id + void purgeEffect (short effectId, const std::string& sourceId); + /// Remove all active effects, if roll succeeds (for each effect) void purgeAll (float chance); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1055e0f4ae..c0fc806925 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -36,6 +36,7 @@ #include "aipursue.hpp" #include "actor.hpp" +#include "summoning.hpp" namespace { @@ -105,7 +106,7 @@ public: , mCommanded(false){} virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -165,30 +166,6 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float } } -void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId) -{ - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId); - if (!ptr.isEmpty()) - { - // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation - // plays though, which is a rather lame exploit in vanilla. - MWBase::Environment::get().getWorld()->deleteObject(ptr); - - const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() - .search("VFX_Summon_End"); - if (fx) - MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, - "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); - } - else - { - // We didn't find the creature. It's probably in an inactive cell. - // Add to graveyard so we can delete it when the cell becomes active. - std::vector& graveyard = casterStats.getSummonedCreatureGraveyard(); - graveyard.push_back(creatureActorId); - } -} - } namespace MWMechanics @@ -203,7 +180,7 @@ namespace MWMechanics : mCreature(trappedCreature) {} virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { if (key.mId != ESM::MagicEffect::Soultrap) @@ -782,131 +759,11 @@ namespace MWMechanics } } - // Update summon effects - static std::map summonMap; - if (summonMap.empty()) - { - summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID"; - summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID"; - summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID"; - summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID"; - summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID"; - summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID"; - summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID"; - summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID"; - summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID"; - summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID"; - summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID"; - summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID"; - summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID"; - summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID"; - summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID"; - summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID"; - summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID"; - summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID"; - summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID"; - summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID"; - summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID"; - summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; - } - - std::map& creatureMap = creatureStats.getSummonedCreatureMap(); - for (std::map::iterator it = summonMap.begin(); it != summonMap.end(); ++it) - { - bool found = creatureMap.find(it->first) != creatureMap.end(); - int magnitude = creatureStats.getMagicEffects().get(it->first).getMagnitude(); - if (found != (magnitude > 0)) - { - if (magnitude > 0) - { - ESM::Position ipos = ptr.getRefData().getPosition(); - Ogre::Vector3 pos(ipos.pos); - Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); - const float distance = 50; - pos = pos + distance*rot.yAxis(); - ipos.pos[0] = pos.x; - ipos.pos[1] = pos.y; - ipos.pos[2] = pos.z; - ipos.rot[0] = 0; - ipos.rot[1] = 0; - ipos.rot[2] = 0; - - std::string creatureID = - MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->getString(); - - if (!creatureID.empty()) - { - MWWorld::CellStore* store = ptr.getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); - ref.getPtr().getCellRef().setPosition(ipos); - - MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); - - // Make the summoned creature follow its master and help in fights - AiFollow package(ptr.getCellRef().getRefId()); - summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); - int creatureActorId = summonedCreatureStats.getActorId(); - - MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); - - MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); - if (anim) - { - const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() - .search("VFX_Summon_Start"); - if (fx) - anim->addEffect("meshes\\" + fx->mModel, -1, false); - } - - creatureMap.insert(std::make_pair(it->first, creatureActorId)); - } - } - else - { - // Effect has ended - std::map::iterator foundCreature = creatureMap.find(it->first); - cleanupSummonedCreature(creatureStats, foundCreature->second); - creatureMap.erase(foundCreature); - } - } - } - - for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) - { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); - if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead()) - { - // Purge the magic effect so a new creature can be summoned if desired - creatureStats.getActiveSpells().purgeEffect(it->first); - if (ptr.getClass().hasInventoryStore(ptr)) - ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first); - - cleanupSummonedCreature(creatureStats, it->second); - creatureMap.erase(it++); - } - else - ++it; - } - - std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard(); - for (std::vector::iterator it = graveyard.begin(); it != graveyard.end(); ) - { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it); - if (!ptr.isEmpty()) - { - it = graveyard.erase(it); - - const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() - .search("VFX_Summon_End"); - if (fx) - MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, - "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); - - MWBase::Environment::get().getWorld()->deleteObject(ptr); - } - else - ++it; - } + UpdateSummonedCreatures updateSummonedCreatures(ptr); + creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures); + if (ptr.getClass().hasInventoryStore(ptr)) + ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures); + updateSummonedCreatures.finish(); } void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index ac6f88d449..c61cc9697a 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -638,7 +638,7 @@ namespace MWMechanics mDeathAnimation = index; } - std::map& CreatureStats::getSummonedCreatureMap() + std::map& CreatureStats::getSummonedCreatureMap() { return mSummonedCreatures; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 9a08b58c9c..145eb8a5bf 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -67,8 +67,11 @@ namespace MWMechanics // The index of the death animation that was played unsigned char mDeathAnimation; - // - std::map mSummonedCreatures; + public: + typedef std::pair SummonKey; // + private: + std::map mSummonedCreatures; // + // Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet. // This may be necessary when the creature is in an inactive cell. std::vector mSummonGraveyard; @@ -216,8 +219,8 @@ namespace MWMechanics void setBlock(bool value); bool getBlock() const; - std::map& getSummonedCreatureMap(); - std::vector& getSummonedCreatureGraveyard(); + std::map& getSummonedCreatureMap(); // + std::vector& getSummonedCreatureGraveyard(); // ActorIds enum Flag { diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 88d8d988f7..c384d0857f 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -73,7 +73,7 @@ namespace MWMechanics struct EffectSourceVisitor { virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) = 0; }; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 58ccd389ac..9cfcdda184 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -21,6 +21,7 @@ #include "magiceffects.hpp" #include "npcstats.hpp" +#include "summoning.hpp" namespace { diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 5953be523b..1f8ada06d5 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -249,7 +249,7 @@ namespace MWMechanics random = it->second.at(i); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; - visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, -1, magnitude); + visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, spell->mId, -1, magnitude); } } } diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp new file mode 100644 index 0000000000..356cb422f1 --- /dev/null +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -0,0 +1,195 @@ +#include "summoning.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/esmstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/inventorystore.hpp" + +#include "../mwrender/animation.hpp" + +#include "creaturestats.hpp" +#include "aifollow.hpp" + + +namespace MWMechanics +{ + + void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId); + if (!ptr.isEmpty()) + { + // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation + // plays though, which is a rather lame exploit in vanilla. + MWBase::Environment::get().getWorld()->deleteObject(ptr); + + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_End"); + if (fx) + MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, + "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); + } + else + { + // We didn't find the creature. It's probably in an inactive cell. + // Add to graveyard so we can delete it when the cell becomes active. + std::vector& graveyard = casterStats.getSummonedCreatureGraveyard(); + graveyard.push_back(creatureActorId); + } + } + + UpdateSummonedCreatures::UpdateSummonedCreatures(const MWWorld::Ptr &actor) + : mActor(actor) + { + + } + + void UpdateSummonedCreatures::visit(EffectKey key, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) + { + if (key.mId >= ESM::MagicEffect::SummonScamp + && key.mId <= ESM::MagicEffect::SummonStormAtronach && magnitude > 0) + { + mActiveEffects.insert(std::make_pair(key.mId, sourceId)); + } + } + + void UpdateSummonedCreatures::finish() + { + static std::map summonMap; + if (summonMap.empty()) + { + summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID"; + summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID"; + summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID"; + summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID"; + summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID"; + summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID"; + summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID"; + summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID"; + summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID"; + summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID"; + summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID"; + summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID"; + summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID"; + summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID"; + summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID"; + summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID"; + summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID"; + summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID"; + summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID"; + summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID"; + summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID"; + summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; + } + + MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); + + // Update summon effects + std::map& creatureMap = creatureStats.getSummonedCreatureMap(); + for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) + { + bool found = mActiveEffects.find(it->first) != mActiveEffects.end(); + if (!found) + { + // Effect has ended + cleanupSummonedCreature(creatureStats, it->second); + creatureMap.erase(it++); + continue; + } + ++it; + } + + for (std::set >::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it) + { + bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end(); + if (!found) + { + ESM::Position ipos = mActor.getRefData().getPosition(); + Ogre::Vector3 pos(ipos.pos); + Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); + const float distance = 50; + pos = pos + distance*rot.yAxis(); + ipos.pos[0] = pos.x; + ipos.pos[1] = pos.y; + ipos.pos[2] = pos.z; + ipos.rot[0] = 0; + ipos.rot[1] = 0; + ipos.rot[2] = 0; + + const std::string& creatureGmst = summonMap[it->first]; + std::string creatureID = + MWBase::Environment::get().getWorld()->getStore().get().find(creatureGmst)->getString(); + + if (!creatureID.empty()) + { + MWWorld::CellStore* store = mActor.getCell(); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); + ref.getPtr().getCellRef().setPosition(ipos); + + MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); + + // Make the summoned creature follow its master and help in fights + AiFollow package(mActor.getCellRef().getRefId()); + summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); + int creatureActorId = summonedCreatureStats.getActorId(); + + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); + + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); + if (anim) + { + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_Start"); + if (fx) + anim->addEffect("meshes\\" + fx->mModel, -1, false); + } + + creatureMap.insert(std::make_pair(*it, creatureActorId)); + } + } + } + + for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); + if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead()) + { + // Purge the magic effect so a new creature can be summoned if desired + creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second); + if (mActor.getClass().hasInventoryStore(ptr)) + mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second); + + cleanupSummonedCreature(creatureStats, it->second); + creatureMap.erase(it++); + } + else + ++it; + } + + std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard(); + for (std::vector::iterator it = graveyard.begin(); it != graveyard.end(); ) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it); + if (!ptr.isEmpty()) + { + it = graveyard.erase(it); + + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_End"); + if (fx) + MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, + "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); + + MWBase::Environment::get().getWorld()->deleteObject(ptr); + } + else + ++it; + } + } + +} diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp new file mode 100644 index 0000000000..b8fe377838 --- /dev/null +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -0,0 +1,35 @@ +#ifndef OPENMW_MECHANICS_SUMMONING_H +#define OPENMW_MECHANICS_SUMMONING_H + +#include + +#include "magiceffects.hpp" +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + class CreatureStats; + + struct UpdateSummonedCreatures : public EffectSourceVisitor + { + UpdateSummonedCreatures(const MWWorld::Ptr& actor); + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1); + + /// To call after all effect sources have been visited + void finish(); + + private: + MWWorld::Ptr mActor; + + std::set > mActiveEffects; + }; + + void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId); + +} + +#endif diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 9c329ce720..445b42d8db 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -583,7 +583,8 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i]; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; magnitude *= params.mMultiplier; - visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), -1, magnitude); + if (magnitude > 0) + visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), (**iter).getCellRef().getRefId(), -1, magnitude); ++i; } @@ -639,6 +640,52 @@ void MWWorld::InventoryStore::purgeEffect(short effectId) mMagicEffects.remove(MWMechanics::EffectKey(effectId)); } +void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId) +{ + TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId); + if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end()) + return; + + for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) + { + if (*iter==end()) + continue; + + if ((*iter)->getClass().getId(**iter) != sourceId) + continue; + + std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter); + + if (!enchantmentId.empty()) + { + const ESM::Enchantment& enchantment = + *MWBase::Environment::get().getWorld()->getStore().get().find (enchantmentId); + + if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect) + continue; + + std::vector& params = effectMagnitudeIt->second; + + int i=0; + for (std::vector::const_iterator effectIt (enchantment.mEffects.mList.begin()); + effectIt!=enchantment.mEffects.mList.end(); ++effectIt, ++i) + { + if (effectIt->mEffectID != effectId) + continue; + + float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; + magnitude *= params[i].mMultiplier; + + if (magnitude) + mMagicEffects.add (*effectIt, -magnitude); + + params[i].mMultiplier = 0; + break; + } + } + } +} + void MWWorld::InventoryStore::clear() { mSlots.clear(); diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 48742b557d..9fd18c54b0 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -203,6 +203,9 @@ namespace MWWorld void purgeEffect (short effectId); ///< Remove a magic effect + void purgeEffect (short effectId, const std::string& sourceId); + ///< Remove a magic effect + virtual void clear(); ///< Empty container. diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index cc76ef0df7..1fdce703c4 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -94,9 +94,10 @@ void ESM::CreatureStats::load (ESMReader &esm) { int magicEffect; esm.getHT(magicEffect); + std::string source = esm.getHNOString("SOUR"); int actorId; esm.getHNT (actorId, "ACID"); - mSummonedCreatureMap[magicEffect] = actorId; + mSummonedCreatureMap[std::make_pair(magicEffect, source)] = actorId; } while (esm.isNextSub("GRAV")) @@ -204,9 +205,10 @@ void ESM::CreatureStats::save (ESMWriter &esm) const mAiSequence.save(esm); mMagicEffects.save(esm); - for (std::map::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) + for (std::map, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) { - esm.writeHNT ("SUMM", it->first); + esm.writeHNT ("SUMM", it->first.first); + esm.writeHNString ("SOUR", it->first.second); esm.writeHNT ("ACID", it->second); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 7946d0e45b..680577f932 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -32,7 +32,7 @@ namespace ESM bool mHasAiSettings; StatState mAiSettings[4]; - std::map mSummonedCreatureMap; + std::map, int> mSummonedCreatureMap; std::vector mSummonGraveyard; ESM::TimeStamp mTradeTime; From 992b87ea441de3be8c673f300532f902330c0dda Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 6 Jan 2015 16:08:23 +0100 Subject: [PATCH 225/404] Reset existing summons when the spell is re-casted (Fixes #2135) --- apps/openmw/mwmechanics/spellcasting.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 9cfcdda184..b7c7e00a10 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -471,6 +471,20 @@ namespace MWMechanics else applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude); + // Re-casting a summon effect will remove the creature from previous castings of that effect. + if (effectIt->mEffectID >= ESM::MagicEffect::SummonScamp + && effectIt->mEffectID <= ESM::MagicEffect::SummonStormAtronach + && !target.isEmpty() && target.getClass().isActor()) + { + CreatureStats& targetStats = target.getClass().getCreatureStats(target); + std::map::iterator found = targetStats.getSummonedCreatureMap().find(std::make_pair(effectIt->mEffectID, mId)); + if (found != targetStats.getSummonedCreatureMap().end()) + { + cleanupSummonedCreature(targetStats, found->second); + targetStats.getSummonedCreatureMap().erase(found); + } + } + // HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent. // This was probably just done to have the effect visible in the magic menu for a while // to notify the player they've been damaged? From c6c7d102d0171c1f0daedc8fa0435cb913bc7b13 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 6 Jan 2015 23:35:40 +0100 Subject: [PATCH 226/404] Revert "components/nif/base.hpp now uses the templated get() function" This reverts commit ad609bff7822abffc76de9ae01b50cb9df97b093. Revert "Made incorrect nif get error message more informative." This reverts commit 9909c4abadbe4c0aedc24a50155908c5e7e39b13. Revert "Build the nif file tester by default" This reverts commit c1315ed90c87a457f17e6076c149465da3fa6c3a. Revert "Converted most nifstream "get multiple" functions to the templated version" This reverts commit 2619d57bb6afc5c31bf1a90b8c033d66f29a9a58. Revert "Add a templated option for getting vectors to NIFStream" This reverts commit f318ee0b8c68a46d53a0fdd216ae8d6b371eedc2. Revert "Made NIFStream getters templated" This reverts commit 4edc4142f3b4f1cde4d99392045d5d25858e6bf7. --- CMakeLists.txt | 2 +- components/nif/base.hpp | 12 +++---- components/nif/data.hpp | 16 ++++----- components/nif/nifstream.cpp | 64 ++++++++++++++++++++---------------- components/nif/nifstream.hpp | 25 ++++---------- components/nif/tests/test.sh | 2 +- 6 files changed, 58 insertions(+), 63 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb89f4e91d..d7c85818e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_WIZARD "build Installation Wizard" ON) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest and GMock frameworks" OFF) -option(BUILD_NIFTEST "build nif file tester" ON) +option(BUILD_NIFTEST "build nif file tester" OFF) option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON) # OS X deployment diff --git a/components/nif/base.hpp b/components/nif/base.hpp index 031000bcc2..30c652b64b 100644 --- a/components/nif/base.hpp +++ b/components/nif/base.hpp @@ -36,12 +36,12 @@ public: { next.read(nif); - flags = nif->get(); + flags = nif->getUShort(); - frequency = nif->get(); - phase = nif->get(); - timeStart = nif->get(); - timeStop = nif->get(); + frequency = nif->getFloat(); + phase = nif->getFloat(); + timeStart = nif->getFloat(); + timeStop = nif->getFloat(); target.read(nif); } @@ -81,7 +81,7 @@ public: void read(NIFStream *nif) { - name = nif->get(); + name = nif->getString(); Controlled::read(nif); } }; diff --git a/components/nif/data.hpp b/components/nif/data.hpp index e6d3370be8..d9de12fb54 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -44,16 +44,16 @@ public: int verts = nif->getUShort(); if(nif->getInt()) - vertices = nif->getItems(verts); + nif->getVector3s(vertices, verts); if(nif->getInt()) - normals = nif->getItems(verts); + nif->getVector3s(normals, verts); center = nif->getVector3(); radius = nif->getFloat(); if(nif->getInt()) - colors = nif->getItems(verts); + nif->getVector4s(colors, verts); // Only the first 6 bits are used as a count. I think the rest are // flags of some sort. @@ -64,7 +64,7 @@ public: { uvlist.resize(uvs); for(int i = 0;i < uvs;i++) - uvlist[i] = nif->getItems(verts); + nif->getVector2s(uvlist[i], verts); } } }; @@ -84,7 +84,7 @@ public: // We have three times as many vertices as triangles, so this // is always equal to tris*3. int cnt = nif->getInt(); - triangles = nif->getItems(cnt); + nif->getShorts(triangles, cnt); // Read the match list, which lists the vertices that are equal to // vertices. We don't actually need need this for anything, so @@ -123,7 +123,7 @@ public: if(nif->getInt()) { // Particle sizes - sizes = nif->getItems(vertices.size()); + nif->getFloats(sizes, vertices.size()); } } }; @@ -140,7 +140,7 @@ public: if(nif->getInt()) { // Rotation quaternions. - rotations = nif->getItems(vertices.size()); + nif->getQuaternions(rotations, vertices.size()); } } }; @@ -341,7 +341,7 @@ struct NiMorphData : public Record for(int i = 0;i < morphCount;i++) { mMorphs[i].mData.read(nif, true); - mMorphs[i].mVertices = nif->getItems(vertCount); + nif->getVector3s(mMorphs[i].mVertices, vertCount); } } }; diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 9eeee6a12b..e5699db7b9 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -106,33 +106,41 @@ std::string NIFStream::getVersionString() return inp->getLine(); } -template <> -char NIFStream::get(){ return getChar(); } -template <> -short NIFStream::get(){ return getShort(); } -template <> -unsigned short NIFStream::get(){ return getUShort(); } -template <> -int NIFStream::get(){ return getInt(); } -template <> -unsigned int NIFStream::get(){ return getUInt(); } -template <> -float NIFStream::get(){ return getFloat(); } - -template <> -Ogre::Vector2 NIFStream::get(){ return getVector2(); } -template <> -Ogre::Vector3 NIFStream::get(){ return getVector3(); } -template <> -Ogre::Vector4 NIFStream::get(){ return getVector4(); } -template <> -Ogre::Matrix3 NIFStream::get(){ return getMatrix3(); } -template <> -Ogre::Quaternion NIFStream::get(){ return getQuaternion(); } -template <> -Transformation NIFStream::get(){ return getTrafo(); } - -template <> -std::string NIFStream::get(){ return getString(); } +void NIFStream::getShorts(std::vector &vec, size_t size) +{ + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getShort(); +} +void NIFStream::getFloats(std::vector &vec, size_t size) +{ + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getFloat(); +} +void NIFStream::getVector2s(std::vector &vec, size_t size) +{ + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector2(); +} +void NIFStream::getVector3s(std::vector &vec, size_t size) +{ + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector3(); +} +void NIFStream::getVector4s(std::vector &vec, size_t size) +{ + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector4(); +} +void NIFStream::getQuaternions(std::vector &quat, size_t size) +{ + quat.resize(size); + for(size_t i = 0;i < quat.size();i++) + quat[i] = getQuaternion(); +} } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 0f9ec9085f..cc14971fd5 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -5,8 +5,6 @@ #include #include -#include -#include #include #include @@ -86,23 +84,12 @@ public: ///This is special since the version string doesn't start with a number, and ends with "\n" std::string getVersionString(); - //Templated functions to handle reads - template - T get(){throw std::runtime_error("Can not read a <"+std::string(typeid(T).name())+"> from a NIF File! The get() function was called with the wrong template!");} - - ///Return a vector of whatever object is needed - template - std::vector getItems(size_t number_of_items) - { - std::vector items; - items.reserve(number_of_items); - for(size_t i=0; i < number_of_items; ++i) - { - items.push_back(get()); - } - return items; - } - + void getShorts(std::vector &vec, size_t size); + void getFloats(std::vector &vec, size_t size); + void getVector2s(std::vector &vec, size_t size); + void getVector3s(std::vector &vec, size_t size); + void getVector4s(std::vector &vec, size_t size); + void getQuaternions(std::vector &quat, size_t size); }; } diff --git a/components/nif/tests/test.sh b/components/nif/tests/test.sh index 424e27b07d..95ecdbfba0 100755 --- a/components/nif/tests/test.sh +++ b/components/nif/tests/test.sh @@ -9,7 +9,7 @@ find "$DATAFILESDIR" -iname *nif >> nifs.txt sed -e 's/.*/\"&\"/' nifs.txt > quoted_nifs.txt -nice -n 10 xargs --arg-file=quoted_nifs.txt ../../../niftest +xargs --arg-file=quoted_nifs.txt ../../../niftest rm nifs.txt rm quoted_nifs.txt From e19ab77d00301108d06307142e201b4f6edbd813 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 6 Jan 2015 19:29:33 +0100 Subject: [PATCH 227/404] Store camera first person state in savegame (Fixes #2255) --- apps/openmw/mwstate/statemanagerimp.cpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 16 ++++++++++++++-- components/esm/defs.hpp | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f77a90d8ee..30dba2aaa3 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -355,6 +355,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_ENAB: case ESM::REC_LEVC: case ESM::REC_LEVI: + case ESM::REC_CAM_: MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; @@ -406,6 +407,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false); + // Vanilla MW will restart startup scripts when a save game is loaded. This is unintuive, + // but some mods may be using it as a reload detector. MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); // Do not trigger erroneous cellChanged events diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1c9b8b9960..a939a6db91 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -201,6 +201,7 @@ namespace MWWorld setupPlayer(); renderPlayer(); + mRendering->resetCamera(); MWBase::Environment::get().getWindowManager()->updatePlayer(); @@ -304,7 +305,8 @@ namespace MWWorld +1 // player record +1 // weather record +1 // actorId counter - +1; // levitation/teleport enabled state + +1 // levitation/teleport enabled state + +1; // camera } void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -333,6 +335,11 @@ namespace MWWorld writer.writeHNT("LEVT", mLevitationEnabled); writer.endRecord(ESM::REC_ENAB); progress.increaseProgress(); + + writer.startRecord(ESM::REC_CAM_); + writer.writeHNT("FIRS", isFirstPerson()); + writer.endRecord(ESM::REC_CAM_); + progress.increaseProgress(); } void World::readRecord (ESM::ESMReader& reader, int32_t type, @@ -347,6 +354,12 @@ namespace MWWorld reader.getHNT(mTeleportEnabled, "TELE"); reader.getHNT(mLevitationEnabled, "LEVT"); return; + case ESM::REC_CAM_: + bool firstperson; + reader.getHNT(firstperson, "FIRS"); + if (firstperson != isFirstPerson()) + togglePOV(); + break; default: if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && @@ -2073,7 +2086,6 @@ namespace MWWorld MWBase::Environment::get().getMechanicsManager()->add(mPlayer->getPlayer()); mPhysics->addActor(mPlayer->getPlayer()); - mRendering->resetCamera(); } int World::canRest () diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index d5ba919b0a..fe45d69149 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -114,6 +114,7 @@ enum RecNameInts REC_DCOU = FourCC<'D','C','O','U'>::value, REC_MARK = FourCC<'M','A','R','K'>::value, REC_ENAB = FourCC<'E','N','A','B'>::value, + REC_CAM_ = FourCC<'C','A','M','_'>::value, // format 1 REC_FILT = 0x544C4946, From 4d9100091d3356dfadef0b55e83970da689ba406 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 6 Jan 2015 22:19:04 +0100 Subject: [PATCH 228/404] Reduce default pathing arrival tolerance to 32 units (Fixes #1605) --- apps/openmw/mwmechanics/aiwander.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 4 ++-- apps/openmw/mwmechanics/pathfinding.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 2df3762ab5..94c4542f81 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -202,7 +202,7 @@ namespace MWMechanics // Are we there yet? bool& chooseAction = storage.mChooseAction; if(walking && - storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2], 64.f)) { stopWalking(actor, storage); moveNow = false; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f1279c415e..f59167df7a 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -297,13 +297,13 @@ namespace MWMechanics return false; } - bool PathFinder::checkPathCompleted(float x, float y, float z) + bool PathFinder::checkPathCompleted(float x, float y, float z, float tolerance) { if(mPath.empty()) return true; ESM::Pathgrid::Point nextPoint = *mPath.begin(); - if(sqrDistanceZCorrected(nextPoint, x, y, z) < 64*64) + if(sqrDistanceZCorrected(nextPoint, x, y, z) < tolerance*tolerance) { mPath.pop_front(); if(mPath.empty()) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 482808dacd..61008577c9 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -39,8 +39,8 @@ namespace MWMechanics void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts = true); - bool checkPathCompleted(float x, float y, float z); - ///< \Returns true if the last point of the path has been reached. + bool checkPathCompleted(float x, float y, float z, float tolerance=32.f); + ///< \Returns true if we are within \a tolerance units of the last path point. bool checkWaypoint(float x, float y, float z); ///< \Returns true if a way point was reached From a17252eab3415d2b1d987ccc8c3a3ef3c1481b32 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 6 Jan 2015 23:23:58 +0100 Subject: [PATCH 229/404] Remove unused checkWaypoint function It was an almost exact copy of the checkPathCompleted function anyway. --- apps/openmw/mwmechanics/aicombat.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 15 --------------- apps/openmw/mwmechanics/pathfinding.hpp | 3 --- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index a23634ea39..5d04705544 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -580,7 +580,7 @@ namespace MWMechanics buildNewPath(actor, target); //may fail to build a path, check before use //delete visited path node - mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]); + mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); // This works on the borders between the path grid and areas with no waypoints. if(inLOS && mPathFinder.getPath().size() > 1) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f59167df7a..0634725a81 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -282,21 +282,6 @@ namespace MWMechanics return Ogre::Math::ATan2(directionX,directionY).valueDegrees(); } - bool PathFinder::checkWaypoint(float x, float y, float z) - { - if(mPath.empty()) - return true; - - ESM::Pathgrid::Point nextPoint = *mPath.begin(); - if(sqrDistanceZCorrected(nextPoint, x, y, z) < 64*64) - { - mPath.pop_front(); - if(mPath.empty()) mIsPathConstructed = false; - return true; - } - return false; - } - bool PathFinder::checkPathCompleted(float x, float y, float z, float tolerance) { if(mPath.empty()) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 61008577c9..7ba2d22ba6 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -42,9 +42,6 @@ namespace MWMechanics bool checkPathCompleted(float x, float y, float z, float tolerance=32.f); ///< \Returns true if we are within \a tolerance units of the last path point. - bool checkWaypoint(float x, float y, float z); - ///< \Returns true if a way point was reached - float getZAngleToNext(float x, float y) const; bool isPathConstructed() const From d02e075bab885d805e8c6d7b994da75c69f69154 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 6 Jan 2015 23:54:53 +0100 Subject: [PATCH 230/404] Add setting for exterior cell grid size (Fixes #1537) --- apps/openmw/mwworld/scene.cpp | 16 +++++++++------- files/settings-default.cfg | 3 +++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 02c9db9ea4..efe88c406e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -296,15 +296,17 @@ namespace MWWorld std::string loadingExteriorText = "#{sLoadingMessage3}"; loadingListener->setLabel(loadingExteriorText); + const int halfGridSize = Settings::Manager::getInt("exterior grid size", "Cells")/2; + CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) { if ((*active)->getCell()->isExterior()) { - if (std::abs (X-(*active)->getCell()->getGridX())<=1 && - std::abs (Y-(*active)->getCell()->getGridY())<=1) + if (std::abs (X-(*active)->getCell()->getGridX())<=halfGridSize && + std::abs (Y-(*active)->getCell()->getGridY())<=halfGridSize) { - // keep cells within the new 3x3 grid + // keep cells within the new grid ++active; continue; } @@ -314,9 +316,9 @@ namespace MWWorld int refsToLoad = 0; // get the number of refs to load - for (int x=X-1; x<=X+1; ++x) + for (int x=X-halfGridSize; x<=X+halfGridSize; ++x) { - for (int y=Y-1; y<=Y+1; ++y) + for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y) { CellStoreCollection::iterator iter = mActiveCells.begin(); @@ -339,9 +341,9 @@ namespace MWWorld loadingListener->setProgressRange(refsToLoad); // Load cells - for (int x=X-1; x<=X+1; ++x) + for (int x=X-halfGridSize; x<=X+halfGridSize; ++x) { - for (int y=Y-1; y<=Y+1; ++y) + for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y) { CellStoreCollection::iterator iter = mActiveCells.begin(); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6ec9b03e9a..f91299a1ff 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -123,6 +123,9 @@ local map resolution = 256 local map widget size = 512 local map hud widget size = 256 +[Cells] +exterior grid size = 3 + [Viewing distance] # Limit the rendering distance of small objects limit small object distance = false From dc5ed5b8613b9074ecc6a25e4a41b1825909abb6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Jan 2015 01:11:39 +0100 Subject: [PATCH 231/404] Remove weather particles underwater (Fixes #2010) --- apps/openmw/mwrender/sky.cpp | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 761d3fd967..0b9dc091e5 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -431,7 +432,10 @@ void SkyManager::updateRain(float dt) Ogre::Vector3 pos = it->first->getPosition(); pos.z -= mRainSpeed * dt; it->first->setPosition(pos); - if (pos.z < -minHeight) + if (pos.z < -minHeight + // Here we might want to add a "splash" effect later + || MWBase::Environment::get().getWorld()->isUnderwater( + MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(), it->first->_getDerivedPosition())) { it->second.setNull(); mSceneMgr->destroySceneNode(it->first); @@ -458,6 +462,12 @@ void SkyManager::updateRain(float dt) // Create a separate node to control the offset, since a node with setInheritOrientation(false) will still // consider the orientation of the parent node for its position, just not for its orientation float startHeight = 700; + Ogre::Vector3 worldPos = mParticleNode->_getDerivedPosition(); + worldPos += Ogre::Vector3(xOffs, yOffs, startHeight); + if (MWBase::Environment::get().getWorld()->isUnderwater( + MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(), worldPos)) + return; + Ogre::SceneNode* offsetNode = mParticleNode->createChildSceneNode(Ogre::Vector3(xOffs,yOffs,startHeight)); // Spawn a new rain object for each instance. @@ -490,6 +500,30 @@ void SkyManager::update(float duration) for (unsigned int i=0; imControllers.size(); ++i) mParticle->mControllers[i].update(); + for (unsigned int i=0; imParticles.size(); ++i) + { + Ogre::ParticleSystem* psys = mParticle->mParticles[i]; + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + #if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + Ogre::Vector3 pos = p->mPosition; + Ogre::Real& timeToLive = p->mTimeToLive; + #else + Ogre::Vector3 pos = p->position; + Ogre::Real& timeToLive = p->timeToLive; + #endif + + if (psys->getKeepParticlesInLocalSpace() && psys->getParentNode()) + pos = psys->getParentNode()->convertLocalToWorldPosition(pos); + + if (MWBase::Environment::get().getWorld()->isUnderwater( + MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(), pos)) + timeToLive = 0; + } + } + if (mIsStorm) mParticleNode->setOrientation(Ogre::Vector3::UNIT_Y.getRotationTo(mStormDirection)); } From 157438460b1500ec2277d4f809b3fc383cba36d0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Jan 2015 01:30:59 +0100 Subject: [PATCH 232/404] Fix being able to activate objects when paralyzed --- apps/openmw/engine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 24e1388d0d..c48f509b56 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -514,7 +514,8 @@ void OMW::Engine::activate() return; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - if (player.getClass().getCreatureStats(player).getKnockedDown()) + if (player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0 + || player.getClass().getCreatureStats(player).getKnockedDown()) return; MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject(); From 4e92f6ab48c5cefb5c4dc60d6ef8ee04fc2e6e70 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Jan 2015 03:03:56 +0100 Subject: [PATCH 233/404] Add commandline option to load a save game on startup --- apps/openmw/engine.cpp | 13 +++++- apps/openmw/engine.hpp | 4 ++ apps/openmw/main.cpp | 4 ++ apps/openmw/mwbase/statemanager.hpp | 11 +++-- apps/openmw/mwgui/savegamedialog.cpp | 2 +- apps/openmw/mwstate/character.cpp | 5 +++ apps/openmw/mwstate/character.hpp | 6 +-- apps/openmw/mwstate/statemanagerimp.cpp | 53 ++++++++++++++++++++----- apps/openmw/mwstate/statemanagerimp.hpp | 11 +++-- 9 files changed, 85 insertions(+), 24 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 24e1388d0d..43adedcee3 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -472,9 +472,13 @@ void OMW::Engine::go() // Play some good 'ol tunes MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); - // start in main menu - if (!mSkipMenu) + if (!mSaveGameFile.empty()) + { + MWBase::Environment::get().getStateManager()->loadGame(mSaveGameFile); + } + else if (!mSkipMenu) { + // start in main menu MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); try { @@ -622,3 +626,8 @@ void OMW::Engine::enableFontExport(bool exportFonts) { mExportFonts = exportFonts; } + +void OMW::Engine::setSaveGameFile(const std::string &savegame) +{ + mSaveGameFile = savegame; +} diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 6cf31cba89..079dd922cc 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -83,6 +83,7 @@ namespace OMW bool mScriptConsoleMode; std::string mStartupScript; int mActivationDistanceOverride; + std::string mSaveGameFile; // Grab mouse? bool mGrab; @@ -204,6 +205,9 @@ namespace OMW void enableFontExport(bool exportFonts); + /// Set the save game file to load after initialising the engine. + void setSaveGameFile(const std::string& savegame); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 9382e2516b..74d434f63b 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -152,6 +152,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-blacklist-use", bpo::value()->implicit_value(true) ->default_value(true), "enable script blacklisting") + ("load-savegame", bpo::value()->default_value(""), + "load a save game file on game startup") + ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") @@ -274,6 +277,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setWarningsMode (variables["script-warn"].as()); engine.setScriptBlacklist (variables["script-blacklist"].as()); engine.setScriptBlacklistUse (variables["script-blacklist-use"].as()); + engine.setSaveGameFile (variables["load-savegame"].as()); // other settings engine.setSoundUsage(!variables["no-sound"].as()); diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 006be921b1..79ddbfa2ab 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -62,10 +62,13 @@ namespace MWBase /// /// \note Slot must belong to the current character. - virtual void loadGame (const MWState::Character *character, const MWState::Slot *slot) = 0; - ///< Load a saved game file from \a slot. - /// - /// \note \a slot must belong to \a character. + virtual void loadGame (const std::string& filepath) = 0; + ///< Load a saved game directly from the given file path. This will search the CharacterManager + /// for a Character containing this save file, and set this Character current if one was found. + /// Otherwise, a new Character will be created. + + virtual void loadGame (const MWState::Character *character, const std::string& filepath) = 0; + ///< Load a saved game file belonging to the given character. ///Simple saver, writes over the file if already existing /** Used for quick save and autosave **/ diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 7b9777c815..3775f53dd1 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -246,7 +246,7 @@ namespace MWGui else { assert (mCurrentCharacter && mCurrentSlot); - MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot); + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot->mPath.string()); } } diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index f8fcfceec6..c2ca5e095c 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -190,3 +190,8 @@ ESM::SavedGame MWState::Character::getSignature() const return slot.mProfile; } + +const boost::filesystem::path& MWState::Character::getPath() const +{ + return mPath; +} diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index 4703f0cca3..32c79a183e 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -54,12 +54,12 @@ namespace MWState /// \attention The \a slot pointer will be invalidated by this call. SlotIterator begin() const; - ///< First slot is the most recent. Other slots follow in descending order of save date. - /// - /// Any call to createSlot and updateSlot can invalidate the returned iterator. + ///< Any call to createSlot and updateSlot can invalidate the returned iterator. SlotIterator end() const; + const boost::filesystem::path& getPath() const; + ESM::SavedGame getSignature() const; ///< Return signature information for this character. /// diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f77a90d8ee..03cb5d9214 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -291,16 +291,47 @@ void MWState::StateManager::quickSave (std::string name) saveGame(name, slot); } -void MWState::StateManager::loadGame (const Character *character, const Slot *slot) +void MWState::StateManager::loadGame(const std::string& filepath) +{ + for (CharacterIterator it = mCharacterManager.begin(); it != mCharacterManager.end(); ++it) + { + const MWState::Character& character = *it; + for (MWState::Character::SlotIterator slotIt = character.begin(); slotIt != character.end(); ++slotIt) + { + const MWState::Slot& slot = *slotIt; + if (slot.mPath == boost::filesystem::path(filepath)) + { + loadGame(&character, slot.mPath.string()); + return; + } + } + } + + // have to peek into the save file to get the player name + ESM::ESMReader reader; + reader.open (filepath); + if (reader.getFormat()>ESM::Header::CurrentFormat) + return; // format is too new -> ignore + if (reader.getRecName()!=ESM::REC_SAVE) + return; // invalid save file -> ignore + reader.getRecHeader(); + ESM::SavedGame profile; + profile.load (reader); + reader.close(); + + MWState::Character* character = mCharacterManager.getCurrentCharacter(true, profile.mPlayerName); + loadGame(character, filepath); + mTimePlayed = profile.mTimePlayed; +} + +void MWState::StateManager::loadGame (const Character *character, const std::string& filepath) { try { cleanup(); - mTimePlayed = slot->mProfile.mTimePlayed; - ESM::ESMReader reader; - reader.open (slot->mPath.string()); + reader.open (filepath); std::map contentFileMap = buildContentFileIndexMap (reader); @@ -319,9 +350,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl switch (n.val) { case ESM::REC_SAVE: - - // don't need to read that here - reader.skipRecord(); + { + ESM::SavedGame profile; + profile.load(reader); + mTimePlayed = profile.mTimePlayed; + } break; case ESM::REC_JOUR: @@ -391,7 +424,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl mState = State_Running; Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); + character->getPath().filename().string()); MWBase::Environment::get().getWindowManager()->setNewGame(false); MWBase::Environment::get().getWorld()->setupPlayer(); @@ -431,7 +464,7 @@ void MWState::StateManager::quickLoad() { if (Character* mCurrentCharacter = getCurrentCharacter (false)) if (const MWState::Slot* slot = &*mCurrentCharacter->begin()) //Get newest save - loadGame (mCurrentCharacter, slot); + loadGame (mCurrentCharacter, slot->mPath.string()); } void MWState::StateManager::deleteGame(const MWState::Character *character, const MWState::Slot *slot) @@ -472,7 +505,7 @@ void MWState::StateManager::update (float duration) //Load last saved game for current character MWState::Slot lastSave = *curCharacter->begin(); - loadGame(curCharacter, &lastSave); + loadGame(curCharacter, lastSave.mPath.string()); } else if(iButton==1) { diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 40c36deb5e..956a2d73c4 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -61,10 +61,13 @@ namespace MWState /** Used for quickload **/ virtual void quickLoad(); - virtual void loadGame (const Character *character, const Slot *slot); - ///< Load a saved game file from \a slot. - /// - /// \note \a slot must belong to \a character. + virtual void loadGame (const std::string& filepath); + ///< Load a saved game directly from the given file path. This will search the CharacterManager + /// for a Character containing this save file, and set this Character current if one was found. + /// Otherwise, a new Character will be created. + + virtual void loadGame (const Character *character, const std::string &filepath); + ///< Load a saved game file belonging to the given character. virtual Character *getCurrentCharacter (bool create = true); ///< \param create Create a new character, if there is no current character. From ef1b0a191b0370a711377093b84ccbbbe676955b Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Jan 2015 03:48:16 +0100 Subject: [PATCH 234/404] Revert "Enchanting: fix inverted self-enchant success chance" It wasn't inverted to begin with. The author of this commit is an idiot. --- apps/openmw/mwmechanics/enchanting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 8c85e5eef5..08df95fd91 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -65,7 +65,7 @@ namespace MWMechanics if(mSelfEnchanting) { - if(std::rand()/static_cast (RAND_MAX)*100 < getEnchantChance()) + if(getEnchantChance() (RAND_MAX)*100) return false; mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2); From 13c5bd5cc27cde385285776d48cd9e1aff7b42eb Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Jan 2015 04:28:56 +0100 Subject: [PATCH 235/404] Enchanting: fix skill-based cast cost bonus being applied twice --- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/spellmodel.cpp | 4 +--- apps/openmw/mwmechanics/enchanting.cpp | 23 ++++++++++------------- apps/openmw/mwmechanics/enchanting.hpp | 3 ++- apps/openmw/mwmechanics/spellcasting.cpp | 16 +++++++++++++--- apps/openmw/mwmechanics/spellcasting.hpp | 2 ++ 6 files changed, 29 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 4744fd1a16..d2315be38d 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -111,7 +111,7 @@ namespace MWGui mCharge->setCaption(boost::lexical_cast(mEnchanting.getGemCharge())); std::stringstream castCost; - castCost << mEnchanting.getCastCost(); + castCost << mEnchanting.getEffectiveCastCost(); mCastCost->setCaption(castCost.str()); mPrice->setCaption(boost::lexical_cast(mEnchanting.getEnchantPrice())); diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index ad9a913fa0..4713720cd0 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -103,9 +103,7 @@ namespace MWGui && item.getClass().canBeEquipped(item, mActor).first == 0) continue; - float enchantCost = enchant->mData.mCost; - int eSkill = mActor.getClass().getSkill(mActor, ESM::Skill::Enchant); - int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); + int castCost = MWMechanics::getEffectiveEnchantmentCastCost(enchant->mData.mCost, mActor); std::string cost = boost::lexical_cast(castCost); int currentCharge = int(item.getCellRef().getEnchantmentCharge()); diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 08df95fd91..96afe2e2a9 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -6,6 +6,7 @@ #include "creaturestats.hpp" #include "npcstats.hpp" +#include "spellcasting.hpp" namespace MWMechanics { @@ -55,7 +56,7 @@ namespace MWMechanics enchantment.mData.mCharge = getGemCharge(); enchantment.mData.mAutocalc = 0; enchantment.mData.mType = mCastStyle; - enchantment.mData.mCost = getCastCost(); + enchantment.mData.mCost = getBaseCastCost(); store.remove(mSoulGemPtr, 1, player); @@ -199,23 +200,19 @@ namespace MWMechanics } - int Enchanting::getCastCost() const + int Enchanting::getBaseCastCost() const { if (mCastStyle == ESM::Enchantment::ConstantEffect) return 0; - const float enchantCost = getEnchantPoints(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWMechanics::NpcStats &stats = player.getClass().getNpcStats(player); - int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); - - /* - * Each point of enchant skill above/under 10 subtracts/adds - * one percent of enchantment cost while minimum is 1. - */ - const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10); + return getEnchantPoints(); + } - return static_cast((castCost < 1) ? 1 : castCost); + int Enchanting::getEffectiveCastCost() const + { + int baseCost = getBaseCastCost(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + return getEffectiveEnchantmentCastCost(baseCost, player); } diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index 2ee5ccce4e..2c05d2d2e9 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -36,7 +36,8 @@ namespace MWMechanics void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object) int getCastStyle() const; int getEnchantPoints() const; - int getCastCost() const; + int getBaseCastCost() const; // To be saved in the enchantment's record + int getEffectiveCastCost() const; // Effective cost taking player Enchant skill into account, used for preview purposes in the UI int getEnchantPrice() const; int getMaxEnchantValue() const; int getGemCharge() const; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index b7c7e00a10..d9df1d00e7 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -689,9 +689,7 @@ namespace MWMechanics // Check if there's enough charge left if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { - const float enchantCost = enchantment->mData.mCost; - int eSkill = mCaster.getClass().getSkill(mCaster, ESM::Skill::Enchant); - const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); + const int castCost = getEffectiveEnchantmentCastCost(enchantment->mData.mCost, mCaster); if (item.getCellRef().getEnchantmentCharge() == -1) item.getCellRef().setEnchantmentCharge(enchantment->mData.mCharge); @@ -915,4 +913,16 @@ namespace MWMechanics return true; } + + int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor) + { + /* + * Each point of enchant skill above/under 10 subtracts/adds + * one percent of enchantment cost while minimum is 1. + */ + int eSkill = actor.getClass().getSkill(actor, ESM::Skill::Enchant); + const float result = castCost - (castCost / 100) * (eSkill - 10); + + return static_cast((result < 1) ? 1 : result); + } } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 395ae043b4..a79eeec6b9 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -58,6 +58,8 @@ namespace MWMechanics float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); + int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); + class CastSpell { private: From 4684014a83d1c8fef28cb7a25bb6528b03e4978a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Jan 2015 15:06:39 +0100 Subject: [PATCH 236/404] Use .omwsave extension for save game files --- apps/openmw/mwstate/character.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index c2ca5e095c..f190565daf 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -61,7 +61,8 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile) stream << "_"; } - slot.mPath = mPath / stream.str(); + const std::string ext = ".omwsave"; + slot.mPath = mPath / (stream.str() + ext); // Append an index if necessary to ensure a unique file int i=0; @@ -70,7 +71,7 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile) std::ostringstream test; test << stream.str(); test << " - " << ++i; - slot.mPath = mPath / test.str(); + slot.mPath = mPath / (test.str() + ext); } slot.mProfile = profile; From 94002b0758a8825215074b5341b6ec6b0cd28660 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Jan 2015 15:07:08 +0100 Subject: [PATCH 237/404] Add freedesktop.org mimeinfo for OpenMW save game files This allows us to launch OpenMW when the user opens a save game file through the file manager. --- CMakeLists.txt | 3 +++ files/openmw-mimeinfo.xml | 11 +++++++++++ files/openmw.desktop | 13 +++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 files/openmw-mimeinfo.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index fb89f4e91d..0edf8cacd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -386,6 +386,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop "${OpenMW_BINARY_DIR}/openmw.desktop") + configure_file(${OpenMW_SOURCE_DIR}/files/openmw-mimeinfo.xml + "${OpenMW_BINARY_DIR}/openmw-mimeinfo.xml") configure_file(${OpenMW_SOURCE_DIR}/files/opencs.desktop "${OpenMW_BINARY_DIR}/opencs.desktop") endif() @@ -451,6 +453,7 @@ IF(NOT WIN32 AND NOT APPLE) # Install icon and desktop file INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") + INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-mimeinfo.xml" DESTINATION "${DATAROOTDIR}/mime/packages" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") IF(BUILD_OPENCS) INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs") diff --git a/files/openmw-mimeinfo.xml b/files/openmw-mimeinfo.xml new file mode 100644 index 0000000000..1355383a5b --- /dev/null +++ b/files/openmw-mimeinfo.xml @@ -0,0 +1,11 @@ + + + + + + + OpenMW Savegame + + + + diff --git a/files/openmw.desktop b/files/openmw.desktop index 4a3a76f52d..709304cb9c 100644 --- a/files/openmw.desktop +++ b/files/openmw.desktop @@ -8,3 +8,16 @@ TryExec=omwlauncher Exec=omwlauncher Icon=openmw Categories=Game;RolePlaying; + +[Desktop Entry] +Type=Application +Name=OpenMW +GenericName=Role Playing Game +Comment=An engine replacement for The Elder Scrolls III: Morrowind +Keywords=Morrowind;Reimplementation Mods;esm;bsa; +Exec=openmw --load-savegame=%f +Icon=openmw +Categories=Game;RolePlaying; +Terminal=false +NoDisplay=true +MimeType=application/x-openmw-savegame; From 083de62be59ad0fe701af233eff2e8d0411ebb18 Mon Sep 17 00:00:00 2001 From: dteviot Date: Thu, 8 Jan 2015 11:18:42 +1300 Subject: [PATCH 238/404] Fixed issues found by Zinnschlag. 1. Errors found are added to default tool tip text. (Instead of replacing it.) 2. If multiple errors are found, all are shown in tool tip text, not just first one. 3. Load Order Errors are updated when files are activated/deactivated, not just when the files have their position in list changed. --- .../contentselector/model/contentmodel.cpp | 94 +++++++++++-------- .../contentselector/model/contentmodel.hpp | 17 +++- .../contentselector/model/loadordererror.cpp | 7 -- .../contentselector/model/loadordererror.hpp | 4 - 4 files changed, 65 insertions(+), 57 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 6366d7f540..57b231357f 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -173,7 +173,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int { case Qt::ForegroundRole: { - if (isLoadOrderError(file->filePath())) + if (isLoadOrderError(file)) { QBrush redBackground(Qt::red, Qt::SolidPattern); return redBackground; @@ -213,7 +213,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int if (column != 0) return QVariant(); - return isLoadOrderError(file->filePath()) ? getLoadOrderError(file->filePath()).toolTip() : file->toolTip(); + return toolTip(file); break; } @@ -302,7 +302,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const { setCheckState(file->filePath(), success); emit dataChanged(index, index); - + checkForLoadOrderErrors(); } else return success; @@ -543,26 +543,14 @@ bool ContentSelectorModel::ContentModel::isEnabled (QModelIndex index) const return (flags(index) & Qt::ItemIsEnabled); } -bool ContentSelectorModel::ContentModel::isLoadOrderError(const QString& filepath) const +bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile *file) const { - return !(getLoadOrderError(filepath) == LoadOrderError::sNoError); -} - -ContentSelectorModel::LoadOrderError ContentSelectorModel::ContentModel::getLoadOrderError(const QString& filepath) const -{ - return mLoadOrderErrors.contains(filepath) ? mLoadOrderErrors[filepath] : ContentSelectorModel::LoadOrderError::sNoError; -} - -void ContentSelectorModel::ContentModel::setLoadOrderError(const QString& filepath, const ContentSelectorModel::LoadOrderError& loadOrderError) -{ - mLoadOrderErrors[filepath] = loadOrderError; - int filePosition = indexFromItem(item(filepath)).row(); - emit dataChanged(index(filePosition, 0, QModelIndex()), index(filePosition, 0, QModelIndex())); + return mPluginsWithLoadOrderError.contains(file->filePath()); } void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileList, bool isChecked) { - mLoadOrderErrors.clear(); + mPluginsWithLoadOrderError.clear(); int previousPosition = -1; foreach (const QString &filepath, fileList) { @@ -590,36 +578,60 @@ void ContentSelectorModel::ContentModel::checkForLoadOrderErrors() for (int row = 0; row < mFiles.count(); ++row) { EsmFile* file = item(row); - bool isRowInError = isLoadOrderError(file->filePath()); - LoadOrderError::ErrorCode error = LoadOrderError::ErrorCode_None; - foreach(QString dependentfileName, file->gameFiles()) + bool isRowInError = checkForLoadOrderErrors(file, row).count() != 0; + if (isRowInError) + { + mPluginsWithLoadOrderError.insert(file->filePath()); + } + else { - const EsmFile* dependentFile = item(dependentfileName); + mPluginsWithLoadOrderError.remove(file->filePath()); + } + } +} - if (!dependentFile) - { - error = LoadOrderError::ErrorCode_MissingDependency; - } - else if (!isChecked(dependentFile->filePath())) - { - error = LoadOrderError::ErrorCode_InactiveDependency; - } - else if (row < indexFromItem(dependentFile).row()) - { - error = LoadOrderError::ErrorCode_LoadOrder; - } +QList ContentSelectorModel::ContentModel::checkForLoadOrderErrors(const EsmFile *file, int row) const +{ + QList errors = QList(); + foreach(QString dependentfileName, file->gameFiles()) + { + const EsmFile* dependentFile = item(dependentfileName); - if (!isRowInError && (error != LoadOrderError::ErrorCode_None)) - { - setLoadOrderError(file->filePath(), LoadOrderError(error, dependentfileName)); - break; - } + if (!dependentFile) + { + errors.append(LoadOrderError(LoadOrderError::ErrorCode_MissingDependency, dependentfileName)); + } + if (!isChecked(dependentFile->filePath())) + { + errors.append(LoadOrderError(LoadOrderError::ErrorCode_InactiveDependency, dependentfileName)); } + if (row < indexFromItem(dependentFile).row()) + { + errors.append(LoadOrderError(LoadOrderError::ErrorCode_LoadOrder, dependentfileName)); + } + } + return errors; +} - if (isRowInError && (error == LoadOrderError::ErrorCode_None)) +QString ContentSelectorModel::ContentModel::toolTip(const EsmFile *file) const +{ + if (isLoadOrderError(file)) + { + QString text(""); + int index = indexFromItem(item(file->filePath())).row(); + foreach(const LoadOrderError& error, checkForLoadOrderErrors(file, index)) { - setLoadOrderError(file->filePath(), LoadOrderError::sNoError); + text += "

"; + text += error.toolTip(); + text += "

"; } + text += ("
"); + text += file->toolTip(); + return text; + } + else + { + return file->toolTip(); } } diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 2a9f2c93bc..f6e3d62bd2 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "loadordererror.hpp" @@ -53,10 +54,6 @@ namespace ContentSelectorModel ContentFileList checkedItems() const; void uncheckAll(); - bool isLoadOrderError(const QString& filepath) const; - LoadOrderError getLoadOrderError(const QString& filepath) const; - void setLoadOrderError(const QString& filepath, const LoadOrderError& loadOrderError); - void refreshModel(); private: @@ -66,12 +63,22 @@ namespace ContentSelectorModel EsmFile *item(int row); void sortFiles(); + + /// Checks all plug-ins for load order errors and updates mPluginsWithLoadOrderError with plug-ins with issues void checkForLoadOrderErrors(); + /// Checks a specific plug-in for load order errors + /// \return all errors found for specific plug-in + QList checkForLoadOrderErrors(const EsmFile *file, int row) const; + + /// \return true if plug-in has a Load Order error + bool isLoadOrderError(const EsmFile *file) const; + + QString toolTip(const EsmFile *file) const; ContentFileList mFiles; QHash mCheckStates; - QHash mLoadOrderErrors; + QSet mPluginsWithLoadOrderError; QTextCodec *mCodec; QString mEncoding; diff --git a/components/contentselector/model/loadordererror.cpp b/components/contentselector/model/loadordererror.cpp index 0d02efbebc..aa69f330e3 100644 --- a/components/contentselector/model/loadordererror.cpp +++ b/components/contentselector/model/loadordererror.cpp @@ -8,15 +8,8 @@ QString ContentSelectorModel::LoadOrderError::sErrorToolTips[ErrorCode_LoadOrder QString("This file needs to load after %1") }; -ContentSelectorModel::LoadOrderError ContentSelectorModel::LoadOrderError::sNoError = ContentSelectorModel::LoadOrderError(); - QString ContentSelectorModel::LoadOrderError::toolTip() const { assert(mErrorCode); return sErrorToolTips[mErrorCode - 1].arg(mFileName); } - -bool ContentSelectorModel::LoadOrderError::operator== (const ContentSelectorModel::LoadOrderError& rhs) const -{ - return (mErrorCode == rhs.mErrorCode) && ((mErrorCode == ErrorCode_None) || (mFileName == rhs.mFileName)); -} \ No newline at end of file diff --git a/components/contentselector/model/loadordererror.hpp b/components/contentselector/model/loadordererror.hpp index f3d3b0f8ae..2b840cf69b 100644 --- a/components/contentselector/model/loadordererror.hpp +++ b/components/contentselector/model/loadordererror.hpp @@ -25,12 +25,8 @@ namespace ContentSelectorModel } inline ErrorCode errorCode() const { return mErrorCode; } inline QString fileName() const { return mFileName; } - bool operator==(const LoadOrderError& rhs) const; QString toolTip() const; - /// Sentinel to represent a "No Load Order Error" condition - static LoadOrderError sNoError; - private: ErrorCode mErrorCode; QString mFileName; From 4b88ef58910c63cfef0fe6a1dac471980813f00c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 8 Jan 2015 13:59:03 +0100 Subject: [PATCH 239/404] fixed QuickFileParser handling of begin line (skip it instead of trying to make sense of it) --- components/compiler/quickfileparser.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/components/compiler/quickfileparser.cpp b/components/compiler/quickfileparser.cpp index 895b7ce659..f3d8063b2f 100644 --- a/components/compiler/quickfileparser.cpp +++ b/components/compiler/quickfileparser.cpp @@ -19,12 +19,6 @@ bool Compiler::QuickFileParser::parseName (const std::string& name, const TokenL bool Compiler::QuickFileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { - if (keyword==Scanner::K_begin) - { - scanner.allowNameStartingwithDigit(); - return true; - } - if (keyword==Scanner::K_end) return false; From ef7e0070a62f1c90c9a9150c397d3666c61a0b0a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 8 Jan 2015 02:34:37 +0100 Subject: [PATCH 240/404] Don't right-shift signed integers which is implementation-defined --- apps/openmw/mwgui/formatting.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 2 +- components/esm/loadligh.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 5831160034..765402a01e 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -340,7 +340,7 @@ namespace MWGui { if (attr.find("color") != attr.end()) { - int color; + unsigned int color; std::stringstream ss; ss << attr.at("color"); ss >> std::hex >> color; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 1ef7a3533d..ea40721cb0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -336,7 +336,7 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScene { const MWWorld::Fallback *fallback = MWBase::Environment::get().getWorld()->getFallback(); - const int clr = light->mData.mColor; + const unsigned int clr = light->mData.mColor; Ogre::ColourValue color(((clr >> 0) & 0xFF) / 255.0f, ((clr >> 8) & 0xFF) / 255.0f, ((clr >> 16) & 0xFF) / 255.0f); diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp index ffc2916099..2c83248f8a 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -37,7 +37,7 @@ struct Light int mValue; int mTime; // Duration int mRadius; - int mColor; // 4-byte rgba value + unsigned int mColor; // 4-byte rgba value int mFlags; }; // Size = 24 bytes From 928b9ee41b2f3296426102d4df1fc9bf85d8ed91 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 8 Jan 2015 03:29:13 +0100 Subject: [PATCH 241/404] Fix missing GUI mode update when showing soulgem dialog --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 72805dd31e..5546480ec5 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1455,6 +1455,8 @@ namespace MWGui void WindowManager::showSoulgemDialog(MWWorld::Ptr item) { mSoulgemDialog->show(item); + MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); + updateVisible(); } void WindowManager::frameStarted (float dt) From d31ae2b345e0058696a41308d370cb6e851f55a2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 8 Jan 2015 16:17:13 +0100 Subject: [PATCH 242/404] Fix mIds mapping for dynamic records being lost on save/reload --- apps/openmw/mwworld/esmstore.cpp | 10 +++++++++- apps/openmw/mwworld/store.hpp | 6 +++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 2a3fd9179e..8e0f58a6b0 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -192,7 +192,15 @@ void ESMStore::setUp() case ESM::REC_LEVI: case ESM::REC_LEVC: - mStores[type]->read (reader); + { + std::string id = reader.getHNString ("NAME"); + mStores[type]->read (reader, id); + + // FIXME: there might be stale dynamic IDs in mIds from an earlier savegame + // that really should be cleared instead of just overwritten + + mIds[id] = type; + } if (type==ESM::REC_NPC_) { diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index dcfbb4eb17..97ba4652f2 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -30,7 +30,7 @@ namespace MWWorld virtual void write (ESM::ESMWriter& writer) const {} - virtual void read (ESM::ESMReader& reader) {} + virtual void read (ESM::ESMReader& reader, const std::string& id) {} ///< Read into dynamic storage }; @@ -329,10 +329,10 @@ namespace MWWorld } } - void read (ESM::ESMReader& reader) + void read (ESM::ESMReader& reader, const std::string& id) { T record; - record.mId = reader.getHNString ("NAME"); + record.mId = id; record.load (reader); insert (record); } From 2ddbe22da311b5f740a53fa1c4eecc39cbde4b9d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 00:44:22 +0100 Subject: [PATCH 243/404] Fix for NPC store clearDynamic bug It was clearing the whole mShared vector, instead of only the dynamic part. Actually, that whole overload was pointless to begin with. All it does is making sure the Player record isn't cleared, but ESMStore::clearDynamic re-inserts the player record anyway after clearing. --- apps/openmw/mwworld/store.hpp | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 97ba4652f2..e94624ade5 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -140,7 +140,8 @@ namespace MWWorld virtual void clearDynamic() { // remove the dynamic part of mShared - mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); + if (mShared.size() > mStatic.size()) + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); mDynamic.clear(); } @@ -217,7 +218,8 @@ namespace MWWorld void setUp() { // remove the dynamic part of mShared - mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); + if (mShared.size() > mStatic.size()) + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); } iterator begin() const { @@ -305,7 +307,8 @@ namespace MWWorld mDynamic.erase(it); // have to reinit the whole shared part - mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); + if (mShared.size() > mStatic.size()) + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); for (it = mDynamic.begin(); it != mDynamic.end(); ++it) { mShared.push_back(&it->second); } @@ -338,20 +341,6 @@ namespace MWWorld } }; - template <> - inline void Store::clearDynamic() - { - std::map::iterator iter = mDynamic.begin(); - - while (iter!=mDynamic.end()) - if (iter->first=="player") - ++iter; - else - mDynamic.erase (iter++); - - mShared.clear(); - } - template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { std::string idLower = Misc::StringUtils::lowerCase(id); From c77660ba206168cd186c26d03f170e3cee3d31a8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 01:01:55 +0100 Subject: [PATCH 244/404] Remove some nonsense code --- apps/openmw/mwworld/esmstore.hpp | 3 --- apps/openmw/mwworld/store.cpp | 3 +-- apps/openmw/mwworld/store.hpp | 6 ------ 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 5d794db895..01770e6b38 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -99,9 +99,6 @@ namespace MWWorld ESMStore() : mDynamicCount(0) { - // Cell store needs access to this for tracking moved references - mCells.mEsmStore = this; - mStores[ESM::REC_ACTI] = &mActivators; mStores[ESM::REC_ALCH] = &mPotions; mStores[ESM::REC_APPA] = &mAppas; diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index caf4083fe0..7d58013e7e 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -11,8 +11,7 @@ void Store::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell) ESM::MovedCellRef cMRef; cell->getNextMVRF(esm, cMRef); - MWWorld::Store &cStore = const_cast&>(mEsmStore->get()); - ESM::Cell *cellAlt = const_cast(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); + ESM::Cell *cellAlt = const_cast(searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index e94624ade5..71e77d0c7c 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -582,14 +582,8 @@ namespace MWWorld void handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell); public: - ESMStore *mEsmStore; - typedef SharedIterator iterator; - Store() - : mEsmStore(NULL) - {} - const ESM::Cell *search(const std::string &id) const { ESM::Cell cell; cell.mName = Misc::StringUtils::lowerCase(id); From ddd6e682bcf2bae8b8447b1203a2f5b8792ee3fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 04:19:38 +0100 Subject: [PATCH 245/404] Dialogue: add conflict resolution for overlapping keywords (Fixes #2245) --- apps/openmw/mwdialogue/hypertextparser.cpp | 10 +++-- apps/openmw/mwdialogue/keywordsearch.hpp | 47 ++++++++++++++++++---- apps/openmw/mwgui/dialogue.cpp | 9 +++-- apps/openmw/mwgui/journalviewmodel.cpp | 10 +++-- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwdialogue/hypertextparser.cpp b/apps/openmw/mwdialogue/hypertextparser.cpp index 776edac949..aa748e772e 100644 --- a/apps/openmw/mwdialogue/hypertextparser.cpp +++ b/apps/openmw/mwdialogue/hypertextparser.cpp @@ -56,13 +56,17 @@ namespace MWDialogue keywordList.sort(Misc::StringUtils::ciLess); KeywordSearch keywordSearch; - KeywordSearch::Match match; for (std::list::const_iterator it = keywordList.begin(); it != keywordList.end(); ++it) keywordSearch.seed(*it, 0 /*unused*/); - for (std::string::const_iterator it = text.begin(); it != text.end() && keywordSearch.search(it, text.end(), match, text.begin()); it = match.mEnd) - tokens.push_back(Token(std::string(match.mBeg, match.mEnd), Token::ImplicitKeyword)); + std::vector::Match> matches; + keywordSearch.highlightKeywords(text.begin(), text.end(), matches); + + for (std::vector::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it) + { + tokens.push_back(Token(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword)); + } } size_t removePseudoAsterisks(std::string & phrase) diff --git a/apps/openmw/mwdialogue/keywordsearch.hpp b/apps/openmw/mwdialogue/keywordsearch.hpp index 51508890c1..5ce492441b 100644 --- a/apps/openmw/mwdialogue/keywordsearch.hpp +++ b/apps/openmw/mwdialogue/keywordsearch.hpp @@ -66,19 +66,20 @@ public: return false; } - bool search (Point beg, Point end, Match & match, Point start) + void highlightKeywords (Point beg, Point end, std::vector& out) { for (Point i = beg; i != end; ++i) { // check if previous character marked start of new word - if (i != start) + if (i != beg) { Point prev = i; - --prev; + --prev; if(isalpha(*prev)) continue; } + // check first character typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (std::tolower (*i, mLocale)); @@ -137,16 +138,48 @@ public: if (t != candidate->second.mKeyword.end ()) continue; - // we did it, report the good news + // found a keyword, but there might still be longer keywords that start somewhere _within_ this keyword + // we will resolve these overlapping keywords later, choosing the longest one in case of conflict + Match match; match.mValue = candidate->second.mValue; match.mBeg = i; match.mEnd = k; - return true; + out.push_back(match); + break; } } - // no match in range, report the bad news - return false; + // resolve overlapping keywords + for (typename std::vector::iterator it = out.begin(); it != out.end();) + { + typename std::vector::iterator next = it; + ++next; + + if (next == out.end()) + break; + + if (it->mEnd <= next->mBeg) + { + ++it; + continue; // no overlap + } + else + { + // prefer the longer keyword + int size = it->mEnd - it->mBeg; + int nextSize = next->mEnd - next->mBeg; + if (size >= nextSize) // if both are the same length, then prefer the first keyword + { + out.erase(next); + continue; + } + else + { + it = out.erase(it); + continue; + } + } + } } private: diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index e1e0167142..db9fa9f6b9 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -177,11 +177,13 @@ namespace MWGui } else { - std::string::const_iterator i = text.begin (); - KeywordSearchT::Match match; + std::vector matches; + keywordSearch->highlightKeywords(text.begin(), text.end(), matches); - while (i != text.end () && keywordSearch->search (i, text.end (), match, text.begin ())) + std::string::const_iterator i = text.begin (); + for (std::vector::iterator it = matches.begin(); it != matches.end(); ++it) { + KeywordSearchT::Match match = *it; if (i != match.mBeg) addTopicLink (typesetter, 0, i - text.begin (), match.mBeg - text.begin ()); @@ -189,7 +191,6 @@ namespace MWGui i = match.mEnd; } - if (i != text.end ()) addTopicLink (typesetter, 0, i - text.begin (), text.size ()); } diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 059af463fc..add2ac62c7 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -174,12 +174,14 @@ struct JournalViewModelImpl : JournalViewModel } else { - std::string::const_iterator i = utf8text.begin (); - - KeywordSearchT::Match match; + std::vector matches; + mModel->mKeywordSearch.highlightKeywords(utf8text.begin(), utf8text.end(), matches); - while (i != utf8text.end () && mModel->mKeywordSearch.search (i, utf8text.end (), match, utf8text.begin())) + std::string::const_iterator i = utf8text.begin (); + for (std::vector::const_iterator it = matches.begin(); it != matches.end(); ++it) { + const KeywordSearchT::Match& match = *it; + if (i != match.mBeg) visitor (0, i - utf8text.begin (), match.mBeg - utf8text.begin ()); From 65ab31eae6d069a9f835f637dc507780116e83b8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 04:31:02 +0100 Subject: [PATCH 246/404] Remove now redundant Store::setUp implementation --- apps/openmw/mwworld/store.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 71e77d0c7c..f3a5aad111 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -217,9 +217,6 @@ namespace MWWorld } void setUp() { - // remove the dynamic part of mShared - if (mShared.size() > mStatic.size()) - mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); } iterator begin() const { From 7fe2f86d06237a6f9d8f4c4289cec8e0143fc6b7 Mon Sep 17 00:00:00 2001 From: dteviot Date: Fri, 9 Jan 2015 21:40:53 +1300 Subject: [PATCH 247/404] Slaughter fish attacks when player only knee deep in water (Fixes #2076) --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwclass/creature.cpp | 6 +++--- apps/openmw/mwmechanics/actors.cpp | 4 ++-- apps/openmw/mwmechanics/aicombat.cpp | 2 +- apps/openmw/mwworld/class.cpp | 10 ++++++++++ apps/openmw/mwworld/class.hpp | 2 ++ apps/openmw/mwworld/physicssystem.cpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 27 ++++++++++++++++++--------- apps/openmw/mwworld/worldimp.hpp | 4 ++++ 9 files changed, 43 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index cdfdfc358a..7432db734b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -392,6 +392,7 @@ namespace MWBase virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0; virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; + virtual bool isWading(const MWWorld::Ptr &object) const = 0; ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const = 0; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 2146fdfdea..e21676f108 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -348,9 +348,9 @@ namespace MWClass // Self defense bool setOnPcHitMe = true; // Note OnPcHitMe is not set for friendly hits. - if ((canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures - // (they have no movement or attacks anyway) - && !attacker.isEmpty()) + + // No retaliation for totally static creatures (they have no movement or attacks anyway) + if (isMobile(ptr) && !attacker.isEmpty()) { setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c0fc806925..0a25ddd42e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -309,8 +309,8 @@ namespace MWMechanics // pure water creatures won't try to fight with the target on the ground // except that creature is already hostile if ((againstPlayer || !creatureStats.getAiSequence().isInCombat()) - && ((actor1.getClass().canSwim(actor1) && !actor1.getClass().canWalk(actor1) // pure water creature - && !MWBase::Environment::get().getWorld()->isSwimming(actor2)) + && ((actor1.getClass().isPureWaterCreature(actor1) + && !MWBase::Environment::get().getWorld()->isWading(actor2)) || (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target return; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 5d04705544..ce5963a917 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -209,7 +209,7 @@ namespace MWMechanics if (!actorClass.isNpc() && // 1. pure water creature and Player moved out of water ((target == world->getPlayerPtr() && - actorClass.canSwim(actor) && !actor.getClass().canWalk(actor) && !world->isSwimming(target)) + actorClass.isPureWaterCreature(actor) && !world->isWading(target)) // 2. creature can't swim to target || (!actorClass.canSwim(actor) && world->isSwimming(target)))) { diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 61c597517a..939b72ddb7 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -383,6 +383,16 @@ namespace MWWorld return false; } + bool Class::isPureWaterCreature(const MWWorld::Ptr& ptr) const + { + return canSwim(ptr) && !canWalk(ptr); + } + + bool Class::isMobile(const MWWorld::Ptr& ptr) const + { + return canSwim(ptr) || canWalk(ptr) || canFly(ptr); + } + int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const { throw std::runtime_error("class does not support skills"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index cc18d830cc..b738cdd445 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -308,6 +308,8 @@ namespace MWWorld virtual bool canFly(const MWWorld::Ptr& ptr) const; virtual bool canSwim(const MWWorld::Ptr& ptr) const; virtual bool canWalk(const MWWorld::Ptr& ptr) const; + bool isPureWaterCreature(const MWWorld::Ptr& ptr) const; + bool isMobile(const MWWorld::Ptr& ptr) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 3a7aa04903..5b712648c2 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -281,7 +281,7 @@ namespace MWWorld // Early-out for totally static creatures // (Not sure if gravity should still apply?) - if (!ptr.getClass().canWalk(ptr) && !ptr.getClass().canFly(ptr) && !ptr.getClass().canSwim(ptr)) + if (!ptr.getClass().isMobile(ptr)) return position; OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); @@ -434,7 +434,7 @@ namespace MWWorld if(result) { // don't let pure water creatures move out of water after stepMove - if((ptr.getClass().canSwim(ptr) && !ptr.getClass().canWalk(ptr)) + if (ptr.getClass().isPureWaterCreature(ptr) && newPosition.z > (waterlevel - halfExtents.z * 0.5)) newPosition = oldPosition; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a939a6db91..b99b4ff24a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1978,25 +1978,34 @@ namespace MWWorld bool World::isSubmerged(const MWWorld::Ptr &object) const { - const float *fpos = object.getRefData().getPosition().pos; - Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); - - const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); - if(actor) pos.z += 1.85*actor->getHalfExtents().z; - - return isUnderwater(object.getCell(), pos); + return isUnderwater(object, 1.85f); } bool World::isSwimming(const MWWorld::Ptr &object) const { /// \todo add check ifActor() - only actors can swim + /// \fixme 3/4ths submerged? + return isUnderwater(object, 1.5f); + } + + bool + World::isWading(const MWWorld::Ptr &object) const + { + return isUnderwater(object, 0.5f); + } + + bool + World::isUnderwater(const MWWorld::Ptr &object, const float hightRatio) const + { const float *fpos = object.getRefData().getPosition().pos; Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); - /// \fixme 3/4ths submerged? const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); - if(actor) pos.z += actor->getHalfExtents().z * 1.5; + if (actor) + { + pos.z += hightRatio*actor->getHalfExtents().z; + } return isUnderwater(object.getCell(), pos); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5810fe42fa..708a9abc35 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -139,6 +139,9 @@ namespace MWWorld void loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ContentLoader& contentLoader); + bool isUnderwater(const MWWorld::Ptr &object, const float hightRatio) const; + ///< helper function for implementing isSwimming(), isSubmerged(), isWading() + bool mTeleportEnabled; bool mLevitationEnabled; bool mGoToJail; @@ -454,6 +457,7 @@ namespace MWWorld virtual bool isSubmerged(const MWWorld::Ptr &object) const; virtual bool isSwimming(const MWWorld::Ptr &object) const; virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const; + virtual bool isWading(const MWWorld::Ptr &object) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; virtual void togglePOV() { From 64cd1396ac0fcd9f18143dbdf142379b907a4e1f Mon Sep 17 00:00:00 2001 From: dteviot Date: Fri, 9 Jan 2015 22:17:53 +1300 Subject: [PATCH 248/404] Fixed minor issues. 1. Renamed "hightRatio" to "heightRatio". 2. Replaced magic numbers with named constants. --- apps/openmw/mwworld/worldimp.cpp | 10 ++++++---- apps/openmw/mwworld/worldimp.hpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b99b4ff24a..f9642226bb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1978,7 +1978,8 @@ namespace MWWorld bool World::isSubmerged(const MWWorld::Ptr &object) const { - return isUnderwater(object, 1.85f); + const float neckDeep = 1.85f; + return isUnderwater(object, neckDeep); } bool @@ -1992,11 +1993,12 @@ namespace MWWorld bool World::isWading(const MWWorld::Ptr &object) const { - return isUnderwater(object, 0.5f); + const float kneeDeep = 0.5f; + return isUnderwater(object, kneeDeep); } bool - World::isUnderwater(const MWWorld::Ptr &object, const float hightRatio) const + World::isUnderwater(const MWWorld::Ptr &object, const float heightRatio) const { const float *fpos = object.getRefData().getPosition().pos; Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); @@ -2004,7 +2006,7 @@ namespace MWWorld const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); if (actor) { - pos.z += hightRatio*actor->getHalfExtents().z; + pos.z += heightRatio*actor->getHalfExtents().z; } return isUnderwater(object.getCell(), pos); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 708a9abc35..dfaa9f7894 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -139,7 +139,7 @@ namespace MWWorld void loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ContentLoader& contentLoader); - bool isUnderwater(const MWWorld::Ptr &object, const float hightRatio) const; + bool isUnderwater(const MWWorld::Ptr &object, const float heightRatio) const; ///< helper function for implementing isSwimming(), isSubmerged(), isWading() bool mTeleportEnabled; From ce2cbab4025204ccf643d6ef1ecd4dc4e18bad35 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 20:08:52 +0100 Subject: [PATCH 249/404] Dialogue: improve conflict resolution for chains of overlapping keywords (unit test for such a case will follow) --- apps/openmw/mwdialogue/keywordsearch.hpp | 59 +++++++++++++++--------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwdialogue/keywordsearch.hpp b/apps/openmw/mwdialogue/keywordsearch.hpp index 5ce492441b..c44139f324 100644 --- a/apps/openmw/mwdialogue/keywordsearch.hpp +++ b/apps/openmw/mwdialogue/keywordsearch.hpp @@ -66,8 +66,14 @@ public: return false; } + static bool sortMatches(const Match& left, const Match& right) + { + return left.mBeg < right.mBeg; + } + void highlightKeywords (Point beg, Point end, std::vector& out) { + std::vector matches; for (Point i = beg; i != end; ++i) { // check if previous character marked start of new word @@ -144,42 +150,51 @@ public: match.mValue = candidate->second.mValue; match.mBeg = i; match.mEnd = k; - out.push_back(match); + matches.push_back(match); break; } } // resolve overlapping keywords - for (typename std::vector::iterator it = out.begin(); it != out.end();) + while (matches.size()) { - typename std::vector::iterator next = it; - ++next; - - if (next == out.end()) - break; - - if (it->mEnd <= next->mBeg) - { - ++it; - continue; // no overlap - } - else + int longestKeywordSize = 0; + typename std::vector::iterator longestKeyword; + for (typename std::vector::iterator it = matches.begin(); it != matches.end(); ++it) { - // prefer the longer keyword int size = it->mEnd - it->mBeg; - int nextSize = next->mEnd - next->mBeg; - if (size >= nextSize) // if both are the same length, then prefer the first keyword + if (size > longestKeywordSize) { - out.erase(next); - continue; + longestKeywordSize = size; + longestKeyword = it; } - else + + typename std::vector::iterator next = it; + ++next; + + if (next == matches.end()) + break; + + if (it->mEnd <= next->mBeg) { - it = out.erase(it); - continue; + break; // no overlap } } + + Match keyword = *longestKeyword; + matches.erase(longestKeyword); + out.push_back(keyword); + // erase anything that overlaps with the keyword we just added to the output + for (typename std::vector::iterator it = matches.begin(); it != matches.end();) + { + if (it->mBeg < keyword.mEnd && it->mEnd > keyword.mBeg) + it = matches.erase(it); + else + ++it; + } } + + std::sort(out.begin(), out.end(), sortMatches); } private: From 9d07edda1394b479a5bf6bf6b751fc4ba073622c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 20:48:36 +0100 Subject: [PATCH 250/404] UnitTests: remove GMock which wasn't used anyway, and the gmock headers fail to compile together with gtest on ubuntu 14.04 --- CMakeLists.txt | 2 +- apps/openmw_test_suite/CMakeLists.txt | 11 +-- apps/openmw_test_suite/openmw_test_suite.cpp | 12 +-- cmake/FindGMock.cmake | 91 -------------------- 4 files changed, 6 insertions(+), 110 deletions(-) delete mode 100644 cmake/FindGMock.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 323b743bc0..60c37d0f55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_WIZARD "build Installation Wizard" ON) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) -option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest and GMock frameworks" OFF) +option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) option(BUILD_NIFTEST "build nif file tester" OFF) option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON) diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 9fe7890acc..44354eac8b 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -1,14 +1,7 @@ -# TODO: This should not be needed, check how it was done in FindGTEST -set(GMOCK_ROOT "/usr/include") -set(GMOCK_BUILD "/usr/lib") - find_package(GTest REQUIRED) -find_package(GMock REQUIRED) - -if (GTEST_FOUND AND GMOCK_FOUND) +if (GTEST_FOUND) include_directories(${GTEST_INCLUDE_DIRS}) - include_directories(${GMOCK_INCLUDE_DIRS}) file(GLOB UNITTEST_SRC_FILES components/misc/test_*.cpp @@ -18,7 +11,7 @@ if (GTEST_FOUND AND GMOCK_FOUND) add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) - target_link_libraries(openmw_test_suite ${GMOCK_BOTH_LIBRARIES} ${GTEST_BOTH_LIBRARIES} components) + target_link_libraries(openmw_test_suite ${GTEST_BOTH_LIBRARIES} components) # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT}) diff --git a/apps/openmw_test_suite/openmw_test_suite.cpp b/apps/openmw_test_suite/openmw_test_suite.cpp index 81476325ea..7cc76b25b7 100644 --- a/apps/openmw_test_suite/openmw_test_suite.cpp +++ b/apps/openmw_test_suite/openmw_test_suite.cpp @@ -1,12 +1,6 @@ -#include #include - -int main(int argc, char** argv) { - // The following line causes Google Mock to throw an exception on failure, - // which will be interpreted by your testing framework as a test failure. - ::testing::GTEST_FLAG(throw_on_failure) = false; - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); +GTEST_API_ int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/cmake/FindGMock.cmake b/cmake/FindGMock.cmake deleted file mode 100644 index eda7d4d72f..0000000000 --- a/cmake/FindGMock.cmake +++ /dev/null @@ -1,91 +0,0 @@ -# Locate the Google C++ Mocking Framework. -# -# Defines the following variables: -# -# GMOCK_FOUND - Found the Google Mocking framework -# GMOCK_INCLUDE_DIRS - Include directories -# -# Also defines the library variables below as normal -# variables. These contain debug/optimized keywords when -# a debugging library is found. -# -# GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock-main -# GMOCK_LIBRARIES - libgmock -# GMOCK_MAIN_LIBRARIES - libgmock-main -# -# Accepts the following variables as input: -# -# GMOCK_ROOT - (as CMake or env. variable) -# The root directory of the gmock install prefix -# -#----------------------- -# Example Usage: -# -# enable_testing(true) -# find_package(GMock REQUIRED) -# include_directories(${GMOCK_INCLUDE_DIRS}) -# -# add_executable(foo foo.cc) -# target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES}) -# -# add_test(AllTestsInFoo foo) -# - -#set (GMOCK_FOUND FALSE) - - -#set (GMOCK_ROOT $ENV{GMOCK_ROOT} CACHE PATH "Path to the gmock root directory.") -if (NOT EXISTS ${GMOCK_ROOT}) - message (FATAL_ERROR "GMOCK_ROOT does not exist.") -endif () - -#set (GMOCK_BUILD ${GMOCK_ROOT}/build CACHE PATH "Path to the gmock build directory.") -if (NOT EXISTS ${GMOCK_BUILD}) - message (FATAL_ERROR "GMOCK_BUILD does not exist.") -endif () - -# Find the include directory -find_path(GMOCK_INCLUDE_DIRS gmock/gmock.h - HINTS - $ENV{GMOCK_ROOT}/include - ${GMOCK_ROOT}/include -) -mark_as_advanced(GMOCK_INCLUDE_DIRS) - -function(_gmock_find_library _name) - find_library(${_name} - NAMES ${ARGN} - HINTS - $ENV{GMOCK_BUILD} - ${GMOCK_BUILD} - ) - mark_as_advanced(${_name}) -endfunction() - -# Find the gmock libraries -if (MSVC) - _gmock_find_library (GMOCK_LIBRARIES_DEBUG gmock ${GMOCK_BUILD}/Debug) - _gmock_find_library (GMOCK_LIBRARIES_RELEASE gmock ${GMOCK_BUILD}/Release) - _gmock_find_library (GMOCK_MAIN_LIBRARIES_DEBUG gmock_main ${GMOCK_BUILD}/Debug) - _gmock_find_library (GMOCK_MAIN_LIBRARIES_RELEASE gmock_main ${GMOCK_BUILD}/Release) - set (GMOCK_LIBRARIES - debug ${GMOCK_LIBRARIES_DEBUG} - optimized ${GMOCK_LIBRARIES_RELEASE} - ) - set (GMOCK_MAIN_LIBRARIES - debug ${GMOCK_MAIN_LIBRARIES_DEBUG} - optimized ${GMOCK_MAIN_LIBRARIES_RELEASE} - ) -else () - _gmock_find_library (GMOCK_LIBRARIES gmock ${GMOCK_BUILD}) - _gmock_find_library (GMOCK_MAIN_LIBRARIES gmock_main ${GMOCK_BUILD} ${GMOCK_BUILD}/Debug) -endif () - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMock DEFAULT_MSG GMOCK_LIBRARIES GMOCK_INCLUDE_DIRS GMOCK_MAIN_LIBRARIES) - -if(GMOCK_FOUND) - set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR}) - set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES}) -endif() - - From 1b302b750cca4b6f88cb4794b97739acc140480c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 20:52:31 +0100 Subject: [PATCH 251/404] UnitTests: add tests for KeywordSearch conflict resolution --- apps/openmw_test_suite/CMakeLists.txt | 1 + .../mwdialogue/test_keywordsearch.cpp | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 44354eac8b..2ffb7ffa0e 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -5,6 +5,7 @@ if (GTEST_FOUND) file(GLOB UNITTEST_SRC_FILES components/misc/test_*.cpp + mwdialogue/test_*.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp b/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp new file mode 100644 index 0000000000..c8ea9d01ca --- /dev/null +++ b/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp @@ -0,0 +1,48 @@ +#include +#include "apps/openmw/mwdialogue/keywordsearch.hpp" + +struct KeywordSearchTest : public ::testing::Test +{ + protected: + virtual void SetUp() + { + } + + virtual void TearDown() + { + } +}; + +TEST_F(KeywordSearchTest, keyword_test_conflict_resolution) +{ + // test to make sure the longest keyword in a chain of conflicting keywords gets chosen + MWDialogue::KeywordSearch search; + search.seed("foo bar", 0); + search.seed("bar lock", 0); + search.seed("lock switch", 0); + + std::string text = "foo bar lock switch"; + + std::vector::Match> matches; + search.highlightKeywords(text.begin(), text.end(), matches); + + // Should contain: "foo bar", "lock switch" + ASSERT_TRUE (matches.size() == 2); + ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == "foo bar"); + ASSERT_TRUE (std::string(matches.rbegin()->mBeg, matches.rbegin()->mEnd) == "lock switch"); +} + +TEST_F(KeywordSearchTest, keyword_test_conflict_resolution2) +{ + MWDialogue::KeywordSearch search; + search.seed("the dwemer", 0); + search.seed("dwemer language", 0); + + std::string text = "the dwemer language"; + + std::vector::Match> matches; + search.highlightKeywords(text.begin(), text.end(), matches); + + ASSERT_TRUE (matches.size() == 1); + ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == "dwemer language"); +} From dfdf26e95e93981f4fa6ff0b3a0de0215d89266e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 20:58:53 +0100 Subject: [PATCH 252/404] Add assertion to Store --- apps/openmw/mwworld/store.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index f3a5aad111..d9e4285fe8 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -140,8 +140,8 @@ namespace MWWorld virtual void clearDynamic() { // remove the dynamic part of mShared - if (mShared.size() > mStatic.size()) - mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); + assert(mShared.size() >= mStatic.size()); + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); mDynamic.clear(); } @@ -304,8 +304,8 @@ namespace MWWorld mDynamic.erase(it); // have to reinit the whole shared part - if (mShared.size() > mStatic.size()) - mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); + assert(mShared.size() >= mStatic.size()); + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); for (it = mDynamic.begin(); it != mDynamic.end(); ++it) { mShared.push_back(&it->second); } From efbc8742a03b9037f465ce6bb350f9a4675bb34a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Jan 2015 21:13:57 +0100 Subject: [PATCH 253/404] Remove some unnecessary includes --- components/misc/stringops.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/components/misc/stringops.cpp b/components/misc/stringops.cpp index 0f801e5549..723c1c1e0b 100644 --- a/components/misc/stringops.cpp +++ b/components/misc/stringops.cpp @@ -1,14 +1,5 @@ #include "stringops.hpp" -#include -#include -#include - -#include -#include - - - namespace Misc { From 2ac23008f5eec975f444a2cd72cba49745d1c579 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 00:07:40 +0100 Subject: [PATCH 254/404] Remove an old workaround (Fixes #2263) The workaround was added when we were still using OIS for input. It doesn't seem to be needed with SDL. --- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 2f6149f091..ece189c752 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -409,8 +409,7 @@ namespace MWInput if (mControlSwitch["playerviewswitch"]) { - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + if (actionIsActive(A_TogglePOV)) { if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += dt) > 0.5) { From 66e7e0480717164fabbd4aae1e5190771db85d0c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 00:26:13 +0100 Subject: [PATCH 255/404] UnitTests: add another dialogue keyword test --- .../mwdialogue/test_keywordsearch.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp b/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp index c8ea9d01ca..e0e1871d2c 100644 --- a/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp +++ b/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp @@ -46,3 +46,22 @@ TEST_F(KeywordSearchTest, keyword_test_conflict_resolution2) ASSERT_TRUE (matches.size() == 1); ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == "dwemer language"); } + + +TEST_F(KeywordSearchTest, keyword_test_conflict_resolution3) +{ + // testing that the longest keyword is chosen, rather than maximizing the + // amount of highlighted characters by highlighting the first and last keyword + MWDialogue::KeywordSearch search; + search.seed("foo bar", 0); + search.seed("bar lock", 0); + search.seed("lock so", 0); + + std::string text = "foo bar lock so"; + + std::vector::Match> matches; + search.highlightKeywords(text.begin(), text.end(), matches); + + ASSERT_TRUE (matches.size() == 1); + ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == "bar lock"); +} From eb99ed697b0029f0e54ce915428c966478b4f92f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 01:00:52 +0100 Subject: [PATCH 256/404] Reduce includes --- apps/openmw/mwgui/alchemywindow.cpp | 27 +++++++++++++------------ apps/openmw/mwgui/alchemywindow.hpp | 9 ++++++--- apps/openmw/mwgui/charactercreation.cpp | 18 +++++++++++------ apps/openmw/mwgui/charactercreation.hpp | 5 +++-- apps/openmw/mwgui/travelwindow.hpp | 4 +++- apps/openmw/mwgui/videowidget.cpp | 19 +++++++++-------- apps/openmw/mwgui/videowidget.hpp | 7 +++++-- apps/openmw/mwworld/class.hpp | 1 - apps/openmw/mwworld/containerstore.hpp | 1 + apps/openmw/mwworld/esmloader.hpp | 6 +++++- apps/openmw/mwworld/ptr.hpp | 2 +- 11 files changed, 61 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index bb201e2dd6..1715c49f8d 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -1,7 +1,6 @@ #include "alchemywindow.hpp" #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -9,6 +8,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/magiceffects.hpp" +#include "../mwmechanics/alchemy.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" @@ -27,6 +27,7 @@ namespace MWGui , mApparatus (4) , mIngredients (4) , mSortModel(NULL) + , mAlchemy(new MWMechanics::Alchemy()) { getWidget(mCreateButton, "CreateButton"); getWidget(mCancelButton, "CancelButton"); @@ -66,7 +67,7 @@ namespace MWGui std::string name = mNameEdit->getCaption(); boost::algorithm::trim(name); - MWMechanics::Alchemy::Result result = mAlchemy.create (mNameEdit->getCaption ()); + MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ()); if (result == MWMechanics::Alchemy::Result_NoName) { @@ -121,7 +122,7 @@ namespace MWGui void AlchemyWindow::open() { - mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayerPtr()); + mAlchemy->setAlchemist (MWBase::Environment::get().getWorld()->getPlayerPtr()); InventoryItemModel* model = new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayerPtr()); mSortModel = new SortFilterItemModel(model); @@ -132,10 +133,10 @@ namespace MWGui int index = 0; - mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayerPtr()); + mAlchemy->setAlchemist (MWBase::Environment::get().getWorld()->getPlayerPtr()); - for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy.beginTools()); - iter!=mAlchemy.endTools() && index (mApparatus.size()); ++iter, ++index) + for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools()); + iter!=mAlchemy->endTools() && index (mApparatus.size()); ++iter, ++index) { mApparatus.at (index)->setItem(*iter); mApparatus.at (index)->clearUserStrings(); @@ -150,7 +151,7 @@ namespace MWGui } void AlchemyWindow::exit() { - mAlchemy.clear(); + mAlchemy->clear(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Inventory); } @@ -164,7 +165,7 @@ namespace MWGui void AlchemyWindow::onSelectedItem(int index) { MWWorld::Ptr item = mSortModel->getItem(index).mBase; - int res = mAlchemy.addIngredient(item); + int res = mAlchemy->addIngredient(item); if (res != -1) { @@ -177,20 +178,20 @@ namespace MWGui void AlchemyWindow::update() { - std::string suggestedName = mAlchemy.suggestPotionName(); + std::string suggestedName = mAlchemy->suggestPotionName(); if (suggestedName != mSuggestedPotionName) mNameEdit->setCaptionWithReplacing(suggestedName); mSuggestedPotionName = suggestedName; mSortModel->clearDragItems(); - MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy.beginIngredients (); + MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy->beginIngredients (); for (int i=0; i<4; ++i) { ItemWidget* ingredient = mIngredients[i]; MWWorld::Ptr item; - if (it != mAlchemy.endIngredients ()) + if (it != mAlchemy->endIngredients ()) { item = *it; ++it; @@ -217,7 +218,7 @@ namespace MWGui mItemView->update(); - std::set effectIds = mAlchemy.listEffects(); + std::set effectIds = mAlchemy->listEffects(); Widgets::SpellEffectList list; for (std::set::iterator it = effectIds.begin(); it != effectIds.end(); ++it) { @@ -252,7 +253,7 @@ namespace MWGui { for (int i=0; i<4; ++i) if (mIngredients[i] == ingredient) - mAlchemy.removeIngredient (i); + mAlchemy->removeIngredient (i); update(); } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 36f540c1b6..69fff8c674 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -3,11 +3,14 @@ #include -#include "../mwmechanics/alchemy.hpp" - #include "widgets.hpp" #include "windowbase.hpp" +namespace MWMechanics +{ + class Alchemy; +} + namespace MWGui { class ItemView; @@ -45,7 +48,7 @@ namespace MWGui void update(); - MWMechanics::Alchemy mAlchemy; + std::auto_ptr mAlchemy; std::vector mApparatus; std::vector mIngredients; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 33d0c4907d..3df8da942b 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -1,20 +1,26 @@ #include "charactercreation.hpp" -#include "textinput.hpp" -#include "race.hpp" -#include "class.hpp" -#include "birth.hpp" -#include "review.hpp" -#include "inventorywindow.hpp" #include + #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" + #include "../mwmechanics/npcstats.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" #include "../mwworld/esmstore.hpp" +#include "textinput.hpp" +#include "race.hpp" +#include "class.hpp" +#include "birth.hpp" +#include "review.hpp" +#include "inventorywindow.hpp" + namespace { struct Step diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 03898093d7..c2486c7f0b 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -4,8 +4,9 @@ #include #include -#include "../mwbase/world.hpp" -#include "../mwbase/windowmanager.hpp" +#include + +#include "../mwmechanics/stat.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 3f19492562..3230f897f2 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -1,7 +1,9 @@ #ifndef MWGUI_TravelWINDOW_H #define MWGUI_TravelWINDOW_H -#include "container.hpp" + +#include "windowbase.hpp" +#include "referenceinterface.hpp" namespace MyGUI { diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index f8054925b6..2ade0f4c52 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -1,5 +1,7 @@ #include "videowidget.hpp" +#include + #include "../mwsound/movieaudiofactory.hpp" namespace MWGui @@ -7,40 +9,41 @@ namespace MWGui VideoWidget::VideoWidget() { + mPlayer.reset(new Video::VideoPlayer()); setNeedKeyFocus(true); } void VideoWidget::playVideo(const std::string &video) { - mPlayer.setAudioFactory(new MWSound::MovieAudioFactory()); - mPlayer.playVideo(video); + mPlayer->setAudioFactory(new MWSound::MovieAudioFactory()); + mPlayer->playVideo(video); - setImageTexture(mPlayer.getTextureName()); + setImageTexture(mPlayer->getTextureName()); } int VideoWidget::getVideoWidth() { - return mPlayer.getVideoWidth(); + return mPlayer->getVideoWidth(); } int VideoWidget::getVideoHeight() { - return mPlayer.getVideoHeight(); + return mPlayer->getVideoHeight(); } bool VideoWidget::update() { - return mPlayer.update(); + return mPlayer->update(); } void VideoWidget::stop() { - mPlayer.close(); + mPlayer->close(); } bool VideoWidget::hasAudioStream() { - return mPlayer.hasAudioStream(); + return mPlayer->hasAudioStream(); } } diff --git a/apps/openmw/mwgui/videowidget.hpp b/apps/openmw/mwgui/videowidget.hpp index 79e1c26bb0..e350573c4f 100644 --- a/apps/openmw/mwgui/videowidget.hpp +++ b/apps/openmw/mwgui/videowidget.hpp @@ -3,7 +3,10 @@ #include -#include +namespace Video +{ + class VideoPlayer; +} namespace MWGui { @@ -33,7 +36,7 @@ namespace MWGui void stop(); private: - Video::VideoPlayer mPlayer; + std::auto_ptr mPlayer; }; } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index cc18d830cc..df9ca6a36b 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -43,7 +43,6 @@ namespace ESM namespace MWWorld { - class Ptr; class ContainerStore; class InventoryStore; class PhysicsSystem; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 1863984b23..2849463c93 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -18,6 +18,7 @@ #include #include "ptr.hpp" +#include "cellreflist.hpp" namespace ESM { diff --git a/apps/openmw/mwworld/esmloader.hpp b/apps/openmw/mwworld/esmloader.hpp index d799c3f152..b96af707ce 100644 --- a/apps/openmw/mwworld/esmloader.hpp +++ b/apps/openmw/mwworld/esmloader.hpp @@ -4,13 +4,17 @@ #include #include "contentloader.hpp" -#include "components/esm/esmreader.hpp" namespace ToUTF8 { class Utf8Encoder; } +namespace ESM +{ + class ESMReader; +} + namespace MWWorld { diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 2f37a1cfd8..4d928dacf1 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -6,7 +6,7 @@ #include #include -#include "cellreflist.hpp" +#include "livecellref.hpp" namespace MWWorld { From 4b704f665fc33f1dbdc26138146d7bc3e377942b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 01:21:17 +0100 Subject: [PATCH 257/404] Reduce includes, move DragAndDrop to separate file --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/mwgui/companionwindow.cpp | 2 +- apps/openmw/mwgui/container.cpp | 114 +--------------------- apps/openmw/mwgui/container.hpp | 19 ---- apps/openmw/mwgui/draganddrop.cpp | 130 +++++++++++++++++++++++++ apps/openmw/mwgui/draganddrop.hpp | 38 ++++++++ apps/openmw/mwgui/enchantingdialog.cpp | 1 - apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwgui/itemselection.hpp | 9 +- apps/openmw/mwgui/tradewindow.hpp | 3 +- apps/openmw/mwgui/windowbase.cpp | 4 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 + 13 files changed, 187 insertions(+), 140 deletions(-) create mode 100644 apps/openmw/mwgui/draganddrop.cpp create mode 100644 apps/openmw/mwgui/draganddrop.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 97ab130127..cb6cc301b8 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -41,6 +41,7 @@ add_openmw_dir (mwgui itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview + draganddrop ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 2dd130b0da..c53e1697e3 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -13,7 +13,7 @@ #include "itemview.hpp" #include "sortfilteritemmodel.hpp" #include "companionitemmodel.hpp" -#include "container.hpp" +#include "draganddrop.hpp" #include "countdialog.hpp" namespace MWGui diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index ee1d285925..85882ec13c 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -24,123 +24,11 @@ #include "inventoryitemmodel.hpp" #include "sortfilteritemmodel.hpp" #include "pickpocketitemmodel.hpp" +#include "draganddrop.hpp" namespace MWGui { - DragAndDrop::DragAndDrop() - : mDraggedWidget(NULL) - , mDraggedCount(0) - , mSourceModel(NULL) - , mSourceView(NULL) - , mSourceSortModel(NULL) - , mIsOnDragAndDrop(false) - { - } - - void DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count) - { - mItem = sourceModel->getItem(index); - mDraggedCount = count; - mSourceModel = sourceModel; - mSourceView = sourceView; - mSourceSortModel = sortModel; - mIsOnDragAndDrop = true; - - // If picking up an item that isn't from the player's inventory, the item gets added to player inventory backend - // immediately, even though it's still floating beneath the mouse cursor. A bit counterintuitive, - // but this is how it works in vanilla, and not doing so would break quests (BM_beasts for instance). - ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); - if (mSourceModel != playerModel) - { - MWWorld::Ptr item = mSourceModel->moveItem(mItem, mDraggedCount, playerModel); - - playerModel->update(); - - ItemModel::ModelIndex newIndex = -1; - for (unsigned int i=0; igetItemCount(); ++i) - { - if (playerModel->getItem(i).mBase == item) - { - newIndex = i; - break; - } - } - mItem = playerModel->getItem(newIndex); - mSourceModel = playerModel; - - SortFilterItemModel* playerFilterModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getSortFilterModel(); - mSourceSortModel = playerFilterModel; - } - - std::string sound = mItem.mBase.getClass().getUpSoundId(mItem.mBase); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - if (mSourceSortModel) - { - mSourceSortModel->clearDragItems(); - mSourceSortModel->addDragItem(mItem.mBase, count); - } - - ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget("MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop"); - - Controllers::ControllerFollowMouse* controller = - MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerFollowMouse::getClassTypeName()) - ->castType(); - MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller); - - mDraggedWidget = baseWidget; - baseWidget->setItem(mItem.mBase); - baseWidget->setNeedMouseFocus(false); - baseWidget->setCount(count); - - sourceView->update(); - - MWBase::Environment::get().getWindowManager()->setDragDrop(true); - } - - void DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView) - { - std::string sound = mItem.mBase.getClass().getDownSoundId(mItem.mBase); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - // We can't drop a conjured item to the ground; the target container should always be the source container - if (mItem.mFlags & ItemStack::Flag_Bound && targetModel != mSourceModel) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); - return; - } - - // If item is dropped where it was taken from, we don't need to do anything - - // otherwise, do the transfer - if (targetModel != mSourceModel) - { - mSourceModel->moveItem(mItem, mDraggedCount, targetModel); - } - - mSourceModel->update(); - - finish(); - if (targetView) - targetView->update(); - - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); - - // We need to update the view since an other item could be auto-equipped. - mSourceView->update(); - } - - void DragAndDrop::finish() - { - mIsOnDragAndDrop = false; - mSourceSortModel->clearDragItems(); - - MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); - mDraggedWidget = 0; - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - } - - ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) : WindowBase("openmw_container_window.layout") , mDragAndDrop(dragAndDrop) diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index e32c6efaf0..87ae993a54 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -28,25 +28,6 @@ namespace MWGui namespace MWGui { - class DragAndDrop - { - public: - bool mIsOnDragAndDrop; - MyGUI::Widget* mDraggedWidget; - ItemModel* mSourceModel; - ItemView* mSourceView; - SortFilterItemModel* mSourceSortModel; - ItemStack mItem; - int mDraggedCount; - - DragAndDrop(); - - void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); - void drop (ItemModel* targetModel, ItemView* targetView); - - void finish(); - }; - class ContainerWindow : public WindowBase, public ReferenceInterface { public: diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp new file mode 100644 index 0000000000..452d862f0b --- /dev/null +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -0,0 +1,130 @@ +#include "draganddrop.hpp" + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/class.hpp" + +#include "sortfilteritemmodel.hpp" +#include "inventorywindow.hpp" +#include "itemwidget.hpp" +#include "itemview.hpp" + +namespace MWGui +{ + + +DragAndDrop::DragAndDrop() + : mDraggedWidget(NULL) + , mDraggedCount(0) + , mSourceModel(NULL) + , mSourceView(NULL) + , mSourceSortModel(NULL) + , mIsOnDragAndDrop(false) +{ +} + +void DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count) +{ + mItem = sourceModel->getItem(index); + mDraggedCount = count; + mSourceModel = sourceModel; + mSourceView = sourceView; + mSourceSortModel = sortModel; + mIsOnDragAndDrop = true; + + // If picking up an item that isn't from the player's inventory, the item gets added to player inventory backend + // immediately, even though it's still floating beneath the mouse cursor. A bit counterintuitive, + // but this is how it works in vanilla, and not doing so would break quests (BM_beasts for instance). + ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); + if (mSourceModel != playerModel) + { + MWWorld::Ptr item = mSourceModel->moveItem(mItem, mDraggedCount, playerModel); + + playerModel->update(); + + ItemModel::ModelIndex newIndex = -1; + for (unsigned int i=0; igetItemCount(); ++i) + { + if (playerModel->getItem(i).mBase == item) + { + newIndex = i; + break; + } + } + mItem = playerModel->getItem(newIndex); + mSourceModel = playerModel; + + SortFilterItemModel* playerFilterModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getSortFilterModel(); + mSourceSortModel = playerFilterModel; + } + + std::string sound = mItem.mBase.getClass().getUpSoundId(mItem.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + if (mSourceSortModel) + { + mSourceSortModel->clearDragItems(); + mSourceSortModel->addDragItem(mItem.mBase, count); + } + + ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget("MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop"); + + Controllers::ControllerFollowMouse* controller = + MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerFollowMouse::getClassTypeName()) + ->castType(); + MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller); + + mDraggedWidget = baseWidget; + baseWidget->setItem(mItem.mBase); + baseWidget->setNeedMouseFocus(false); + baseWidget->setCount(count); + + sourceView->update(); + + MWBase::Environment::get().getWindowManager()->setDragDrop(true); +} + +void DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView) +{ + std::string sound = mItem.mBase.getClass().getDownSoundId(mItem.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + // We can't drop a conjured item to the ground; the target container should always be the source container + if (mItem.mFlags & ItemStack::Flag_Bound && targetModel != mSourceModel) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); + return; + } + + // If item is dropped where it was taken from, we don't need to do anything - + // otherwise, do the transfer + if (targetModel != mSourceModel) + { + mSourceModel->moveItem(mItem, mDraggedCount, targetModel); + } + + mSourceModel->update(); + + finish(); + if (targetView) + targetView->update(); + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + + // We need to update the view since an other item could be auto-equipped. + mSourceView->update(); +} + +void DragAndDrop::finish() +{ + mIsOnDragAndDrop = false; + mSourceSortModel->clearDragItems(); + + MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); + mDraggedWidget = 0; + MWBase::Environment::get().getWindowManager()->setDragDrop(false); +} + +} diff --git a/apps/openmw/mwgui/draganddrop.hpp b/apps/openmw/mwgui/draganddrop.hpp new file mode 100644 index 0000000000..a356fe4e29 --- /dev/null +++ b/apps/openmw/mwgui/draganddrop.hpp @@ -0,0 +1,38 @@ +#ifndef OPENMW_MWGUI_DRAGANDDROP_H +#define OPENMW_MWGUI_DRAGANDDROP_H + +#include "itemmodel.hpp" + +namespace MyGUI +{ + class Widget; +} + +namespace MWGui +{ + + class ItemView; + class SortFilterItemModel; + + class DragAndDrop + { + public: + bool mIsOnDragAndDrop; + MyGUI::Widget* mDraggedWidget; + ItemModel* mSourceModel; + ItemView* mSourceView; + SortFilterItemModel* mSourceSortModel; + ItemStack mItem; + int mDraggedCount; + + DragAndDrop(); + + void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); + void drop (ItemModel* targetModel, ItemView* targetView); + + void finish(); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index d2315be38d..5bd799dacb 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -15,7 +15,6 @@ #include "../mwworld/esmstore.hpp" #include "itemselection.hpp" -#include "container.hpp" #include "itemwidget.hpp" #include "sortfilteritemmodel.hpp" diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index f4c1f524ad..b7035ee35a 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -19,7 +19,7 @@ #include "console.hpp" #include "spellicons.hpp" #include "itemmodel.hpp" -#include "container.hpp" +#include "draganddrop.hpp" #include "itemmodel.hpp" #include "itemwidget.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 60f40e6fb3..0588d5002f 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -25,7 +25,7 @@ #include "tradeitemmodel.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" -#include "container.hpp" +#include "draganddrop.hpp" namespace { diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp index 28c45c6056..50e15a3fe1 100644 --- a/apps/openmw/mwgui/itemselection.hpp +++ b/apps/openmw/mwgui/itemselection.hpp @@ -1,7 +1,14 @@ #ifndef OPENMW_GAME_MWGUI_ITEMSELECTION_H #define OPENMW_GAME_MWGUI_ITEMSELECTION_H -#include "container.hpp" +#include + +#include "windowbase.hpp" + +namespace MWWorld +{ + class Ptr; +} namespace MWGui { diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index b8bdfe6480..61587e3dff 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -1,7 +1,8 @@ #ifndef MWGUI_TRADEWINDOW_H #define MWGUI_TRADEWINDOW_H -#include "container.hpp" +#include "referenceinterface.hpp" +#include "windowbase.hpp" namespace Gui { diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 9c12b04ef7..d164320dde 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -1,9 +1,9 @@ #include "windowbase.hpp" #include "../mwbase/windowmanager.hpp" -#include "container.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/windowmanagerimp.hpp" + +#include "draganddrop.hpp" using namespace MWGui; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5546480ec5..5531a4980e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -74,6 +74,8 @@ #include "screenfader.hpp" #include "debugwindow.hpp" #include "spellview.hpp" +#include "draganddrop.hpp" +#include "container.hpp" namespace MWGui { From eecea4131f18cb400fc74afba2b9d6e20a62b714 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 02:50:43 +0100 Subject: [PATCH 258/404] Reduce MyGUI includes --- CMakeLists.txt | 2 +- apps/openmw/mwgui/alchemywindow.cpp | 2 + apps/openmw/mwgui/birth.cpp | 4 + apps/openmw/mwgui/bookwindow.cpp | 2 + apps/openmw/mwgui/class.cpp | 4 + apps/openmw/mwgui/companionwindow.cpp | 2 + apps/openmw/mwgui/confirmationdialog.cpp | 3 + apps/openmw/mwgui/console.cpp | 2 + apps/openmw/mwgui/container.cpp | 3 + apps/openmw/mwgui/controllers.cpp | 1 + apps/openmw/mwgui/controllers.hpp | 6 +- apps/openmw/mwgui/countdialog.cpp | 4 +- apps/openmw/mwgui/debugwindow.cpp | 4 + apps/openmw/mwgui/dialogue.cpp | 4 + apps/openmw/mwgui/dialogue.hpp | 2 +- apps/openmw/mwgui/draganddrop.cpp | 4 + apps/openmw/mwgui/formatting.cpp | 18 ++-- apps/openmw/mwgui/formatting.hpp | 2 +- apps/openmw/mwgui/hud.cpp | 9 ++ apps/openmw/mwgui/inventorywindow.cpp | 13 +++ apps/openmw/mwgui/inventorywindow.hpp | 19 ++-- apps/openmw/mwgui/itemselection.cpp | 3 + apps/openmw/mwgui/journalwindow.cpp | 15 +-- apps/openmw/mwgui/levelupdialog.cpp | 18 ++-- apps/openmw/mwgui/loadingscreen.cpp | 5 + apps/openmw/mwgui/mainmenu.cpp | 4 + apps/openmw/mwgui/mapwindow.cpp | 11 +- apps/openmw/mwgui/merchantrepair.cpp | 4 + apps/openmw/mwgui/messagebox.cpp | 8 +- apps/openmw/mwgui/quickkeysmenu.cpp | 4 + apps/openmw/mwgui/race.cpp | 5 + apps/openmw/mwgui/recharge.cpp | 3 + apps/openmw/mwgui/repair.cpp | 3 + apps/openmw/mwgui/review.cpp | 14 +++ apps/openmw/mwgui/savegamedialog.cpp | 5 + apps/openmw/mwgui/screenfader.cpp | 2 + apps/openmw/mwgui/scrollwindow.cpp | 2 + apps/openmw/mwgui/settingswindow.cpp | 8 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 4 + apps/openmw/mwgui/spellcreationdialog.cpp | 3 + apps/openmw/mwgui/spellicons.cpp | 6 +- apps/openmw/mwgui/spellwindow.cpp | 2 + apps/openmw/mwgui/statswindow.cpp | 6 ++ apps/openmw/mwgui/textinput.cpp | 14 +++ apps/openmw/mwgui/textinput.hpp | 4 +- apps/openmw/mwgui/tooltips.cpp | 5 + apps/openmw/mwgui/tradewindow.cpp | 8 +- apps/openmw/mwgui/tradewindow.hpp | 5 +- apps/openmw/mwgui/trainingwindow.cpp | 2 + apps/openmw/mwgui/travelwindow.cpp | 4 + apps/openmw/mwgui/waitdialog.cpp | 2 + apps/openmw/mwgui/widgets.cpp | 11 +- apps/openmw/mwgui/widgets.hpp | 2 +- apps/openmw/mwgui/windowbase.cpp | 2 + apps/openmw/mwgui/windowmanagerimp.cpp | 16 ++- apps/openmw/mwgui/windowpinnablebase.cpp | 2 + libs/openengine/gui/layout.cpp | 103 ++++++++++++++++++ libs/openengine/gui/layout.hpp | 121 +++++----------------- 58 files changed, 395 insertions(+), 151 deletions(-) create mode 100644 libs/openengine/gui/layout.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 60c37d0f55..79fc7471f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,7 @@ set(OENGINE_OGRE set(OENGINE_GUI ${LIBS_DIR}/openengine/gui/loglistener.cpp ${LIBS_DIR}/openengine/gui/manager.cpp - ${LIBS_DIR}/openengine/gui/layout.hpp + ${LIBS_DIR}/openengine/gui/layout.cpp ) set(OENGINE_BULLET diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 1715c49f8d..b28e4de09f 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 50c6639ddf..f6e8ec0a85 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -1,5 +1,9 @@ #include "birth.hpp" +#include +#include +#include + #include #include diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 92e8767082..69d4a72881 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -1,5 +1,7 @@ #include "bookwindow.hpp" +#include + #include #include diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 4e72a3f59f..3e8734c716 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -1,5 +1,9 @@ #include "class.hpp" +#include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index c53e1697e3..a863000ff5 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index a4e10749a2..83650b1951 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -1,5 +1,8 @@ #include "confirmationdialog.hpp" +#include +#include + namespace MWGui { ConfirmationDialog::ConfirmationDialog() : diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 97f11f4f3f..5629c25f1b 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -1,5 +1,7 @@ #include "console.hpp" +#include + #include #include diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 85882ec13c..cb256e0fd8 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -2,6 +2,9 @@ #include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" diff --git a/apps/openmw/mwgui/controllers.cpp b/apps/openmw/mwgui/controllers.cpp index 5ebd2b1e74..ad804997ad 100644 --- a/apps/openmw/mwgui/controllers.cpp +++ b/apps/openmw/mwgui/controllers.cpp @@ -1,6 +1,7 @@ #include "controllers.hpp" #include +#include namespace MWGui { diff --git a/apps/openmw/mwgui/controllers.hpp b/apps/openmw/mwgui/controllers.hpp index 003027a46a..4208f048c9 100644 --- a/apps/openmw/mwgui/controllers.hpp +++ b/apps/openmw/mwgui/controllers.hpp @@ -1,9 +1,13 @@ #ifndef MWGUI_CONTROLLERS_H #define MWGUI_CONTROLLERS_H -#include +#include #include +namespace MyGUI +{ + class Widget; +} namespace MWGui { diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 24d0af0d7d..c6f2180c92 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -1,6 +1,8 @@ #include "countdialog.hpp" -#include +#include +#include +#include #include diff --git a/apps/openmw/mwgui/debugwindow.cpp b/apps/openmw/mwgui/debugwindow.cpp index fab386bda1..a6dab66c10 100644 --- a/apps/openmw/mwgui/debugwindow.cpp +++ b/apps/openmw/mwgui/debugwindow.cpp @@ -1,5 +1,9 @@ #include "debugwindow.hpp" +#include +#include +#include +#include #include diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index db9fa9f6b9..1e8e06dc0e 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -3,6 +3,10 @@ #include #include +#include +#include +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 23709e8fe3..4ab88c06fb 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -171,7 +171,7 @@ namespace MWGui BookPage* mHistory; Gui::MWList* mTopicsList; MyGUI::ScrollBar* mScrollBar; - MyGUI::Progress* mDispositionBar; + MyGUI::ProgressBar* mDispositionBar; MyGUI::EditBox* mDispositionText; PersuasionDialog mPersuasionDialog; diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index 452d862f0b..fcb381b954 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -1,5 +1,8 @@ #include "draganddrop.hpp" +#include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -10,6 +13,7 @@ #include "inventorywindow.hpp" #include "itemwidget.hpp" #include "itemview.hpp" +#include "controllers.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 765402a01e..3f2b7d2288 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -1,19 +1,23 @@ #include "formatting.hpp" -#include -#include -#include +#include +#include -#include "../mwscript/interpretercontext.hpp" +#include +#include +#include +#include +#include #include #include #include -#include -#include +#include +#include +#include -#include +#include "../mwscript/interpretercontext.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 0d0f74b720..d525baacec 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_FORMATTING_H #define MWGUI_FORMATTING_H -#include +#include #include namespace MWGui diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index b7035ee35a..053106043c 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -2,6 +2,15 @@ #include +#include + +#include +#include +#include +#include +#include +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0588d5002f..bc39c36d2b 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -2,6 +2,12 @@ #include +#include +#include +#include +#include +#include + #include #include "../mwbase/world.hpp" @@ -15,6 +21,7 @@ #include "../mwworld/action.hpp" #include "../mwscript/interpretercontext.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwrender/characterpreview.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" @@ -26,6 +33,7 @@ #include "countdialog.hpp" #include "tradewindow.hpp" #include "draganddrop.hpp" +#include "widgets.hpp" namespace { @@ -667,4 +675,9 @@ namespace MWGui useItem(model.getItem(cycled).mBase); } + + void InventoryWindow::rebuildAvatar() + { + mPreview->rebuild(); + } } diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 4f53a1e24c..ee71d9b04c 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -1,14 +1,23 @@ #ifndef MGUI_Inventory_H #define MGUI_Inventory_H -#include "../mwrender/characterpreview.hpp" - #include "windowpinnablebase.hpp" -#include "widgets.hpp" #include "mode.hpp" +#include "../mwworld/ptr.hpp" + +namespace MWRender +{ + class InventoryPreview; +} + namespace MWGui { + namespace Widgets + { + class MWDynamicStat; + } + class ItemView; class SortFilterItemModel; class TradeItemModel; @@ -33,9 +42,7 @@ namespace MWGui MWWorld::Ptr getAvatarSelectedItem(int x, int y); - void rebuildAvatar() { - mPreview->rebuild(); - } + void rebuildAvatar(); SortFilterItemModel* getSortFilterModel(); TradeItemModel* getTradeModel(); diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 1b197f6d80..916f133607 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -1,5 +1,8 @@ #include "itemselection.hpp" +#include +#include + #include "itemview.hpp" #include "inventoryitemmodel.hpp" #include "sortfilteritemmodel.hpp" diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 0bcd5062d2..c4b80b5fe8 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -1,22 +1,25 @@ #include "journalwindow.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/soundmanager.hpp" -#include "../mwbase/windowmanager.hpp" -#include "../mwbase/journal.hpp" - #include #include #include #include #include + +#include + #include #include -#include "boost/lexical_cast.hpp" +#include #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/journal.hpp" + #include "bookpage.hpp" #include "windowbase.hpp" #include "journalviewmodel.hpp" diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 56a2319470..8553811aac 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -1,6 +1,8 @@ #include "levelupdialog.hpp" -#include +#include +#include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" @@ -34,17 +36,17 @@ namespace MWGui for (int i=1; i<9; ++i) { MyGUI::TextBox* t; - getWidget(t, "AttribVal" + boost::lexical_cast(i)); + getWidget(t, "AttribVal" + MyGUI::utility::toString(i)); MyGUI::Button* b; - getWidget(b, "Attrib" + boost::lexical_cast(i)); + getWidget(b, "Attrib" + MyGUI::utility::toString(i)); b->setUserData (i-1); b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); mAttributes.push_back(b); mAttributeValues.push_back(t); - getWidget(t, "AttribMultiplier" + boost::lexical_cast(i)); + getWidget(t, "AttribMultiplier" + MyGUI::utility::toString(i)); mAttributeMultipliers.push_back(t); } @@ -76,7 +78,7 @@ namespace MWGui if (val >= 100) val = 100; - mAttributeValues[i]->setCaption(boost::lexical_cast(val)); + mAttributeValues[i]->setCaption(MyGUI::utility::toString(val)); } } @@ -154,13 +156,13 @@ namespace MWGui mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); int level = creatureStats.getLevel ()+1; - mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); + mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level)); std::string levelupdescription; if(level > 20) levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default"); else - levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast(level)); + levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+MyGUI::utility::toString(level)); mLevelDescription->setCaption (levelupdescription); @@ -174,7 +176,7 @@ namespace MWGui availableAttributes++; int mult = pcStats.getLevelupAttributeMultiplier (i); - text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast(mult)); + text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult)); } else { diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 4ff6b0c89b..855a6fc848 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -9,6 +9,11 @@ #include #include +#include +#include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/statemanager.hpp" diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index f1e7b4fc5a..c7855b367e 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -2,6 +2,10 @@ #include +#include +#include +#include + #include #include diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 729ee92773..cf8326b5ce 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -5,6 +5,15 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -15,7 +24,7 @@ #include "../mwrender/globalmap.hpp" -#include "../components/esm/globalmap.hpp" +#include #include "widgets.hpp" #include "confirmationdialog.hpp" diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index e85681c048..3f2f2ba027 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -4,6 +4,10 @@ #include +#include +#include +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index d4520aad7e..43debd0e53 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -1,6 +1,12 @@ +#include "messagebox.hpp" + +#include +#include +#include +#include + #include -#include "messagebox.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index d59b29f0fc..6e10e4a972 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -2,6 +2,10 @@ #include +#include +#include +#include + #include #include diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 62bdd7f6be..9c5091b2b1 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -1,5 +1,10 @@ #include "race.hpp" +#include +#include +#include +#include + #include #include diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 7458a6effb..81e36c958e 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include + #include #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 019f341b57..56ce09c845 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -4,6 +4,9 @@ #include +#include +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index ca1b6ed5d7..2f52aae855 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -1,5 +1,9 @@ #include "review.hpp" +#include +#include +#include + #include #include "../mwbase/environment.hpp" @@ -12,6 +16,16 @@ #undef min #undef max +namespace +{ + void adjustButtonSize(MyGUI::Button *button) + { + // adjust size of button to fit its text + MyGUI::IntSize size = button->getTextSize(); + button->setSize(size.width + 24, button->getSize().height); + } +} + namespace MWGui { diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 3775f53dd1..d022c8e251 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -4,6 +4,11 @@ #include #include +#include +#include +#include +#include + #include #include diff --git a/apps/openmw/mwgui/screenfader.cpp b/apps/openmw/mwgui/screenfader.cpp index a0421ec288..473776a82a 100644 --- a/apps/openmw/mwgui/screenfader.cpp +++ b/apps/openmw/mwgui/screenfader.cpp @@ -1,5 +1,7 @@ #include "screenfader.hpp" +#include + namespace MWGui { diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 038d91ca91..d49d5ad5f2 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -1,5 +1,7 @@ #include "scrollwindow.hpp" +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 05664386b8..f170559b0c 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1,7 +1,13 @@ #include "settingswindow.hpp" #include -#include + +#include +#include +#include +#include +#include +#include #include #include diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 38b1bce7b5..9c3534d4a8 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -2,6 +2,10 @@ #include +#include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 0db2e30ced..275762d15c 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -2,6 +2,9 @@ #include +#include +#include + #include #include diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index d23f1a2350..7edc6c8dac 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -1,10 +1,12 @@ #include "spellicons.hpp" -#include - #include #include +#include + +#include + #include #include diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 98ee588b19..6d3c79b349 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index dcf85ddb73..e565b6f0ed 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -1,5 +1,11 @@ #include "statswindow.hpp" +#include +#include +#include +#include +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 80652b4305..958b52dc08 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -3,6 +3,9 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" +#include +#include + namespace MWGui { @@ -64,4 +67,15 @@ namespace MWGui onOkClicked(_sender); } + std::string TextInputDialog::getTextInput() const + { + return mTextEdit->getCaption(); + } + + void TextInputDialog::setTextInput(const std::string &text) + { + mTextEdit->setCaption(text); + } + + } diff --git a/apps/openmw/mwgui/textinput.hpp b/apps/openmw/mwgui/textinput.hpp index c83e3ffa99..57dd34dd62 100644 --- a/apps/openmw/mwgui/textinput.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -15,8 +15,8 @@ namespace MWGui public: TextInputDialog(); - std::string getTextInput() const { return mTextEdit->getCaption(); } - void setTextInput(const std::string &text) { mTextEdit->setCaption(text); } + std::string getTextInput() const; + void setTextInput(const std::string &text); void setNextButtonShow(bool shown); void setTextLabel(const std::string &label); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index aafea11780..3f3540a21a 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -2,6 +2,11 @@ #include +#include +#include +#include +#include + #include #include diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 6f0074e7e5..bafa89d5b1 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -1,6 +1,8 @@ #include "tradewindow.hpp" -#include +#include +#include +#include #include @@ -484,7 +486,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(playerGold)); + mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + MyGUI::utility::toString(playerGold)); if (mCurrentBalance > 0) { @@ -497,7 +499,7 @@ namespace MWGui mTotalBalance->setValue(std::abs(mCurrentBalance)); - mMerchantGold->setCaptionWithReplacing("#{sSellerGold} " + boost::lexical_cast(getMerchantGold())); + mMerchantGold->setCaptionWithReplacing("#{sSellerGold} " + MyGUI::utility::toString(getMerchantGold())); } void TradeWindow::updateOffer() diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 61587e3dff..a23196d70e 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -9,12 +9,11 @@ namespace Gui class NumericEditBox; } -namespace MWGui +namespace MyGUI { - class WindowManager; + class ControllerItem; } - namespace MWGui { class ItemView; diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 56c9dff98d..fc9167f3c0 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 6a0c99b8a3..3d37d0ab0e 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -2,6 +2,10 @@ #include +#include +#include +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index f2f4a1f91f..6f87bffe0b 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 6af7714cbc..381fc301e1 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -1,21 +1,24 @@ #include "widgets.hpp" -#include "../mwworld/esmstore.hpp" - -#include #include #include -#include +#include #include #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/esmstore.hpp" + +#include "controllers.hpp" + #undef min #undef max diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 6ce114131e..09a3c11acf 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -2,7 +2,6 @@ #define MWGUI_WIDGETS_H #include "../mwmechanics/stat.hpp" -#include "controllers.hpp" #include #include @@ -14,6 +13,7 @@ namespace MyGUI { class ImageBox; + class ControllerItem; } namespace MWBase diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index d164320dde..7f2eaec2ec 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -1,5 +1,7 @@ #include "windowbase.hpp" +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5531a4980e..aa80e69fc9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -6,10 +6,17 @@ #include #include -#include "MyGUI_UString.h" -#include "MyGUI_IPointer.h" -#include "MyGUI_ResourceImageSetPointer.h" -#include "MyGUI_TextureUtility.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -76,6 +83,7 @@ #include "spellview.hpp" #include "draganddrop.hpp" #include "container.hpp" +#include "controllers.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp index f9bbca6653..7307ece2d6 100644 --- a/apps/openmw/mwgui/windowpinnablebase.cpp +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -1,5 +1,7 @@ #include "windowpinnablebase.hpp" +#include + #include "exposedwindow.hpp" namespace MWGui diff --git a/libs/openengine/gui/layout.cpp b/libs/openengine/gui/layout.cpp new file mode 100644 index 0000000000..238c4232b4 --- /dev/null +++ b/libs/openengine/gui/layout.cpp @@ -0,0 +1,103 @@ +#include "layout.hpp" + +#include +#include +#include +#include +#include + +namespace OEngine +{ +namespace GUI +{ + void Layout::initialise(const std::string& _layout, MyGUI::Widget* _parent) + { + const std::string MAIN_WINDOW = "_Main"; + mLayoutName = _layout; + + if (mLayoutName.empty()) + mMainWidget = _parent; + else + { + mPrefix = MyGUI::utility::toString(this, "_"); + mListWindowRoot = MyGUI::LayoutManager::getInstance().loadLayout(mLayoutName, mPrefix, _parent); + + const std::string main_name = mPrefix + MAIN_WINDOW; + for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); iter!=mListWindowRoot.end(); ++iter) + { + if ((*iter)->getName() == main_name) + { + mMainWidget = (*iter); + break; + } + } + MYGUI_ASSERT(mMainWidget, "root widget name '" << MAIN_WINDOW << "' in layout '" << mLayoutName << "' not found."); + } + } + + void Layout::shutdown() + { + MyGUI::Gui::getInstance().destroyWidget(mMainWidget); + mListWindowRoot.clear(); + } + + void Layout::setCoord(int x, int y, int w, int h) + { + mMainWidget->setCoord(x,y,w,h); + } + + void Layout::adjustWindowCaption() + { + MyGUI::TextBox* box = mMainWidget->castType(mMainWidget)->getCaptionWidget(); + box->setSize(box->getTextSize().width + 24, box->getSize().height); + + // in order to trigger alignment updates, we need to update the parent + // mygui doesn't provide a proper way of doing this, so we are just changing size + box->getParent()->setCoord(MyGUI::IntCoord( + box->getParent()->getCoord().left, + box->getParent()->getCoord().top, + box->getParent()->getCoord().width, + box->getParent()->getCoord().height+1 + )); + box->getParent()->setCoord(MyGUI::IntCoord( + box->getParent()->getCoord().left, + box->getParent()->getCoord().top, + box->getParent()->getCoord().width, + box->getParent()->getCoord().height-1 + )); + } + + void Layout::setVisible(bool b) + { + mMainWidget->setVisible(b); + } + + void Layout::setText(const std::string &name, const std::string &caption) + { + MyGUI::Widget* pt; + getWidget(pt, name); + static_cast(pt)->setCaption(caption); + } + + void Layout::setTitle(const std::string& title) + { + static_cast(mMainWidget)->setCaptionWithReplacing(title); + adjustWindowCaption(); + } + + MyGUI::Widget* Layout::getWidget(const std::string &_name) + { + for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); + iter!=mListWindowRoot.end(); ++iter) + { + MyGUI::Widget* find = (*iter)->findWidget(mPrefix + _name); + if (nullptr != find) + { + return find; + } + } + MYGUI_EXCEPT("widget name '" << _name << "' in layout '" << mLayoutName << "' not found."); + } + +} +} diff --git a/libs/openengine/gui/layout.hpp b/libs/openengine/gui/layout.hpp index cd530a1d93..eb9205b820 100644 --- a/libs/openengine/gui/layout.hpp +++ b/libs/openengine/gui/layout.hpp @@ -1,7 +1,9 @@ #ifndef OENGINE_MYGUI_LAYOUT_H #define OENGINE_MYGUI_LAYOUT_H -#include +#include +#include +#include namespace OEngine { namespace GUI @@ -17,118 +19,43 @@ namespace GUI { initialise(_layout, _parent); } virtual ~Layout() { shutdown(); } + MyGUI::Widget* getWidget(const std::string& _name); + template - void getWidget(T * & _widget, const std::string & _name, bool _throw = true) + void getWidget(T * & _widget, const std::string & _name) { - _widget = nullptr; - for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); - iter!=mListWindowRoot.end(); ++iter) + MyGUI::Widget* w = getWidget(_name); + T* cast = w->castType(false); + if (!cast) { - MyGUI::Widget* find = (*iter)->findWidget(mPrefix + _name); - if (nullptr != find) - { - T * cast = find->castType(false); - if (nullptr != cast) - _widget = cast; - else if (_throw) - { - MYGUI_EXCEPT("Error cast : dest type = '" << T::getClassTypeName() - << "' source name = '" << find->getName() - << "' source type = '" << find->getTypeName() << "' in layout '" << mLayoutName << "'"); - } - return; - } + MYGUI_EXCEPT("Error cast : dest type = '" << T::getClassTypeName() + << "' source name = '" << w->getName() + << "' source type = '" << w->getTypeName() << "' in layout '" << mLayoutName << "'"); } - MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' in layout '" << mLayoutName << "' not found."); + else + _widget = cast; } private: void initialise(const std::string & _layout, - MyGUI::Widget* _parent = nullptr) - { - const std::string MAIN_WINDOW = "_Main"; - mLayoutName = _layout; - - if (mLayoutName.empty()) - mMainWidget = _parent; - else - { - mPrefix = MyGUI::utility::toString(this, "_"); - mListWindowRoot = MyGUI::LayoutManager::getInstance().loadLayout(mLayoutName, mPrefix, _parent); + MyGUI::Widget* _parent = nullptr); - const std::string main_name = mPrefix + MAIN_WINDOW; - for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); iter!=mListWindowRoot.end(); ++iter) - { - if ((*iter)->getName() == main_name) - { - mMainWidget = (*iter); - break; - } - } - MYGUI_ASSERT(mMainWidget, "root widget name '" << MAIN_WINDOW << "' in layout '" << mLayoutName << "' not found."); - } - } - - void shutdown() - { - MyGUI::Gui::getInstance().destroyWidget(mMainWidget); - mListWindowRoot.clear(); - } + void shutdown(); public: - void setCoord(int x, int y, int w, int h) - { - mMainWidget->setCoord(x,y,w,h); - } + void setCoord(int x, int y, int w, int h); - void adjustWindowCaption() - { - // adjust the size of the window caption so that all text is visible - // NOTE: this assumes that mMainWidget is of type Window. - MyGUI::TextBox* box = static_cast(mMainWidget)->getCaptionWidget(); - box->setSize(box->getTextSize().width + 24, box->getSize().height); + // adjust the size of the window caption so that all text is visible + // NOTE: this assumes that mMainWidget is of type Window. + void adjustWindowCaption(); - // in order to trigger alignment updates, we need to update the parent - // mygui doesn't provide a proper way of doing this, so we are just changing size - box->getParent()->setCoord(MyGUI::IntCoord( - box->getParent()->getCoord().left, - box->getParent()->getCoord().top, - box->getParent()->getCoord().width, - box->getParent()->getCoord().height+1 - )); - box->getParent()->setCoord(MyGUI::IntCoord( - box->getParent()->getCoord().left, - box->getParent()->getCoord().top, - box->getParent()->getCoord().width, - box->getParent()->getCoord().height-1 - )); - } + virtual void setVisible(bool b); - virtual void setVisible(bool b) - { - mMainWidget->setVisible(b); - } + void setText(const std::string& name, const std::string& caption); - void setText(const std::string& name, const std::string& caption) - { - MyGUI::Widget* pt; - getWidget(pt, name); - static_cast(pt)->setCaption(caption); - } + // NOTE: this assume that mMainWidget is of type Window. + void setTitle(const std::string& title); - void setTitle(const std::string& title) - { - // NOTE: this assume that mMainWidget is of type Window. - static_cast(mMainWidget)->setCaptionWithReplacing(title); - adjustWindowCaption(); - } - - void adjustButtonSize(MyGUI::Button* button) - { - // adjust size of button to fit its text - MyGUI::IntSize size = button->getTextSize(); - button->setSize(size.width + 24, button->getSize().height); - } MyGUI::Widget* mMainWidget; protected: From 728b842e72c748afd9fe4a173ba924d378edbe97 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 03:01:01 +0100 Subject: [PATCH 259/404] Use MyGUI::utility in favor of boost lexical_cast --- apps/openmw/mwgui/birth.cpp | 4 +-- apps/openmw/mwgui/bookwindow.cpp | 6 ++--- apps/openmw/mwgui/charactercreation.cpp | 12 ++++----- apps/openmw/mwgui/companionwindow.cpp | 4 +-- apps/openmw/mwgui/container.cpp | 2 -- apps/openmw/mwgui/dialogue.cpp | 7 +++-- apps/openmw/mwgui/enchantingdialog.cpp | 6 ++--- apps/openmw/mwgui/formatting.cpp | 9 +++---- apps/openmw/mwgui/hud.cpp | 10 +++---- apps/openmw/mwgui/inventorywindow.cpp | 6 ++--- apps/openmw/mwgui/itemview.cpp | 2 -- apps/openmw/mwgui/itemwidget.cpp | 6 ++--- apps/openmw/mwgui/journalwindow.cpp | 3 +-- apps/openmw/mwgui/mapwindow.cpp | 10 +++---- apps/openmw/mwgui/merchantrepair.cpp | 10 +++---- apps/openmw/mwgui/quickkeysmenu.cpp | 6 ++--- apps/openmw/mwgui/race.cpp | 5 ++-- apps/openmw/mwgui/recharge.cpp | 3 +-- apps/openmw/mwgui/repair.cpp | 4 +-- apps/openmw/mwgui/review.cpp | 14 +++++----- apps/openmw/mwgui/settingswindow.cpp | 25 +++++++++--------- apps/openmw/mwgui/spellbuyingwindow.cpp | 6 ++--- apps/openmw/mwgui/spellcreationdialog.cpp | 20 +++++++------- apps/openmw/mwgui/spellicons.cpp | 4 +-- apps/openmw/mwgui/statswindow.cpp | 32 +++++++++++------------ apps/openmw/mwgui/tooltips.cpp | 4 +-- apps/openmw/mwgui/trainingwindow.cpp | 6 ++--- apps/openmw/mwgui/travelwindow.cpp | 6 ++--- apps/openmw/mwgui/waitdialog.cpp | 12 ++++----- apps/openmw/mwgui/widgets.cpp | 14 +++++----- 30 files changed, 103 insertions(+), 155 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index f6e8ec0a85..668253712d 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include #include @@ -234,7 +232,7 @@ namespace MWGui for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) { const std::string &spellId = *it; - spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); + spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + MyGUI::utility::toString(i)); spellWidget->setSpellId(spellId); mSpellItems.push_back(spellWidget); diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 69d4a72881..da0d1950e9 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include "../mwbase/environment.hpp" @@ -142,8 +140,8 @@ namespace MWGui void BookWindow::updatePages() { - mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); - mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); + mLeftPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 1) ); + mRightPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 2) ); //If it is the last page, hide the button "Next Page" if ( (mCurrentPage+1)*2 == mPages.size() diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 3df8da942b..ab412f63b2 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -1,7 +1,5 @@ #include "charactercreation.hpp" -#include - #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -34,11 +32,11 @@ namespace Step sGenerateClassSteps(int number) { number++; const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); - Step step = {fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_Question"), - {fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerOne"), - fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerTwo"), - fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerThree")}, - "vo\\misc\\chargen qa"+boost::lexical_cast(number)+".wav" + Step step = {fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_Question"), + {fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerOne"), + fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerTwo"), + fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerThree")}, + "vo\\misc\\chargen qa"+MyGUI::utility::toString(number)+".wav" }; return step; } diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index a863000ff5..7583160308 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -1,7 +1,5 @@ #include "companionwindow.hpp" -#include - #include #include "../mwbase/environment.hpp" @@ -122,7 +120,7 @@ void CompanionWindow::updateEncumbranceBar() else { MWMechanics::NpcStats& stats = mPtr.getClass().getNpcStats(mPtr); - mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + boost::lexical_cast(stats.getProfit())); + mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + MyGUI::utility::toString(stats.getProfit())); } } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index cb256e0fd8..49e641aedc 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -1,7 +1,5 @@ #include "container.hpp" -#include - #include #include diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 1e8e06dc0e..eee86c6d21 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -1,7 +1,6 @@ #include "dialogue.hpp" #include -#include #include #include @@ -100,7 +99,7 @@ namespace MWGui mBribe100Button->setEnabled (playerGold >= 100); mBribe1000Button->setEnabled (playerGold >= 1000); - mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); + mGoldLabel->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); } void PersuasionDialog::exit() @@ -630,7 +629,7 @@ namespace MWGui dispositionVisible = true; mDispositionBar->setProgressRange(100); mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); - mDispositionText->setCaption(boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")); + mDispositionText->setCaption(MyGUI::utility::toString(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")); } bool dispositionWasVisible = mDispositionBar->getVisible(); @@ -672,7 +671,7 @@ namespace MWGui + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); mDispositionBar->setProgressRange(100); mDispositionBar->setProgressPosition(disp); - mDispositionText->setCaption(boost::lexical_cast(disp)+std::string("/100")); + mDispositionText->setCaption(MyGUI::utility::toString(disp)+std::string("/100")); } } } diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 5bd799dacb..a17c0d0e6e 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -105,15 +105,15 @@ namespace MWGui { std::stringstream enchantCost; enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantPoints(); - mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast(mEnchanting.getMaxEnchantValue())); + mEnchantmentPoints->setCaption(enchantCost.str() + " / " + MyGUI::utility::toString(mEnchanting.getMaxEnchantValue())); - mCharge->setCaption(boost::lexical_cast(mEnchanting.getGemCharge())); + mCharge->setCaption(MyGUI::utility::toString(mEnchanting.getGemCharge())); std::stringstream castCost; castCost << mEnchanting.getEffectiveCastCost(); mCastCost->setCaption(castCost.str()); - mPrice->setCaption(boost::lexical_cast(mEnchanting.getEnchantPrice())); + mPrice->setCaption(MyGUI::utility::toString(mEnchanting.getEnchantPrice())); switch(mEnchanting.getCastStyle()) { diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 3f2b7d2288..761a89ca62 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -284,8 +283,8 @@ namespace MWGui continue; std::string src = attr.at("src"); - int width = boost::lexical_cast(attr.at("width")); - int height = boost::lexical_cast(attr.at("height")); + int width = MyGUI::utility::parseInt(attr.at("width")); + int height = MyGUI::utility::parseInt(attr.at("height")); ImageElement elem(paper, pag, mBlockStyle, src, width, height); @@ -397,7 +396,7 @@ namespace MWGui { MyGUI::EditBox* box = parent->createWidget("NormalText", MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, - parent->getName() + boost::lexical_cast(parent->getChildCount())); + parent->getName() + MyGUI::utility::toString(parent->getChildCount())); box->setProperty("Static", "true"); box->setProperty("MultiLine", "true"); box->setProperty("WordWrap", "true"); @@ -465,7 +464,7 @@ namespace MWGui mImageBox = parent->createWidget ("ImageBox", MyGUI::IntCoord(left, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top, - parent->getName() + boost::lexical_cast(parent->getChildCount())); + parent->getName() + MyGUI::utility::toString(parent->getChildCount())); std::string image = Misc::ResourceHelpers::correctBookartPath(src, width, mImageHeight); mImageBox->setImageTexture(image); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 053106043c..b4312dc401 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -1,7 +1,5 @@ #include "hud.hpp" -#include - #include #include @@ -214,17 +212,17 @@ namespace MWGui void HUD::setFPS(float fps) { if (mFpsCounter) - mFpsCounter->setCaption(boost::lexical_cast((int)fps)); + mFpsCounter->setCaption(MyGUI::utility::toString((int)fps)); } void HUD::setTriangleCount(unsigned int count) { - mTriangleCounter->setCaption(boost::lexical_cast(count)); + mTriangleCounter->setCaption(MyGUI::utility::toString(count)); } void HUD::setBatchCount(unsigned int count) { - mBatchCounter->setCaption(boost::lexical_cast(count)); + mBatchCounter->setCaption(MyGUI::utility::toString(count)); } void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) @@ -233,7 +231,7 @@ namespace MWGui int modified = static_cast(value.getModified()); MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(current) + "/" + boost::lexical_cast(modified); + std::string valStr = MyGUI::utility::toString(current) + "/" + MyGUI::utility::toString(modified); if (id == "HBar") { mHealth->setProgressRange(modified); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index bc39c36d2b..ed0f505f53 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -8,8 +8,6 @@ #include #include -#include - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -542,9 +540,9 @@ namespace MWGui if (mPreviewResize || mPreviewDirty) { mArmorRating->setCaptionWithReplacing ("#{sArmor}: " - + boost::lexical_cast(static_cast(mPtr.getClass().getArmorRating(mPtr)))); + + MyGUI::utility::toString(static_cast(mPtr.getClass().getArmorRating(mPtr)))); if (mArmorRating->getTextSize().width > mArmorRating->getSize().width) - mArmorRating->setCaptionWithReplacing (boost::lexical_cast(static_cast(mPtr.getClass().getArmorRating(mPtr)))); + mArmorRating->setCaptionWithReplacing (MyGUI::utility::toString(static_cast(mPtr.getClass().getArmorRating(mPtr)))); } if (mPreviewResize) { diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index bf8bf1adf0..41c3604b42 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include #include diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp index b6a2880788..36940113e1 100644 --- a/apps/openmw/mwgui/itemwidget.cpp +++ b/apps/openmw/mwgui/itemwidget.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include #include "../mwworld/class.hpp" @@ -17,9 +15,9 @@ namespace if (count == 1) return ""; if (count > 9999) - return boost::lexical_cast(int(count/1000.f)) + "k"; + return MyGUI::utility::toString(int(count/1000.f)) + "k"; else - return boost::lexical_cast(count); + return MyGUI::utility::toString(count); } } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index c4b80b5fe8..afff5601af 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -74,7 +73,7 @@ namespace void setText (char const * name, value_type const & value) { getWidget (name) -> - setCaption (boost::lexical_cast (value)); + setCaption (MyGUI::utility::toString (value)); } void setVisible (char const * name, bool visible) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index cf8326b5ce..0f9fa2775c 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1,7 +1,5 @@ #include "mapwindow.hpp" -#include - #include #include @@ -235,8 +233,8 @@ namespace MWGui { for (int my=0; my<3; ++my) { - std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" - + boost::lexical_cast(mCurY + (-1*(my-1))); + std::string image = mPrefix+"_"+ MyGUI::utility::toString(mCurX + (mx-1)) + "_" + + MyGUI::utility::toString(mCurY + (-1*(my-1))); MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; fog->setImageTexture(mFogOfWar ? ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" @@ -362,8 +360,8 @@ namespace MWGui for (int my=0; my<3; ++my) { // map - std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" - + boost::lexical_cast(y + (-1*(my-1))); + std::string image = mPrefix+"_"+ MyGUI::utility::toString(x + (mx-1)) + "_" + + MyGUI::utility::toString(y + (-1*(my-1))); MyGUI::ImageBox* box = mMapWidgets[my + 3*mx]; diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 3f2f2ba027..907c664b1a 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include #include @@ -71,7 +69,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) std::string name = iter->getClass().getName(*iter) - + " - " + boost::lexical_cast(price) + + " - " + MyGUI::utility::toString(price) + MWBase::Environment::get().getWorld()->getStore().get() .find("sgp")->getString(); @@ -87,7 +85,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) currentY += 18; - button->setUserString("Price", boost::lexical_cast(price)); + button->setUserString("Price", MyGUI::utility::toString(price)); button->setUserData(*iter); button->setCaptionWithReplacing(name); button->setSize(button->getTextSize().width,18); @@ -102,7 +100,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) mList->setVisibleVScroll(true); mGoldLabel->setCaptionWithReplacing("#{sGold}: " - + boost::lexical_cast(playerGold)); + + MyGUI::utility::toString(playerGold)); } void MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel) @@ -127,7 +125,7 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - int price = boost::lexical_cast(sender->getUserString("Price")); + int price = MyGUI::utility::parseInt(sender->getUserString("Price")); if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 6e10e4a972..deba644345 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -1,7 +1,5 @@ #include "quickkeysmenu.hpp" -#include - #include #include #include @@ -57,7 +55,7 @@ namespace MWGui for (int i = 0; i < 10; ++i) { ItemWidget* button; - getWidget(button, "QuickKey" + boost::lexical_cast(i+1)); + getWidget(button, "QuickKey" + MyGUI::utility::toString(i+1)); button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); @@ -100,7 +98,7 @@ namespace MWGui MyGUI::TextBox* textBox = key->createWidgetReal("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); textBox->setTextAlign (MyGUI::Align::Center); - textBox->setCaption (boost::lexical_cast(index+1)); + textBox->setCaption (MyGUI::utility::toString(index+1)); textBox->setNeedMouseFocus (false); } diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 9c5091b2b1..f6aa8ee4c7 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include "../mwworld/esmstore.hpp" @@ -402,7 +401,7 @@ namespace MWGui continue; skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, - std::string("Skill") + boost::lexical_cast(i)); + std::string("Skill") + MyGUI::utility::toString(i)); skillWidget->setSkillNumber(skillId); skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(race->mData.mBonus[i].mBonus)); ToolTips::createSkillToolTip(skillWidget, skillId); @@ -436,7 +435,7 @@ namespace MWGui for (int i = 0; it != end; ++it) { const std::string &spellpower = *it; - Widgets::MWSpellPtr spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); + Widgets::MWSpellPtr spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + MyGUI::utility::toString(i)); spellPowerWidget->setSpellId(spellpower); spellPowerWidget->setUserString("ToolTipType", "Spell"); spellPowerWidget->setUserString("Spell", spellpower); diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 81e36c958e..2c854a8f54 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -1,6 +1,5 @@ #include "recharge.hpp" -#include #include #include @@ -66,7 +65,7 @@ void Recharge::updateView() std::string soul = gem.getCellRef().getSoul(); const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get().find(soul); - mChargeLabel->setCaptionWithReplacing("#{sCharges} " + boost::lexical_cast(creature->mData.mSoul)); + mChargeLabel->setCaptionWithReplacing("#{sCharges} " + MyGUI::utility::toString(creature->mData.mSoul)); bool toolBoxVisible = (gem.getRefData().getCount() != 0); mGemBox->setVisible(toolBoxVisible); diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 56ce09c845..c3c9714005 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include @@ -68,7 +66,7 @@ void Repair::updateRepairView() std::stringstream qualityStr; qualityStr << std::setprecision(3) << quality; - mUsesLabel->setCaptionWithReplacing("#{sUses} " + boost::lexical_cast(uses)); + mUsesLabel->setCaptionWithReplacing("#{sUses} " + MyGUI::utility::toString(uses)); mQualityLabel->setCaptionWithReplacing("#{sQuality} " + qualityStr.str()); bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 2f52aae855..1bcd2d31aa 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" @@ -77,7 +75,7 @@ namespace MWGui Widgets::MWAttributePtr attribute; for (int idx = 0; idx < ESM::Attribute::Length; ++idx) { - getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); + getWidget(attribute, std::string("Attribute") + MyGUI::utility::toString(idx)); mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue()); @@ -149,21 +147,21 @@ namespace MWGui void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) { mHealth->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + std::string valStr = MyGUI::utility::toString(value.getCurrent()) + "/" + MyGUI::utility::toString(value.getModified()); mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); } void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) { mMagicka->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + std::string valStr = MyGUI::utility::toString(value.getCurrent()) + "/" + MyGUI::utility::toString(value.getModified()); mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); } void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) { mFatigue->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + std::string valStr = MyGUI::utility::toString(value.getCurrent()) + "/" + MyGUI::utility::toString(value.getModified()); mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } @@ -183,7 +181,7 @@ namespace MWGui if (widget) { float modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(std::floor(modified)); + std::string text = MyGUI::utility::toString(std::floor(modified)); std::string state = "normal"; if (modified > base) state = "increased"; @@ -303,7 +301,7 @@ namespace MWGui state = "increased"; else if (modified < base) state = "decreased"; - MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), state, coord1, coord2); + MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), MyGUI::utility::toString(static_cast(modified)), state, coord1, coord2); for (int i=0; i<2; ++i) { diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f170559b0c..f8741a67a2 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -54,8 +53,8 @@ namespace assert (split.size() >= 2); boost::trim(split[0]); boost::trim(split[1]); - x = boost::lexical_cast (split[0]); - y = boost::lexical_cast (split[1]); + x = MyGUI::utility::parseInt (split[0]); + y = MyGUI::utility::parseInt (split[1]); } bool sortResolutions (std::pair left, std::pair right) @@ -73,7 +72,7 @@ namespace // special case: 8 : 5 is usually referred to as 16:10 if (xaspect == 8 && yaspect == 5) return "16 : 10"; - return boost::lexical_cast(xaspect) + " : " + boost::lexical_cast(yaspect); + return MyGUI::utility::toString(xaspect) + " : " + MyGUI::utility::toString(yaspect); } std::string hlslGlsl () @@ -111,9 +110,9 @@ namespace min = 0.f; max = 1.f; if (!widget->getUserString(settingMin).empty()) - min = boost::lexical_cast(widget->getUserString(settingMin)); + min = MyGUI::utility::parseFloat(widget->getUserString(settingMin)); if (!widget->getUserString(settingMax).empty()) - max = boost::lexical_cast(widget->getUserString(settingMax)); + max = MyGUI::utility::parseFloat(widget->getUserString(settingMax)); } } @@ -230,7 +229,7 @@ namespace MWGui for (std::vector < std::pair >::const_iterator it=resolutions.begin(); it!=resolutions.end(); ++it) { - std::string str = boost::lexical_cast(it->first) + " x " + boost::lexical_cast(it->second) + std::string str = MyGUI::utility::toString(it->first) + " x " + MyGUI::utility::toString(it->second) + " (" + getAspect(it->first,it->second) + ")"; if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE) @@ -239,7 +238,7 @@ namespace MWGui std::string tf = Settings::Manager::getString("texture filtering", "General"); mTextureFilteringButton->setCaption(textureFilteringToStr(tf)); - mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast(Settings::Manager::getInt("anisotropy", "General")) + ")"); + mAnisotropyLabel->setCaption("Anisotropy (" + MyGUI::utility::toString(Settings::Manager::getInt("anisotropy", "General")) + ")"); mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); @@ -255,11 +254,11 @@ namespace MWGui MyGUI::TextBox* fovText; getWidget(fovText, "FovText"); - fovText->setCaption("Field of View (" + boost::lexical_cast(int(Settings::Manager::getInt("field of view", "General"))) + ")"); + fovText->setCaption("Field of View (" + MyGUI::utility::toString(int(Settings::Manager::getInt("field of view", "General"))) + ")"); MyGUI::TextBox* diffText; getWidget(diffText, "DifficultyText"); - diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast(int(Settings::Manager::getInt("difficulty", "Game"))) + ")"); + diffText->setCaptionWithReplacing("#{sDifficulty} (" + MyGUI::utility::toString(int(Settings::Manager::getInt("difficulty", "Game"))) + ")"); mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video")); } @@ -431,13 +430,13 @@ namespace MWGui { MyGUI::TextBox* fovText; getWidget(fovText, "FovText"); - fovText->setCaption("Field of View (" + boost::lexical_cast(int(value)) + ")"); + fovText->setCaption("Field of View (" + MyGUI::utility::toString(int(value)) + ")"); } if (scroller == mDifficultySlider) { MyGUI::TextBox* diffText; getWidget(diffText, "DifficultyText"); - diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast(int(value)) + ")"); + diffText->setCaptionWithReplacing("#{sDifficulty} (" + MyGUI::utility::toString(int(value)) + ")"); } } else @@ -445,7 +444,7 @@ namespace MWGui Settings::Manager::setInt(getSettingName(scroller), getSettingCategory(scroller), pos); if (scroller == mAnisotropySlider) { - mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast(pos) + ")"); + mAnisotropyLabel->setCaption("Anisotropy (" + MyGUI::utility::toString(pos) + ")"); } } apply(); diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 9c3534d4a8..54094b6062 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -1,7 +1,5 @@ #include "spellbuyingwindow.hpp" -#include - #include #include #include @@ -69,7 +67,7 @@ namespace MWGui mCurrentY += sLineHeight; toAdd->setUserData(price); - toAdd->setCaptionWithReplacing(spell->mName+" - "+boost::lexical_cast(price)+"#{sgp}"); + toAdd->setCaptionWithReplacing(spell->mName+" - "+MyGUI::utility::toString(price)+"#{sgp}"); toAdd->setSize(toAdd->getTextSize().width,sLineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); @@ -170,7 +168,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 275762d15c..1ff379c0bf 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -1,7 +1,5 @@ #include "spellcreationdialog.hpp" -#include - #include #include @@ -269,7 +267,7 @@ namespace MWGui void EditEffectDialog::onMagnitudeMinChanged (MyGUI::ScrollBar* sender, size_t pos) { - mMagnitudeMinValue->setCaption(boost::lexical_cast(pos+1)); + mMagnitudeMinValue->setCaption(MyGUI::utility::toString(pos+1)); mEffect.mMagnMin = pos+1; // trigger the check again (see below) @@ -289,21 +287,21 @@ namespace MWGui mEffect.mMagnMax = pos+1; - mMagnitudeMaxValue->setCaption("- " + boost::lexical_cast(pos+1)); + mMagnitudeMaxValue->setCaption("- " + MyGUI::utility::toString(pos+1)); eventEffectModified(mEffect); } void EditEffectDialog::onDurationChanged (MyGUI::ScrollBar* sender, size_t pos) { - mDurationValue->setCaption(boost::lexical_cast(pos+1)); + mDurationValue->setCaption(MyGUI::utility::toString(pos+1)); mEffect.mDuration = pos+1; eventEffectModified(mEffect); } void EditEffectDialog::onAreaChanged (MyGUI::ScrollBar* sender, size_t pos) { - mAreaValue->setCaption(boost::lexical_cast(pos)); + mAreaValue->setCaption(MyGUI::utility::toString(pos)); mEffect.mArea = pos; eventEffectModified(mEffect); } @@ -365,7 +363,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - if (boost::lexical_cast(mPriceLabel->getCaption()) > playerGold) + if (MyGUI::utility::parseInt(mPriceLabel->getCaption()) > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; @@ -373,7 +371,7 @@ namespace MWGui mSpell.mName = mNameEdit->getCaption(); - int price = boost::lexical_cast(mPriceLabel->getCaption()); + int price = MyGUI::utility::parseInt(mPriceLabel->getCaption()); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); @@ -451,17 +449,17 @@ namespace MWGui mSpell.mData.mType = ESM::Spell::ST_Spell; mSpell.mData.mFlags = 0; - mMagickaCost->setCaption(boost::lexical_cast(int(y))); + mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); float fSpellMakingValueMult = store.get().find("fSpellMakingValueMult")->getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,int(y) * fSpellMakingValueMult,true); - mPriceLabel->setCaption(boost::lexical_cast(int(price))); + mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWBase::Environment::get().getWorld()->getPlayerPtr()); - mSuccessChance->setCaption(boost::lexical_cast(int(chance))); + mSuccessChance->setCaption(MyGUI::utility::toString(int(chance))); } // ------------------------------------------------------------------------------------------------ diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 7edc6c8dac..20c6f3ba83 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -3,8 +3,6 @@ #include #include -#include - #include #include @@ -117,7 +115,7 @@ namespace MWGui } else if ( displayType != ESM::MagicEffect::MDT_None ) { - sourcesDescription += ": " + boost::lexical_cast(effectIt->mMagnitude); + sourcesDescription += ": " + MyGUI::utility::toString(effectIt->mMagnitude); if ( displayType == ESM::MagicEffect::MDT_Percentage ) sourcesDescription += MWBase::Environment::get().getWindowManager()->getGameSettingString("spercent", ""); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index e565b6f0ed..b5abf91fdb 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -6,8 +6,6 @@ #include #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" @@ -151,7 +149,7 @@ namespace MWGui // health, magicka, fatigue tooltip MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(current) + "/" + boost::lexical_cast(modified); + std::string valStr = MyGUI::utility::toString(current) + "/" + MyGUI::utility::toString(modified); if (id == "HBar") { getWidget(w, "Health"); @@ -196,7 +194,7 @@ namespace MWGui if (widget) { int modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(modified); + std::string text = MyGUI::utility::toString(modified); std::string state = "normal"; if (modified > base) state = "increased"; @@ -245,10 +243,10 @@ namespace MWGui { int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->getInt(); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); - levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); - levelWidget->setUserString("Range_LevelProgress", boost::lexical_cast(max)); - levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/" - + boost::lexical_cast(max)); + levelWidget->setUserString("RangePosition_LevelProgress", MyGUI::utility::toString(PCstats.getLevelProgress())); + levelWidget->setUserString("Range_LevelProgress", MyGUI::utility::toString(max)); + levelWidget->setUserString("Caption_LevelProgressText", MyGUI::utility::toString(PCstats.getLevelProgress()) + "/" + + MyGUI::utility::toString(max)); } setFactions(PCstats.getFactionRanks()); @@ -403,7 +401,7 @@ namespace MWGui else if (modified < base) state = "decreased"; MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), - boost::lexical_cast(static_cast(modified)), state, coord1, coord2); + MyGUI::utility::toString(static_cast(modified)), state, coord1, coord2); for (int i=0; i<2; ++i) { @@ -421,9 +419,9 @@ namespace MWGui mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Visible_SkillProgressVBox", "true"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("UserData^Hidden_SkillProgressVBox", "false"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", MyGUI::utility::toString(progressPercent)+"/100"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", MyGUI::utility::toString(progressPercent)); } else { mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Visible_SkillMaxed", "true"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("UserData^Hidden_SkillMaxed", "false"); @@ -534,8 +532,8 @@ namespace MWGui const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); - text += "\n#{fontcolourhtml=normal}#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) - + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); + text += "\n#{fontcolourhtml=normal}#{" + attr1->mName + "}: " + MyGUI::utility::toString(rankData.mAttribute1) + + ", #{" + attr2->mName + "}: " + MyGUI::utility::toString(rankData.mAttribute2); text += "\n\n#{fontcolourhtml=header}#{sFavoriteSkills}"; text += "\n#{fontcolourhtml=normal}"; @@ -556,9 +554,9 @@ namespace MWGui text += "\n"; if (rankData.mSkill1 > 0) - text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); + text += "\n#{sNeedOneSkill} " + MyGUI::utility::toString(rankData.mSkill1); if (rankData.mSkill2 > 0) - text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); + text += "\n#{sNeedTwoSkills} " + MyGUI::utility::toString(rankData.mSkill2); } } @@ -587,7 +585,7 @@ namespace MWGui addSeparator(coord1, coord2); addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sReputation", "Reputation"), - boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); + MyGUI::utility::toString(static_cast(mReputation)), "normal", coord1, coord2); for (int i=0; i<2; ++i) { @@ -597,7 +595,7 @@ namespace MWGui } addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBounty", "Bounty"), - boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); + MyGUI::utility::toString(static_cast(mBounty)), "normal", coord1, coord2); for (int i=0; i<2; ++i) { diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 3f3540a21a..303b8819ff 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -7,8 +7,6 @@ #include #include -#include - #include #include "../mwbase/world.hpp" @@ -556,7 +554,7 @@ namespace MWGui if (value == 1) return ""; else - return " (" + boost::lexical_cast(value) + ")"; + return " (" + MyGUI::utility::toString(value) + ")"; } std::string ToolTips::getCellRefString(const MWWorld::CellRef& cellref) diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index fc9167f3c0..88ba62cc1e 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -1,7 +1,5 @@ #include "trainingwindow.hpp" -#include - #include #include "../mwbase/windowmanager.hpp" @@ -67,7 +65,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); @@ -102,7 +100,7 @@ namespace MWGui button->setUserData(skills[i].first); button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); - button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[skills[i].first] + "} - " + boost::lexical_cast(price)); + button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[skills[i].first] + "} - " + MyGUI::utility::toString(price)); button->setSize(button->getTextSize ().width+12, button->getSize().height); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 3d37d0ab0e..50e08223e4 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -1,7 +1,5 @@ #include "travelwindow.hpp" -#include - #include #include #include @@ -91,7 +89,7 @@ namespace MWGui oss << price; toAdd->setUserString("price",oss.str()); - toAdd->setCaptionWithReplacing("#{sCell=" + name + "} - " + boost::lexical_cast(price)+"#{sgp}"); + toAdd->setCaptionWithReplacing("#{sCell=" + name + "} - " + MyGUI::utility::toString(price)+"#{sgp}"); toAdd->setSize(toAdd->getTextSize().width,sLineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); toAdd->setUserString("Destination", name); @@ -196,7 +194,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 6f87bffe0b..c53a887c9b 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -1,7 +1,5 @@ #include "waitdialog.hpp" -#include - #include #include @@ -40,7 +38,7 @@ namespace MWGui { mProgressBar->setProgressRange (total); mProgressBar->setProgressPosition (cur); - mProgressText->setCaption(boost::lexical_cast(cur) + "/" + boost::lexical_cast(total)); + mProgressText->setCaption(MyGUI::utility::toString(cur) + "/" + MyGUI::utility::toString(total)); } // --------------------------------------------------------------------------------------------------------- @@ -106,9 +104,9 @@ namespace MWGui if (hour == 0) hour = 12; std::string dateTimeText = - boost::lexical_cast(MWBase::Environment::get().getWorld ()->getDay ()) + " " - + month + " (#{sDay} " + boost::lexical_cast(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) - + ") " + boost::lexical_cast(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " " + + month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) + + ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mDateTimeText->setCaptionWithReplacing (dateTimeText); } @@ -174,7 +172,7 @@ namespace MWGui void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position) { - mHourText->setCaptionWithReplacing (boost::lexical_cast(position+1) + " #{sRestMenu2}"); + mHourText->setCaptionWithReplacing (MyGUI::utility::toString(position+1) + " #{sRestMenu2}"); mManualHours = position+1; } diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 381fc301e1..26fe315679 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -3,8 +3,6 @@ #include #include -#include - #include #include #include @@ -74,7 +72,7 @@ namespace MWGui if (mSkillValueWidget) { SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); - mSkillValueWidget->setCaption(boost::lexical_cast(modified)); + mSkillValueWidget->setCaption(MyGUI::utility::toString(modified)); if (modified > base) mSkillValueWidget->_setWidgetState("increased"); else if (modified < base) @@ -170,7 +168,7 @@ namespace MWGui if (mAttributeValueWidget) { int modified = mValue.getModified(), base = mValue.getBase(); - mAttributeValueWidget->setCaption(boost::lexical_cast(modified)); + mAttributeValueWidget->setCaption(MyGUI::utility::toString(modified)); if (modified > base) mAttributeValueWidget->_setWidgetState("increased"); else if (modified < base) @@ -432,9 +430,9 @@ namespace MWGui spellLine += formatter.str(); } else if ( displayType != ESM::MagicEffect::MDT_None ) { - spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin); + spellLine += " " + MyGUI::utility::toString(mEffectParams.mMagnMin); if (mEffectParams.mMagnMin != mEffectParams.mMagnMax) - spellLine += to + boost::lexical_cast(mEffectParams.mMagnMax); + spellLine += to + MyGUI::utility::toString(mEffectParams.mMagnMax); if ( displayType == ESM::MagicEffect::MDT_Percentage ) spellLine += pct; @@ -452,12 +450,12 @@ namespace MWGui { if (mEffectParams.mDuration > 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) { - spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + MyGUI::utility::toString(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); } if (mEffectParams.mArea > 0) { - spellLine += " #{sin} " + boost::lexical_cast(mEffectParams.mArea) + " #{sfootarea}"; + spellLine += " #{sin} " + MyGUI::utility::toString(mEffectParams.mArea) + " #{sfootarea}"; } // potions have no target From 063388e0eb6113d617a437ec5ef0f52fb601cdc1 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sat, 10 Jan 2015 15:04:45 +1300 Subject: [PATCH 260/404] Change "Profile" to "Content List" in user visible strings in Launcher. (Fixes #810) --- apps/launcher/datafilespage.cpp | 10 +++++----- apps/launcher/settingspage.cpp | 2 +- files/ui/datafilespage.ui | 18 +++++++++--------- files/ui/playpage.ui | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index a0ef97ff9c..8fca73c89f 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -30,7 +30,7 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config: setObjectName ("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); - mProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); + mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); @@ -44,12 +44,12 @@ void Launcher::DataFilesPage::buildView() ui.verticalLayout->insertWidget (0, mSelector->uiWidget()); //tool buttons - ui.newProfileButton->setToolTip ("Create a new profile"); - ui.deleteProfileButton->setToolTip ("Delete an existing profile"); + ui.newProfileButton->setToolTip ("Create a new Content List"); + ui.deleteProfileButton->setToolTip ("Delete an existing Content List"); //combo box ui.profilesComboBox->addItem ("Default"); - ui.profilesComboBox->setPlaceholderText (QString("Select a profile...")); + ui.profilesComboBox->setPlaceholderText (QString("Select a Content List...")); ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(QLatin1String("Default"))); // Add the actions to the toolbuttons @@ -306,7 +306,7 @@ void Launcher::DataFilesPage::checkForDefaultProfile() bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text) { QMessageBox msgBox(this); - msgBox.setWindowTitle(tr("Delete Profile")); + msgBox.setWindowTitle(tr("Delete Content List")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Cancel); msgBox.setText(tr("Are you sure you want to delete %0?").arg(text)); diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index 5422e79574..26908746bc 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -51,7 +51,7 @@ Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg, connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(importerFinished(int,QProcess::ExitStatus))); - mProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); + mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index eb5ebc61d6..5a6b7265af 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -29,7 +29,7 @@ Qt::NoFocus
- Profile + Content List false @@ -69,10 +69,10 @@ - New Profile + New Content List - &New Profile + &New Content List true @@ -82,10 +82,10 @@ - Delete Profile + Delete Content List - Delete Profile + Delete Content List Ctrl+D @@ -106,10 +106,10 @@ - New Profile + New Content List - New Profile + New Content List Ctrl+N @@ -125,10 +125,10 @@ - Delete Profile + Delete Content List - Delete Profile + Delete Content List
diff --git a/files/ui/playpage.ui b/files/ui/playpage.ui index bf883b96ef..1d529e2d7b 100644 --- a/files/ui/playpage.ui +++ b/files/ui/playpage.ui @@ -101,7 +101,7 @@ - Current Profile: + Current Content List: From 5b9d10f85156a41581ea29bbcbc9e5ce2f3e48b5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 03:56:06 +0100 Subject: [PATCH 261/404] Reduce includes in MWGui --- apps/openmw/mwgui/birth.hpp | 5 ----- apps/openmw/mwgui/class.hpp | 5 ----- apps/openmw/mwgui/companionwindow.cpp | 1 + apps/openmw/mwgui/dialogue.hpp | 5 ----- apps/openmw/mwgui/enchantingdialog.cpp | 5 +++++ apps/openmw/mwgui/hud.hpp | 6 +++++- apps/openmw/mwgui/journalwindow.hpp | 1 - apps/openmw/mwgui/loadingscreen.cpp | 1 + apps/openmw/mwgui/loadingscreen.hpp | 7 ++++++- apps/openmw/mwgui/merchantrepair.hpp | 2 -- apps/openmw/mwgui/messagebox.cpp | 1 + apps/openmw/mwgui/messagebox.hpp | 2 -- apps/openmw/mwgui/race.cpp | 6 ++++++ apps/openmw/mwgui/race.hpp | 17 ++++++++++------- apps/openmw/mwgui/recharge.hpp | 5 ++++- apps/openmw/mwgui/review.hpp | 5 ----- apps/openmw/mwgui/scrollwindow.cpp | 1 + apps/openmw/mwgui/scrollwindow.hpp | 7 +++++-- apps/openmw/mwgui/soulgemdialog.cpp | 1 + apps/openmw/mwgui/spellcreationdialog.cpp | 2 ++ apps/openmw/mwgui/spellcreationdialog.hpp | 7 +++++-- apps/openmw/mwgui/statswindow.cpp | 1 + apps/openmw/mwgui/statswindow.hpp | 4 ++-- apps/openmw/mwgui/waitdialog.cpp | 2 ++ apps/openmw/mwgui/waitdialog.hpp | 6 +++++- apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ 26 files changed, 65 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 257dc6fefe..0a84bb4e98 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -3,11 +3,6 @@ #include "windowbase.hpp" -/* - This file contains the dialog for choosing a birth sign. - Layout is defined by resources/mygui/openmw_chargen_race.layout. - */ - namespace MWGui { class BirthDialog : public WindowModal diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 9d529ece01..e36a9a98b6 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -6,11 +6,6 @@ #include "widgets.hpp" #include "windowbase.hpp" -/* - This file contains the dialogs for choosing a class. - Layout is defined by resources/mygui/openmw_chargen_class.layout. - */ - namespace MWGui { class InfoBoxDialog : public WindowModal diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 7583160308..53b14691b5 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -4,6 +4,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 4ab88c06fb..769c32af7c 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -18,11 +18,6 @@ namespace MWGui class WindowManager; } -/* - This file contains the dialouge window - Layout is defined by resources/mygui/openmw_dialogue_window.layout. - */ - namespace MWGui { class DialogueHistoryViewModel; diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index a17c0d0e6e..3376858c1c 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -3,7 +3,12 @@ #include #include + +#include +#include + #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index ca68d907a9..41a535a089 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -4,7 +4,11 @@ #include "mapwindow.hpp" #include "../mwmechanics/stat.hpp" -#include "../mwworld/ptr.hpp" + +namespace MWWorld +{ + class Ptr; +} namespace MWGui { diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 63770ec1aa..5d2a5318ac 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -1,7 +1,6 @@ #ifndef MWGUI_JOURNAL_H #define MWGUI_JOURNAL_H -#include #include namespace MWBase { class WindowManager; } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 855a6fc848..d10a295c16 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 892710433f..0d3ffbbec7 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -1,13 +1,18 @@ #ifndef MWGUI_LOADINGSCREEN_H #define MWGUI_LOADINGSCREEN_H -#include #include +#include #include "windowbase.hpp" #include +namespace Ogre +{ + class SceneManager; +} + namespace MWGui { class BackgroundImage; diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index 2f13873654..231d11089b 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -4,8 +4,6 @@ #include "windowbase.hpp" #include "../mwworld/ptr.hpp" - - namespace MWGui { diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 43debd0e53..c45136eb38 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 5f180df204..59804e097c 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -3,8 +3,6 @@ #include "windowbase.hpp" -#include "../mwbase/windowmanager.hpp" - #undef MessageBox namespace MyGUI diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index f6aa8ee4c7..b03bf758af 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwrender/characterpreview.hpp" #include "tooltips.hpp" @@ -446,4 +447,9 @@ namespace MWGui ++i; } } + + const ESM::NPC& RaceDialog::getResult() const + { + return mPreview->getPrototype(); + } } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 454c1d0b61..be16af5d1e 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -1,8 +1,6 @@ #ifndef MWGUI_RACE_H #define MWGUI_RACE_H -#include "../mwrender/characterpreview.hpp" - #include "windowbase.hpp" @@ -11,10 +9,15 @@ namespace MWGui class WindowManager; } -/* - This file contains the dialog for choosing a race. - Layout is defined by resources/mygui/openmw_chargen_race.layout. - */ +namespace MWRender +{ + class RaceSelectionPreview; +} + +namespace ESM +{ + struct NPC; +} namespace MWGui { @@ -29,7 +32,7 @@ namespace MWGui GM_Female }; - const ESM::NPC &getResult() const { return mPreview->getPrototype(); } + const ESM::NPC &getResult() const; const std::string &getRaceId() const { return mCurrentRaceId; } Gender getGender() const { return mGenderIndex == 0 ? GM_Male : GM_Female; } // getFace() diff --git a/apps/openmw/mwgui/recharge.hpp b/apps/openmw/mwgui/recharge.hpp index 17d700649b..3e8e1269e7 100644 --- a/apps/openmw/mwgui/recharge.hpp +++ b/apps/openmw/mwgui/recharge.hpp @@ -3,7 +3,10 @@ #include "windowbase.hpp" -#include "../mwworld/ptr.hpp" +namespace MWWorld +{ + class Ptr; +} namespace MWGui { diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 1419925b59..111d7de1dc 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -11,11 +11,6 @@ namespace MWGui class WindowManager; } -/* -This file contains the dialog for reviewing the generated character. -Layout is defined by resources/mygui/openmw_chargen_review.layout. -*/ - namespace MWGui { class ReviewDialog : public WindowModal diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index d49d5ad5f2..d61693d39e 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index 08ce6a9054..e1f86529a3 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -3,10 +3,13 @@ #include "windowbase.hpp" -#include - #include "../mwworld/ptr.hpp" +namespace Gui +{ + class ImageButton; +} + namespace MWGui { class ScrollWindow : public WindowBase diff --git a/apps/openmw/mwgui/soulgemdialog.cpp b/apps/openmw/mwgui/soulgemdialog.cpp index 6d70c85d9d..0232eb7b32 100644 --- a/apps/openmw/mwgui/soulgemdialog.cpp +++ b/apps/openmw/mwgui/soulgemdialog.cpp @@ -1,6 +1,7 @@ #include "soulgemdialog.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "messagebox.hpp" diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 1ff379c0bf..a00afdab8c 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" @@ -22,6 +23,7 @@ #include "tooltips.hpp" #include "class.hpp" +#include "widgets.hpp" namespace { diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 72e581c876..6cdf74d103 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -3,11 +3,14 @@ #include #include -#include #include "windowbase.hpp" #include "referenceinterface.hpp" -#include "widgets.hpp" + +namespace Gui +{ + class MWList; +} namespace MWGui { diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index b5abf91fdb..19f9c749c8 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -12,6 +12,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index f41995ac07..2cf4ca819b 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -1,11 +1,11 @@ #ifndef MWGUI_STATS_WINDOW_H #define MWGUI_STATS_WINDOW_H -#include "../mwworld/esmstore.hpp" - #include "../mwmechanics/stat.hpp" #include "windowpinnablebase.hpp" +#include + namespace MWGui { class WindowManager; diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index c53a887c9b..718e04e52e 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -19,6 +19,8 @@ #include "../mwstate/charactermanager.hpp" +#include "widgets.hpp" + namespace MWGui { diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 1cf05bb06f..435c1cb547 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -2,11 +2,15 @@ #define MWGUI_WAIT_DIALOG_H #include "windowbase.hpp" -#include "widgets.hpp" namespace MWGui { + namespace Widgets + { + class MWScrollBar; + } + class WaitDialogProgressBar : public WindowBase { public: diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index aa80e69fc9..5d737ec413 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -34,6 +35,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" From 05b89be8bfc19a653287a90b05eb54f63b8a5609 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sat, 10 Jan 2015 18:46:47 +1300 Subject: [PATCH 262/404] Launcher sets content list to match values in openmw.cfg (Fixes #811) I took the liberty to add accessor & mutator functions for classes ContentListsGameSettings and LauncherSettings , as existing code can reverse order of entries. Also replaced some "magic strings" with named constants. --- apps/launcher/datafilespage.cpp | 31 +++--- apps/launcher/maindialog.cpp | 10 +- apps/launcher/settingspage.cpp | 17 +--- apps/wizard/mainwizard.cpp | 4 +- components/config/gamesettings.cpp | 30 ++++-- components/config/gamesettings.hpp | 7 +- components/config/launchersettings.cpp | 134 +++++++++++++++++++++---- components/config/launchersettings.hpp | 43 +++++++- components/config/settingsbase.hpp | 4 +- 9 files changed, 207 insertions(+), 73 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8fca73c89f..8429aaadc5 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -82,8 +82,8 @@ bool Launcher::DataFilesPage::loadSettings() paths.insert (0, mDataLocal); PathIterator pathIterator (paths); - QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); - QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile"); + QStringList profiles = mLauncherSettings.getContentLists(); + QString currentProfile = mLauncherSettings.getCurrentContentListName(); qDebug() << "current profile is: " << currentProfile; @@ -101,15 +101,12 @@ bool Launcher::DataFilesPage::loadSettings() QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName, PathIterator& pathIterator) { - QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/content"), Qt::MatchExactly); + QStringList files = mLauncherSettings.getContentListFiles(profileName); QStringList filepaths; - // mLauncherSettings.values() returns the files in reverse load order - QListIterator i(files); - i.toBack(); - while (i.hasPrevious()) + foreach(const QString& file, files) { - QString filepath = pathIterator.findFirstPath(i.previous()); + QString filepath = pathIterator.findFirstPath(file); if (!filepath.isEmpty()) filepaths << filepath; @@ -128,24 +125,20 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile) //retrieve the files selected for the profile ContentSelectorModel::ContentFileList items = mSelector->selectedFiles(); - removeProfile (profileName); - - mGameSettings.remove(QString("content")); - //set the value of the current profile (not necessarily the profile being saved!) - mLauncherSettings.setValue(QString("Profiles/currentprofile"), ui.profilesComboBox->currentText()); + mLauncherSettings.setCurrentContentListName(ui.profilesComboBox->currentText()); + QStringList fileNames; foreach(const ContentSelectorModel::EsmFile *item, items) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/content"), item->fileName()); - mGameSettings.setMultiValue(QString("content"), item->fileName()); + fileNames.append(item->fileName()); } - + mLauncherSettings.setContentList(profileName, fileNames); + mGameSettings.setContentList(fileNames); } void Launcher::DataFilesPage::removeProfile(const QString &profile) { - mLauncherSettings.remove(QString("Profiles/") + profile); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/content")); + mLauncherSettings.removeContentList(profile); } QAbstractItemModel *Launcher::DataFilesPage::profilesModel() const @@ -233,7 +226,7 @@ void Launcher::DataFilesPage::on_newProfileAction_triggered() saveSettings(); - mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); + mLauncherSettings.setCurrentContentListName(profile); addProfile(profile, true); mSelector->clearCheckStates(); diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 975958d7ac..00c5499695 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -209,6 +209,8 @@ bool Launcher::MainDialog::setup() if (!setupGameSettings()) return false; + mLauncherSettings.setContentList(mGameSettings); + if (!setupGraphicsSettings()) return false; @@ -232,6 +234,8 @@ bool Launcher::MainDialog::reloadSettings() if (!setupGameSettings()) return false; + mLauncherSettings.setContentList(mGameSettings); + if (!setupGraphicsSettings()) return false; @@ -280,8 +284,8 @@ bool Launcher::MainDialog::setupLauncherSettings() QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QStringList paths; - paths.append(QString("launcher.cfg")); - paths.append(userPath + QString("launcher.cfg")); + paths.append(QString(Config::LauncherSettings::sLauncherConfigFileName)); + paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName)); foreach (const QString &path, paths) { qDebug() << "Loading config file:" << qPrintable(path); @@ -562,7 +566,7 @@ bool Launcher::MainDialog::writeSettings() file.close(); // Launcher settings - file.setFileName(userPath + QString("launcher.cfg")); + file.setFileName(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName)); if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { // File cannot be opened or created diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index 26908746bc..ace8f43103 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -207,18 +207,9 @@ void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus if (mProfileDialog->exec() == QDialog::Accepted) { const QString profile(mProfileDialog->lineEdit()->text()); - const QStringList files(mGameSettings.values(QLatin1String("content"))); - - qDebug() << "Profile " << profile << files; - - // Doesn't quite work right now - mLauncherSettings.setValue(QLatin1String("Profiles/currentprofile"), profile); - - foreach (const QString &file, files) { - mLauncherSettings.setMultiValue(QLatin1String("Profiles/") + profile + QLatin1String("/content"), file); - } - - mGameSettings.remove(QLatin1String("content")); + const QStringList files(mGameSettings.getContentList()); + mLauncherSettings.setCurrentContentListName(profile); + mLauncherSettings.setContentList(profile, files); } } @@ -234,7 +225,7 @@ void Launcher::SettingsPage::updateOkButton(const QString &text) return; } - const QStringList profiles(mLauncherSettings.subKeys(QString("Profiles/"))); + const QStringList profiles(mLauncherSettings.getContentLists()); (profiles.contains(text)) ? mProfileDialog->setOkButtonEnabled(false) diff --git a/apps/wizard/mainwizard.cpp b/apps/wizard/mainwizard.cpp index e68070c4d5..9a8ec2056b 100644 --- a/apps/wizard/mainwizard.cpp +++ b/apps/wizard/mainwizard.cpp @@ -182,7 +182,7 @@ void Wizard::MainWizard::setupGameSettings() void Wizard::MainWizard::setupLauncherSettings() { QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str())); - path.append(QLatin1String("launcher.cfg")); + path.append(QLatin1String(Config::LauncherSettings::sLauncherConfigFileName)); QString message(tr("

Could not open %1 for reading

\

Please make sure you have the right permissions \ @@ -427,7 +427,7 @@ void Wizard::MainWizard::writeSettings() file.close(); // Launcher settings - file.setFileName(userPath + QLatin1String("launcher.cfg")); + file.setFileName(userPath + QLatin1String(Config::LauncherSettings::sLauncherConfigFileName)); if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { // File cannot be opened or created diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 1e7f716e21..0481235c78 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -1,4 +1,5 @@ #include "gamesettings.hpp" +#include "launchersettings.hpp" #include #include @@ -26,6 +27,7 @@ namespace boost } /* namespace boost */ #endif /* (BOOST_VERSION <= 104600) */ +const char Config::GameSettings::sContentKey[] = "content"; Config::GameSettings::GameSettings(Files::ConfigurationManager &cfg) : mCfgMgr(cfg) @@ -81,7 +83,7 @@ void Config::GameSettings::validatePaths() } } -QStringList Config::GameSettings::values(const QString &key, const QStringList &defaultValues) +QStringList Config::GameSettings::values(const QString &key, const QStringList &defaultValues) const { if (!mSettings.values(key).isEmpty()) return mSettings.values(key); @@ -149,9 +151,6 @@ bool Config::GameSettings::writeFile(QTextStream &stream) while (i.hasPrevious()) { i.previous(); - if (i.key() == QLatin1String("content")) - continue; - // Quote paths with spaces if (i.key() == QLatin1String("data") || i.key() == QLatin1String("data-local") @@ -171,18 +170,13 @@ bool Config::GameSettings::writeFile(QTextStream &stream) } - QStringList content = mUserSettings.values(QString("content")); - for (int i = content.count(); i--;) { - stream << "content=" << content.at(i) << "\n"; - } - return true; } bool Config::GameSettings::hasMaster() { bool result = false; - QStringList content = mSettings.values(QString("content")); + QStringList content = mSettings.values(QString(Config::GameSettings::sContentKey)); for (int i = 0; i < content.count(); ++i) { if (content.at(i).contains(".omwgame") || content.at(i).contains(".esm")) { result = true; @@ -192,3 +186,19 @@ bool Config::GameSettings::hasMaster() return result; } + +void Config::GameSettings::setContentList(const QStringList& fileNames) +{ + remove(sContentKey); + foreach(const QString& fileName, fileNames) + { + setMultiValue(sContentKey, fileName); + } +} + +QStringList Config::GameSettings::getContentList() const +{ + // QMap returns multiple rows in LIFO order, so need to reverse + return Config::LauncherSettings::reverse(values(sContentKey)); +} + diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index c4a6ead79b..cc5033f351 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -59,7 +59,7 @@ namespace Config bool hasMaster(); - QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); + QStringList values(const QString &key, const QStringList &defaultValues = QStringList()) const; bool readFile(QTextStream &stream); bool readFile(QTextStream &stream, QMap &settings); @@ -67,6 +67,9 @@ namespace Config bool writeFile(QTextStream &stream); + void setContentList(const QStringList& fileNames); + QStringList getContentList() const; + private: Files::ConfigurationManager &mCfgMgr; @@ -76,6 +79,8 @@ namespace Config QStringList mDataDirs; QString mDataLocal; + + static const char sContentKey[]; }; } #endif // GAMESETTINGS_HPP diff --git a/components/config/launchersettings.cpp b/components/config/launchersettings.cpp index c014579dc5..66f05f691c 100644 --- a/components/config/launchersettings.cpp +++ b/components/config/launchersettings.cpp @@ -7,6 +7,11 @@ #include +const char Config::LauncherSettings::sCurrentContentListKey[] = "Profiles/currentprofile"; +const char Config::LauncherSettings::sLauncherConfigFileName[] = "launcher.cfg"; +const char Config::LauncherSettings::sContentListsSectionPrefix[] = "Profiles/"; +const char Config::LauncherSettings::sContentListSuffix[] = "/content"; + Config::LauncherSettings::LauncherSettings() { } @@ -15,27 +20,6 @@ Config::LauncherSettings::~LauncherSettings() { } -QStringList Config::LauncherSettings::values(const QString &key, Qt::MatchFlags flags) -{ - QMap settings = SettingsBase::getSettings(); - - if (flags == Qt::MatchExactly) - return settings.values(key); - - QStringList result; - - if (flags == Qt::MatchStartsWith) { - QStringList keys = settings.keys(); - - foreach (const QString ¤tKey, keys) { - if (currentKey.startsWith(key)) - result.append(settings.value(currentKey)); - } - } - - return result; -} - QStringList Config::LauncherSettings::subKeys(const QString &key) { QMap settings = SettingsBase::getSettings(); @@ -66,6 +50,7 @@ QStringList Config::LauncherSettings::subKeys(const QString &key) return result; } + bool Config::LauncherSettings::writeFile(QTextStream &stream) { QString sectionPrefix; @@ -104,3 +89,110 @@ bool Config::LauncherSettings::writeFile(QTextStream &stream) return true; } + +QStringList Config::LauncherSettings::getContentLists() +{ + return subKeys(QString(sContentListsSectionPrefix)); +} + +QString Config::LauncherSettings::makeContentListKey(const QString& contentListName) +{ + return QString(sContentListsSectionPrefix) + contentListName + QString(sContentListSuffix); +} + +void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) +{ + // obtain content list from game settings (if present) + const QStringList files(gameSettings.getContentList()); + + // if any existing profile in launcher matches the content list, make that profile the default + foreach(const QString &listName, getContentLists()) + { + if (isEqual(files, getContentListFiles(listName))) + { + setCurrentContentListName(listName); + return; + } + } + + // otherwise, add content list + QString newContentListName(makeNewContentListName()); + setCurrentContentListName(newContentListName); + setContentList(newContentListName, files); +} + +void Config::LauncherSettings::removeContentList(const QString &contentListName) +{ + remove(makeContentListKey(contentListName)); +} + +void Config::LauncherSettings::setCurrentContentListName(const QString &contentListName) +{ + remove(QString(sCurrentContentListKey)); + setValue(QString(sCurrentContentListKey), contentListName); +} + +void Config::LauncherSettings::setContentList(const QString& contentListName, const QStringList& fileNames) +{ + removeContentList(contentListName); + QString key = makeContentListKey(contentListName); + foreach(const QString& fileName, fileNames) + { + setMultiValue(key, fileName); + } +} + +QString Config::LauncherSettings::getCurrentContentListName() const +{ + return value(QString(sCurrentContentListKey)); +} + +QStringList Config::LauncherSettings::getContentListFiles(const QString& contentListName) const +{ + // QMap returns multiple rows in LIFO order, so need to reverse + return reverse(getSettings().values(makeContentListKey(contentListName))); +} + +QStringList Config::LauncherSettings::reverse(const QStringList& toReverse) +{ + QStringList result; + result.reserve(toReverse.size()); + std::reverse_copy(toReverse.begin(), toReverse.end(), std::back_inserter(result)); + return result; +} + +bool Config::LauncherSettings::isEqual(const QStringList& list1, const QStringList& list2) +{ + if (list1.count() != list2.count()) + { + return false; + } + + for (int i = 0; i < list1.count(); ++i) + { + if (list1.at(i) != list2.at(i)) + { + return false; + } + } + + // if get here, lists are same + return true; +} + +QString Config::LauncherSettings::makeNewContentListName() +{ + // basically, use date and time as the name e.g. YYYY-MM-DDThh:mm:ss + time_t rawtime; + struct tm * timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + int base = 10; + QChar zeroPad('0'); + return QString("%1-%2-%3T%4:%5:%6") + .arg(timeinfo->tm_year + 1900, 4).arg(timeinfo->tm_mon + 1, 2, base, zeroPad).arg(timeinfo->tm_mday, 2, base, zeroPad) + .arg(timeinfo->tm_hour, 2, base, zeroPad).arg(timeinfo->tm_min, 2, base, zeroPad).arg(timeinfo->tm_sec, 2, base, zeroPad); +} + + diff --git a/components/config/launchersettings.hpp b/components/config/launchersettings.hpp index 042823ca93..cbe21c54a6 100644 --- a/components/config/launchersettings.hpp +++ b/components/config/launchersettings.hpp @@ -2,6 +2,7 @@ #define LAUNCHERSETTINGS_HPP #include "settingsbase.hpp" +#include "gamesettings.hpp" namespace Config { @@ -11,11 +12,49 @@ namespace Config LauncherSettings(); ~LauncherSettings(); + bool writeFile(QTextStream &stream); + + /// \return names of all Content Lists in the launcher's .cfg file. + QStringList getContentLists(); + + /// Set initally selected content list to match values from openmw.cfg, creating if necessary + void setContentList(const GameSettings& gameSettings); + + /// Create a Content List (or replace if it already exists) + void setContentList(const QString& contentListName, const QStringList& fileNames); + + void removeContentList(const QString &contentListName); + + void setCurrentContentListName(const QString &contentListName); + + QString getCurrentContentListName() const; + + QStringList getContentListFiles(const QString& contentListName) const; + + /// \return new list that is reversed order of input + static QStringList reverse(const QStringList& toReverse); + + static const char sLauncherConfigFileName[]; + + private: + + /// \return key to use to get/set the files in the specified Content List + static QString makeContentListKey(const QString& contentListName); + + /// \return true if both lists are same + static bool isEqual(const QStringList& list1, const QStringList& list2); + + static QString makeNewContentListName(); + QStringList subKeys(const QString &key); - QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); - bool writeFile(QTextStream &stream); + /// name of entry in launcher.cfg that holds name of currently selected Content List + static const char sCurrentContentListKey[]; + + /// section of launcher.cfg holding the Content Lists + static const char sContentListsSectionPrefix[]; + static const char sContentListSuffix[]; }; } #endif // LAUNCHERSETTINGS_HPP diff --git a/components/config/settingsbase.hpp b/components/config/settingsbase.hpp index e6b0908e0a..c798d2893b 100644 --- a/components/config/settingsbase.hpp +++ b/components/config/settingsbase.hpp @@ -17,7 +17,7 @@ namespace Config SettingsBase() { mMultiValue = false; } ~SettingsBase() {} - inline QString value(const QString &key, const QString &defaultValue = QString()) + inline QString value(const QString &key, const QString &defaultValue = QString()) const { return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); } @@ -46,7 +46,7 @@ namespace Config mSettings.remove(key); } - Map getSettings() {return mSettings;} + Map getSettings() const {return mSettings;} bool readFile(QTextStream &stream) { From 37bea9d4dc16bbefc70f66128abff9d6b51d89c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 15:06:30 +0100 Subject: [PATCH 263/404] Fix exception for empty dialog topics (Fixes #2267) --- apps/openmw/mwdialogue/keywordsearch.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwdialogue/keywordsearch.hpp b/apps/openmw/mwdialogue/keywordsearch.hpp index c44139f324..0bb3616d9f 100644 --- a/apps/openmw/mwdialogue/keywordsearch.hpp +++ b/apps/openmw/mwdialogue/keywordsearch.hpp @@ -28,6 +28,8 @@ public: void seed (string_t keyword, value_t value) { + if (keyword.empty()) + return; seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot); } From 412496e28cc1d20f88cbe5eec65380cae44e1253 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Dec 2014 23:19:37 +0100 Subject: [PATCH 264/404] Enable coverity scan for Travis CI --- .travis.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 233117718b..c5ed43543c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,22 @@ branches: only: - master - /openmw-.*$/ +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "CcQGmzgaBRTPWqPZLoLX4Zkt5pOQb5c6Pr5OFt15C8xQRhWOLrWcdsQEO1UuEJ70/YJgbvN+wQk8B5COBDLxM1roBNBu7zMpK//393Qx3rL2f9PdaZGLaCEtS5DyuXimyUXWzjMt8gsnsBxtomTgHBY4E7F5ag0Ub2z6ZsIQE1A=" + +addons: + coverity_scan: + project: + name: "scrawl/openmw" + description: "" + notification_email: scrawl@baseoftrash.de + build_command_prepend: "cmake ." + build_command: "make -j4" + branch_pattern: coverity_scan + before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi @@ -14,7 +30,7 @@ before_script: - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi script: - cd ./build - - make -j4 + - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j4; fi after_script: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi notifications: From 3378dece60ef086c2ef5de54302373274fc5d1af Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Dec 2014 23:22:20 +0100 Subject: [PATCH 265/404] Add coverity_scan branch to .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c5ed43543c..7acb92554b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ language: cpp branches: only: - master + - coverity_scan - /openmw-.*$/ env: global: From bf1739ae2cab2c0e3f30e51c74b70dcd4c7f29d7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 15:54:13 +0100 Subject: [PATCH 266/404] Adjust COVERITY_SCAN_TOKEN --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7acb92554b..1200c6bbce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,7 @@ env: global: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - - secure: "CcQGmzgaBRTPWqPZLoLX4Zkt5pOQb5c6Pr5OFt15C8xQRhWOLrWcdsQEO1UuEJ70/YJgbvN+wQk8B5COBDLxM1roBNBu7zMpK//393Qx3rL2f9PdaZGLaCEtS5DyuXimyUXWzjMt8gsnsBxtomTgHBY4E7F5ag0Ub2z6ZsIQE1A=" - + - secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE=" addons: coverity_scan: project: From 2d51d599ea04f04e949efd248a974cf40fc3e580 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 15:57:13 +0100 Subject: [PATCH 267/404] Adjust coverity config to official repo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1200c6bbce..d24f5ebd85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ env: addons: coverity_scan: project: - name: "scrawl/openmw" + name: "openmw/openmw" description: "" notification_email: scrawl@baseoftrash.de build_command_prepend: "cmake ." From c37c071dfe1cb088e778d3e8fb0a9ad518f4d085 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:07:12 +0100 Subject: [PATCH 268/404] Repository name fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d24f5ebd85..079e84a8e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ env: addons: coverity_scan: project: - name: "openmw/openmw" + name: "OpenMW/openmw" description: "" notification_email: scrawl@baseoftrash.de build_command_prepend: "cmake ." From 23ffd33d9e6e1f12faf2b7d8e7e4bc3cde49efc8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:27:15 +0100 Subject: [PATCH 269/404] Convert readme to markdown --- readme.txt => README.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) rename readme.txt => README.md (99%) diff --git a/readme.txt b/README.md similarity index 99% rename from readme.txt rename to README.md index 5d344c0b50..304844892a 100644 --- a/readme.txt +++ b/README.md @@ -1,18 +1,19 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind +============================================================== OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.34.0 -License: GPL (see GPL3.txt for more information) -Website: http://www.openmw.org +* Version: 0.34.0 +* License: GPL (see GPL3.txt for more information) +* Website: http://www.openmw.org Font Licenses: DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) - -INSTALLATION +Installation +============ Windows: Run the installer. @@ -28,18 +29,21 @@ https://www.archlinux.org/packages/?sort=&q=openmw OS X: Open DMG file, copy OpenMW folder anywhere, for example in /Applications -BUILD FROM SOURCE +Build from source +================= https://wiki.openmw.org/index.php?title=Development_Environment_Setup -THE DATA PATH +The data path +============= The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install). -COMMAND LINE OPTIONS +Command line options +==================== Syntax: openmw Allowed options: @@ -96,7 +100,8 @@ Allowed options: --no-grab Don't grab mouse cursor --activate-dist arg (=-1) activation distance override -CHANGELOG +Changelog +========= 0.34.0 From 88a562a6481ace71a0569aa07c695437c4ed29fb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:35:03 +0100 Subject: [PATCH 270/404] Move changelog to its own file, formatting fixes --- CHANGELOG.md | 1465 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 1474 +------------------------------------------------- 2 files changed, 1470 insertions(+), 1469 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..09f318d9d1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1465 @@ +

+0.34.0
+
+Bug #904: omwlauncher doesn't allow installing Tribunal and Bloodmoon if only MW is installed
+Bug #986: Launcher: renaming profile names is broken
+Bug #1061: "Browse to CD..." launcher crash
+Bug #1135: Launcher crashes if user does not have write permission
+Bug #1231: Current installer in launcher does not correctly import russian Morrowind.ini settings from setup.inx
+Bug #1288: Fix the Alignment of the Resolution Combobox
+Bug #1343: BIK videos occasionally out of sync with audio
+Bug #1684: Morrowind Grass Mod graphical glitches
+Bug #1734: NPC in fight with invisible/sneaking player
+Bug #1982: Long class names are cut off in the UI
+Bug #2012: Editor: OpenCS script compiler sometimes fails to find IDs
+Bug #2015: Running while levitating does not affect speed but still drains fatigue
+Bug #2018: OpenMW don´t reset modified cells to vanilla when a plugin is deselected and don´t apply changes to cells already visited.
+Bug #2045: ToggleMenus command should close dialogue windows
+Bug #2046: Crash: light_de_streetlight_01_223
+Bug #2047: Buglamp tooltip minor correction
+Bug #2050: Roobrush floating texture bits
+Bug #2053: Slaves react negatively to PC picking up slave's bracers
+Bug #2055: Dremora corpses use the wrong model
+Bug #2056: Mansilamat Vabdas's corpse is floating in the water
+Bug #2057: "Quest: Larius Varro Tells A Little Story": Bounty not completely removed after finishing quest
+Bug #2059: Silenced enemies try to cast spells anyway
+Bug #2060: Editor: Special case implementation for top level window with single sub-window should be optional
+Bug #2061: Editor: SubView closing that is not directly triggered by the user isn't handled properly
+Bug #2063: Tribunal: Quest 'The Warlords' doesn't work
+Bug #2064: Sneak attack on hostiles causes bounty
+Bug #2065: Editor: Qt signal-slot error when closing a dialogue subview
+Bug #2070: Loading ESP in OpenMW works but fails in OpenCS
+Bug #2071: CTD in 0.33
+Bug #2073: Storm atronach animation stops now and then
+Bug #2075: Molag Amur Region, Map shows water on solid ground
+Bug #2080: game won't work with fair magicka regen
+Bug #2082: NPCs appear frozen or switched off after leaving and quickly reentering a cell
+Bug #2088: OpenMW is unable to play OGG files.
+Bug #2093: Darth Gares talks to you in Ilunibi even when he's not there, screwing up the Main Quests
+Bug #2095: Coordinate and rotation editing in the Reference table does not work.
+Bug #2096: Some overflow fun and bartering exploit
+Bug #2098: [D3D] Game crash on maximize
+Bug #2099: Activate, player seems not to work
+Bug #2104: Only labels are sensitive in buttons
+Bug #2107: "Slowfall" effect is too weak
+Bug #2114: OpenCS doesn't load an ESP file full of errors even though Vanilla MW Construction Set can
+Bug #2117: Crash when encountering bandits on opposite side of river from the egg mine south of Balmora
+Bug #2124: [Mod: Baldurians Transparent Glass Amor] Armor above head
+Bug #2125: Unnamed NiNodes in weapons problem in First Person
+Bug #2126: Dirty dialog script in tribunal.esm causing bug in Tribunal MQ
+Bug #2128: Crash when picking character's face
+Bug #2129: Disable the third-person zoom feature by default
+Bug #2130: Ash storm particles shown too long during transition to clear sky
+Bug #2137: Editor: exception caused by following the Creature column of a SoundGen record
+Bug #2139: Mouse movement should be ignored during intro video
+Bug #2143: Editor: Saving is broken
+Bug #2145: OpenMW - crash while exiting x64 debug build
+Bug #2152: You can attack Almalexia during her final monologue
+Bug #2154: Visual effects behave weirdly after loading/taking a screenshot
+Bug #2155: Vivec has too little magicka
+Bug #2156: Azura's spirit fades away too fast
+Bug #2158: [Mod]Julan Ashlander Companion 2.0: Negative magicka
+Bug #2161: Editor: combat/magic/stealth values of creature not displayed correctly
+Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon.
+Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly.
+Bug #2170: Mods using conversations to update PC inconsistant
+Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources
+Bug #2212: Crash or unexpected behavior while closing OpenCS cell render window on OS X
+Feature #238: Add UI to run INI-importer from the launcher
+Feature #854: Editor: Add user setting to show status bar
+Feature #987: Launcher: first launch instructions for CD need to be more explicit
+Feature #1232: There is no way to set the "encoding" option using launcher UI.
+Feature #1281: Editor: Render cell markers
+Feature #1918: Editor: Functionality for Double-Clicking in Tables
+Feature #1966: Editor: User Settings dialogue grouping/labelling/tooltips
+Feature #2097: Editor: Edit position of references in 3D scene
+Feature #2121: Editor: Add edit mode button to scene toolbar
+Task #1965: Editor: Improve layout of user settings dialogue
+
+0.33.1
+
+Bug #2108: OpenCS fails to build
+
+0.33.0
+
+Bug #371: If console assigned to ` (probably to any symbolic key), "`" symbol will be added to console every time it closed
+Bug #1148: Some books'/scrolls' contents are displayed incorrectly
+Bug #1290: Editor: status bar is not updated when record filter is changed
+Bug #1292: Editor: Documents are not removed on closing the last view
+Bug #1301: Editor: File->Exit only checks the document it was issued from.
+Bug #1353: Bluetooth on with no speaker connected results in significantly longer initial load times
+Bug #1436: NPCs react from too far distance
+Bug #1472: PC is placed on top of following NPC when changing cell
+Bug #1487: Tall PC can get stuck in staircases
+Bug #1565: Editor: Subviews are deleted on shutdown instead when they are closed
+Bug #1623: Door marker on Ghorak Manor's balcony makes PC stuck
+Bug #1633: Loaddoor to Sadrith Mora, Telvanni Council House spawns PC in the air
+Bug #1655: Use Appropriate Application Icons on Windows
+Bug #1679: Tribunal expansion, Meryn Othralas the backstage manager in the theatre group in Mournhold in the great bazaar district is floating a good feet above the ground.
+Bug #1705: Rain is broken in third person
+Bug #1706: Thunder and lighting still occurs while the game is paused during the rain
+Bug #1708: No long jumping
+Bug #1710: Editor: ReferenceableID drag to references record filter field creates incorrect filter
+Bug #1712: Rest on Water
+Bug #1715: "Cancel" button is not always on the same side of menu
+Bug #1725: Editor: content file can be opened multiple times from the same dialogue
+Bug #1730: [MOD: Less Generic Nerevarine] Compile failure attempting to enter the Corprusarium.
+Bug #1733: Unhandled ffmpeg sample formats
+Bug #1735: Editor: "Edit Record" context menu button not opening subview for journal infos
+Bug #1750: Editor: record edits result in duplicate entries
+Bug #1789: Editor: Some characters cannot be used in addon name
+Bug #1803: Resizing the map does not keep the pre-resize center at the post-resize center
+Bug #1821: Recovering Cloudcleaver quest: attacking Sosia is considered a crime when you side with Hlormar
+Bug #1838: Editor: Preferences window appears off screen
+Bug #1839: Editor: Record filter title should be moved two pixels to the right
+Bug #1849: Subrecord error in MAO_Containers
+Bug #1854: Knocked-out actors don't fully act knocked out
+Bug #1855: "Soul trapped" sound doesn't play
+Bug #1857: Missing sound effect for enchanted items with empty charge
+Bug #1859: Missing console command: ResetActors (RA)
+Bug #1861: Vendor category "MagicItems" is unhandled
+Bug #1862: Launcher doesn't start if a file listed in launcher.cfg has correct name but wrong capitalization
+Bug #1864: Editor: Region field for cell record in dialogue subview not working
+Bug #1869: Editor: Change label "Musics" to "Music"
+Bug #1870: Goblins killed while knocked down remain in knockdown-pose
+Bug #1874: CellChanged events should not trigger when crossing exterior cell border
+Bug #1877: Spriggans killed instantly if hit while regening
+Bug #1878: Magic Menu text not un-highlighting correctly when going from spell to item as active magic
+Bug #1881: Stuck in ceiling when entering castle karstaags tower
+Bug #1884: Unlit torches still produce a burning sound
+Bug #1885: Can type text in price field in barter window
+Bug #1887: Equipped items do not emit sounds
+Bug #1889: draugr lord aesliip will attack you and remain non-hostile
+Bug #1892: Guard asks player to pay bounty of 0 gold
+Bug #1895: getdistance should only return max float if ref and target are in different worldspaces
+Bug #1896: Crash Report
+Bug #1897: Conjured Equipment cant be re-equipped if removed
+Bug #1898: Only Gidar Verothan follows you during establish the mine quest
+Bug #1900: Black screen when you open the door and breath underwater
+Bug #1904: Crash on casting recall spell
+Bug #1906: Bound item checks should use the GMSTs
+Bug #1907: Bugged door. Mournhold, The Winged Guar
+Bug #1908: Crime reported for attacking Drathas Nerus's henchmen while they attack Dilborn
+Bug #1909: Weird Quest Flow Infidelities quest
+Bug #1910: Follower fighting with gone npc
+Bug #1911: Npcs will drown themselves
+Bug #1912: World map arrow stays static when inside a building
+Bug #1920: Ulyne Henim disappears when game is loaded inside Vas
+Bug #1922: alchemy-> potion of paralyze
+Bug #1923: "levitation magic cannot be used here" shows outside of tribunal
+Bug #1927: AI prefer melee over magic.
+Bug #1929: Tamriel Rebuilt: Named cells that lie within the overlap with Morrowind.esm are not shown
+Bug #1932: BTB - Spells 14.1 magic effects don´t overwrite the Vanilla ones but are added
+Bug #1935: Stacks of items are worth more when sold individually
+Bug #1940: Launcher does not list addon files if base game file is renamed to a different case
+Bug #1946: Mod "Tel Nechim - moved" breaks savegames
+Bug #1947: Buying/Selling price doesn't properly affect the growth of mercantile skill
+Bug #1950: followers from east empire company quest will fight each other if combat happens with anything
+Bug #1958: Journal can be scrolled indefinitely with a mouse wheel
+Bug #1959: Follower not leaving party on quest end
+Bug #1960: Key bindings not always saved correctly
+Bug #1961: Spell merchants selling racial bonus spells
+Bug #1967: segmentation fault on load saves
+Bug #1968: Jump sounds are not controlled by footsteps slider, sound weird compared to footsteps
+Bug #1970: PC suffers silently when taking damage from lava
+Bug #1971: Dwarven Sceptre collision area is not removed after killing one
+Bug #1974: Dalin/Daris Norvayne follows player indefinitely
+Bug #1975: East Empire Company faction rank breaks during Raven Rock questline
+Bug #1979: 0 strength = permanently over encumbered
+Bug #1993: Shrine blessing in Maar Gan doesn't work
+Bug #2008: Enchanted items do not recharge
+Bug #2011: Editor: OpenCS script compiler doesn't handle member variable access properly
+Bug #2016: Dagoth Ur already dead in Facility Cavern
+Bug #2017: Fighters Guild Quest: The Code Book - dialogue loop when UMP is loaded.
+Bug #2019: Animation of 'Correct UV Mudcrabs' broken
+Bug #2022: Alchemy window - Removing ingredient doesn't remove the number of ingredients
+Bug #2025: Missing mouse-over text for non affordable items
+Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell "Ruinous Keep, Great Hall"
+Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding
+Feature #471: Editor: Special case implementation for top-level window with single sub-window
+Feature #472: Editor: Sub-Window re-use settings
+Feature #704: Font colors import from fallback settings
+Feature #879: Editor: Open sub-views in a new top-level window
+Feature #932: Editor: magic effect table
+Feature #937: Editor: Path Grid table
+Feature #938: Editor: Sound Gen table
+Feature #1117: Death and LevelUp music
+Feature #1226: Editor: Request UniversalId editing from table columns
+Feature #1545: Targeting console on player
+Feature #1597: Editor: Render terrain
+Feature #1695: Editor: add column for CellRef's global variable
+Feature #1696: Editor: use ESM::Cell's RefNum counter
+Feature #1697: Redden player's vision when hit
+Feature #1856: Spellcasting for non-biped creatures
+Feature #1879: Editor: Run OpenMW with the currently edited content list
+Task #1851: Move AI temporary state out of AI packages
+Task #1865: Replace char type in records
+
+0.32.0
+
+Bug #1132: Unable to jump when facing a wall
+Bug #1341: Summoned Creatures do not immediately disappear when killed.
+Bug #1430: CharGen Revamped script does not compile
+Bug #1451: NPCs shouldn't equip weapons prior to fighting
+Bug #1461: Stopped start scripts do not restart on load
+Bug #1473: Dead NPC standing and in 2 pieces
+Bug #1482: Abilities are depleted when interrupted during casting
+Bug #1503: Behaviour of NPCs facing the player
+Bug #1506: Missing character, French edition: three-points
+Bug #1528: Inventory very slow after 2 hours
+Bug #1540: Extra arguments should be ignored for script functions
+Bug #1541: Helseth's Champion: Tribunal
+Bug #1570: Journal cannot be opened while in inventory screen
+Bug #1573: PC joins factions at random
+Bug #1576: NPCs aren't switching their weapons when out of ammo
+Bug #1579: Guards detect creatures in far distance, instead on sight
+Bug #1588: The Siege of the Skaal Village: bloodmoon
+Bug #1593: The script compiler isn't recognising some names that contain a -
+Bug #1606: Books: Question marks instead of quotation marks
+Bug #1608: Dead bodies prevent door from opening/closing.
+Bug #1609: Imperial guards in Sadrith Mora are not using their spears
+Bug #1610: The bounty number is not displayed properly with high numbers
+Bug #1620: Implement correct formula for auto-calculated NPC spells
+Bug #1630: Boats standing vertically in Vivec
+Bug #1635: Arrest dialogue is executed second time after I select "Go to jail"
+Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults?
+Bug #1641: Persuasion dialog remains after loading, possibly resulting in crash
+Bug #1644: "Goodbye" and similar options on dialogues prevents escape working properly.
+Bug #1646: PC skill stats are not updated immediately when changing equipment
+Bug #1652: Non-aggressive creature
+Bug #1653: Quickloading while the container window is open crashes the game
+Bug #1654: Priority of checks in organic containers
+Bug #1656: Inventory items merge issue when repairing
+Bug #1657: Attacked state of NPCs is not saved properly
+Bug #1660: Rank dialogue condition ignored
+Bug #1668: Game starts on day 2 instead of day 1
+Bug #1669: Critical Strikes while fighting a target who is currently fighting me
+Bug #1672: OpenCS doesn't save the projects
+Bug #1673: Fatigue decreasing by only one point when running
+Bug #1675: Minimap and localmap graphic glitches
+Bug #1676: Pressing the OK button on the travel menu cancels the travel and exits the menu
+Bug #1677: Sleeping in a rented bed is considered a crime
+Bug #1685: NPCs turn towards player even if invisible/sneaking
+Bug #1686: UI bug: cursor is clicking "world/local" map button while inventory window is closed?
+Bug #1690: Double clicking on a inventory window header doesn't close it.
+Bug #1693: Spell Absorption does not absorb shrine blessings
+Bug #1694: journal displays learned topics as quests
+Bug #1700: Sideways scroll of text boxes
+Bug #1701: Player enchanting requires player hold money, always 100% sucessful.
+Bug #1704: self-made Fortify Intelligence/Drain willpower potions are broken
+Bug #1707: Pausing the game through the esc menu will silence rain, pausing it by opening the inventory will not.
+Bug #1709: Remesa Othril is hostile to Hlaalu members
+Bug #1713: Crash on load after death
+Bug #1719: Blind effect has slight border at the edge of the screen where it is ineffective.
+Bug #1722: Crash after creating enchanted item, reloading saved game
+Bug #1723: Content refs that are stacked share the same index after unstacking
+Bug #1726: Can't finish Aengoth the Jeweler's quest : Retrieve the Scrap Metal
+Bug #1727: Targets almost always resist soultrap scrolls
+Bug #1728: Casting a soultrap spell on invalid target yields no message
+Bug #1729: Chop attack doesn't work if walking diagonally
+Bug #1732: Error handling for missing script function arguments produces weird message
+Bug #1736: Alt-tabbing removes detail from overworld map.
+Bug #1737: Going through doors with (high magnitude?) leviation will put the player high up, possibly even out of bounds.
+Bug #1739: Setting a variable on an NPC from another NPC's dialogue result sets the wrong variable
+Bug #1741: The wait dialogue doesn't black the screen out properly during waiting.
+Bug #1742: ERROR: Object 'sDifficulty' not found (const)
+Bug #1744: Night sky in Skies V.IV (& possibly v3) by SWG rendered incorrectly
+Bug #1746: Bow/marksman weapon condition does not degrade with use
+Bug #1749: Constant Battle Music
+Bug #1752: Alt-Tabbing in the character menus makes the paper doll disappear temporarily
+Bug #1753: Cost of training is not added to merchant's inventory
+Bug #1755: Disposition changes do not persist if the conversation menu is closed by purchasing training.
+Bug #1756: Caught Blight after being cured of Corprus
+Bug #1758: Crash Upon Loading New Cell
+Bug #1760: Player's Magicka is not recalculated upon drained or boosted intelligence
+Bug #1761: Equiped torches lost on reload
+Bug #1762: Your spell did not get a target. Soul trap. Gorenea Andrano
+Bug #1763: Custom Spell Magicka Cost
+Bug #1765: Azuras Star breaks on recharging item
+Bug #1767: GetPCRank did not handle ignored explicit references
+Bug #1772: Dark Brotherhood Assassins never use their Carved Ebony Dart, sticking to their melee weapon.
+Bug #1774: String table overflow also occurs when loading TheGloryRoad.esm
+Bug #1776: dagoth uthol runs in slow motion
+Bug #1778: Incorrect values in spellmaking window
+Bug #1779: Icon of Master Propylon Index is not visible
+Bug #1783: Invisible NPC after looting corpse
+Bug #1787: Health Calculation
+Bug #1788: Skeletons, ghosts etc block doors when we try to open
+Bug #1791: [MOD: LGNPC Foreign Quarter] NPC in completely the wrong place.
+Bug #1792: Potions should show more effects
+Bug #1793: Encumbrance while bartering
+Bug #1794: Fortify attribute not affecting fatigue
+Bug #1795: Too much magicka
+Bug #1796: "Off by default" torch burning
+Bug #1797: Fish too slow
+Bug #1798: Rest until healed shouldn't show with full health and magicka
+Bug #1802: Mark location moved
+Bug #1804: stutter with recent builds
+Bug #1810: attack gothens dremora doesnt agro the others.
+Bug #1811: Regression: Crash Upon Loading New Cell
+Bug #1812: Mod: "QuickChar" weird button placement
+Bug #1815: Keys show value and weight, Vanilla Morrowind's keys dont.
+Bug #1817: Persuasion results do not show using unpatched MW ESM
+Bug #1818: Quest B3_ZainabBride moves to stage 47 upon loading save while Falura Llervu is following
+Bug #1823: AI response to theft incorrect - only guards react, in vanilla everyone does.
+Bug #1829: On-Target Spells Rendered Behind Water Surface Effects
+Bug #1830: Galsa Gindu's house is on fire
+Bug #1832: Fatal Error: OGRE Exception(2:InvalidParametersException)
+Bug #1836: Attacked Guards open "fine/jail/resist"-dialogue after killing you
+Bug #1840: Infinite recursion in ActionTeleport
+Bug #1843: Escorted people change into player's cell after completion of escort stage
+Bug #1845: Typing 'j' into 'Name' fields opens the journal
+Bug #1846: Text pasted into the console still appears twice (Windows)
+Bug #1847: "setfatigue 0" doesn't render NPC unconscious
+Bug #1848: I can talk to unconscious actors
+Bug #1866: Crash when player gets killed by a creature summoned by him
+Bug #1868: Memory leaking when openmw window is minimized
+Feature #47: Magic Effects
+Feature #642: Control NPC mouth movement using current Say sound
+Feature #939: Editor: Resources tables
+Feature #961: AI Combat for magic (spells, potions and enchanted items)
+Feature #1111: Collision script instructions (used e.g. by Lava)
+Feature #1120: Command creature/humanoid magic effects
+Feature #1121: Elemental shield magic effects
+Feature #1122: Light magic effect
+Feature #1139: AI: Friendly hits
+Feature #1141: AI: combat party
+Feature #1326: Editor: Add tooltips to all graphical buttons
+Feature #1489: Magic effect Get/Mod/Set functions
+Feature #1505: Difficulty slider
+Feature #1538: Targeted scripts
+Feature #1571: Allow creating custom markers on the local map
+Feature #1615: Determine local variables from compiled scripts instead of the values in the script record
+Feature #1616: Editor: Body part record verifier
+Feature #1651: Editor: Improved keyboard navigation for scene toolbar
+Feature #1666: Script blacklisting
+Feature #1711: Including the Git revision number from the command line "--version" switch.
+Feature #1721: NPC eye blinking
+Feature #1740: Scene toolbar buttons for selecting which type of elements are rendered
+Feature #1790: Mouse wheel scrolling for the journal
+Feature #1850: NiBSPArrayController
+Task #768: On windows, settings folder should be "OpenMW", not "openmw"
+Task #908: Share keyframe data
+Task #1716: Remove defunct option for building without FFmpeg
+
+0.31.0
+
+Bug #245: Cloud direction and weather systems differ from Morrowind
+Bug #275: Local Map does not always show objects that span multiple cells
+Bug #538: Update CenterOnCell (COC) function behavior
+Bug #618: Local and World Map Textures are sometimes Black
+Bug #640: Water behaviour at night
+Bug #668: OpenMW doesn't support non-latin paths on Windows
+Bug #746: OpenMW doesn't check if the background music was already played
+Bug #747: Door is stuck if cell is left before animation finishes
+Bug #772: Disabled statics are visible on map
+Bug #829: OpenMW uses up all available vram, when playing for extended time
+Bug #869: Dead bodies don't collide with anything
+Bug #894: Various character creation issues
+Bug #897/#1369: opencs Segmentation Fault after "new" or "load"
+Bug #899: Various jumping issues
+Bug #952: Reflection effects are one frame delayed
+Bug #993: Able to interact with world during Wait/Rest dialog
+Bug #995: Dropped items can be placed inside the wall
+Bug #1008: Corpses always face up upon reentering the cell
+Bug #1035: Random colour patterns appearing in automap
+Bug #1037: Footstep volume issues
+Bug #1047: Creation of wrong links in dialogue window
+Bug #1129: Summoned creature time life duration seems infinite
+Bug #1134: Crimes can be committed against hostile NPCs
+Bug #1136: Creature run speed formula is incorrect
+Bug #1150: Weakness to Fire doesn't apply to Fire Damage in the same spell
+Bug #1155: NPCs killing each other
+Bug #1166: Bittercup script still does not work
+Bug #1178: .bsa file names are case sensitive.
+Bug #1179: Crash after trying to load game after being killed
+Bug #1180: Changing footstep sound location
+Bug #1196: Jumping not disabled when showing messageboxes
+Bug #1202: "strange" keys are not shown in binding menu, and are not saved either, but works
+Bug #1216: Broken dialog topics in russian Morrowind
+Bug #1217: Container content changes based on the current position of the mouse
+Bug #1234: Loading/saving issues with dynamic records
+Bug #1277: Text pasted into the console appears twice
+Bug #1284: Crash on New Game
+Bug #1303: It's possible to skip the chargen
+Bug #1304: Slaughterfish should not detect the player unless the player is in the water
+Bug #1311: Editor: deleting Record Filter line does not reset the filter
+Bug #1324: ERROR: ESM Error: String table overflow when loading Animated Morrowind.esp
+Bug #1328: Editor: Bogus Filter created when dragging multiple records to filter bar of non-applicable table
+Bug #1331: Walking/running sound persist after killing NPC`s that are walking/running.
+Bug #1334: Previously equipped items not shown as unequipped after attempting to sell them.
+Bug #1335: Actors ignore vertical axis when deciding to attack
+Bug #1338: Unknown toggle option for shadows
+Bug #1339: "Ashlands Region" is visible when beginning new game during "Loading Area" process
+Bug #1340: Guards prompt Player with punishment options after resisting arrest with another guard.
+Bug #1348: Regression: Bug #1098 has returned with a vengeance
+Bug #1349: [TR] TR_Data mesh tr_ex_imp_gatejamb01 cannot be activated
+Bug #1352: Disabling an ESX file does not disable dependent ESX files
+Bug #1355: CppCat Checks OpenMW
+Bug #1356: Incorrect voice type filtering for sleep interrupts
+Bug #1357: Restarting the game clears saves
+Bug #1360: Seyda Neen silk rider dialog problem
+Bug #1361: Some lights don't work
+Bug #1364: It is difficult to bind "Mouse 1" to an action in the options menu
+Bug #1370: Animation compilation mod does not work properly
+Bug #1371: SL_Pick01.nif from third party fails to load in openmw, but works in Vanilla
+Bug #1373: When stealing in front of Sellus Gravius cannot exit the dialog
+Bug #1378: Installs to /usr/local are not working
+Bug #1380: Loading a save file fail if one of the content files is disabled
+Bug #1382: "getHExact() size mismatch" crash on loading official plugin "Siege at Firemoth.esp"
+Bug #1386: Arkngthand door will not open
+Bug #1388: Segfault when modifying View Distance in Menu options
+Bug #1389: Crash when loading a save after dying
+Bug #1390: Apostrophe characters not displayed [French version]
+Bug #1391: Custom made icon background texture for magical weapons and stuff isn't scaled properly on GUI.
+Bug #1393: Coin icon during the level up dialogue are off of the background
+Bug #1394: Alt+F4 doesn't work on Win version
+Bug #1395: Changing rings switches only the last one put on
+Bug #1396: Pauldron parts aren't showing when the robe is equipped
+Bug #1402: Dialogue of some shrines have wrong button orientation
+Bug #1403: Items are floating in the air when they're dropped onto dead bodies.
+Bug #1404: Forearms are not rendered on Argonian females
+Bug #1407: Alchemy allows making potions from two of the same item
+Bug #1408: "Max sale" button gives you all the items AND all the trader's gold
+Bug #1409: Rest "Until Healed" broken for characters with stunted magicka.
+Bug #1412: Empty travel window opens while playing through start game
+Bug #1413: Save game ignores missing writing permission
+Bug #1414: The Underground 2 ESM Error
+Bug #1416: Not all splash screens in the Splash directory are used
+Bug #1417: Loading saved game does not terminate
+Bug #1419: Skyrim: Home of the Nords error
+Bug #1422: ClearInfoActor
+Bug #1423: ForceGreeting closes existing dialogue windows
+Bug #1425: Cannot load save game
+Bug #1426: Read skill books aren't stored in savegame
+Bug #1427: Useless items can be set under hotkeys
+Bug #1429: Text variables in journal
+Bug #1432: When attacking friendly NPC, the crime is reported and bounty is raised after each swing
+Bug #1435: Stealing priceless items is without punishment
+Bug #1437: Door marker at Jobasha's Rare Books is spawning PC in the air
+Bug #1440: Topic selection menu should be wider
+Bug #1441: Dropping items on the rug makes them inaccessible
+Bug #1442: When dropping and taking some looted items, bystanders consider that as a crime
+Bug #1444: Arrows and bolts are not dropped where the cursor points
+Bug #1445: Security trainers offering acrobatics instead
+Bug #1447: Character dash not displayed, French edition
+Bug #1448: When the player is killed by the guard while having a bounty on his head, the guard dialogue opens over and over instead of loading dialogue
+Bug #1454: Script error in SkipTutorial
+Bug #1456: Bad lighting when using certain Morrowind.ini generated by MGE
+Bug #1457: Heart of Lorkan comes after you when attacking it
+Bug #1458: Modified Keybindings are not remembered
+Bug #1459: Dura Gra-Bol doesn't respond to PC attack
+Bug #1462: Interior cells not loaded with Morrowind Patch active
+Bug #1469: Item tooltip should show the base value, not real value
+Bug #1477: Death count is not stored in savegame
+Bug #1478: AiActivate does not trigger activate scripts
+Bug #1481: Weapon not rendered when partially submerged in water
+Bug #1483: Enemies are attacking even while dying
+Bug #1486: ESM Error: Don't know what to do with INFO
+Bug #1490: Arrows shot at PC can end up in inventory
+Bug #1492: Monsters respawn on top of one another
+Bug #1493: Dialogue box opens with follower NPC even if NPC is dead
+Bug #1494: Paralysed cliffracers remain airbourne
+Bug #1495: Dialogue box opens with follower NPC even the game is paused
+Bug #1496: GUI messages are not cleared when loading another saved game
+Bug #1499: Underwater sound sometimes plays when transitioning from interior.
+Bug #1500: Targetted spells and water.
+Bug #1502: Console error message on info refusal
+Bug #1507: Bloodmoon MQ The Ritual of Beasts: Can't remove the arrow
+Bug #1508: Bloodmoon: Fort Frostmoth, cant talk with Carnius Magius
+Bug #1516: PositionCell doesn't move actors to current cell
+Bug #1518: ForceGreeting broken for explicit references
+Bug #1522: Crash after attempting to play non-music file
+Bug #1523: World map empty after loading interior save
+Bug #1524: Arrows in waiting/resting dialog act like minimum and maximum buttons
+Bug #1525: Werewolf: Killed NPC's don't fill werewolfs hunger for blood
+Bug #1527: Werewolf: Detect life detects wrong type of actor
+Bug #1529: OpenMW crash during "the shrine of the dead" mission (tribunal)
+Bug #1530: Selected text in the console has the same color as the background
+Bug #1539: Barilzar's Mazed Band: Tribunal
+Bug #1542: Looping taunts from NPC`s after death: Tribunal
+Bug #1543: OpenCS crash when using drag&drop in script editor
+Bug #1547: Bamz-Amschend: Centurion Archers combat problem
+Bug #1548: The Missing Hand: Tribunal
+Bug #1549: The Mad God: Tribunal, Dome of Serlyn
+Bug #1557: A bounty is calculated from actual item cost
+Bug #1562: Invisible terrain on top of Red Mountain
+Bug #1564: Cave of the hidden music: Bloodmoon
+Bug #1567: Editor: Deleting of referenceables does not work
+Bug #1568: Picking up a stack of items and holding the enter key and moving your mouse around paints a bunch of garbage on screen.
+Bug #1574: Solstheim: Drauger cant inflict damage on player
+Bug #1578: Solstheim: Bonewolf running animation not working
+Bug #1585: Particle effects on PC are stopped when paralyzed
+Bug #1589: Tribunal: Crimson Plague quest does not update when Gedna Relvel is killed
+Bug #1590: Failed to save game: compile error
+Bug #1598: Segfault when making Drain/Fortify Skill spells
+Bug #1599: Unable to switch to fullscreen
+Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed
+Bug #1618: Death notice fails to show up
+Bug #1628: Alt+Tab Segfault
+Feature #32: Periodic Cleanup/Refill
+Feature #41: Precipitation and weather particles
+Feature #568: Editor: Configuration setup
+Feature #649: Editor: Threaded loading
+Feature #930: Editor: Cell record saving
+Feature #934: Editor: Body part table
+Feature #935: Editor: Enchantment effect table
+Feature #1162: Dialogue merging
+Feature #1174: Saved Game: add missing creature state
+Feature #1177: Saved Game: fog of war state
+Feature #1312: Editor: Combat/Magic/Stealth values for creatures are not displayed
+Feature #1314: Make NPCs and creatures fight each other
+Feature #1315: Crime: Murder
+Feature #1321: Sneak skill enhancements
+Feature #1323: Handle restocking items
+Feature #1332: Saved Game: levelled creatures
+Feature #1347: modFactionReaction script instruction
+Feature #1362: Animated main menu support
+Feature #1433: Store walk/run toggle
+Feature #1449: Use names instead of numbers for saved game files and folders
+Feature #1453: Adding Delete button to the load menu
+Feature #1460: Enable Journal screen while in dialogue
+Feature #1480: Play Battle music when in combat
+Feature #1501: Followers unable to fast travel with you
+Feature #1520: Disposition and distance-based aggression/ShouldAttack
+Feature #1595: Editor: Object rendering in cells
+Task #940: Move license to locations where applicable
+Task #1333: Remove cmake git tag reading
+Task #1566: Editor: Object rendering refactoring
+
+0.30.0
+
+Bug #416: Extreme shaking can occur during cell transitions while moving
+Bug #1003: Province Cyrodiil: Ogre Exception in Stirk
+Bug #1071: Crash when given a non-existent content file
+Bug #1080: OpenMW allows resting/using a bed while in combat
+Bug #1097: Wrong punishment for stealing in Census and Excise Office at the start of a new game
+Bug #1098: Unlocked evidence chests should get locked after new evidence is put into them
+Bug #1099: NPCs that you attacked still fight you after you went to jail/paid your fine
+Bug #1100: Taking items from a corpse is considered stealing
+Bug #1126: Some creatures can't get close enough to attack
+Bug #1144: Killed creatures seem to die again each time player transitions indoors/outdoors
+Bug #1181: loading a saved game does not reset the player control status
+Bug #1185: Collision issues in Addamasartus
+Bug #1187: Athyn Sarethi mission, rescuing varvur sarethi from the doesnt end the mission
+Bug #1189: Crash when entering interior cell "Gnisis, Arvs-Drelen"
+Bug #1191: Picking up papers without inventory in new game
+Bug #1195: NPCs do not equip torches in certain interiors
+Bug #1197: mouse wheel makes things scroll too fast
+Bug #1200: door blocked by monsters
+Bug #1201: item's magical charges are only refreshed when they are used
+Bug #1203: Scribs do not defend themselves
+Bug #1204: creatures life is not empty when they are dead
+Bug #1205: armor experience does not progress when hits are taken
+Bug #1206: blood particules always red. Undeads and mechanicals should have a different one.
+Bug #1209: Tarhiel never falls
+Bug #1210: journal adding script is ran again after having saved/loaded
+Bug #1224: Names of custom classes are not properly handled in save games
+Bug #1227: Editor: Fixed case handling for broken localised versions of Morrowind.esm
+Bug #1235: Indoors walk stutter
+Bug #1236: Aborting intro movie brings up the menu
+Bug #1239: NPCs get stuck when walking past each other
+Bug #1240: BTB - Settings 14.1 and Health Bar.
+Bug #1241: BTB - Character and Khajiit Prejudice
+Bug #1248: GUI Weapon icon is changed to hand-to-hand after save load
+Bug #1254: Guild ranks do not show in dialogue
+Bug #1255: When opening a container and selecting "Take All", the screen flashes blue
+Bug #1260: Level Up menu doesn't show image when using a custom class
+Bug #1265: Quit Menu Has Misaligned Buttons
+Bug #1270: Active weapon icon is not updated when weapon is repaired
+Bug #1271: NPC Stuck in hovering "Jumping" animation
+Bug #1272: Crash when attempting to load Big City esm file.
+Bug #1276: Editor: Dropping a region into the filter of a cell subview fails
+Bug #1286: Dialogue topic list clips with window frame
+Bug #1291: Saved game: store faction membership
+Bug #1293: Pluginless Khajiit Head Pack by ashiraniir makes OpenMW close.
+Bug #1294: Pasting in console adds text to end, not at cursor
+Bug #1295: Conversation loop when asking about "specific place" in Vivec
+Bug #1296: Caius doesn't leave at start of quest "Mehra Milo and the Lost Prophecies"
+Bug #1297: Saved game: map markers
+Bug #1302: ring_keley script causes vector::_M_range_check exception
+Bug #1309: Bug on "You violated the law" dialog
+Bug #1319: Creatures sometimes rendered incorrectly
+Feature #50: Ranged Combat
+Feature #58: Sneaking Skill
+Feature #73: Crime and Punishment
+Feature #135: Editor: OGRE integration
+Feature #541: Editor: Dialogue Sub-Views
+Feature #853: Editor: Rework User Settings
+Feature #944: Editor: lighting modes
+Feature #945: Editor: Camera navigation mode
+Feature #953: Trader gold
+Feature #1140: AI: summoned creatures
+Feature #1142: AI follow: Run stance
+Feature #1154: Not all NPCs get aggressive when one is attacked
+Feature #1169: Terrain threading
+Feature #1172: Loading screen and progress bars during saved/loading game
+Feature #1173: Saved Game: include weather state
+Feature #1207: Class creation form does not remember
+Feature #1220: Editor: Preview Subview
+Feature #1223: Saved Game: Local Variables
+Feature #1229: Quicksave, quickload, autosave
+Feature #1230: Deleting saves
+Feature #1233: Bribe gold is placed into NPCs inventory
+Feature #1252: Saved Game: quick key bindings
+Feature #1273: Editor: Region Map context menu
+Feature #1274: Editor: Region Map drag & drop
+Feature #1275: Editor: Scene subview drop
+Feature #1282: Non-faction member crime recognition.
+Feature #1289: NPCs return to default position
+Task #941: Remove unused cmake files
+
+0.29.0
+
+Bug #556: Video soundtrack not played when music volume is set to zero
+Bug #829: OpenMW uses up all available vram, when playing for extended time
+Bug #848: Wrong amount of footsteps playing in 1st person
+Bug #888: Ascended Sleepers have movement issues
+Bug #892: Explicit references are allowed on all script functions
+Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly
+Bug #1009: Lake Fjalding AI related slowdown.
+Bug #1041: Music playback issues on OS X >= 10.9
+Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window
+Bug #1060: Some message boxes are cut off at the bottom
+Bug #1062: Bittercup script does not work ('end' variable)
+Bug #1074: Inventory paperdoll obscures armour rating
+Bug #1077: Message after killing an essential NPC disappears too fast
+Bug #1078: "Clutterbane" shows empty charge bar
+Bug #1083: UndoWerewolf fails
+Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered
+Bug #1090: Start scripts fail when going to a non-predefined cell
+Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed.
+Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior
+Bug #1105: Magicka is depleted when using uncastable spells
+Bug #1106: Creatures should be able to run
+Bug #1107: TR cliffs have way too huge collision boxes in OpenMW
+Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded.
+Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop)
+Bug #1115: Memory leak when spying on Fargoth
+Bug #1137: Script execution fails (drenSlaveOwners script)
+Bug #1143: Mehra Milo quest (vivec informants) is broken
+Bug #1145: Issues with moving gold between inventory and containers
+Bug #1146: Issues with picking up stacks of gold
+Bug #1147: Dwemer Crossbows are held incorrectly
+Bug #1158: Armor rating should always stay below inventory mannequin
+Bug #1159: Quick keys can be set during character generation
+Bug #1160: Crash on equip lockpick when
+Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file
+Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file
+Feature #30: Loading/Saving (still missing a few parts)
+Feature #101: AI Package: Activate
+Feature #103: AI Package: Follow, FollowCell
+Feature #138: Editor: Drag & Drop
+Feature #428: Player death
+Feature #505: Editor: Record Cloning
+Feature #701: Levelled creatures
+Feature #708: Improved Local Variable handling
+Feature #709: Editor: Script verifier
+Feature #764: Missing journal backend features
+Feature #777: Creature weapons/shields
+Feature #789: Editor: Referenceable record verifier
+Feature #924: Load/Save GUI (still missing loading screen and progress bars)
+Feature #946: Knockdown
+Feature #947: Decrease fatigue when running, swimming and attacking
+Feature #956: Melee Combat: Blocking
+Feature #957: Area magic
+Feature #960: Combat/AI combat for creatures
+Feature #962: Combat-Related AI instructions
+Feature #1075: Damage/Restore skill/attribute magic effects
+Feature #1076: Soultrap magic effect
+Feature #1081: Disease contraction
+Feature #1086: Blood particles
+Feature #1092: Interrupt resting
+Feature #1101: Inventory equip scripts
+Feature #1116: Version/Build number in Launcher window
+Feature #1119: Resistance/weakness to normal weapons magic effect
+Feature #1123: Slow Fall magic effect
+Feature #1130: Auto-calculate spells
+Feature #1164: Editor: Case-insensitive sorting in tables
+
+0.28.0
+
+Bug #399: Inventory changes are not visible immediately
+Bug #417: Apply weather instantly when teleporting
+Bug #566: Global Map position marker not updated for interior cells
+Bug #712: Looting corpse delay
+Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod
+Bug #805: Two TR meshes appear black (v0.24RC)
+Bug #841: Third-person activation distance taken from camera rather than head
+Bug #845: NPCs hold torches during the day
+Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24
+Bug #856: Maormer race by Mac Kom - The heads are way up
+Bug #864: Walk locks during loading in 3rd person
+Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog
+Bug #882: Hircine's Ring doesn't always work
+Bug #909: [Tamriel Rebuilt] crashes in Akamora
+Bug #922: Launcher writing merged openmw.cfg files
+Bug #943: Random magnitude should be calculated per effect
+Bug #948: Negative fatigue level should be allowed
+Bug #949: Particles in world space
+Bug #950: Hard crash on x64 Linux running --new-game (on startup)
+Bug #951: setMagicka and setFatigue have no effect
+Bug #954: Problem with equipping inventory items when using a keyboard shortcut
+Bug #955: Issues with equipping torches
+Bug #966: Shield is visible when casting spell
+Bug #967: Game crashes when equipping silver candlestick
+Bug #970: Segmentation fault when starting at Bal Isra
+Bug #977: Pressing down key in console doesn't go forward in history
+Bug #979: Tooltip disappears when changing inventory
+Bug #980: Barter: item category is remembered, but not shown
+Bug #981: Mod: replacing model has wrong position/orientation
+Bug #982: Launcher: Addon unchecking is not saved
+Bug #983: Fix controllers to affect objects attached to the base node
+Bug #985: Player can talk to NPCs who are in combat
+Bug #989: OpenMW crashes when trying to include mod with capital .ESP
+Bug #991: Merchants equip items with harmful constant effect enchantments
+Bug #994: Don't cap skills/attributes when set via console
+Bug #998: Setting the max health should also set the current health
+Bug #1005: Torches are visible when casting spells and during hand to hand combat.
+Bug #1006: Many NPCs have 0 skill
+Bug #1007: Console fills up with text
+Bug #1013: Player randomly loses health or dies
+Bug #1014: Persuasion window is not centered in maximized window
+Bug #1015: Player status window scroll state resets on status change
+Bug #1016: Notification window not big enough for all skill level ups
+Bug #1020: Saved window positions are not rescaled appropriately on resolution change
+Bug #1022: Messages stuck permanently on screen when they pile up
+Bug #1023: Journals doesn't open
+Bug #1026: Game loses track of torch usage.
+Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level
+Bug #1029: Quick keys menu: Select compatible replacement when tool used up
+Bug #1042: TES3 header data wrong encoding
+Bug #1045: OS X: deployed OpenCS won't launch
+Bug #1046: All damaged weaponry is worth 1 gold
+Bug #1048: Links in "locked" dialogue are still clickable
+Bug #1052: Using color codes when naming your character actually changes the name's color
+Bug #1054: Spell effects not visible in front of water
+Bug #1055: Power-Spell animation starts even though you already casted it that day
+Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability
+Bug #1063: Crash upon checking out game start ship area in Seyda Neen
+Bug #1064: openmw binaries link to unnecessary libraries
+Bug #1065: Landing from a high place in water still causes fall damage
+Bug #1072: Drawing weapon increases torch brightness
+Bug #1073: Merchants sell stacks of gold
+Feature #43: Visuals for Magic Effects
+Feature #51: Ranged Magic
+Feature #52: Touch Range Magic
+Feature #53: Self Range Magic
+Feature #54: Spell Casting
+Feature #70: Vampirism
+Feature #100: Combat AI
+Feature #171: Implement NIF record NiFlipController
+Feature #410: Window to restore enchanted item charge
+Feature #647: Enchanted item glow
+Feature #723: Invisibility/Chameleon magic effects
+Feature #737: Resist Magicka magic effect
+Feature #758: GetLOS
+Feature #926: Editor: Info-Record tables
+Feature #958: Material controllers
+Feature #959: Terrain bump, specular, & parallax mapping
+Feature #990: Request: unlock mouse when in any menu
+Feature #1018: Do not allow view mode switching while performing an action
+Feature #1027: Vertex morph animation (NiGeomMorpherController)
+Feature #1031: Handle NiBillboardNode
+Feature #1051: Implement NIF texture slot DarkTexture
+Task #873: Unify OGRE initialisation
+
+0.27.0
+
+Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp
+Bug #794: incorrect display of decimal numbers
+Bug #840: First-person sneaking camera height
+Bug #887: Ambient sounds playing while paused
+Bug #902: Problems with Polish character encoding
+Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key
+Bug #910: Some CDs not working correctly with Unshield installer
+Bug #917: Quick character creation plugin does not work
+Bug #918: Fatigue does not refill
+Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2)
+Feature #57: Acrobatics Skill
+Feature #462: Editor: Start Dialogue
+Feature #546: Modify ESX selector to handle new content file scheme
+Feature #588: Editor: Adjust name/path of edited content files
+Feature #644: Editor: Save
+Feature #710: Editor: Configure script compiler context
+Feature #790: God Mode
+Feature #881: Editor: Allow only one instance of OpenCS
+Feature #889: Editor: Record filtering
+Feature #895: Extinguish torches
+Feature #898: Breath meter enhancements
+Feature #901: Editor: Default record filter
+Feature #913: Merge --master and --plugin switches
+
+0.26.0
+
+Bug #274: Inconsistencies in the terrain
+Bug #557: Already-dead NPCs do not equip clothing/items.
+Bug #592: Window resizing
+Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren)
+Bug #664: Heart of lorkhan acts like a dead body (container)
+Bug #767: Wonky ramp physics & water
+Bug #780: Swimming out of water
+Bug #792: Wrong ground alignment on actors when no clipping
+Bug #796: Opening and closing door sound issue
+Bug #797: No clipping hinders opening and closing of doors
+Bug #799: sliders in enchanting window
+Bug #838: Pressing key during startup procedure freezes the game
+Bug #839: Combat/magic stances during character creation
+Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment
+Bug #844: Resting "until healed" option given even with full stats
+Bug #846: Equipped torches are invisible.
+Bug #847: Incorrect formula for autocalculated NPC initial health
+Bug #850: Shealt weapon sound plays when leaving magic-ready stance
+Bug #852: Some boots do not produce footstep sounds
+Bug #860: FPS bar misalignment
+Bug #861: Unable to print screen
+Bug #863: No sneaking and jumping at the same time
+Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start.
+Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance.
+Bug #868: Idle animations are repeated
+Bug #874: Underwater swimming close to the ground is jerky
+Bug #875: Animation problem while swimming on the surface and looking up
+Bug #876: Always a starting upper case letter in the inventory
+Bug #878: Active spell effects don't update the layout properly when ended
+Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load
+Bug #896: New game sound issue
+Feature #49: Melee Combat
+Feature #71: Lycanthropy
+Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList
+Feature #622: Multiple positions for inventory window
+Feature #627: Drowning
+Feature #786: Allow the 'Activate' key to close the countdialog window
+Feature #798: Morrowind installation via Launcher (Linux/Max OS only)
+Feature #851: First/Third person transitions with mouse wheel
+Task #689: change PhysicActor::enableCollisions
+Task #707: Reorganise Compiler
+
+0.25.0
+
+Bug #411: Launcher crash on OS X < 10.8
+Bug #604: Terrible performance drop in the Census and Excise Office.
+Bug #676: Start Scripts fail to load
+Bug #677: OpenMW does not accept script names with -
+Bug #766: Extra space in front of topic links
+Bug #793: AIWander Isn't Being Passed The Repeat Parameter
+Bug #795: Sound playing with drawn weapon and crossing cell-border
+Bug #800: can't select weapon for enchantment
+Bug #801: Player can move while over-encumbered
+Bug #802: Dead Keys not working
+Bug #808: mouse capture
+Bug #809: ini Importer does not work without an existing cfg file
+Bug #812: Launcher will run OpenMW with no ESM or ESP selected
+Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected
+Bug #817: Dead NPCs and Creatures still have collision boxes
+Bug #820: Incorrect sorting of answers (Dialogue)
+Bug #826: mwinimport dumps core when given an unknown parameter
+Bug #833: getting stuck in door
+Bug #835: Journals/books not showing up properly.
+Feature #38: SoundGen
+Feature #105: AI Package: Wander
+Feature #230: 64-bit compatibility for OS X
+Feature #263: Hardware mouse cursors
+Feature #449: Allow mouse outside of window while paused
+Feature #736: First person animations
+Feature #750: Using mouse wheel in third person mode
+Feature #822: Autorepeat for slider buttons
+
+0.24.0
+
+Bug #284: Book's text misalignment
+Bug #445: Camera able to get slightly below floor / terrain
+Bug #582: Seam issue in Red Mountain
+Bug #632: Journal Next Button shows white square
+Bug #653: IndexedStore ignores index
+Bug #694: Parser does not recognize float values starting with .
+Bug #699: Resource handling broken with Ogre 1.9 trunk
+Bug #718: components/esm/loadcell is using the mwworld subsystem
+Bug #729: Levelled item list tries to add nonexistent item
+Bug #730: Arrow buttons in the settings menu do not work.
+Bug #732: Erroneous behavior when binding keys
+Bug #733: Unclickable dialogue topic
+Bug #734: Book empty line problem
+Bug #738: OnDeath only works with implicit references
+Bug #740: Script compiler fails on scripts with special names
+Bug #742: Wait while no clipping
+Bug #743: Problem with changeweather console command
+Bug #744: No wait dialogue after starting a new game
+Bug #748: Player is not able to unselect objects with the console
+Bug #751: AddItem should only spawn a message box when called from dialogue
+Bug #752: The enter button has several functions in trade and looting that is not impelemted.
+Bug #753: Fargoth's Ring Quest Strange Behavior
+Bug #755: Launcher writes duplicate lines into settings.cfg
+Bug #759: Second quest in mages guild does not work
+Bug #763: Enchantment cast cost is wrong
+Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly
+Bug #773: AIWander Isn't Being Passed The Correct idle Values
+Bug #778: The journal can be opened at the start of a new game
+Bug #779: Divayth Fyr starts as dead
+Bug #787: "Batch count" on detailed FPS counter gets cut-off
+Bug #788: chargen scroll layout does not match vanilla
+Feature #60: Atlethics Skill
+Feature #65: Security Skill
+Feature #74: Interaction with non-load-doors
+Feature #98: Render Weapon and Shield
+Feature #102: AI Package: Escort, EscortCell
+Feature #182: Advanced Journal GUI
+Feature #288: Trading enhancements
+Feature #405: Integrate "new game" into the menu
+Feature #537: Highlight dialogue topic links
+Feature #658: Rotate, RotateWorld script instructions and local rotations
+Feature #690: Animation Layering
+Feature #722: Night Eye/Blind magic effects
+Feature #735: Move, MoveWorld script instructions.
+Feature #760: Non-removable corpses
+
+0.23.0
+
+Bug #522: Player collides with placeable items
+Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open
+Bug #561: Tooltip word wrapping delay
+Bug #578: Bribing works incorrectly
+Bug #601: PositionCell fails on negative coordinates
+Bug #606: Some NPCs hairs not rendered with Better Heads addon
+Bug #609: Bad rendering of bone boots
+Bug #613: Messagebox causing assert to fail
+Bug #631: Segfault on shutdown
+Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard
+Bug #635: Scale NPCs depending on race
+Bug #643: Dialogue Race select function is inverted
+Bug #646: Twohanded weapons don't work properly
+Bug #654: Crash when dropping objects without a collision shape
+Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell
+Bug #660: "g" in "change" cut off in Race Menu
+Bug #661: Arrille sells me the key to his upstairs room
+Bug #662: Day counter starts at 2 instead of 1
+Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur
+Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window.
+Bug #666: Looking up/down problem
+Bug #667: Active effects border visible during loading
+Bug #669: incorrect player position at new game start
+Bug #670: race selection menu: sex, face and hair left button not totally clickable
+Bug #671: new game: player is naked
+Bug #674: buying or selling items doesn't change amount of gold
+Bug #675: fatigue is not set to its maximum when starting a new game
+Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly
+Bug #680: different gold coins in Tel Mara
+Bug #682: Race menu ignores playable flag for some hairs and faces
+Bug #685: Script compiler does not accept ":" after a function name
+Bug #688: dispose corpse makes cross-hair to disappear
+Bug #691: Auto equipping ignores equipment conditions
+Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder
+Bug #696: Draugr incorrect head offset
+Bug #697: Sail transparency issue
+Bug #700: "On the rocks" mod does not load its UV coordinates correctly.
+Bug #702: Some race mods don't work
+Bug #711: Crash during character creation
+Bug #715: Growing Tauryon
+Bug #725: Auto calculate stats
+Bug #728: Failure to open container and talk dialogue
+Bug #731: Crash with Mush-Mere's "background" topic
+Feature #55/657: Item Repairing
+Feature #62/87: Enchanting
+Feature #99: Pathfinding
+Feature #104: AI Package: Travel
+Feature #129: Levelled items
+Feature #204: Texture animations
+Feature #239: Fallback-Settings
+Feature #535: Console object selection improvements
+Feature #629: Add levelup description in levelup layout dialog
+Feature #630: Optional format subrecord in (tes3) header
+Feature #641: Armor rating
+Feature #645: OnDeath script function
+Feature #683: Companion item UI
+Feature #698: Basic Particles
+Task #648: Split up components/esm/loadlocks
+Task #695: mwgui cleanup
+
+0.22.0
+
+Bug #311: Potential infinite recursion in script compiler
+Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit.
+Bug #382: Weird effect in 3rd person on water
+Bug #387: Always use detailed shape for physics raycasts
+Bug #420: Potion/ingredient effects do not stack
+Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips
+Bug #434/Bug #605: Object movement between cells not properly implemented
+Bug #502: Duplicate player collision model at origin
+Bug #509: Dialogue topic list shifts inappropriately
+Bug #513: Sliding stairs
+Bug #515: Launcher does not support non-latin strings
+Bug #525: Race selection preview camera wrong position
+Bug #526: Attributes / skills should not go below zero
+Bug #529: Class and Birthsign menus options should be preselected
+Bug #530: Lock window button graphic missing
+Bug #532: Missing map menu graphics
+Bug #545: ESX selector does not list ESM files properly
+Bug #547: Global variables of type short are read incorrectly
+Bug #550: Invisible meshes collision and tooltip
+Bug #551: Performance drop when loading multiple ESM files
+Bug #552: Don't list CG in options if it is not available
+Bug #555: Character creation windows "OK" button broken
+Bug #558: Segmentation fault when Alt-tabbing with console opened
+Bug #559: Dialog window should not be available before character creation is finished
+Bug #560: Tooltip borders should be stretched
+Bug #562: Sound should not be played when an object cannot be picked up
+Bug #565: Water animation speed + timescale
+Bug #572: Better Bodies' textures don't work
+Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod)
+Bug #574: Moving left/right should not cancel auto-run
+Bug #575: Crash entering the Chamber of Song
+Bug #576: Missing includes
+Bug #577: Left Gloves Addon causes ESMReader exception
+Bug #579: Unable to open container "Kvama Egg Sack"
+Bug #581: Mimicking vanilla Morrowind water
+Bug #583: Gender not recognized
+Bug #586: Wrong char gen behaviour
+Bug #587: "End" script statements with spaces don't work
+Bug #589: Closing message boxes by pressing the activation key
+Bug #590: Ugly Dagoth Ur rendering
+Bug #591: Race selection issues
+Bug #593: Persuasion response should be random
+Bug #595: Footless guard
+Bug #599: Waterfalls are invisible from a certain distance
+Bug #600: Waterfalls rendered incorrectly, cut off by water
+Bug #607: New beast bodies mod crashes
+Bug #608: Crash in cell "Mournhold, Royal Palace"
+Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt
+Bug #613: Messagebox causing assert to fail
+Bug #615: Meshes invisible from above water
+Bug #617: Potion effects should be hidden until discovered
+Bug #619: certain moss hanging from tree has rendering bug
+Bug #621: Batching bloodmoon's trees
+Bug #623: NiMaterialProperty alpha unhandled
+Bug #628: Launcher in latest master crashes the game
+Bug #633: Crash on startup: Better Heads
+Bug #636: Incorrect Char Gen Menu Behavior
+Feature #29: Allow ESPs and multiple ESMs
+Feature #94: Finish class selection-dialogue
+Feature #149: Texture Alphas
+Feature #237: Run Morrowind-ini importer from launcher
+Feature #286: Update Active Spell Icons
+Feature #334: Swimming animation
+Feature #335: Walking animation
+Feature #360: Proper collision shapes for NPCs and creatures
+Feature #367: Lights that behave more like original morrowind implementation
+Feature #477: Special local scripting variables
+Feature #528: Message boxes should close when enter is pressed under certain conditions.
+Feature #543: Add bsa files to the settings imported by the ini importer
+Feature #594: coordinate space and utility functions
+Feature #625: Zoom in vanity mode
+Task #464: Refactor launcher ESX selector into a re-usable component
+Task #624: Unified implementation of type-variable sub-records
+
+0.21.0
+
+Bug #253: Dialogs don't work for Russian version of Morrowind
+Bug #267: Activating creatures without dialogue can still activate the dialogue GUI
+Bug #354: True flickering lights
+Bug #386: The main menu's first entry is wrong (in french)
+Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations
+Bug #495: Activation Range
+Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned
+Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available
+Bug #500: Disposition for most NPCs is 0/100
+Bug #501: Getdisposition command wrongly returns base disposition
+Bug #506: Journal UI doesn't update anymore
+Bug #507: EnableRestMenu is not a valid command - change it to EnableRest
+Bug #508: Crash in Ald Daedroth Shrine
+Bug #517: Wrong price calculation when untrading an item
+Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin
+Bug #524: Beast races are able to wear shoes
+Bug #527: Background music fails to play
+Bug #533: The arch at Gnisis entrance is not displayed
+Bug #534: Terrain gets its correct shape only some time after the cell is loaded
+Bug #536: The same entry can be added multiple times to the journal
+Bug #539: Race selection is broken
+Bug #544: Terrain normal map corrupt when the map is rendered
+Feature #39: Video Playback
+Feature #151: ^-escape sequences in text output
+Feature #392: Add AI related script functions
+Feature #456: Determine required ini fallback values and adjust the ini importer accordingly
+Feature #460: Experimental DirArchives improvements
+Feature #540: Execute scripts of objects in containers/inventories in active cells
+Task #401: Review GMST fixing
+Task #453: Unify case smashing/folding
+Task #512: Rewrite utf8 component
+
+0.20.0
+
+Bug #366: Changing the player's race during character creation does not change the look of the player character
+Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell
+Bug #437: Stop animations when paused
+Bug #438: Time displays as "0 a.m." when it should be "12 a.m."
+Bug #439: Text in "name" field of potion/spell creation window is persistent
+Bug #440: Starting date at a new game is off by one day
+Bug #442: Console window doesn't close properly sometimes
+Bug #448: Do not break container window formatting when item names are very long
+Bug #458: Topics sometimes not automatically added to known topic list
+Bug #476: Auto-Moving allows player movement after using DisablePlayerControls
+Bug #478: After sleeping in a bed the rest dialogue window opens automtically again
+Bug #492: On creating potions the ingredients are removed twice
+Feature #63: Mercantile skill
+Feature #82: Persuasion Dialogue
+Feature #219: Missing dialogue filters/functions
+Feature #369: Add a FailedAction
+Feature #377: Select head/hair on character creation
+Feature #391: Dummy AI package classes
+Feature #435: Global Map, 2nd Layer
+Feature #450: Persuasion
+Feature #457: Add more script instructions
+Feature #474: update the global variable pcrace when the player's race is changed
+Task #158: Move dynamically generated classes from Player class to World Class
+Task #159: ESMStore rework and cleanup
+Task #163: More Component Namespace Cleanup
+Task #402: Move player data from MWWorld::Player to the player's NPC record
+Task #446: Fix no namespace in BulletShapeLoader
+
+0.19.0
+
+Bug #374: Character shakes in 3rd person mode near the origin
+Bug #404: Gamma correct rendering
+Bug #407: Shoes of St. Rilm do not work
+Bug #408: Rugs has collision even if they are not supposed to
+Bug #412: Birthsign menu sorted incorrectly
+Bug #413: Resolutions presented multiple times in launcher
+Bug #414: launcher.cfg file stored in wrong directory
+Bug #415: Wrong esm order in openmw.cfg
+Bug #418: Sound listener position updates incorrectly
+Bug #423: wrong usage of "Version" entry in openmw.desktop
+Bug #426: Do not use hardcoded splash images
+Bug #431: Don't use markers for raycast
+Bug #432: Crash after picking up items from an NPC
+Feature #21/#95: Sleeping/resting
+Feature #61: Alchemy Skill
+Feature #68: Death
+Feature #69/#86: Spell Creation
+Feature #72/#84: Travel
+Feature #76: Global Map, 1st Layer
+Feature #120: Trainer Window
+Feature #152: Skill Increase from Skill Books
+Feature #160: Record Saving
+Task #400: Review GMST access
+
+0.18.0
+
+Bug #310: Button of the "preferences menu" are too small
+Bug #361: Hand-to-hand skill is always 100
+Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to
+Bug #372: playSound3D uses original coordinates instead of current coordinates.
+Bug #373: Static OGRE build faulty
+Bug #375: Alt-tab toggle view
+Bug #376: Screenshots are disable
+Bug #378: Exception when drinking self-made potions
+Bug #380: Cloth visibility problem
+Bug #384: Weird character on doors tooltip.
+Bug #398: Some objects do not collide in MW, but do so in OpenMW
+Feature #22: Implement level-up
+Feature #36: Hide Marker
+Feature #88: Hotkey Window
+Feature #91: Level-Up Dialogue
+Feature #118: Keyboard and Mouse-Button bindings
+Feature #119: Spell Buying Window
+Feature #133: Handle resources across multiple data directories
+Feature #134: Generate a suitable default-value for --data-local
+Feature #292: Object Movement/Creation Script Instructions
+Feature #340: AIPackage data structures
+Feature #356: Ingredients use
+Feature #358: Input system rewrite
+Feature #370: Target handling in actions
+Feature #379: Door markers on the local map
+Feature #389: AI framework
+Feature #395: Using keys to open doors / containers
+Feature #396: Loading screens
+Feature #397: Inventory avatar image and race selection head preview
+Task #339: Move sounds into Action
+
+0.17.0
+
+Bug #225: Valgrind reports about 40MB of leaked memory
+Bug #241: Some physics meshes still don't match
+Bug #248: Some textures are too dark
+Bug #300: Dependency on proprietary CG toolkit
+Bug #302: Some objects don't collide although they should
+Bug #308: Freeze in Balmora, Meldor: Armorer
+Bug #313: openmw without a ~/.config/openmw folder segfault.
+Bug #317: adding non-existing spell via console locks game
+Bug #318: Wrong character normals
+Bug #341: Building with Ogre Debug libraries does not use debug version of plugins
+Bug #347: Crash when running openmw with --start="XYZ"
+Bug #353: FindMyGUI.cmake breaks path on Windows
+Bug #359: WindowManager throws exception at destruction
+Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation
+Feature #33: Allow objects to cross cell-borders
+Feature #59: Dropping Items (replaced stopgap implementation with a proper one)
+Feature #93: Main Menu
+Feature #96/329/330/331/332/333: Player Control
+Feature #180: Object rotation and scaling.
+Feature #272: Incorrect NIF material sharing
+Feature #314: Potion usage
+Feature #324: Skill Gain
+Feature #342: Drain/fortify dynamic stats/attributes magic effects
+Feature #350: Allow console only script instructions
+Feature #352: Run scripts in console on startup
+Task #107: Refactor mw*-subsystems
+Task #325: Make CreatureStats into a class
+Task #345: Use Ogre's animation system
+Task #351: Rewrite Action class to support automatic sound playing
+
+0.16.0
+
+Bug #250: OpenMW launcher erratic behaviour
+Bug #270: Crash because of underwater effect on OS X
+Bug #277: Auto-equipping in some cells not working
+Bug #294: Container GUI ignores disabled inventory menu
+Bug #297: Stats review dialog shows all skills and attribute values as 0
+Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses
+Bug #299: Crash in World::disable
+Bug #306: Non-existent ~/.config/openmw "crash" the launcher.
+Bug #307: False "Data Files" location make the launcher "crash"
+Feature #81: Spell Window
+Feature #85: Alchemy Window
+Feature #181: Support for x.y script syntax
+Feature #242: Weapon and Spell icons
+Feature #254: Ingame settings window
+Feature #293: Allow "stacking" game modes
+Feature #295: Class creation dialog tooltips
+Feature #296: Clicking on the HUD elements should show/hide the respective window
+Feature #301: Direction after using a Teleport Door
+Feature #303: Allow object selection in the console
+Feature #305: Allow the use of = as a synonym for ==
+Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts
+Task #176: Restructure enabling/disabling of MW-references
+Task #283: Integrate ogre.cfg file in settings file
+Task #290: Auto-Close MW-reference related GUI windows
+
+0.15.0
+
+Bug #5: Physics reimplementation (fixes various issues)
+Bug #258: Resizing arrow's background is not transparent
+Bug #268: Widening the stats window in X direction causes layout problems
+Bug #269: Topic pane in dialgoue window is too small for some longer topics
+Bug #271: Dialog choices are sorted incorrectly
+Bug #281: The single quote character is not rendered on dialog windows
+Bug #285: Terrain not handled properly in cells that are not predefined
+Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs
+Feature #15: Collision with Terrain
+Feature #17: Inventory-, Container- and Trade-Windows
+Feature #44: Floating Labels above Focussed Objects
+Feature #80: Tooltips
+Feature #83: Barter Dialogue
+Feature #90: Book and Scroll Windows
+Feature #156: Item Stacking in Containers
+Feature #213: Pulsating lights
+Feature #218: Feather & Burden
+Feature #256: Implement magic effect bookkeeping
+Feature #259: Add missing information to Stats window
+Feature #260: Correct case for dialogue topics
+Feature #280: GUI texture atlasing
+Feature #291: Ability to use GMST strings from GUI layout files
+Task #255: Make MWWorld::Environment into a singleton
+
+0.14.0
+
+Bug #1: Meshes rendered with wrong orientation
+Bug #6/Task #220: Picking up small objects doesn't always work
+Bug #127: tcg doesn't work
+Bug #178: Compablity problems with Ogre 1.8.0 RC 1
+Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI
+Bug #227: Terrain crashes when moving away from predefined cells
+Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces
+Bug #235: TGA texture loading problem
+Bug #246: wireframe mode does not work in water
+Feature #8/#232: Water Rendering
+Feature #13: Terrain Rendering
+Feature #37: Render Path Grid
+Feature #66: Factions
+Feature #77: Local Map
+Feature #78: Compass/Mini-Map
+Feature #97: Render Clothing/Armour
+Feature #121: Window Pinning
+Feature #205: Auto equip
+Feature #217: Contiainer should track changes to its content
+Feature #221: NPC Dialogue Window Enhancements
+Feature #233: Game settings manager
+Feature #240: Spell List and selected spell (no GUI yet)
+Feature #243: Draw State
+Task #113: Morrowind.ini Importer
+Task #215: Refactor the sound code
+Task #216: Update MyGUI
+
+0.13.0
+
+Bug #145: Fixed sound problems after cell change
+Bug #179: Pressing space in console triggers activation
+Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux
+Bug #189: ASCII 16 character added to console on it's activation on Mac OS X
+Bug #190: Case Folding fails with music files
+Bug #192: Keypresses write Text into Console no matter which gui element is active
+Bug #196: Collision shapes out of place
+Bug #202: ESMTool doesn't not work with localised ESM files anymore
+Bug #203: Torch lights only visible on short distance
+Bug #207: Ogre.log not written
+Bug #209: Sounds do not play
+Bug #210: Ogre crash at Dren plantation
+Bug #214: Unsupported file format version
+Bug #222: Launcher is writing openmw.cfg file to wrong location
+Feature #9: NPC Dialogue Window
+Feature #16/42: New sky/weather implementation
+Feature #40: Fading
+Feature #48: NPC Dialogue System
+Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet)
+Feature #161: Load REC_PGRD records
+Feature #195: Wireframe-mode
+Feature #198/199: Various sound effects
+Feature #206: Allow picking data path from launcher if non is set
+Task #108: Refactor window manager class
+Task #172: Sound Manager Cleanup
+Task #173: Create OpenEngine systems in the appropriate manager classes
+Task #184: Adjust MSVC and gcc warning levels
+Task #185: RefData rewrite
+Task #201: Workaround for transparency issues
+Task #208: silenced esm_reader.hpp warning
+
+0.12.0
+
+Bug #154: FPS Drop
+Bug #169: Local scripts continue running if associated object is deleted
+Bug #174: OpenMW fails to start if the config directory doesn't exist
+Bug #187: Missing lighting
+Bug #188: Lights without a mesh are not rendered
+Bug #191: Taking screenshot causes crash when running installed
+Feature #28: Sort out the cell load problem
+Feature #31: Allow the player to move away from pre-defined cells
+Feature #35: Use alternate storage location for modified object position
+Feature #45: NPC animations
+Feature #46: Creature Animation
+Feature #89: Basic Journal Window
+Feature #110: Automatically pick up the path of existing MW-installations
+Feature #183: More FPS display settings
+Task #19: Refactor engine class
+Task #109/Feature #162: Automate Packaging
+Task #112: Catch exceptions thrown in input handling functions
+Task #128/#168: Cleanup Configuration File Handling
+Task #131: NPC Activation doesn't work properly
+Task #144: MWRender cleanup
+Task #155: cmake cleanup
+
+0.11.1
+
+Bug #2: Resources loading doesn't work outside of bsa files
+Bug #3: GUI does not render non-English characters
+Bug #7: openmw.cfg location doesn't match
+Bug #124: The TCL alias for ToggleCollision is missing.
+Bug #125: Some command line options can't be used from a .cfg file
+Bug #126: Toggle-type script instructions are less verbose compared with original MW
+Bug #130: NPC-Record Loading fails for some NPCs
+Bug #167: Launcher sets invalid parameters in ogre config
+Feature #10: Journal
+Feature #12: Rendering Optimisations
+Feature #23: Change Launcher GUI to a tabbed interface
+Feature #24: Integrate the OGRE settings window into the launcher
+Feature #25: Determine openmw.cfg location (Launcher)
+Feature #26: Launcher Profiles
+Feature #79: MessageBox
+Feature #116: Tab-Completion in Console
+Feature #132: --data-local and multiple --data
+Feature #143: Non-Rendering Performance-Optimisations
+Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs
+Feature #157: Version Handling
+Task #14: Replace tabs with 4 spaces
+Task #18: Move components from global namespace into their own namespace
+Task #123: refactor header files in components/esm
+
+0.10.0
+
+* NPC dialogue window (not functional yet)
+* Collisions with objects
+* Refactor the PlayerPos class
+* Adjust file locations
+* CMake files and test linking for Bullet
+* Replace Ogre raycasting test for activation with something more precise
+* Adjust player movement according to collision results
+* FPS display
+* Various Portability Improvements
+* Mac OS X support is back!
+
+0.9.0
+
+* Exterior cells loading, unloading and management
+* Character Creation GUI
+* Character creation
+* Make cell names case insensitive when doing internal lookups
+* Music player
+* NPCs rendering
+
+0.8.0
+
+* GUI
+* Complete and working script engine
+* In game console
+* Sky rendering
+* Sound and music
+* Tons of smaller stuff
+
+0.7.0
+
+* This release is a complete rewrite in C++.
+* All D code has been culled, and all modules have been rewritten.
+* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase.
+
+0.6.0
+
+* Coded a GUI system using MyGUI
+* Skinned MyGUI to look like Morrowind (work in progress)
+* Integrated the Monster script engine
+* Rewrote some functions into script code
+* Very early MyGUI < > Monster binding
+* Fixed Windows sound problems (replaced old openal32.dll)
+
+0.5.0
+
+* Collision detection with Bullet
+* Experimental walk & fall character physics
+* New key bindings:
+  * t toggle physics mode (walking, flying, ghost),
+  * n night eye, brightens the scene
+* Fixed incompatability with DMD 1.032 and newer compilers
+* * (thanks to tomqyp)
+* Various minor changes and updates
+
+0.4.0
+
+* Switched from Audiere to OpenAL
+* * (BIG thanks to Chris Robinson)
+* Added complete Makefile (again) as a alternative build tool
+* More realistic lighting (thanks again to Chris Robinson)
+* Various localization fixes tested with Russian and French versions
+* Temporary workaround for the Unicode issue: invalid UTF displayed as '?'
+* Added ns option to disable sound, for debugging
+* Various bug fixes
+* Cosmetic changes to placate gdc Wall
+
+0.3.0
+
+* Built and tested on Windows XP
+* Partial support for FreeBSD (exceptions do not work)
+* You no longer have to download Monster separately
+* Made an alternative for building without DSSS (but DSSS still works)
+* Renamed main program from 'morro' to 'openmw'
+* Made the config system more robust
+* Added oc switch for showing Ogre config window on startup
+* Removed some config files, these are auto generated when missing.
+* Separated plugins.cfg into linux and windows versions.
+* Updated Makefile and sources for increased portability
+* confirmed to work against OIS 1.0.0 (Ubuntu repository package)
+
+0.2.0
+
+* Compiles with gdc
+* Switched to DSSS for building D code
+* Includes the program esmtool
+
+0.1.0
+
+first release
+
diff --git a/README.md b/README.md index 304844892a..862ae16c75 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ Morrowind by Bethesda Softworks. You need to own and install the original game f * Website: http://www.openmw.org Font Licenses: -DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) - +* DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) Installation ============ @@ -34,17 +33,15 @@ Build from source https://wiki.openmw.org/index.php?title=Development_Environment_Setup - The data path ============= -The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to -pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly -(installing Morrowind under WINE is considered a proper install). +The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install). Command line options ==================== +
 Syntax: openmw 
 Allowed options:
   --help                                print help message
@@ -99,1470 +96,9 @@ Allowed options:
   --fallback arg                        fallback values
   --no-grab                             Don't grab mouse cursor
   --activate-dist arg (=-1)             activation distance override
+
Changelog ========= -0.34.0 - -Bug #904: omwlauncher doesn't allow installing Tribunal and Bloodmoon if only MW is installed -Bug #986: Launcher: renaming profile names is broken -Bug #1061: "Browse to CD..." launcher crash -Bug #1135: Launcher crashes if user does not have write permission -Bug #1231: Current installer in launcher does not correctly import russian Morrowind.ini settings from setup.inx -Bug #1288: Fix the Alignment of the Resolution Combobox -Bug #1343: BIK videos occasionally out of sync with audio -Bug #1684: Morrowind Grass Mod graphical glitches -Bug #1734: NPC in fight with invisible/sneaking player -Bug #1982: Long class names are cut off in the UI -Bug #2012: Editor: OpenCS script compiler sometimes fails to find IDs -Bug #2015: Running while levitating does not affect speed but still drains fatigue -Bug #2018: OpenMW don´t reset modified cells to vanilla when a plugin is deselected and don´t apply changes to cells already visited. -Bug #2045: ToggleMenus command should close dialogue windows -Bug #2046: Crash: light_de_streetlight_01_223 -Bug #2047: Buglamp tooltip minor correction -Bug #2050: Roobrush floating texture bits -Bug #2053: Slaves react negatively to PC picking up slave's bracers -Bug #2055: Dremora corpses use the wrong model -Bug #2056: Mansilamat Vabdas's corpse is floating in the water -Bug #2057: "Quest: Larius Varro Tells A Little Story": Bounty not completely removed after finishing quest -Bug #2059: Silenced enemies try to cast spells anyway -Bug #2060: Editor: Special case implementation for top level window with single sub-window should be optional -Bug #2061: Editor: SubView closing that is not directly triggered by the user isn't handled properly -Bug #2063: Tribunal: Quest 'The Warlords' doesn't work -Bug #2064: Sneak attack on hostiles causes bounty -Bug #2065: Editor: Qt signal-slot error when closing a dialogue subview -Bug #2070: Loading ESP in OpenMW works but fails in OpenCS -Bug #2071: CTD in 0.33 -Bug #2073: Storm atronach animation stops now and then -Bug #2075: Molag Amur Region, Map shows water on solid ground -Bug #2080: game won't work with fair magicka regen -Bug #2082: NPCs appear frozen or switched off after leaving and quickly reentering a cell -Bug #2088: OpenMW is unable to play OGG files. -Bug #2093: Darth Gares talks to you in Ilunibi even when he's not there, screwing up the Main Quests -Bug #2095: Coordinate and rotation editing in the Reference table does not work. -Bug #2096: Some overflow fun and bartering exploit -Bug #2098: [D3D] Game crash on maximize -Bug #2099: Activate, player seems not to work -Bug #2104: Only labels are sensitive in buttons -Bug #2107: "Slowfall" effect is too weak -Bug #2114: OpenCS doesn't load an ESP file full of errors even though Vanilla MW Construction Set can -Bug #2117: Crash when encountering bandits on opposite side of river from the egg mine south of Balmora -Bug #2124: [Mod: Baldurians Transparent Glass Amor] Armor above head -Bug #2125: Unnamed NiNodes in weapons problem in First Person -Bug #2126: Dirty dialog script in tribunal.esm causing bug in Tribunal MQ -Bug #2128: Crash when picking character's face -Bug #2129: Disable the third-person zoom feature by default -Bug #2130: Ash storm particles shown too long during transition to clear sky -Bug #2137: Editor: exception caused by following the Creature column of a SoundGen record -Bug #2139: Mouse movement should be ignored during intro video -Bug #2143: Editor: Saving is broken -Bug #2145: OpenMW - crash while exiting x64 debug build -Bug #2152: You can attack Almalexia during her final monologue -Bug #2154: Visual effects behave weirdly after loading/taking a screenshot -Bug #2155: Vivec has too little magicka -Bug #2156: Azura's spirit fades away too fast -Bug #2158: [Mod]Julan Ashlander Companion 2.0: Negative magicka -Bug #2161: Editor: combat/magic/stealth values of creature not displayed correctly -Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon. -Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly. -Bug #2170: Mods using conversations to update PC inconsistant -Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources -Bug #2212: Crash or unexpected behavior while closing OpenCS cell render window on OS X -Feature #238: Add UI to run INI-importer from the launcher -Feature #854: Editor: Add user setting to show status bar -Feature #987: Launcher: first launch instructions for CD need to be more explicit -Feature #1232: There is no way to set the "encoding" option using launcher UI. -Feature #1281: Editor: Render cell markers -Feature #1918: Editor: Functionality for Double-Clicking in Tables -Feature #1966: Editor: User Settings dialogue grouping/labelling/tooltips -Feature #2097: Editor: Edit position of references in 3D scene -Feature #2121: Editor: Add edit mode button to scene toolbar -Task #1965: Editor: Improve layout of user settings dialogue - -0.33.1 - -Bug #2108: OpenCS fails to build - -0.33.0 - -Bug #371: If console assigned to ` (probably to any symbolic key), "`" symbol will be added to console every time it closed -Bug #1148: Some books'/scrolls' contents are displayed incorrectly -Bug #1290: Editor: status bar is not updated when record filter is changed -Bug #1292: Editor: Documents are not removed on closing the last view -Bug #1301: Editor: File->Exit only checks the document it was issued from. -Bug #1353: Bluetooth on with no speaker connected results in significantly longer initial load times -Bug #1436: NPCs react from too far distance -Bug #1472: PC is placed on top of following NPC when changing cell -Bug #1487: Tall PC can get stuck in staircases -Bug #1565: Editor: Subviews are deleted on shutdown instead when they are closed -Bug #1623: Door marker on Ghorak Manor's balcony makes PC stuck -Bug #1633: Loaddoor to Sadrith Mora, Telvanni Council House spawns PC in the air -Bug #1655: Use Appropriate Application Icons on Windows -Bug #1679: Tribunal expansion, Meryn Othralas the backstage manager in the theatre group in Mournhold in the great bazaar district is floating a good feet above the ground. -Bug #1705: Rain is broken in third person -Bug #1706: Thunder and lighting still occurs while the game is paused during the rain -Bug #1708: No long jumping -Bug #1710: Editor: ReferenceableID drag to references record filter field creates incorrect filter -Bug #1712: Rest on Water -Bug #1715: "Cancel" button is not always on the same side of menu -Bug #1725: Editor: content file can be opened multiple times from the same dialogue -Bug #1730: [MOD: Less Generic Nerevarine] Compile failure attempting to enter the Corprusarium. -Bug #1733: Unhandled ffmpeg sample formats -Bug #1735: Editor: "Edit Record" context menu button not opening subview for journal infos -Bug #1750: Editor: record edits result in duplicate entries -Bug #1789: Editor: Some characters cannot be used in addon name -Bug #1803: Resizing the map does not keep the pre-resize center at the post-resize center -Bug #1821: Recovering Cloudcleaver quest: attacking Sosia is considered a crime when you side with Hlormar -Bug #1838: Editor: Preferences window appears off screen -Bug #1839: Editor: Record filter title should be moved two pixels to the right -Bug #1849: Subrecord error in MAO_Containers -Bug #1854: Knocked-out actors don't fully act knocked out -Bug #1855: "Soul trapped" sound doesn't play -Bug #1857: Missing sound effect for enchanted items with empty charge -Bug #1859: Missing console command: ResetActors (RA) -Bug #1861: Vendor category "MagicItems" is unhandled -Bug #1862: Launcher doesn't start if a file listed in launcher.cfg has correct name but wrong capitalization -Bug #1864: Editor: Region field for cell record in dialogue subview not working -Bug #1869: Editor: Change label "Musics" to "Music" -Bug #1870: Goblins killed while knocked down remain in knockdown-pose -Bug #1874: CellChanged events should not trigger when crossing exterior cell border -Bug #1877: Spriggans killed instantly if hit while regening -Bug #1878: Magic Menu text not un-highlighting correctly when going from spell to item as active magic -Bug #1881: Stuck in ceiling when entering castle karstaags tower -Bug #1884: Unlit torches still produce a burning sound -Bug #1885: Can type text in price field in barter window -Bug #1887: Equipped items do not emit sounds -Bug #1889: draugr lord aesliip will attack you and remain non-hostile -Bug #1892: Guard asks player to pay bounty of 0 gold -Bug #1895: getdistance should only return max float if ref and target are in different worldspaces -Bug #1896: Crash Report -Bug #1897: Conjured Equipment cant be re-equipped if removed -Bug #1898: Only Gidar Verothan follows you during establish the mine quest -Bug #1900: Black screen when you open the door and breath underwater -Bug #1904: Crash on casting recall spell -Bug #1906: Bound item checks should use the GMSTs -Bug #1907: Bugged door. Mournhold, The Winged Guar -Bug #1908: Crime reported for attacking Drathas Nerus's henchmen while they attack Dilborn -Bug #1909: Weird Quest Flow Infidelities quest -Bug #1910: Follower fighting with gone npc -Bug #1911: Npcs will drown themselves -Bug #1912: World map arrow stays static when inside a building -Bug #1920: Ulyne Henim disappears when game is loaded inside Vas -Bug #1922: alchemy-> potion of paralyze -Bug #1923: "levitation magic cannot be used here" shows outside of tribunal -Bug #1927: AI prefer melee over magic. -Bug #1929: Tamriel Rebuilt: Named cells that lie within the overlap with Morrowind.esm are not shown -Bug #1932: BTB - Spells 14.1 magic effects don´t overwrite the Vanilla ones but are added -Bug #1935: Stacks of items are worth more when sold individually -Bug #1940: Launcher does not list addon files if base game file is renamed to a different case -Bug #1946: Mod "Tel Nechim - moved" breaks savegames -Bug #1947: Buying/Selling price doesn't properly affect the growth of mercantile skill -Bug #1950: followers from east empire company quest will fight each other if combat happens with anything -Bug #1958: Journal can be scrolled indefinitely with a mouse wheel -Bug #1959: Follower not leaving party on quest end -Bug #1960: Key bindings not always saved correctly -Bug #1961: Spell merchants selling racial bonus spells -Bug #1967: segmentation fault on load saves -Bug #1968: Jump sounds are not controlled by footsteps slider, sound weird compared to footsteps -Bug #1970: PC suffers silently when taking damage from lava -Bug #1971: Dwarven Sceptre collision area is not removed after killing one -Bug #1974: Dalin/Daris Norvayne follows player indefinitely -Bug #1975: East Empire Company faction rank breaks during Raven Rock questline -Bug #1979: 0 strength = permanently over encumbered -Bug #1993: Shrine blessing in Maar Gan doesn't work -Bug #2008: Enchanted items do not recharge -Bug #2011: Editor: OpenCS script compiler doesn't handle member variable access properly -Bug #2016: Dagoth Ur already dead in Facility Cavern -Bug #2017: Fighters Guild Quest: The Code Book - dialogue loop when UMP is loaded. -Bug #2019: Animation of 'Correct UV Mudcrabs' broken -Bug #2022: Alchemy window - Removing ingredient doesn't remove the number of ingredients -Bug #2025: Missing mouse-over text for non affordable items -Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell "Ruinous Keep, Great Hall" -Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding -Feature #471: Editor: Special case implementation for top-level window with single sub-window -Feature #472: Editor: Sub-Window re-use settings -Feature #704: Font colors import from fallback settings -Feature #879: Editor: Open sub-views in a new top-level window -Feature #932: Editor: magic effect table -Feature #937: Editor: Path Grid table -Feature #938: Editor: Sound Gen table -Feature #1117: Death and LevelUp music -Feature #1226: Editor: Request UniversalId editing from table columns -Feature #1545: Targeting console on player -Feature #1597: Editor: Render terrain -Feature #1695: Editor: add column for CellRef's global variable -Feature #1696: Editor: use ESM::Cell's RefNum counter -Feature #1697: Redden player's vision when hit -Feature #1856: Spellcasting for non-biped creatures -Feature #1879: Editor: Run OpenMW with the currently edited content list -Task #1851: Move AI temporary state out of AI packages -Task #1865: Replace char type in records - -0.32.0 - -Bug #1132: Unable to jump when facing a wall -Bug #1341: Summoned Creatures do not immediately disappear when killed. -Bug #1430: CharGen Revamped script does not compile -Bug #1451: NPCs shouldn't equip weapons prior to fighting -Bug #1461: Stopped start scripts do not restart on load -Bug #1473: Dead NPC standing and in 2 pieces -Bug #1482: Abilities are depleted when interrupted during casting -Bug #1503: Behaviour of NPCs facing the player -Bug #1506: Missing character, French edition: three-points -Bug #1528: Inventory very slow after 2 hours -Bug #1540: Extra arguments should be ignored for script functions -Bug #1541: Helseth's Champion: Tribunal -Bug #1570: Journal cannot be opened while in inventory screen -Bug #1573: PC joins factions at random -Bug #1576: NPCs aren't switching their weapons when out of ammo -Bug #1579: Guards detect creatures in far distance, instead on sight -Bug #1588: The Siege of the Skaal Village: bloodmoon -Bug #1593: The script compiler isn't recognising some names that contain a - -Bug #1606: Books: Question marks instead of quotation marks -Bug #1608: Dead bodies prevent door from opening/closing. -Bug #1609: Imperial guards in Sadrith Mora are not using their spears -Bug #1610: The bounty number is not displayed properly with high numbers -Bug #1620: Implement correct formula for auto-calculated NPC spells -Bug #1630: Boats standing vertically in Vivec -Bug #1635: Arrest dialogue is executed second time after I select "Go to jail" -Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults? -Bug #1641: Persuasion dialog remains after loading, possibly resulting in crash -Bug #1644: "Goodbye" and similar options on dialogues prevents escape working properly. -Bug #1646: PC skill stats are not updated immediately when changing equipment -Bug #1652: Non-aggressive creature -Bug #1653: Quickloading while the container window is open crashes the game -Bug #1654: Priority of checks in organic containers -Bug #1656: Inventory items merge issue when repairing -Bug #1657: Attacked state of NPCs is not saved properly -Bug #1660: Rank dialogue condition ignored -Bug #1668: Game starts on day 2 instead of day 1 -Bug #1669: Critical Strikes while fighting a target who is currently fighting me -Bug #1672: OpenCS doesn't save the projects -Bug #1673: Fatigue decreasing by only one point when running -Bug #1675: Minimap and localmap graphic glitches -Bug #1676: Pressing the OK button on the travel menu cancels the travel and exits the menu -Bug #1677: Sleeping in a rented bed is considered a crime -Bug #1685: NPCs turn towards player even if invisible/sneaking -Bug #1686: UI bug: cursor is clicking "world/local" map button while inventory window is closed? -Bug #1690: Double clicking on a inventory window header doesn't close it. -Bug #1693: Spell Absorption does not absorb shrine blessings -Bug #1694: journal displays learned topics as quests -Bug #1700: Sideways scroll of text boxes -Bug #1701: Player enchanting requires player hold money, always 100% sucessful. -Bug #1704: self-made Fortify Intelligence/Drain willpower potions are broken -Bug #1707: Pausing the game through the esc menu will silence rain, pausing it by opening the inventory will not. -Bug #1709: Remesa Othril is hostile to Hlaalu members -Bug #1713: Crash on load after death -Bug #1719: Blind effect has slight border at the edge of the screen where it is ineffective. -Bug #1722: Crash after creating enchanted item, reloading saved game -Bug #1723: Content refs that are stacked share the same index after unstacking -Bug #1726: Can't finish Aengoth the Jeweler's quest : Retrieve the Scrap Metal -Bug #1727: Targets almost always resist soultrap scrolls -Bug #1728: Casting a soultrap spell on invalid target yields no message -Bug #1729: Chop attack doesn't work if walking diagonally -Bug #1732: Error handling for missing script function arguments produces weird message -Bug #1736: Alt-tabbing removes detail from overworld map. -Bug #1737: Going through doors with (high magnitude?) leviation will put the player high up, possibly even out of bounds. -Bug #1739: Setting a variable on an NPC from another NPC's dialogue result sets the wrong variable -Bug #1741: The wait dialogue doesn't black the screen out properly during waiting. -Bug #1742: ERROR: Object 'sDifficulty' not found (const) -Bug #1744: Night sky in Skies V.IV (& possibly v3) by SWG rendered incorrectly -Bug #1746: Bow/marksman weapon condition does not degrade with use -Bug #1749: Constant Battle Music -Bug #1752: Alt-Tabbing in the character menus makes the paper doll disappear temporarily -Bug #1753: Cost of training is not added to merchant's inventory -Bug #1755: Disposition changes do not persist if the conversation menu is closed by purchasing training. -Bug #1756: Caught Blight after being cured of Corprus -Bug #1758: Crash Upon Loading New Cell -Bug #1760: Player's Magicka is not recalculated upon drained or boosted intelligence -Bug #1761: Equiped torches lost on reload -Bug #1762: Your spell did not get a target. Soul trap. Gorenea Andrano -Bug #1763: Custom Spell Magicka Cost -Bug #1765: Azuras Star breaks on recharging item -Bug #1767: GetPCRank did not handle ignored explicit references -Bug #1772: Dark Brotherhood Assassins never use their Carved Ebony Dart, sticking to their melee weapon. -Bug #1774: String table overflow also occurs when loading TheGloryRoad.esm -Bug #1776: dagoth uthol runs in slow motion -Bug #1778: Incorrect values in spellmaking window -Bug #1779: Icon of Master Propylon Index is not visible -Bug #1783: Invisible NPC after looting corpse -Bug #1787: Health Calculation -Bug #1788: Skeletons, ghosts etc block doors when we try to open -Bug #1791: [MOD: LGNPC Foreign Quarter] NPC in completely the wrong place. -Bug #1792: Potions should show more effects -Bug #1793: Encumbrance while bartering -Bug #1794: Fortify attribute not affecting fatigue -Bug #1795: Too much magicka -Bug #1796: "Off by default" torch burning -Bug #1797: Fish too slow -Bug #1798: Rest until healed shouldn't show with full health and magicka -Bug #1802: Mark location moved -Bug #1804: stutter with recent builds -Bug #1810: attack gothens dremora doesnt agro the others. -Bug #1811: Regression: Crash Upon Loading New Cell -Bug #1812: Mod: "QuickChar" weird button placement -Bug #1815: Keys show value and weight, Vanilla Morrowind's keys dont. -Bug #1817: Persuasion results do not show using unpatched MW ESM -Bug #1818: Quest B3_ZainabBride moves to stage 47 upon loading save while Falura Llervu is following -Bug #1823: AI response to theft incorrect - only guards react, in vanilla everyone does. -Bug #1829: On-Target Spells Rendered Behind Water Surface Effects -Bug #1830: Galsa Gindu's house is on fire -Bug #1832: Fatal Error: OGRE Exception(2:InvalidParametersException) -Bug #1836: Attacked Guards open "fine/jail/resist"-dialogue after killing you -Bug #1840: Infinite recursion in ActionTeleport -Bug #1843: Escorted people change into player's cell after completion of escort stage -Bug #1845: Typing 'j' into 'Name' fields opens the journal -Bug #1846: Text pasted into the console still appears twice (Windows) -Bug #1847: "setfatigue 0" doesn't render NPC unconscious -Bug #1848: I can talk to unconscious actors -Bug #1866: Crash when player gets killed by a creature summoned by him -Bug #1868: Memory leaking when openmw window is minimized -Feature #47: Magic Effects -Feature #642: Control NPC mouth movement using current Say sound -Feature #939: Editor: Resources tables -Feature #961: AI Combat for magic (spells, potions and enchanted items) -Feature #1111: Collision script instructions (used e.g. by Lava) -Feature #1120: Command creature/humanoid magic effects -Feature #1121: Elemental shield magic effects -Feature #1122: Light magic effect -Feature #1139: AI: Friendly hits -Feature #1141: AI: combat party -Feature #1326: Editor: Add tooltips to all graphical buttons -Feature #1489: Magic effect Get/Mod/Set functions -Feature #1505: Difficulty slider -Feature #1538: Targeted scripts -Feature #1571: Allow creating custom markers on the local map -Feature #1615: Determine local variables from compiled scripts instead of the values in the script record -Feature #1616: Editor: Body part record verifier -Feature #1651: Editor: Improved keyboard navigation for scene toolbar -Feature #1666: Script blacklisting -Feature #1711: Including the Git revision number from the command line "--version" switch. -Feature #1721: NPC eye blinking -Feature #1740: Scene toolbar buttons for selecting which type of elements are rendered -Feature #1790: Mouse wheel scrolling for the journal -Feature #1850: NiBSPArrayController -Task #768: On windows, settings folder should be "OpenMW", not "openmw" -Task #908: Share keyframe data -Task #1716: Remove defunct option for building without FFmpeg - -0.31.0 - -Bug #245: Cloud direction and weather systems differ from Morrowind -Bug #275: Local Map does not always show objects that span multiple cells -Bug #538: Update CenterOnCell (COC) function behavior -Bug #618: Local and World Map Textures are sometimes Black -Bug #640: Water behaviour at night -Bug #668: OpenMW doesn't support non-latin paths on Windows -Bug #746: OpenMW doesn't check if the background music was already played -Bug #747: Door is stuck if cell is left before animation finishes -Bug #772: Disabled statics are visible on map -Bug #829: OpenMW uses up all available vram, when playing for extended time -Bug #869: Dead bodies don't collide with anything -Bug #894: Various character creation issues -Bug #897/#1369: opencs Segmentation Fault after "new" or "load" -Bug #899: Various jumping issues -Bug #952: Reflection effects are one frame delayed -Bug #993: Able to interact with world during Wait/Rest dialog -Bug #995: Dropped items can be placed inside the wall -Bug #1008: Corpses always face up upon reentering the cell -Bug #1035: Random colour patterns appearing in automap -Bug #1037: Footstep volume issues -Bug #1047: Creation of wrong links in dialogue window -Bug #1129: Summoned creature time life duration seems infinite -Bug #1134: Crimes can be committed against hostile NPCs -Bug #1136: Creature run speed formula is incorrect -Bug #1150: Weakness to Fire doesn't apply to Fire Damage in the same spell -Bug #1155: NPCs killing each other -Bug #1166: Bittercup script still does not work -Bug #1178: .bsa file names are case sensitive. -Bug #1179: Crash after trying to load game after being killed -Bug #1180: Changing footstep sound location -Bug #1196: Jumping not disabled when showing messageboxes -Bug #1202: "strange" keys are not shown in binding menu, and are not saved either, but works -Bug #1216: Broken dialog topics in russian Morrowind -Bug #1217: Container content changes based on the current position of the mouse -Bug #1234: Loading/saving issues with dynamic records -Bug #1277: Text pasted into the console appears twice -Bug #1284: Crash on New Game -Bug #1303: It's possible to skip the chargen -Bug #1304: Slaughterfish should not detect the player unless the player is in the water -Bug #1311: Editor: deleting Record Filter line does not reset the filter -Bug #1324: ERROR: ESM Error: String table overflow when loading Animated Morrowind.esp -Bug #1328: Editor: Bogus Filter created when dragging multiple records to filter bar of non-applicable table -Bug #1331: Walking/running sound persist after killing NPC`s that are walking/running. -Bug #1334: Previously equipped items not shown as unequipped after attempting to sell them. -Bug #1335: Actors ignore vertical axis when deciding to attack -Bug #1338: Unknown toggle option for shadows -Bug #1339: "Ashlands Region" is visible when beginning new game during "Loading Area" process -Bug #1340: Guards prompt Player with punishment options after resisting arrest with another guard. -Bug #1348: Regression: Bug #1098 has returned with a vengeance -Bug #1349: [TR] TR_Data mesh tr_ex_imp_gatejamb01 cannot be activated -Bug #1352: Disabling an ESX file does not disable dependent ESX files -Bug #1355: CppCat Checks OpenMW -Bug #1356: Incorrect voice type filtering for sleep interrupts -Bug #1357: Restarting the game clears saves -Bug #1360: Seyda Neen silk rider dialog problem -Bug #1361: Some lights don't work -Bug #1364: It is difficult to bind "Mouse 1" to an action in the options menu -Bug #1370: Animation compilation mod does not work properly -Bug #1371: SL_Pick01.nif from third party fails to load in openmw, but works in Vanilla -Bug #1373: When stealing in front of Sellus Gravius cannot exit the dialog -Bug #1378: Installs to /usr/local are not working -Bug #1380: Loading a save file fail if one of the content files is disabled -Bug #1382: "getHExact() size mismatch" crash on loading official plugin "Siege at Firemoth.esp" -Bug #1386: Arkngthand door will not open -Bug #1388: Segfault when modifying View Distance in Menu options -Bug #1389: Crash when loading a save after dying -Bug #1390: Apostrophe characters not displayed [French version] -Bug #1391: Custom made icon background texture for magical weapons and stuff isn't scaled properly on GUI. -Bug #1393: Coin icon during the level up dialogue are off of the background -Bug #1394: Alt+F4 doesn't work on Win version -Bug #1395: Changing rings switches only the last one put on -Bug #1396: Pauldron parts aren't showing when the robe is equipped -Bug #1402: Dialogue of some shrines have wrong button orientation -Bug #1403: Items are floating in the air when they're dropped onto dead bodies. -Bug #1404: Forearms are not rendered on Argonian females -Bug #1407: Alchemy allows making potions from two of the same item -Bug #1408: "Max sale" button gives you all the items AND all the trader's gold -Bug #1409: Rest "Until Healed" broken for characters with stunted magicka. -Bug #1412: Empty travel window opens while playing through start game -Bug #1413: Save game ignores missing writing permission -Bug #1414: The Underground 2 ESM Error -Bug #1416: Not all splash screens in the Splash directory are used -Bug #1417: Loading saved game does not terminate -Bug #1419: Skyrim: Home of the Nords error -Bug #1422: ClearInfoActor -Bug #1423: ForceGreeting closes existing dialogue windows -Bug #1425: Cannot load save game -Bug #1426: Read skill books aren't stored in savegame -Bug #1427: Useless items can be set under hotkeys -Bug #1429: Text variables in journal -Bug #1432: When attacking friendly NPC, the crime is reported and bounty is raised after each swing -Bug #1435: Stealing priceless items is without punishment -Bug #1437: Door marker at Jobasha's Rare Books is spawning PC in the air -Bug #1440: Topic selection menu should be wider -Bug #1441: Dropping items on the rug makes them inaccessible -Bug #1442: When dropping and taking some looted items, bystanders consider that as a crime -Bug #1444: Arrows and bolts are not dropped where the cursor points -Bug #1445: Security trainers offering acrobatics instead -Bug #1447: Character dash not displayed, French edition -Bug #1448: When the player is killed by the guard while having a bounty on his head, the guard dialogue opens over and over instead of loading dialogue -Bug #1454: Script error in SkipTutorial -Bug #1456: Bad lighting when using certain Morrowind.ini generated by MGE -Bug #1457: Heart of Lorkan comes after you when attacking it -Bug #1458: Modified Keybindings are not remembered -Bug #1459: Dura Gra-Bol doesn't respond to PC attack -Bug #1462: Interior cells not loaded with Morrowind Patch active -Bug #1469: Item tooltip should show the base value, not real value -Bug #1477: Death count is not stored in savegame -Bug #1478: AiActivate does not trigger activate scripts -Bug #1481: Weapon not rendered when partially submerged in water -Bug #1483: Enemies are attacking even while dying -Bug #1486: ESM Error: Don't know what to do with INFO -Bug #1490: Arrows shot at PC can end up in inventory -Bug #1492: Monsters respawn on top of one another -Bug #1493: Dialogue box opens with follower NPC even if NPC is dead -Bug #1494: Paralysed cliffracers remain airbourne -Bug #1495: Dialogue box opens with follower NPC even the game is paused -Bug #1496: GUI messages are not cleared when loading another saved game -Bug #1499: Underwater sound sometimes plays when transitioning from interior. -Bug #1500: Targetted spells and water. -Bug #1502: Console error message on info refusal -Bug #1507: Bloodmoon MQ The Ritual of Beasts: Can't remove the arrow -Bug #1508: Bloodmoon: Fort Frostmoth, cant talk with Carnius Magius -Bug #1516: PositionCell doesn't move actors to current cell -Bug #1518: ForceGreeting broken for explicit references -Bug #1522: Crash after attempting to play non-music file -Bug #1523: World map empty after loading interior save -Bug #1524: Arrows in waiting/resting dialog act like minimum and maximum buttons -Bug #1525: Werewolf: Killed NPC's don't fill werewolfs hunger for blood -Bug #1527: Werewolf: Detect life detects wrong type of actor -Bug #1529: OpenMW crash during "the shrine of the dead" mission (tribunal) -Bug #1530: Selected text in the console has the same color as the background -Bug #1539: Barilzar's Mazed Band: Tribunal -Bug #1542: Looping taunts from NPC`s after death: Tribunal -Bug #1543: OpenCS crash when using drag&drop in script editor -Bug #1547: Bamz-Amschend: Centurion Archers combat problem -Bug #1548: The Missing Hand: Tribunal -Bug #1549: The Mad God: Tribunal, Dome of Serlyn -Bug #1557: A bounty is calculated from actual item cost -Bug #1562: Invisible terrain on top of Red Mountain -Bug #1564: Cave of the hidden music: Bloodmoon -Bug #1567: Editor: Deleting of referenceables does not work -Bug #1568: Picking up a stack of items and holding the enter key and moving your mouse around paints a bunch of garbage on screen. -Bug #1574: Solstheim: Drauger cant inflict damage on player -Bug #1578: Solstheim: Bonewolf running animation not working -Bug #1585: Particle effects on PC are stopped when paralyzed -Bug #1589: Tribunal: Crimson Plague quest does not update when Gedna Relvel is killed -Bug #1590: Failed to save game: compile error -Bug #1598: Segfault when making Drain/Fortify Skill spells -Bug #1599: Unable to switch to fullscreen -Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed -Bug #1618: Death notice fails to show up -Bug #1628: Alt+Tab Segfault -Feature #32: Periodic Cleanup/Refill -Feature #41: Precipitation and weather particles -Feature #568: Editor: Configuration setup -Feature #649: Editor: Threaded loading -Feature #930: Editor: Cell record saving -Feature #934: Editor: Body part table -Feature #935: Editor: Enchantment effect table -Feature #1162: Dialogue merging -Feature #1174: Saved Game: add missing creature state -Feature #1177: Saved Game: fog of war state -Feature #1312: Editor: Combat/Magic/Stealth values for creatures are not displayed -Feature #1314: Make NPCs and creatures fight each other -Feature #1315: Crime: Murder -Feature #1321: Sneak skill enhancements -Feature #1323: Handle restocking items -Feature #1332: Saved Game: levelled creatures -Feature #1347: modFactionReaction script instruction -Feature #1362: Animated main menu support -Feature #1433: Store walk/run toggle -Feature #1449: Use names instead of numbers for saved game files and folders -Feature #1453: Adding Delete button to the load menu -Feature #1460: Enable Journal screen while in dialogue -Feature #1480: Play Battle music when in combat -Feature #1501: Followers unable to fast travel with you -Feature #1520: Disposition and distance-based aggression/ShouldAttack -Feature #1595: Editor: Object rendering in cells -Task #940: Move license to locations where applicable -Task #1333: Remove cmake git tag reading -Task #1566: Editor: Object rendering refactoring - -0.30.0 - -Bug #416: Extreme shaking can occur during cell transitions while moving -Bug #1003: Province Cyrodiil: Ogre Exception in Stirk -Bug #1071: Crash when given a non-existent content file -Bug #1080: OpenMW allows resting/using a bed while in combat -Bug #1097: Wrong punishment for stealing in Census and Excise Office at the start of a new game -Bug #1098: Unlocked evidence chests should get locked after new evidence is put into them -Bug #1099: NPCs that you attacked still fight you after you went to jail/paid your fine -Bug #1100: Taking items from a corpse is considered stealing -Bug #1126: Some creatures can't get close enough to attack -Bug #1144: Killed creatures seem to die again each time player transitions indoors/outdoors -Bug #1181: loading a saved game does not reset the player control status -Bug #1185: Collision issues in Addamasartus -Bug #1187: Athyn Sarethi mission, rescuing varvur sarethi from the doesnt end the mission -Bug #1189: Crash when entering interior cell "Gnisis, Arvs-Drelen" -Bug #1191: Picking up papers without inventory in new game -Bug #1195: NPCs do not equip torches in certain interiors -Bug #1197: mouse wheel makes things scroll too fast -Bug #1200: door blocked by monsters -Bug #1201: item's magical charges are only refreshed when they are used -Bug #1203: Scribs do not defend themselves -Bug #1204: creatures life is not empty when they are dead -Bug #1205: armor experience does not progress when hits are taken -Bug #1206: blood particules always red. Undeads and mechanicals should have a different one. -Bug #1209: Tarhiel never falls -Bug #1210: journal adding script is ran again after having saved/loaded -Bug #1224: Names of custom classes are not properly handled in save games -Bug #1227: Editor: Fixed case handling for broken localised versions of Morrowind.esm -Bug #1235: Indoors walk stutter -Bug #1236: Aborting intro movie brings up the menu -Bug #1239: NPCs get stuck when walking past each other -Bug #1240: BTB - Settings 14.1 and Health Bar. -Bug #1241: BTB - Character and Khajiit Prejudice -Bug #1248: GUI Weapon icon is changed to hand-to-hand after save load -Bug #1254: Guild ranks do not show in dialogue -Bug #1255: When opening a container and selecting "Take All", the screen flashes blue -Bug #1260: Level Up menu doesn't show image when using a custom class -Bug #1265: Quit Menu Has Misaligned Buttons -Bug #1270: Active weapon icon is not updated when weapon is repaired -Bug #1271: NPC Stuck in hovering "Jumping" animation -Bug #1272: Crash when attempting to load Big City esm file. -Bug #1276: Editor: Dropping a region into the filter of a cell subview fails -Bug #1286: Dialogue topic list clips with window frame -Bug #1291: Saved game: store faction membership -Bug #1293: Pluginless Khajiit Head Pack by ashiraniir makes OpenMW close. -Bug #1294: Pasting in console adds text to end, not at cursor -Bug #1295: Conversation loop when asking about "specific place" in Vivec -Bug #1296: Caius doesn't leave at start of quest "Mehra Milo and the Lost Prophecies" -Bug #1297: Saved game: map markers -Bug #1302: ring_keley script causes vector::_M_range_check exception -Bug #1309: Bug on "You violated the law" dialog -Bug #1319: Creatures sometimes rendered incorrectly -Feature #50: Ranged Combat -Feature #58: Sneaking Skill -Feature #73: Crime and Punishment -Feature #135: Editor: OGRE integration -Feature #541: Editor: Dialogue Sub-Views -Feature #853: Editor: Rework User Settings -Feature #944: Editor: lighting modes -Feature #945: Editor: Camera navigation mode -Feature #953: Trader gold -Feature #1140: AI: summoned creatures -Feature #1142: AI follow: Run stance -Feature #1154: Not all NPCs get aggressive when one is attacked -Feature #1169: Terrain threading -Feature #1172: Loading screen and progress bars during saved/loading game -Feature #1173: Saved Game: include weather state -Feature #1207: Class creation form does not remember -Feature #1220: Editor: Preview Subview -Feature #1223: Saved Game: Local Variables -Feature #1229: Quicksave, quickload, autosave -Feature #1230: Deleting saves -Feature #1233: Bribe gold is placed into NPCs inventory -Feature #1252: Saved Game: quick key bindings -Feature #1273: Editor: Region Map context menu -Feature #1274: Editor: Region Map drag & drop -Feature #1275: Editor: Scene subview drop -Feature #1282: Non-faction member crime recognition. -Feature #1289: NPCs return to default position -Task #941: Remove unused cmake files - -0.29.0 - -Bug #556: Video soundtrack not played when music volume is set to zero -Bug #829: OpenMW uses up all available vram, when playing for extended time -Bug #848: Wrong amount of footsteps playing in 1st person -Bug #888: Ascended Sleepers have movement issues -Bug #892: Explicit references are allowed on all script functions -Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly -Bug #1009: Lake Fjalding AI related slowdown. -Bug #1041: Music playback issues on OS X >= 10.9 -Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window -Bug #1060: Some message boxes are cut off at the bottom -Bug #1062: Bittercup script does not work ('end' variable) -Bug #1074: Inventory paperdoll obscures armour rating -Bug #1077: Message after killing an essential NPC disappears too fast -Bug #1078: "Clutterbane" shows empty charge bar -Bug #1083: UndoWerewolf fails -Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered -Bug #1090: Start scripts fail when going to a non-predefined cell -Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. -Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior -Bug #1105: Magicka is depleted when using uncastable spells -Bug #1106: Creatures should be able to run -Bug #1107: TR cliffs have way too huge collision boxes in OpenMW -Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. -Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) -Bug #1115: Memory leak when spying on Fargoth -Bug #1137: Script execution fails (drenSlaveOwners script) -Bug #1143: Mehra Milo quest (vivec informants) is broken -Bug #1145: Issues with moving gold between inventory and containers -Bug #1146: Issues with picking up stacks of gold -Bug #1147: Dwemer Crossbows are held incorrectly -Bug #1158: Armor rating should always stay below inventory mannequin -Bug #1159: Quick keys can be set during character generation -Bug #1160: Crash on equip lockpick when -Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file -Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file -Feature #30: Loading/Saving (still missing a few parts) -Feature #101: AI Package: Activate -Feature #103: AI Package: Follow, FollowCell -Feature #138: Editor: Drag & Drop -Feature #428: Player death -Feature #505: Editor: Record Cloning -Feature #701: Levelled creatures -Feature #708: Improved Local Variable handling -Feature #709: Editor: Script verifier -Feature #764: Missing journal backend features -Feature #777: Creature weapons/shields -Feature #789: Editor: Referenceable record verifier -Feature #924: Load/Save GUI (still missing loading screen and progress bars) -Feature #946: Knockdown -Feature #947: Decrease fatigue when running, swimming and attacking -Feature #956: Melee Combat: Blocking -Feature #957: Area magic -Feature #960: Combat/AI combat for creatures -Feature #962: Combat-Related AI instructions -Feature #1075: Damage/Restore skill/attribute magic effects -Feature #1076: Soultrap magic effect -Feature #1081: Disease contraction -Feature #1086: Blood particles -Feature #1092: Interrupt resting -Feature #1101: Inventory equip scripts -Feature #1116: Version/Build number in Launcher window -Feature #1119: Resistance/weakness to normal weapons magic effect -Feature #1123: Slow Fall magic effect -Feature #1130: Auto-calculate spells -Feature #1164: Editor: Case-insensitive sorting in tables - -0.28.0 - -Bug #399: Inventory changes are not visible immediately -Bug #417: Apply weather instantly when teleporting -Bug #566: Global Map position marker not updated for interior cells -Bug #712: Looting corpse delay -Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod -Bug #805: Two TR meshes appear black (v0.24RC) -Bug #841: Third-person activation distance taken from camera rather than head -Bug #845: NPCs hold torches during the day -Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24 -Bug #856: Maormer race by Mac Kom - The heads are way up -Bug #864: Walk locks during loading in 3rd person -Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog -Bug #882: Hircine's Ring doesn't always work -Bug #909: [Tamriel Rebuilt] crashes in Akamora -Bug #922: Launcher writing merged openmw.cfg files -Bug #943: Random magnitude should be calculated per effect -Bug #948: Negative fatigue level should be allowed -Bug #949: Particles in world space -Bug #950: Hard crash on x64 Linux running --new-game (on startup) -Bug #951: setMagicka and setFatigue have no effect -Bug #954: Problem with equipping inventory items when using a keyboard shortcut -Bug #955: Issues with equipping torches -Bug #966: Shield is visible when casting spell -Bug #967: Game crashes when equipping silver candlestick -Bug #970: Segmentation fault when starting at Bal Isra -Bug #977: Pressing down key in console doesn't go forward in history -Bug #979: Tooltip disappears when changing inventory -Bug #980: Barter: item category is remembered, but not shown -Bug #981: Mod: replacing model has wrong position/orientation -Bug #982: Launcher: Addon unchecking is not saved -Bug #983: Fix controllers to affect objects attached to the base node -Bug #985: Player can talk to NPCs who are in combat -Bug #989: OpenMW crashes when trying to include mod with capital .ESP -Bug #991: Merchants equip items with harmful constant effect enchantments -Bug #994: Don't cap skills/attributes when set via console -Bug #998: Setting the max health should also set the current health -Bug #1005: Torches are visible when casting spells and during hand to hand combat. -Bug #1006: Many NPCs have 0 skill -Bug #1007: Console fills up with text -Bug #1013: Player randomly loses health or dies -Bug #1014: Persuasion window is not centered in maximized window -Bug #1015: Player status window scroll state resets on status change -Bug #1016: Notification window not big enough for all skill level ups -Bug #1020: Saved window positions are not rescaled appropriately on resolution change -Bug #1022: Messages stuck permanently on screen when they pile up -Bug #1023: Journals doesn't open -Bug #1026: Game loses track of torch usage. -Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level -Bug #1029: Quick keys menu: Select compatible replacement when tool used up -Bug #1042: TES3 header data wrong encoding -Bug #1045: OS X: deployed OpenCS won't launch -Bug #1046: All damaged weaponry is worth 1 gold -Bug #1048: Links in "locked" dialogue are still clickable -Bug #1052: Using color codes when naming your character actually changes the name's color -Bug #1054: Spell effects not visible in front of water -Bug #1055: Power-Spell animation starts even though you already casted it that day -Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability -Bug #1063: Crash upon checking out game start ship area in Seyda Neen -Bug #1064: openmw binaries link to unnecessary libraries -Bug #1065: Landing from a high place in water still causes fall damage -Bug #1072: Drawing weapon increases torch brightness -Bug #1073: Merchants sell stacks of gold -Feature #43: Visuals for Magic Effects -Feature #51: Ranged Magic -Feature #52: Touch Range Magic -Feature #53: Self Range Magic -Feature #54: Spell Casting -Feature #70: Vampirism -Feature #100: Combat AI -Feature #171: Implement NIF record NiFlipController -Feature #410: Window to restore enchanted item charge -Feature #647: Enchanted item glow -Feature #723: Invisibility/Chameleon magic effects -Feature #737: Resist Magicka magic effect -Feature #758: GetLOS -Feature #926: Editor: Info-Record tables -Feature #958: Material controllers -Feature #959: Terrain bump, specular, & parallax mapping -Feature #990: Request: unlock mouse when in any menu -Feature #1018: Do not allow view mode switching while performing an action -Feature #1027: Vertex morph animation (NiGeomMorpherController) -Feature #1031: Handle NiBillboardNode -Feature #1051: Implement NIF texture slot DarkTexture -Task #873: Unify OGRE initialisation - -0.27.0 - -Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp -Bug #794: incorrect display of decimal numbers -Bug #840: First-person sneaking camera height -Bug #887: Ambient sounds playing while paused -Bug #902: Problems with Polish character encoding -Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key -Bug #910: Some CDs not working correctly with Unshield installer -Bug #917: Quick character creation plugin does not work -Bug #918: Fatigue does not refill -Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) -Feature #57: Acrobatics Skill -Feature #462: Editor: Start Dialogue -Feature #546: Modify ESX selector to handle new content file scheme -Feature #588: Editor: Adjust name/path of edited content files -Feature #644: Editor: Save -Feature #710: Editor: Configure script compiler context -Feature #790: God Mode -Feature #881: Editor: Allow only one instance of OpenCS -Feature #889: Editor: Record filtering -Feature #895: Extinguish torches -Feature #898: Breath meter enhancements -Feature #901: Editor: Default record filter -Feature #913: Merge --master and --plugin switches - -0.26.0 - -Bug #274: Inconsistencies in the terrain -Bug #557: Already-dead NPCs do not equip clothing/items. -Bug #592: Window resizing -Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) -Bug #664: Heart of lorkhan acts like a dead body (container) -Bug #767: Wonky ramp physics & water -Bug #780: Swimming out of water -Bug #792: Wrong ground alignment on actors when no clipping -Bug #796: Opening and closing door sound issue -Bug #797: No clipping hinders opening and closing of doors -Bug #799: sliders in enchanting window -Bug #838: Pressing key during startup procedure freezes the game -Bug #839: Combat/magic stances during character creation -Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment -Bug #844: Resting "until healed" option given even with full stats -Bug #846: Equipped torches are invisible. -Bug #847: Incorrect formula for autocalculated NPC initial health -Bug #850: Shealt weapon sound plays when leaving magic-ready stance -Bug #852: Some boots do not produce footstep sounds -Bug #860: FPS bar misalignment -Bug #861: Unable to print screen -Bug #863: No sneaking and jumping at the same time -Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. -Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. -Bug #868: Idle animations are repeated -Bug #874: Underwater swimming close to the ground is jerky -Bug #875: Animation problem while swimming on the surface and looking up -Bug #876: Always a starting upper case letter in the inventory -Bug #878: Active spell effects don't update the layout properly when ended -Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load -Bug #896: New game sound issue -Feature #49: Melee Combat -Feature #71: Lycanthropy -Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList -Feature #622: Multiple positions for inventory window -Feature #627: Drowning -Feature #786: Allow the 'Activate' key to close the countdialog window -Feature #798: Morrowind installation via Launcher (Linux/Max OS only) -Feature #851: First/Third person transitions with mouse wheel -Task #689: change PhysicActor::enableCollisions -Task #707: Reorganise Compiler - -0.25.0 - -Bug #411: Launcher crash on OS X < 10.8 -Bug #604: Terrible performance drop in the Census and Excise Office. -Bug #676: Start Scripts fail to load -Bug #677: OpenMW does not accept script names with - -Bug #766: Extra space in front of topic links -Bug #793: AIWander Isn't Being Passed The Repeat Parameter -Bug #795: Sound playing with drawn weapon and crossing cell-border -Bug #800: can't select weapon for enchantment -Bug #801: Player can move while over-encumbered -Bug #802: Dead Keys not working -Bug #808: mouse capture -Bug #809: ini Importer does not work without an existing cfg file -Bug #812: Launcher will run OpenMW with no ESM or ESP selected -Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected -Bug #817: Dead NPCs and Creatures still have collision boxes -Bug #820: Incorrect sorting of answers (Dialogue) -Bug #826: mwinimport dumps core when given an unknown parameter -Bug #833: getting stuck in door -Bug #835: Journals/books not showing up properly. -Feature #38: SoundGen -Feature #105: AI Package: Wander -Feature #230: 64-bit compatibility for OS X -Feature #263: Hardware mouse cursors -Feature #449: Allow mouse outside of window while paused -Feature #736: First person animations -Feature #750: Using mouse wheel in third person mode -Feature #822: Autorepeat for slider buttons - -0.24.0 - -Bug #284: Book's text misalignment -Bug #445: Camera able to get slightly below floor / terrain -Bug #582: Seam issue in Red Mountain -Bug #632: Journal Next Button shows white square -Bug #653: IndexedStore ignores index -Bug #694: Parser does not recognize float values starting with . -Bug #699: Resource handling broken with Ogre 1.9 trunk -Bug #718: components/esm/loadcell is using the mwworld subsystem -Bug #729: Levelled item list tries to add nonexistent item -Bug #730: Arrow buttons in the settings menu do not work. -Bug #732: Erroneous behavior when binding keys -Bug #733: Unclickable dialogue topic -Bug #734: Book empty line problem -Bug #738: OnDeath only works with implicit references -Bug #740: Script compiler fails on scripts with special names -Bug #742: Wait while no clipping -Bug #743: Problem with changeweather console command -Bug #744: No wait dialogue after starting a new game -Bug #748: Player is not able to unselect objects with the console -Bug #751: AddItem should only spawn a message box when called from dialogue -Bug #752: The enter button has several functions in trade and looting that is not impelemted. -Bug #753: Fargoth's Ring Quest Strange Behavior -Bug #755: Launcher writes duplicate lines into settings.cfg -Bug #759: Second quest in mages guild does not work -Bug #763: Enchantment cast cost is wrong -Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly -Bug #773: AIWander Isn't Being Passed The Correct idle Values -Bug #778: The journal can be opened at the start of a new game -Bug #779: Divayth Fyr starts as dead -Bug #787: "Batch count" on detailed FPS counter gets cut-off -Bug #788: chargen scroll layout does not match vanilla -Feature #60: Atlethics Skill -Feature #65: Security Skill -Feature #74: Interaction with non-load-doors -Feature #98: Render Weapon and Shield -Feature #102: AI Package: Escort, EscortCell -Feature #182: Advanced Journal GUI -Feature #288: Trading enhancements -Feature #405: Integrate "new game" into the menu -Feature #537: Highlight dialogue topic links -Feature #658: Rotate, RotateWorld script instructions and local rotations -Feature #690: Animation Layering -Feature #722: Night Eye/Blind magic effects -Feature #735: Move, MoveWorld script instructions. -Feature #760: Non-removable corpses - -0.23.0 - -Bug #522: Player collides with placeable items -Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open -Bug #561: Tooltip word wrapping delay -Bug #578: Bribing works incorrectly -Bug #601: PositionCell fails on negative coordinates -Bug #606: Some NPCs hairs not rendered with Better Heads addon -Bug #609: Bad rendering of bone boots -Bug #613: Messagebox causing assert to fail -Bug #631: Segfault on shutdown -Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard -Bug #635: Scale NPCs depending on race -Bug #643: Dialogue Race select function is inverted -Bug #646: Twohanded weapons don't work properly -Bug #654: Crash when dropping objects without a collision shape -Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell -Bug #660: "g" in "change" cut off in Race Menu -Bug #661: Arrille sells me the key to his upstairs room -Bug #662: Day counter starts at 2 instead of 1 -Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur -Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. -Bug #666: Looking up/down problem -Bug #667: Active effects border visible during loading -Bug #669: incorrect player position at new game start -Bug #670: race selection menu: sex, face and hair left button not totally clickable -Bug #671: new game: player is naked -Bug #674: buying or selling items doesn't change amount of gold -Bug #675: fatigue is not set to its maximum when starting a new game -Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly -Bug #680: different gold coins in Tel Mara -Bug #682: Race menu ignores playable flag for some hairs and faces -Bug #685: Script compiler does not accept ":" after a function name -Bug #688: dispose corpse makes cross-hair to disappear -Bug #691: Auto equipping ignores equipment conditions -Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder -Bug #696: Draugr incorrect head offset -Bug #697: Sail transparency issue -Bug #700: "On the rocks" mod does not load its UV coordinates correctly. -Bug #702: Some race mods don't work -Bug #711: Crash during character creation -Bug #715: Growing Tauryon -Bug #725: Auto calculate stats -Bug #728: Failure to open container and talk dialogue -Bug #731: Crash with Mush-Mere's "background" topic -Feature #55/657: Item Repairing -Feature #62/87: Enchanting -Feature #99: Pathfinding -Feature #104: AI Package: Travel -Feature #129: Levelled items -Feature #204: Texture animations -Feature #239: Fallback-Settings -Feature #535: Console object selection improvements -Feature #629: Add levelup description in levelup layout dialog -Feature #630: Optional format subrecord in (tes3) header -Feature #641: Armor rating -Feature #645: OnDeath script function -Feature #683: Companion item UI -Feature #698: Basic Particles -Task #648: Split up components/esm/loadlocks -Task #695: mwgui cleanup - -0.22.0 - -Bug #311: Potential infinite recursion in script compiler -Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. -Bug #382: Weird effect in 3rd person on water -Bug #387: Always use detailed shape for physics raycasts -Bug #420: Potion/ingredient effects do not stack -Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips -Bug #434/Bug #605: Object movement between cells not properly implemented -Bug #502: Duplicate player collision model at origin -Bug #509: Dialogue topic list shifts inappropriately -Bug #513: Sliding stairs -Bug #515: Launcher does not support non-latin strings -Bug #525: Race selection preview camera wrong position -Bug #526: Attributes / skills should not go below zero -Bug #529: Class and Birthsign menus options should be preselected -Bug #530: Lock window button graphic missing -Bug #532: Missing map menu graphics -Bug #545: ESX selector does not list ESM files properly -Bug #547: Global variables of type short are read incorrectly -Bug #550: Invisible meshes collision and tooltip -Bug #551: Performance drop when loading multiple ESM files -Bug #552: Don't list CG in options if it is not available -Bug #555: Character creation windows "OK" button broken -Bug #558: Segmentation fault when Alt-tabbing with console opened -Bug #559: Dialog window should not be available before character creation is finished -Bug #560: Tooltip borders should be stretched -Bug #562: Sound should not be played when an object cannot be picked up -Bug #565: Water animation speed + timescale -Bug #572: Better Bodies' textures don't work -Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) -Bug #574: Moving left/right should not cancel auto-run -Bug #575: Crash entering the Chamber of Song -Bug #576: Missing includes -Bug #577: Left Gloves Addon causes ESMReader exception -Bug #579: Unable to open container "Kvama Egg Sack" -Bug #581: Mimicking vanilla Morrowind water -Bug #583: Gender not recognized -Bug #586: Wrong char gen behaviour -Bug #587: "End" script statements with spaces don't work -Bug #589: Closing message boxes by pressing the activation key -Bug #590: Ugly Dagoth Ur rendering -Bug #591: Race selection issues -Bug #593: Persuasion response should be random -Bug #595: Footless guard -Bug #599: Waterfalls are invisible from a certain distance -Bug #600: Waterfalls rendered incorrectly, cut off by water -Bug #607: New beast bodies mod crashes -Bug #608: Crash in cell "Mournhold, Royal Palace" -Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt -Bug #613: Messagebox causing assert to fail -Bug #615: Meshes invisible from above water -Bug #617: Potion effects should be hidden until discovered -Bug #619: certain moss hanging from tree has rendering bug -Bug #621: Batching bloodmoon's trees -Bug #623: NiMaterialProperty alpha unhandled -Bug #628: Launcher in latest master crashes the game -Bug #633: Crash on startup: Better Heads -Bug #636: Incorrect Char Gen Menu Behavior -Feature #29: Allow ESPs and multiple ESMs -Feature #94: Finish class selection-dialogue -Feature #149: Texture Alphas -Feature #237: Run Morrowind-ini importer from launcher -Feature #286: Update Active Spell Icons -Feature #334: Swimming animation -Feature #335: Walking animation -Feature #360: Proper collision shapes for NPCs and creatures -Feature #367: Lights that behave more like original morrowind implementation -Feature #477: Special local scripting variables -Feature #528: Message boxes should close when enter is pressed under certain conditions. -Feature #543: Add bsa files to the settings imported by the ini importer -Feature #594: coordinate space and utility functions -Feature #625: Zoom in vanity mode -Task #464: Refactor launcher ESX selector into a re-usable component -Task #624: Unified implementation of type-variable sub-records - -0.21.0 - -Bug #253: Dialogs don't work for Russian version of Morrowind -Bug #267: Activating creatures without dialogue can still activate the dialogue GUI -Bug #354: True flickering lights -Bug #386: The main menu's first entry is wrong (in french) -Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations -Bug #495: Activation Range -Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned -Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available -Bug #500: Disposition for most NPCs is 0/100 -Bug #501: Getdisposition command wrongly returns base disposition -Bug #506: Journal UI doesn't update anymore -Bug #507: EnableRestMenu is not a valid command - change it to EnableRest -Bug #508: Crash in Ald Daedroth Shrine -Bug #517: Wrong price calculation when untrading an item -Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin -Bug #524: Beast races are able to wear shoes -Bug #527: Background music fails to play -Bug #533: The arch at Gnisis entrance is not displayed -Bug #534: Terrain gets its correct shape only some time after the cell is loaded -Bug #536: The same entry can be added multiple times to the journal -Bug #539: Race selection is broken -Bug #544: Terrain normal map corrupt when the map is rendered -Feature #39: Video Playback -Feature #151: ^-escape sequences in text output -Feature #392: Add AI related script functions -Feature #456: Determine required ini fallback values and adjust the ini importer accordingly -Feature #460: Experimental DirArchives improvements -Feature #540: Execute scripts of objects in containers/inventories in active cells -Task #401: Review GMST fixing -Task #453: Unify case smashing/folding -Task #512: Rewrite utf8 component - -0.20.0 - -Bug #366: Changing the player's race during character creation does not change the look of the player character -Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell -Bug #437: Stop animations when paused -Bug #438: Time displays as "0 a.m." when it should be "12 a.m." -Bug #439: Text in "name" field of potion/spell creation window is persistent -Bug #440: Starting date at a new game is off by one day -Bug #442: Console window doesn't close properly sometimes -Bug #448: Do not break container window formatting when item names are very long -Bug #458: Topics sometimes not automatically added to known topic list -Bug #476: Auto-Moving allows player movement after using DisablePlayerControls -Bug #478: After sleeping in a bed the rest dialogue window opens automtically again -Bug #492: On creating potions the ingredients are removed twice -Feature #63: Mercantile skill -Feature #82: Persuasion Dialogue -Feature #219: Missing dialogue filters/functions -Feature #369: Add a FailedAction -Feature #377: Select head/hair on character creation -Feature #391: Dummy AI package classes -Feature #435: Global Map, 2nd Layer -Feature #450: Persuasion -Feature #457: Add more script instructions -Feature #474: update the global variable pcrace when the player's race is changed -Task #158: Move dynamically generated classes from Player class to World Class -Task #159: ESMStore rework and cleanup -Task #163: More Component Namespace Cleanup -Task #402: Move player data from MWWorld::Player to the player's NPC record -Task #446: Fix no namespace in BulletShapeLoader - -0.19.0 - -Bug #374: Character shakes in 3rd person mode near the origin -Bug #404: Gamma correct rendering -Bug #407: Shoes of St. Rilm do not work -Bug #408: Rugs has collision even if they are not supposed to -Bug #412: Birthsign menu sorted incorrectly -Bug #413: Resolutions presented multiple times in launcher -Bug #414: launcher.cfg file stored in wrong directory -Bug #415: Wrong esm order in openmw.cfg -Bug #418: Sound listener position updates incorrectly -Bug #423: wrong usage of "Version" entry in openmw.desktop -Bug #426: Do not use hardcoded splash images -Bug #431: Don't use markers for raycast -Bug #432: Crash after picking up items from an NPC -Feature #21/#95: Sleeping/resting -Feature #61: Alchemy Skill -Feature #68: Death -Feature #69/#86: Spell Creation -Feature #72/#84: Travel -Feature #76: Global Map, 1st Layer -Feature #120: Trainer Window -Feature #152: Skill Increase from Skill Books -Feature #160: Record Saving -Task #400: Review GMST access - -0.18.0 - -Bug #310: Button of the "preferences menu" are too small -Bug #361: Hand-to-hand skill is always 100 -Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to -Bug #372: playSound3D uses original coordinates instead of current coordinates. -Bug #373: Static OGRE build faulty -Bug #375: Alt-tab toggle view -Bug #376: Screenshots are disable -Bug #378: Exception when drinking self-made potions -Bug #380: Cloth visibility problem -Bug #384: Weird character on doors tooltip. -Bug #398: Some objects do not collide in MW, but do so in OpenMW -Feature #22: Implement level-up -Feature #36: Hide Marker -Feature #88: Hotkey Window -Feature #91: Level-Up Dialogue -Feature #118: Keyboard and Mouse-Button bindings -Feature #119: Spell Buying Window -Feature #133: Handle resources across multiple data directories -Feature #134: Generate a suitable default-value for --data-local -Feature #292: Object Movement/Creation Script Instructions -Feature #340: AIPackage data structures -Feature #356: Ingredients use -Feature #358: Input system rewrite -Feature #370: Target handling in actions -Feature #379: Door markers on the local map -Feature #389: AI framework -Feature #395: Using keys to open doors / containers -Feature #396: Loading screens -Feature #397: Inventory avatar image and race selection head preview -Task #339: Move sounds into Action - -0.17.0 - -Bug #225: Valgrind reports about 40MB of leaked memory -Bug #241: Some physics meshes still don't match -Bug #248: Some textures are too dark -Bug #300: Dependency on proprietary CG toolkit -Bug #302: Some objects don't collide although they should -Bug #308: Freeze in Balmora, Meldor: Armorer -Bug #313: openmw without a ~/.config/openmw folder segfault. -Bug #317: adding non-existing spell via console locks game -Bug #318: Wrong character normals -Bug #341: Building with Ogre Debug libraries does not use debug version of plugins -Bug #347: Crash when running openmw with --start="XYZ" -Bug #353: FindMyGUI.cmake breaks path on Windows -Bug #359: WindowManager throws exception at destruction -Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation -Feature #33: Allow objects to cross cell-borders -Feature #59: Dropping Items (replaced stopgap implementation with a proper one) -Feature #93: Main Menu -Feature #96/329/330/331/332/333: Player Control -Feature #180: Object rotation and scaling. -Feature #272: Incorrect NIF material sharing -Feature #314: Potion usage -Feature #324: Skill Gain -Feature #342: Drain/fortify dynamic stats/attributes magic effects -Feature #350: Allow console only script instructions -Feature #352: Run scripts in console on startup -Task #107: Refactor mw*-subsystems -Task #325: Make CreatureStats into a class -Task #345: Use Ogre's animation system -Task #351: Rewrite Action class to support automatic sound playing - -0.16.0 - -Bug #250: OpenMW launcher erratic behaviour -Bug #270: Crash because of underwater effect on OS X -Bug #277: Auto-equipping in some cells not working -Bug #294: Container GUI ignores disabled inventory menu -Bug #297: Stats review dialog shows all skills and attribute values as 0 -Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses -Bug #299: Crash in World::disable -Bug #306: Non-existent ~/.config/openmw "crash" the launcher. -Bug #307: False "Data Files" location make the launcher "crash" -Feature #81: Spell Window -Feature #85: Alchemy Window -Feature #181: Support for x.y script syntax -Feature #242: Weapon and Spell icons -Feature #254: Ingame settings window -Feature #293: Allow "stacking" game modes -Feature #295: Class creation dialog tooltips -Feature #296: Clicking on the HUD elements should show/hide the respective window -Feature #301: Direction after using a Teleport Door -Feature #303: Allow object selection in the console -Feature #305: Allow the use of = as a synonym for == -Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts -Task #176: Restructure enabling/disabling of MW-references -Task #283: Integrate ogre.cfg file in settings file -Task #290: Auto-Close MW-reference related GUI windows - -0.15.0 - -Bug #5: Physics reimplementation (fixes various issues) -Bug #258: Resizing arrow's background is not transparent -Bug #268: Widening the stats window in X direction causes layout problems -Bug #269: Topic pane in dialgoue window is too small for some longer topics -Bug #271: Dialog choices are sorted incorrectly -Bug #281: The single quote character is not rendered on dialog windows -Bug #285: Terrain not handled properly in cells that are not predefined -Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs -Feature #15: Collision with Terrain -Feature #17: Inventory-, Container- and Trade-Windows -Feature #44: Floating Labels above Focussed Objects -Feature #80: Tooltips -Feature #83: Barter Dialogue -Feature #90: Book and Scroll Windows -Feature #156: Item Stacking in Containers -Feature #213: Pulsating lights -Feature #218: Feather & Burden -Feature #256: Implement magic effect bookkeeping -Feature #259: Add missing information to Stats window -Feature #260: Correct case for dialogue topics -Feature #280: GUI texture atlasing -Feature #291: Ability to use GMST strings from GUI layout files -Task #255: Make MWWorld::Environment into a singleton - -0.14.0 - -Bug #1: Meshes rendered with wrong orientation -Bug #6/Task #220: Picking up small objects doesn't always work -Bug #127: tcg doesn't work -Bug #178: Compablity problems with Ogre 1.8.0 RC 1 -Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI -Bug #227: Terrain crashes when moving away from predefined cells -Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces -Bug #235: TGA texture loading problem -Bug #246: wireframe mode does not work in water -Feature #8/#232: Water Rendering -Feature #13: Terrain Rendering -Feature #37: Render Path Grid -Feature #66: Factions -Feature #77: Local Map -Feature #78: Compass/Mini-Map -Feature #97: Render Clothing/Armour -Feature #121: Window Pinning -Feature #205: Auto equip -Feature #217: Contiainer should track changes to its content -Feature #221: NPC Dialogue Window Enhancements -Feature #233: Game settings manager -Feature #240: Spell List and selected spell (no GUI yet) -Feature #243: Draw State -Task #113: Morrowind.ini Importer -Task #215: Refactor the sound code -Task #216: Update MyGUI - -0.13.0 - -Bug #145: Fixed sound problems after cell change -Bug #179: Pressing space in console triggers activation -Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux -Bug #189: ASCII 16 character added to console on it's activation on Mac OS X -Bug #190: Case Folding fails with music files -Bug #192: Keypresses write Text into Console no matter which gui element is active -Bug #196: Collision shapes out of place -Bug #202: ESMTool doesn't not work with localised ESM files anymore -Bug #203: Torch lights only visible on short distance -Bug #207: Ogre.log not written -Bug #209: Sounds do not play -Bug #210: Ogre crash at Dren plantation -Bug #214: Unsupported file format version -Bug #222: Launcher is writing openmw.cfg file to wrong location -Feature #9: NPC Dialogue Window -Feature #16/42: New sky/weather implementation -Feature #40: Fading -Feature #48: NPC Dialogue System -Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) -Feature #161: Load REC_PGRD records -Feature #195: Wireframe-mode -Feature #198/199: Various sound effects -Feature #206: Allow picking data path from launcher if non is set -Task #108: Refactor window manager class -Task #172: Sound Manager Cleanup -Task #173: Create OpenEngine systems in the appropriate manager classes -Task #184: Adjust MSVC and gcc warning levels -Task #185: RefData rewrite -Task #201: Workaround for transparency issues -Task #208: silenced esm_reader.hpp warning - -0.12.0 - -Bug #154: FPS Drop -Bug #169: Local scripts continue running if associated object is deleted -Bug #174: OpenMW fails to start if the config directory doesn't exist -Bug #187: Missing lighting -Bug #188: Lights without a mesh are not rendered -Bug #191: Taking screenshot causes crash when running installed -Feature #28: Sort out the cell load problem -Feature #31: Allow the player to move away from pre-defined cells -Feature #35: Use alternate storage location for modified object position -Feature #45: NPC animations -Feature #46: Creature Animation -Feature #89: Basic Journal Window -Feature #110: Automatically pick up the path of existing MW-installations -Feature #183: More FPS display settings -Task #19: Refactor engine class -Task #109/Feature #162: Automate Packaging -Task #112: Catch exceptions thrown in input handling functions -Task #128/#168: Cleanup Configuration File Handling -Task #131: NPC Activation doesn't work properly -Task #144: MWRender cleanup -Task #155: cmake cleanup - -0.11.1 - -Bug #2: Resources loading doesn't work outside of bsa files -Bug #3: GUI does not render non-English characters -Bug #7: openmw.cfg location doesn't match -Bug #124: The TCL alias for ToggleCollision is missing. -Bug #125: Some command line options can't be used from a .cfg file -Bug #126: Toggle-type script instructions are less verbose compared with original MW -Bug #130: NPC-Record Loading fails for some NPCs -Bug #167: Launcher sets invalid parameters in ogre config -Feature #10: Journal -Feature #12: Rendering Optimisations -Feature #23: Change Launcher GUI to a tabbed interface -Feature #24: Integrate the OGRE settings window into the launcher -Feature #25: Determine openmw.cfg location (Launcher) -Feature #26: Launcher Profiles -Feature #79: MessageBox -Feature #116: Tab-Completion in Console -Feature #132: --data-local and multiple --data -Feature #143: Non-Rendering Performance-Optimisations -Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs -Feature #157: Version Handling -Task #14: Replace tabs with 4 spaces -Task #18: Move components from global namespace into their own namespace -Task #123: refactor header files in components/esm - -0.10.0 - -* NPC dialogue window (not functional yet) -* Collisions with objects -* Refactor the PlayerPos class -* Adjust file locations -* CMake files and test linking for Bullet -* Replace Ogre raycasting test for activation with something more precise -* Adjust player movement according to collision results -* FPS display -* Various Portability Improvements -* Mac OS X support is back! - -0.9.0 - -* Exterior cells loading, unloading and management -* Character Creation GUI -* Character creation -* Make cell names case insensitive when doing internal lookups -* Music player -* NPCs rendering - -0.8.0 - -* GUI -* Complete and working script engine -* In game console -* Sky rendering -* Sound and music -* Tons of smaller stuff - -0.7.0 - -* This release is a complete rewrite in C++. -* All D code has been culled, and all modules have been rewritten. -* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. - -0.6.0 - -* Coded a GUI system using MyGUI -* Skinned MyGUI to look like Morrowind (work in progress) -* Integrated the Monster script engine -* Rewrote some functions into script code -* Very early MyGUI < > Monster binding -* Fixed Windows sound problems (replaced old openal32.dll) - -0.5.0 - -* Collision detection with Bullet -* Experimental walk & fall character physics -* New key bindings: - * t toggle physics mode (walking, flying, ghost), - * n night eye, brightens the scene -* Fixed incompatability with DMD 1.032 and newer compilers -* * (thanks to tomqyp) -* Various minor changes and updates - -0.4.0 - -* Switched from Audiere to OpenAL -* * (BIG thanks to Chris Robinson) -* Added complete Makefile (again) as a alternative build tool -* More realistic lighting (thanks again to Chris Robinson) -* Various localization fixes tested with Russian and French versions -* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' -* Added ns option to disable sound, for debugging -* Various bug fixes -* Cosmetic changes to placate gdc Wall - -0.3.0 - -* Built and tested on Windows XP -* Partial support for FreeBSD (exceptions do not work) -* You no longer have to download Monster separately -* Made an alternative for building without DSSS (but DSSS still works) -* Renamed main program from 'morro' to 'openmw' -* Made the config system more robust -* Added oc switch for showing Ogre config window on startup -* Removed some config files, these are auto generated when missing. -* Separated plugins.cfg into linux and windows versions. -* Updated Makefile and sources for increased portability -* confirmed to work against OIS 1.0.0 (Ubuntu repository package) - -0.2.0 - -* Compiles with gdc -* Switched to DSSS for building D code -* Includes the program esmtool - -0.1.0 - -first release +See CHANGELOG.md From c8373198a1b8c714ead2db7d38641d4f30a5db3e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:36:59 +0100 Subject: [PATCH 271/404] Add Travis-CI build status image to Readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 862ae16c75..63bf1bcf39 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind ============================================================== +[![Build Status](https://travis-ci.org/OpenMW/openmw.svg?branch=coverity_scan)](https://travis-ci.org/OpenMW/openmw) + OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. From 32ad1ae1761e1961ccb2acf25669f3e97fb2799c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:39:00 +0100 Subject: [PATCH 272/404] Add coverity scan build status image to Readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 63bf1bcf39..bc1efa562b 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind [![Build Status](https://travis-ci.org/OpenMW/openmw.svg?branch=coverity_scan)](https://travis-ci.org/OpenMW/openmw) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) + OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. From 5ee78602e98326601a7058183677497c22552ced Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:44:28 +0100 Subject: [PATCH 273/404] Improve readme formatting --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index bc1efa562b..754e4d5f7a 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,18 @@ Font Licenses: Installation ============ -Windows: +Windows +------- Run the installer. -Linux: -Ubuntu (and most others) -Download the .deb file and install it in the usual way. +Linux +----- +* Ubuntu (and most others): Download the .deb file and install it in the usual way. -Arch Linux -There's an OpenMW package available in the [community] Repository: -https://www.archlinux.org/packages/?sort=&q=openmw +* Arch Linux: There's an OpenMW package available in the [community] Repository: https://www.archlinux.org/packages/?sort=&q=openmw -OS X: +OS X +---- Open DMG file, copy OpenMW folder anywhere, for example in /Applications Build from source From 3c74c554f811fe8e71d50fe6ab4ac0f8857385f0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:45:20 +0100 Subject: [PATCH 274/404] Add IRC channel to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 754e4d5f7a..de06554c1e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Morrowind by Bethesda Softworks. You need to own and install the original game f * Version: 0.34.0 * License: GPL (see GPL3.txt for more information) * Website: http://www.openmw.org +* IRC: #openmw on irc.freenode.net Font Licenses: * DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) From d1b07faa5678999f04e3d6e06162cef49115cf7d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:49:59 +0100 Subject: [PATCH 275/404] Replace outdated Installation section with link --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index de06554c1e..279d1d6d78 100644 --- a/README.md +++ b/README.md @@ -19,19 +19,7 @@ Font Licenses: Installation ============ -Windows -------- -Run the installer. - -Linux ------ -* Ubuntu (and most others): Download the .deb file and install it in the usual way. - -* Arch Linux: There's an OpenMW package available in the [community] Repository: https://www.archlinux.org/packages/?sort=&q=openmw - -OS X ----- -Open DMG file, copy OpenMW folder anywhere, for example in /Applications +Head to [Downloads](http://openmw.org/downloads/) to find a packaged release for your platform of choice. Build from source ================= From bcddfabea135be3ff034ee11a0d0f79736272a57 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:54:55 +0100 Subject: [PATCH 276/404] Fix license file names in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 279d1d6d78..ff5185b5df 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. * Version: 0.34.0 -* License: GPL (see GPL3.txt for more information) +* License: GPL (see docs/license/GPL3.txt for more information) * Website: http://www.openmw.org * IRC: #openmw on irc.freenode.net Font Licenses: -* DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) +* DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information) Installation ============ From 36a2c11da4e0f59c871673e482f3ab4c80f28f90 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 16:59:21 +0100 Subject: [PATCH 277/404] Move credits.txt to AUTHORS.md to make it consistent with most other github projects --- credits.txt => AUTHORS.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) rename credits.txt => AUTHORS.md (93%) diff --git a/credits.txt b/AUTHORS.md similarity index 93% rename from credits.txt rename to AUTHORS.md index dfdf875653..b6aec55f5c 100644 --- a/credits.txt +++ b/AUTHORS.md @@ -1,4 +1,5 @@ Contributors +============ The OpenMW project was started in 2008 by Nicolay Korslund. In the course of years many people have contributed to the project. @@ -7,7 +8,9 @@ If you feel your name is missing from this list, please notify a developer. -Programmers: +Programmers +----------- + Marc Zinnschlag (Zini) - Lead Programmer/Project Manager Adam Hogan (aurix) @@ -103,7 +106,8 @@ viadanna Vincent Heuken vocollapse -Packagers: +Packagers +--------- Alexander Olofsson (Ace) - Windows Bret Curtis (psi29a) - Ubuntu Linux Edmondo Tommasina (edmondo) - Gentoo Linux @@ -113,7 +117,8 @@ Kenny Armstrong (artorius) - Fedora Linux Nikolay Kasyanov (corristo) - Mac OS X Sandy Carter (bwrsandman) - Arch Linux -Public Relations and Translations: +Public Relations and Translations +--------------------------------- Alex McKibben (WeirdSexy) - Podcaster Artem Kotsynyak (greye) - Russian News Writer Jim Clauwaert (Zedd) - Public Outreach @@ -125,12 +130,14 @@ Pithorn - Chinese News Writer sir_herrbatka - Polish News Writer Dawid Lakomy (Vedyimyn) - Polish News Writer -Website: +Website +------- Lukasz Gromanowski (Lgro) - Website Administrator Ryan Sardonic (Wry) - Wiki Editor sir_herrbatka - Forum Administrator -Formula Research: +Formula Research +---------------- Hrnchamd Epsilon fragonard @@ -141,12 +148,14 @@ Myckel natirips Sadler -Artwork: +Artwork +------- Necrod - OpenMW Logo Mickey Lyle (raevol) - Wordpress Theme Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel - OpenMW Editor Icons -Inactive Contributors: +Inactive Contributors +--------------------- Ardekantur Armin Preiml Berulacks @@ -174,7 +183,8 @@ Thoronador Yuri Krupenin -Additional Credits: +Additional Credits +------------------ In this section we would like to thank people not part of OpenMW for their work. Thanks to Maxim Nikolaev, From 6079bcad245e22a9c778571a4b4387e367e804f0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 17:03:41 +0100 Subject: [PATCH 278/404] Formatting fix --- AUTHORS.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index b6aec55f5c..dddc61a131 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -4,13 +4,12 @@ Contributors The OpenMW project was started in 2008 by Nicolay Korslund. In the course of years many people have contributed to the project. -If you feel your name is missing from this list, -please notify a developer. +If you feel your name is missing from this list, please notify a developer. Programmers ----------- - +
 Marc Zinnschlag (Zini) - Lead Programmer/Project Manager
 
 Adam Hogan (aurix)
@@ -105,9 +104,10 @@ Torben Leif Carrington (TorbenC)
 viadanna
 Vincent Heuken
 vocollapse
-
+
Packagers --------- +
 Alexander Olofsson (Ace) - Windows
 Bret Curtis (psi29a) - Ubuntu Linux
 Edmondo Tommasina (edmondo) - Gentoo Linux
@@ -116,9 +116,10 @@ Karl-Felix Glatzer (k1ll) - Linux Binaries
 Kenny Armstrong (artorius) - Fedora Linux
 Nikolay Kasyanov (corristo) - Mac OS X
 Sandy Carter (bwrsandman) - Arch Linux
-
+
Public Relations and Translations --------------------------------- +
 Alex McKibben (WeirdSexy) - Podcaster
 Artem Kotsynyak (greye) - Russian News Writer
 Jim Clauwaert (Zedd) - Public Outreach
@@ -129,15 +130,17 @@ Mickey Lyle (raevol) - Release Manager
 Pithorn - Chinese News Writer
 sir_herrbatka - Polish News Writer
 Dawid Lakomy (Vedyimyn) - Polish News Writer
-
+
Website ------- +
 Lukasz Gromanowski (Lgro) - Website Administrator
 Ryan Sardonic (Wry) - Wiki Editor
 sir_herrbatka - Forum Administrator
-
+
Formula Research ---------------- +
 Hrnchamd
 Epsilon
 fragonard
@@ -147,15 +150,17 @@ modred11
 Myckel
 natirips
 Sadler
-
+
Artwork ------- +
 Necrod - OpenMW Logo
 Mickey Lyle (raevol) - Wordpress Theme
 Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel - OpenMW Editor Icons
-
+
Inactive Contributors --------------------- +
 Ardekantur
 Armin Preiml
 Berulacks
@@ -181,7 +186,7 @@ spyboot
 Star-Demon
 Thoronador
 Yuri Krupenin
-
+
Additional Credits ------------------ From 529fd353fe0724a179595854f0f92f137cd39fca Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 17:05:24 +0100 Subject: [PATCH 279/404] EB Garamond and Daedric Fontface are no longer used --- AUTHORS.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index dddc61a131..f6da26e77d 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -196,17 +196,10 @@ Thanks to Maxim Nikolaev, for allowing us to use his excellent Morrowind fan-art on our website and in other places. Thanks to DokterDume, -for kindly providing us with the Moon and Star logo, -used as the application icon and project logo. +for kindly providing us with the Moon and Star logo, used as the application icon and project logo. Thanks to Kevin Ryan, for creating the icon used for the Data Files tab of the OpenMW Launcher. -Thanks to Georg Duffner, -for his EB Garamond fontface, see OFL.txt for his license terms. - -Thanks to Dongle, -for his Daedric fontface, see Daedric Font License.txt for his license terms. - Thanks to DejaVu team, for their DejaVuLGCSansMono fontface, see DejaVu Font License.txt for their license terms. From 3684f586cd6cad55cc5fb1d49db18d12a1ff8308 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 17:18:33 +0100 Subject: [PATCH 280/404] Changelog formatting --- CHANGELOG.md | 2677 +++++++++++++++++++++++++------------------------- 1 file changed, 1355 insertions(+), 1322 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09f318d9d1..750cb5476f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,1376 +1,1401 @@ -
 0.34.0
-
-Bug #904: omwlauncher doesn't allow installing Tribunal and Bloodmoon if only MW is installed
-Bug #986: Launcher: renaming profile names is broken
-Bug #1061: "Browse to CD..." launcher crash
-Bug #1135: Launcher crashes if user does not have write permission
-Bug #1231: Current installer in launcher does not correctly import russian Morrowind.ini settings from setup.inx
-Bug #1288: Fix the Alignment of the Resolution Combobox
-Bug #1343: BIK videos occasionally out of sync with audio
-Bug #1684: Morrowind Grass Mod graphical glitches
-Bug #1734: NPC in fight with invisible/sneaking player
-Bug #1982: Long class names are cut off in the UI
-Bug #2012: Editor: OpenCS script compiler sometimes fails to find IDs
-Bug #2015: Running while levitating does not affect speed but still drains fatigue
-Bug #2018: OpenMW don´t reset modified cells to vanilla when a plugin is deselected and don´t apply changes to cells already visited.
-Bug #2045: ToggleMenus command should close dialogue windows
-Bug #2046: Crash: light_de_streetlight_01_223
-Bug #2047: Buglamp tooltip minor correction
-Bug #2050: Roobrush floating texture bits
-Bug #2053: Slaves react negatively to PC picking up slave's bracers
-Bug #2055: Dremora corpses use the wrong model
-Bug #2056: Mansilamat Vabdas's corpse is floating in the water
-Bug #2057: "Quest: Larius Varro Tells A Little Story": Bounty not completely removed after finishing quest
-Bug #2059: Silenced enemies try to cast spells anyway
-Bug #2060: Editor: Special case implementation for top level window with single sub-window should be optional
-Bug #2061: Editor: SubView closing that is not directly triggered by the user isn't handled properly
-Bug #2063: Tribunal: Quest 'The Warlords' doesn't work
-Bug #2064: Sneak attack on hostiles causes bounty
-Bug #2065: Editor: Qt signal-slot error when closing a dialogue subview
-Bug #2070: Loading ESP in OpenMW works but fails in OpenCS
-Bug #2071: CTD in 0.33
-Bug #2073: Storm atronach animation stops now and then
-Bug #2075: Molag Amur Region, Map shows water on solid ground
-Bug #2080: game won't work with fair magicka regen
-Bug #2082: NPCs appear frozen or switched off after leaving and quickly reentering a cell
-Bug #2088: OpenMW is unable to play OGG files.
-Bug #2093: Darth Gares talks to you in Ilunibi even when he's not there, screwing up the Main Quests
-Bug #2095: Coordinate and rotation editing in the Reference table does not work.
-Bug #2096: Some overflow fun and bartering exploit
-Bug #2098: [D3D] Game crash on maximize
-Bug #2099: Activate, player seems not to work
-Bug #2104: Only labels are sensitive in buttons
-Bug #2107: "Slowfall" effect is too weak
-Bug #2114: OpenCS doesn't load an ESP file full of errors even though Vanilla MW Construction Set can
-Bug #2117: Crash when encountering bandits on opposite side of river from the egg mine south of Balmora
-Bug #2124: [Mod: Baldurians Transparent Glass Amor] Armor above head
-Bug #2125: Unnamed NiNodes in weapons problem in First Person
-Bug #2126: Dirty dialog script in tribunal.esm causing bug in Tribunal MQ
-Bug #2128: Crash when picking character's face
-Bug #2129: Disable the third-person zoom feature by default
-Bug #2130: Ash storm particles shown too long during transition to clear sky
-Bug #2137: Editor: exception caused by following the Creature column of a SoundGen record
-Bug #2139: Mouse movement should be ignored during intro video
-Bug #2143: Editor: Saving is broken
-Bug #2145: OpenMW - crash while exiting x64 debug build
-Bug #2152: You can attack Almalexia during her final monologue
-Bug #2154: Visual effects behave weirdly after loading/taking a screenshot
-Bug #2155: Vivec has too little magicka
-Bug #2156: Azura's spirit fades away too fast
-Bug #2158: [Mod]Julan Ashlander Companion 2.0: Negative magicka
-Bug #2161: Editor: combat/magic/stealth values of creature not displayed correctly
-Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon.
-Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly.
-Bug #2170: Mods using conversations to update PC inconsistant
-Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources
-Bug #2212: Crash or unexpected behavior while closing OpenCS cell render window on OS X
-Feature #238: Add UI to run INI-importer from the launcher
-Feature #854: Editor: Add user setting to show status bar
-Feature #987: Launcher: first launch instructions for CD need to be more explicit
-Feature #1232: There is no way to set the "encoding" option using launcher UI.
-Feature #1281: Editor: Render cell markers
-Feature #1918: Editor: Functionality for Double-Clicking in Tables
-Feature #1966: Editor: User Settings dialogue grouping/labelling/tooltips
-Feature #2097: Editor: Edit position of references in 3D scene
-Feature #2121: Editor: Add edit mode button to scene toolbar
-Task #1965: Editor: Improve layout of user settings dialogue
+------
+
+    Bug #904: omwlauncher doesn't allow installing Tribunal and Bloodmoon if only MW is installed
+    Bug #986: Launcher: renaming profile names is broken
+    Bug #1061: "Browse to CD..." launcher crash
+    Bug #1135: Launcher crashes if user does not have write permission
+    Bug #1231: Current installer in launcher does not correctly import russian Morrowind.ini settings from setup.inx
+    Bug #1288: Fix the Alignment of the Resolution Combobox
+    Bug #1343: BIK videos occasionally out of sync with audio
+    Bug #1684: Morrowind Grass Mod graphical glitches
+    Bug #1734: NPC in fight with invisible/sneaking player
+    Bug #1982: Long class names are cut off in the UI
+    Bug #2012: Editor: OpenCS script compiler sometimes fails to find IDs
+    Bug #2015: Running while levitating does not affect speed but still drains fatigue
+    Bug #2018: OpenMW don´t reset modified cells to vanilla when a plugin is deselected and don´t apply changes to cells already visited.
+    Bug #2045: ToggleMenus command should close dialogue windows
+    Bug #2046: Crash: light_de_streetlight_01_223
+    Bug #2047: Buglamp tooltip minor correction
+    Bug #2050: Roobrush floating texture bits
+    Bug #2053: Slaves react negatively to PC picking up slave's bracers
+    Bug #2055: Dremora corpses use the wrong model
+    Bug #2056: Mansilamat Vabdas's corpse is floating in the water
+    Bug #2057: "Quest: Larius Varro Tells A Little Story": Bounty not completely removed after finishing quest
+    Bug #2059: Silenced enemies try to cast spells anyway
+    Bug #2060: Editor: Special case implementation for top level window with single sub-window should be optional
+    Bug #2061: Editor: SubView closing that is not directly triggered by the user isn't handled properly
+    Bug #2063: Tribunal: Quest 'The Warlords' doesn't work
+    Bug #2064: Sneak attack on hostiles causes bounty
+    Bug #2065: Editor: Qt signal-slot error when closing a dialogue subview
+    Bug #2070: Loading ESP in OpenMW works but fails in OpenCS
+    Bug #2071: CTD in 0.33
+    Bug #2073: Storm atronach animation stops now and then
+    Bug #2075: Molag Amur Region, Map shows water on solid ground
+    Bug #2080: game won't work with fair magicka regen
+    Bug #2082: NPCs appear frozen or switched off after leaving and quickly reentering a cell
+    Bug #2088: OpenMW is unable to play OGG files.
+    Bug #2093: Darth Gares talks to you in Ilunibi even when he's not there, screwing up the Main Quests
+    Bug #2095: Coordinate and rotation editing in the Reference table does not work.
+    Bug #2096: Some overflow fun and bartering exploit
+    Bug #2098: [D3D] Game crash on maximize
+    Bug #2099: Activate, player seems not to work
+    Bug #2104: Only labels are sensitive in buttons
+    Bug #2107: "Slowfall" effect is too weak
+    Bug #2114: OpenCS doesn't load an ESP file full of errors even though Vanilla MW Construction Set can
+    Bug #2117: Crash when encountering bandits on opposite side of river from the egg mine south of Balmora
+    Bug #2124: [Mod: Baldurians Transparent Glass Amor] Armor above head
+    Bug #2125: Unnamed NiNodes in weapons problem in First Person
+    Bug #2126: Dirty dialog script in tribunal.esm causing bug in Tribunal MQ
+    Bug #2128: Crash when picking character's face
+    Bug #2129: Disable the third-person zoom feature by default
+    Bug #2130: Ash storm particles shown too long during transition to clear sky
+    Bug #2137: Editor: exception caused by following the Creature column of a SoundGen record
+    Bug #2139: Mouse movement should be ignored during intro video
+    Bug #2143: Editor: Saving is broken
+    Bug #2145: OpenMW - crash while exiting x64 debug build
+    Bug #2152: You can attack Almalexia during her final monologue
+    Bug #2154: Visual effects behave weirdly after loading/taking a screenshot
+    Bug #2155: Vivec has too little magicka
+    Bug #2156: Azura's spirit fades away too fast
+    Bug #2158: [Mod]Julan Ashlander Companion 2.0: Negative magicka
+    Bug #2161: Editor: combat/magic/stealth values of creature not displayed correctly
+    Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon.
+    Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly.
+    Bug #2170: Mods using conversations to update PC inconsistant
+    Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources
+    Bug #2212: Crash or unexpected behavior while closing OpenCS cell render window on OS X
+    Feature #238: Add UI to run INI-importer from the launcher
+    Feature #854: Editor: Add user setting to show status bar
+    Feature #987: Launcher: first launch instructions for CD need to be more explicit
+    Feature #1232: There is no way to set the "encoding" option using launcher UI.
+    Feature #1281: Editor: Render cell markers
+    Feature #1918: Editor: Functionality for Double-Clicking in Tables
+    Feature #1966: Editor: User Settings dialogue grouping/labelling/tooltips
+    Feature #2097: Editor: Edit position of references in 3D scene
+    Feature #2121: Editor: Add edit mode button to scene toolbar
+    Task #1965: Editor: Improve layout of user settings dialogue
 
 0.33.1
+------
 
-Bug #2108: OpenCS fails to build
+    Bug #2108: OpenCS fails to build
 
 0.33.0
-
-Bug #371: If console assigned to ` (probably to any symbolic key), "`" symbol will be added to console every time it closed
-Bug #1148: Some books'/scrolls' contents are displayed incorrectly
-Bug #1290: Editor: status bar is not updated when record filter is changed
-Bug #1292: Editor: Documents are not removed on closing the last view
-Bug #1301: Editor: File->Exit only checks the document it was issued from.
-Bug #1353: Bluetooth on with no speaker connected results in significantly longer initial load times
-Bug #1436: NPCs react from too far distance
-Bug #1472: PC is placed on top of following NPC when changing cell
-Bug #1487: Tall PC can get stuck in staircases
-Bug #1565: Editor: Subviews are deleted on shutdown instead when they are closed
-Bug #1623: Door marker on Ghorak Manor's balcony makes PC stuck
-Bug #1633: Loaddoor to Sadrith Mora, Telvanni Council House spawns PC in the air
-Bug #1655: Use Appropriate Application Icons on Windows
-Bug #1679: Tribunal expansion, Meryn Othralas the backstage manager in the theatre group in Mournhold in the great bazaar district is floating a good feet above the ground.
-Bug #1705: Rain is broken in third person
-Bug #1706: Thunder and lighting still occurs while the game is paused during the rain
-Bug #1708: No long jumping
-Bug #1710: Editor: ReferenceableID drag to references record filter field creates incorrect filter
-Bug #1712: Rest on Water
-Bug #1715: "Cancel" button is not always on the same side of menu
-Bug #1725: Editor: content file can be opened multiple times from the same dialogue
-Bug #1730: [MOD: Less Generic Nerevarine] Compile failure attempting to enter the Corprusarium.
-Bug #1733: Unhandled ffmpeg sample formats
-Bug #1735: Editor: "Edit Record" context menu button not opening subview for journal infos
-Bug #1750: Editor: record edits result in duplicate entries
-Bug #1789: Editor: Some characters cannot be used in addon name
-Bug #1803: Resizing the map does not keep the pre-resize center at the post-resize center
-Bug #1821: Recovering Cloudcleaver quest: attacking Sosia is considered a crime when you side with Hlormar
-Bug #1838: Editor: Preferences window appears off screen
-Bug #1839: Editor: Record filter title should be moved two pixels to the right
-Bug #1849: Subrecord error in MAO_Containers
-Bug #1854: Knocked-out actors don't fully act knocked out
-Bug #1855: "Soul trapped" sound doesn't play
-Bug #1857: Missing sound effect for enchanted items with empty charge
-Bug #1859: Missing console command: ResetActors (RA)
-Bug #1861: Vendor category "MagicItems" is unhandled
-Bug #1862: Launcher doesn't start if a file listed in launcher.cfg has correct name but wrong capitalization
-Bug #1864: Editor: Region field for cell record in dialogue subview not working
-Bug #1869: Editor: Change label "Musics" to "Music"
-Bug #1870: Goblins killed while knocked down remain in knockdown-pose
-Bug #1874: CellChanged events should not trigger when crossing exterior cell border
-Bug #1877: Spriggans killed instantly if hit while regening
-Bug #1878: Magic Menu text not un-highlighting correctly when going from spell to item as active magic
-Bug #1881: Stuck in ceiling when entering castle karstaags tower
-Bug #1884: Unlit torches still produce a burning sound
-Bug #1885: Can type text in price field in barter window
-Bug #1887: Equipped items do not emit sounds
-Bug #1889: draugr lord aesliip will attack you and remain non-hostile
-Bug #1892: Guard asks player to pay bounty of 0 gold
-Bug #1895: getdistance should only return max float if ref and target are in different worldspaces
-Bug #1896: Crash Report
-Bug #1897: Conjured Equipment cant be re-equipped if removed
-Bug #1898: Only Gidar Verothan follows you during establish the mine quest
-Bug #1900: Black screen when you open the door and breath underwater
-Bug #1904: Crash on casting recall spell
-Bug #1906: Bound item checks should use the GMSTs
-Bug #1907: Bugged door. Mournhold, The Winged Guar
-Bug #1908: Crime reported for attacking Drathas Nerus's henchmen while they attack Dilborn
-Bug #1909: Weird Quest Flow Infidelities quest
-Bug #1910: Follower fighting with gone npc
-Bug #1911: Npcs will drown themselves
-Bug #1912: World map arrow stays static when inside a building
-Bug #1920: Ulyne Henim disappears when game is loaded inside Vas
-Bug #1922: alchemy-> potion of paralyze
-Bug #1923: "levitation magic cannot be used here" shows outside of tribunal
-Bug #1927: AI prefer melee over magic.
-Bug #1929: Tamriel Rebuilt: Named cells that lie within the overlap with Morrowind.esm are not shown
-Bug #1932: BTB - Spells 14.1 magic effects don´t overwrite the Vanilla ones but are added
-Bug #1935: Stacks of items are worth more when sold individually
-Bug #1940: Launcher does not list addon files if base game file is renamed to a different case
-Bug #1946: Mod "Tel Nechim - moved" breaks savegames
-Bug #1947: Buying/Selling price doesn't properly affect the growth of mercantile skill
-Bug #1950: followers from east empire company quest will fight each other if combat happens with anything
-Bug #1958: Journal can be scrolled indefinitely with a mouse wheel
-Bug #1959: Follower not leaving party on quest end
-Bug #1960: Key bindings not always saved correctly
-Bug #1961: Spell merchants selling racial bonus spells
-Bug #1967: segmentation fault on load saves
-Bug #1968: Jump sounds are not controlled by footsteps slider, sound weird compared to footsteps
-Bug #1970: PC suffers silently when taking damage from lava
-Bug #1971: Dwarven Sceptre collision area is not removed after killing one
-Bug #1974: Dalin/Daris Norvayne follows player indefinitely
-Bug #1975: East Empire Company faction rank breaks during Raven Rock questline
-Bug #1979: 0 strength = permanently over encumbered
-Bug #1993: Shrine blessing in Maar Gan doesn't work
-Bug #2008: Enchanted items do not recharge
-Bug #2011: Editor: OpenCS script compiler doesn't handle member variable access properly
-Bug #2016: Dagoth Ur already dead in Facility Cavern
-Bug #2017: Fighters Guild Quest: The Code Book - dialogue loop when UMP is loaded.
-Bug #2019: Animation of 'Correct UV Mudcrabs' broken
-Bug #2022: Alchemy window - Removing ingredient doesn't remove the number of ingredients
-Bug #2025: Missing mouse-over text for non affordable items
-Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell "Ruinous Keep, Great Hall"
-Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding
-Feature #471: Editor: Special case implementation for top-level window with single sub-window
-Feature #472: Editor: Sub-Window re-use settings
-Feature #704: Font colors import from fallback settings
-Feature #879: Editor: Open sub-views in a new top-level window
-Feature #932: Editor: magic effect table
-Feature #937: Editor: Path Grid table
-Feature #938: Editor: Sound Gen table
-Feature #1117: Death and LevelUp music
-Feature #1226: Editor: Request UniversalId editing from table columns
-Feature #1545: Targeting console on player
-Feature #1597: Editor: Render terrain
-Feature #1695: Editor: add column for CellRef's global variable
-Feature #1696: Editor: use ESM::Cell's RefNum counter
-Feature #1697: Redden player's vision when hit
-Feature #1856: Spellcasting for non-biped creatures
-Feature #1879: Editor: Run OpenMW with the currently edited content list
-Task #1851: Move AI temporary state out of AI packages
-Task #1865: Replace char type in records
+------
+
+    Bug #371: If console assigned to ` (probably to any symbolic key), "`" symbol will be added to console every time it closed
+    Bug #1148: Some books'/scrolls' contents are displayed incorrectly
+    Bug #1290: Editor: status bar is not updated when record filter is changed
+    Bug #1292: Editor: Documents are not removed on closing the last view
+    Bug #1301: Editor: File->Exit only checks the document it was issued from.
+    Bug #1353: Bluetooth on with no speaker connected results in significantly longer initial load times
+    Bug #1436: NPCs react from too far distance
+    Bug #1472: PC is placed on top of following NPC when changing cell
+    Bug #1487: Tall PC can get stuck in staircases
+    Bug #1565: Editor: Subviews are deleted on shutdown instead when they are closed
+    Bug #1623: Door marker on Ghorak Manor's balcony makes PC stuck
+    Bug #1633: Loaddoor to Sadrith Mora, Telvanni Council House spawns PC in the air
+    Bug #1655: Use Appropriate Application Icons on Windows
+    Bug #1679: Tribunal expansion, Meryn Othralas the backstage manager in the theatre group in Mournhold in the great bazaar district is floating a good feet above the ground.
+    Bug #1705: Rain is broken in third person
+    Bug #1706: Thunder and lighting still occurs while the game is paused during the rain
+    Bug #1708: No long jumping
+    Bug #1710: Editor: ReferenceableID drag to references record filter field creates incorrect filter
+    Bug #1712: Rest on Water
+    Bug #1715: "Cancel" button is not always on the same side of menu
+    Bug #1725: Editor: content file can be opened multiple times from the same dialogue
+    Bug #1730: [MOD: Less Generic Nerevarine] Compile failure attempting to enter the Corprusarium.
+    Bug #1733: Unhandled ffmpeg sample formats
+    Bug #1735: Editor: "Edit Record" context menu button not opening subview for journal infos
+    Bug #1750: Editor: record edits result in duplicate entries
+    Bug #1789: Editor: Some characters cannot be used in addon name
+    Bug #1803: Resizing the map does not keep the pre-resize center at the post-resize center
+    Bug #1821: Recovering Cloudcleaver quest: attacking Sosia is considered a crime when you side with Hlormar
+    Bug #1838: Editor: Preferences window appears off screen
+    Bug #1839: Editor: Record filter title should be moved two pixels to the right
+    Bug #1849: Subrecord error in MAO_Containers
+    Bug #1854: Knocked-out actors don't fully act knocked out
+    Bug #1855: "Soul trapped" sound doesn't play
+    Bug #1857: Missing sound effect for enchanted items with empty charge
+    Bug #1859: Missing console command: ResetActors (RA)
+    Bug #1861: Vendor category "MagicItems" is unhandled
+    Bug #1862: Launcher doesn't start if a file listed in launcher.cfg has correct name but wrong capitalization
+    Bug #1864: Editor: Region field for cell record in dialogue subview not working
+    Bug #1869: Editor: Change label "Musics" to "Music"
+    Bug #1870: Goblins killed while knocked down remain in knockdown-pose
+    Bug #1874: CellChanged events should not trigger when crossing exterior cell border
+    Bug #1877: Spriggans killed instantly if hit while regening
+    Bug #1878: Magic Menu text not un-highlighting correctly when going from spell to item as active magic
+    Bug #1881: Stuck in ceiling when entering castle karstaags tower
+    Bug #1884: Unlit torches still produce a burning sound
+    Bug #1885: Can type text in price field in barter window
+    Bug #1887: Equipped items do not emit sounds
+    Bug #1889: draugr lord aesliip will attack you and remain non-hostile
+    Bug #1892: Guard asks player to pay bounty of 0 gold
+    Bug #1895: getdistance should only return max float if ref and target are in different worldspaces
+    Bug #1896: Crash Report
+    Bug #1897: Conjured Equipment cant be re-equipped if removed
+    Bug #1898: Only Gidar Verothan follows you during establish the mine quest
+    Bug #1900: Black screen when you open the door and breath underwater
+    Bug #1904: Crash on casting recall spell
+    Bug #1906: Bound item checks should use the GMSTs
+    Bug #1907: Bugged door. Mournhold, The Winged Guar
+    Bug #1908: Crime reported for attacking Drathas Nerus's henchmen while they attack Dilborn
+    Bug #1909: Weird Quest Flow Infidelities quest
+    Bug #1910: Follower fighting with gone npc
+    Bug #1911: Npcs will drown themselves
+    Bug #1912: World map arrow stays static when inside a building
+    Bug #1920: Ulyne Henim disappears when game is loaded inside Vas
+    Bug #1922: alchemy-> potion of paralyze
+    Bug #1923: "levitation magic cannot be used here" shows outside of tribunal
+    Bug #1927: AI prefer melee over magic.
+    Bug #1929: Tamriel Rebuilt: Named cells that lie within the overlap with Morrowind.esm are not shown
+    Bug #1932: BTB - Spells 14.1 magic effects don´t overwrite the Vanilla ones but are added
+    Bug #1935: Stacks of items are worth more when sold individually
+    Bug #1940: Launcher does not list addon files if base game file is renamed to a different case
+    Bug #1946: Mod "Tel Nechim - moved" breaks savegames
+    Bug #1947: Buying/Selling price doesn't properly affect the growth of mercantile skill
+    Bug #1950: followers from east empire company quest will fight each other if combat happens with anything
+    Bug #1958: Journal can be scrolled indefinitely with a mouse wheel
+    Bug #1959: Follower not leaving party on quest end
+    Bug #1960: Key bindings not always saved correctly
+    Bug #1961: Spell merchants selling racial bonus spells
+    Bug #1967: segmentation fault on load saves
+    Bug #1968: Jump sounds are not controlled by footsteps slider, sound weird compared to footsteps
+    Bug #1970: PC suffers silently when taking damage from lava
+    Bug #1971: Dwarven Sceptre collision area is not removed after killing one
+    Bug #1974: Dalin/Daris Norvayne follows player indefinitely
+    Bug #1975: East Empire Company faction rank breaks during Raven Rock questline
+    Bug #1979: 0 strength = permanently over encumbered
+    Bug #1993: Shrine blessing in Maar Gan doesn't work
+    Bug #2008: Enchanted items do not recharge
+    Bug #2011: Editor: OpenCS script compiler doesn't handle member variable access properly
+    Bug #2016: Dagoth Ur already dead in Facility Cavern
+    Bug #2017: Fighters Guild Quest: The Code Book - dialogue loop when UMP is loaded.
+    Bug #2019: Animation of 'Correct UV Mudcrabs' broken
+    Bug #2022: Alchemy window - Removing ingredient doesn't remove the number of ingredients
+    Bug #2025: Missing mouse-over text for non affordable items
+    Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell "Ruinous Keep, Great Hall"
+    Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding
+    Feature #471: Editor: Special case implementation for top-level window with single sub-window
+    Feature #472: Editor: Sub-Window re-use settings
+    Feature #704: Font colors import from fallback settings
+    Feature #879: Editor: Open sub-views in a new top-level window
+    Feature #932: Editor: magic effect table
+    Feature #937: Editor: Path Grid table
+    Feature #938: Editor: Sound Gen table
+    Feature #1117: Death and LevelUp music
+    Feature #1226: Editor: Request UniversalId editing from table columns
+    Feature #1545: Targeting console on player
+    Feature #1597: Editor: Render terrain
+    Feature #1695: Editor: add column for CellRef's global variable
+    Feature #1696: Editor: use ESM::Cell's RefNum counter
+    Feature #1697: Redden player's vision when hit
+    Feature #1856: Spellcasting for non-biped creatures
+    Feature #1879: Editor: Run OpenMW with the currently edited content list
+    Task #1851: Move AI temporary state out of AI packages
+    Task #1865: Replace char type in records
 
 0.32.0
-
-Bug #1132: Unable to jump when facing a wall
-Bug #1341: Summoned Creatures do not immediately disappear when killed.
-Bug #1430: CharGen Revamped script does not compile
-Bug #1451: NPCs shouldn't equip weapons prior to fighting
-Bug #1461: Stopped start scripts do not restart on load
-Bug #1473: Dead NPC standing and in 2 pieces
-Bug #1482: Abilities are depleted when interrupted during casting
-Bug #1503: Behaviour of NPCs facing the player
-Bug #1506: Missing character, French edition: three-points
-Bug #1528: Inventory very slow after 2 hours
-Bug #1540: Extra arguments should be ignored for script functions
-Bug #1541: Helseth's Champion: Tribunal
-Bug #1570: Journal cannot be opened while in inventory screen
-Bug #1573: PC joins factions at random
-Bug #1576: NPCs aren't switching their weapons when out of ammo
-Bug #1579: Guards detect creatures in far distance, instead on sight
-Bug #1588: The Siege of the Skaal Village: bloodmoon
-Bug #1593: The script compiler isn't recognising some names that contain a -
-Bug #1606: Books: Question marks instead of quotation marks
-Bug #1608: Dead bodies prevent door from opening/closing.
-Bug #1609: Imperial guards in Sadrith Mora are not using their spears
-Bug #1610: The bounty number is not displayed properly with high numbers
-Bug #1620: Implement correct formula for auto-calculated NPC spells
-Bug #1630: Boats standing vertically in Vivec
-Bug #1635: Arrest dialogue is executed second time after I select "Go to jail"
-Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults?
-Bug #1641: Persuasion dialog remains after loading, possibly resulting in crash
-Bug #1644: "Goodbye" and similar options on dialogues prevents escape working properly.
-Bug #1646: PC skill stats are not updated immediately when changing equipment
-Bug #1652: Non-aggressive creature
-Bug #1653: Quickloading while the container window is open crashes the game
-Bug #1654: Priority of checks in organic containers
-Bug #1656: Inventory items merge issue when repairing
-Bug #1657: Attacked state of NPCs is not saved properly
-Bug #1660: Rank dialogue condition ignored
-Bug #1668: Game starts on day 2 instead of day 1
-Bug #1669: Critical Strikes while fighting a target who is currently fighting me
-Bug #1672: OpenCS doesn't save the projects
-Bug #1673: Fatigue decreasing by only one point when running
-Bug #1675: Minimap and localmap graphic glitches
-Bug #1676: Pressing the OK button on the travel menu cancels the travel and exits the menu
-Bug #1677: Sleeping in a rented bed is considered a crime
-Bug #1685: NPCs turn towards player even if invisible/sneaking
-Bug #1686: UI bug: cursor is clicking "world/local" map button while inventory window is closed?
-Bug #1690: Double clicking on a inventory window header doesn't close it.
-Bug #1693: Spell Absorption does not absorb shrine blessings
-Bug #1694: journal displays learned topics as quests
-Bug #1700: Sideways scroll of text boxes
-Bug #1701: Player enchanting requires player hold money, always 100% sucessful.
-Bug #1704: self-made Fortify Intelligence/Drain willpower potions are broken
-Bug #1707: Pausing the game through the esc menu will silence rain, pausing it by opening the inventory will not.
-Bug #1709: Remesa Othril is hostile to Hlaalu members
-Bug #1713: Crash on load after death
-Bug #1719: Blind effect has slight border at the edge of the screen where it is ineffective.
-Bug #1722: Crash after creating enchanted item, reloading saved game
-Bug #1723: Content refs that are stacked share the same index after unstacking
-Bug #1726: Can't finish Aengoth the Jeweler's quest : Retrieve the Scrap Metal
-Bug #1727: Targets almost always resist soultrap scrolls
-Bug #1728: Casting a soultrap spell on invalid target yields no message
-Bug #1729: Chop attack doesn't work if walking diagonally
-Bug #1732: Error handling for missing script function arguments produces weird message
-Bug #1736: Alt-tabbing removes detail from overworld map.
-Bug #1737: Going through doors with (high magnitude?) leviation will put the player high up, possibly even out of bounds.
-Bug #1739: Setting a variable on an NPC from another NPC's dialogue result sets the wrong variable
-Bug #1741: The wait dialogue doesn't black the screen out properly during waiting.
-Bug #1742: ERROR: Object 'sDifficulty' not found (const)
-Bug #1744: Night sky in Skies V.IV (& possibly v3) by SWG rendered incorrectly
-Bug #1746: Bow/marksman weapon condition does not degrade with use
-Bug #1749: Constant Battle Music
-Bug #1752: Alt-Tabbing in the character menus makes the paper doll disappear temporarily
-Bug #1753: Cost of training is not added to merchant's inventory
-Bug #1755: Disposition changes do not persist if the conversation menu is closed by purchasing training.
-Bug #1756: Caught Blight after being cured of Corprus
-Bug #1758: Crash Upon Loading New Cell
-Bug #1760: Player's Magicka is not recalculated upon drained or boosted intelligence
-Bug #1761: Equiped torches lost on reload
-Bug #1762: Your spell did not get a target. Soul trap. Gorenea Andrano
-Bug #1763: Custom Spell Magicka Cost
-Bug #1765: Azuras Star breaks on recharging item
-Bug #1767: GetPCRank did not handle ignored explicit references
-Bug #1772: Dark Brotherhood Assassins never use their Carved Ebony Dart, sticking to their melee weapon.
-Bug #1774: String table overflow also occurs when loading TheGloryRoad.esm
-Bug #1776: dagoth uthol runs in slow motion
-Bug #1778: Incorrect values in spellmaking window
-Bug #1779: Icon of Master Propylon Index is not visible
-Bug #1783: Invisible NPC after looting corpse
-Bug #1787: Health Calculation
-Bug #1788: Skeletons, ghosts etc block doors when we try to open
-Bug #1791: [MOD: LGNPC Foreign Quarter] NPC in completely the wrong place.
-Bug #1792: Potions should show more effects
-Bug #1793: Encumbrance while bartering
-Bug #1794: Fortify attribute not affecting fatigue
-Bug #1795: Too much magicka
-Bug #1796: "Off by default" torch burning
-Bug #1797: Fish too slow
-Bug #1798: Rest until healed shouldn't show with full health and magicka
-Bug #1802: Mark location moved
-Bug #1804: stutter with recent builds
-Bug #1810: attack gothens dremora doesnt agro the others.
-Bug #1811: Regression: Crash Upon Loading New Cell
-Bug #1812: Mod: "QuickChar" weird button placement
-Bug #1815: Keys show value and weight, Vanilla Morrowind's keys dont.
-Bug #1817: Persuasion results do not show using unpatched MW ESM
-Bug #1818: Quest B3_ZainabBride moves to stage 47 upon loading save while Falura Llervu is following
-Bug #1823: AI response to theft incorrect - only guards react, in vanilla everyone does.
-Bug #1829: On-Target Spells Rendered Behind Water Surface Effects
-Bug #1830: Galsa Gindu's house is on fire
-Bug #1832: Fatal Error: OGRE Exception(2:InvalidParametersException)
-Bug #1836: Attacked Guards open "fine/jail/resist"-dialogue after killing you
-Bug #1840: Infinite recursion in ActionTeleport
-Bug #1843: Escorted people change into player's cell after completion of escort stage
-Bug #1845: Typing 'j' into 'Name' fields opens the journal
-Bug #1846: Text pasted into the console still appears twice (Windows)
-Bug #1847: "setfatigue 0" doesn't render NPC unconscious
-Bug #1848: I can talk to unconscious actors
-Bug #1866: Crash when player gets killed by a creature summoned by him
-Bug #1868: Memory leaking when openmw window is minimized
-Feature #47: Magic Effects
-Feature #642: Control NPC mouth movement using current Say sound
-Feature #939: Editor: Resources tables
-Feature #961: AI Combat for magic (spells, potions and enchanted items)
-Feature #1111: Collision script instructions (used e.g. by Lava)
-Feature #1120: Command creature/humanoid magic effects
-Feature #1121: Elemental shield magic effects
-Feature #1122: Light magic effect
-Feature #1139: AI: Friendly hits
-Feature #1141: AI: combat party
-Feature #1326: Editor: Add tooltips to all graphical buttons
-Feature #1489: Magic effect Get/Mod/Set functions
-Feature #1505: Difficulty slider
-Feature #1538: Targeted scripts
-Feature #1571: Allow creating custom markers on the local map
-Feature #1615: Determine local variables from compiled scripts instead of the values in the script record
-Feature #1616: Editor: Body part record verifier
-Feature #1651: Editor: Improved keyboard navigation for scene toolbar
-Feature #1666: Script blacklisting
-Feature #1711: Including the Git revision number from the command line "--version" switch.
-Feature #1721: NPC eye blinking
-Feature #1740: Scene toolbar buttons for selecting which type of elements are rendered
-Feature #1790: Mouse wheel scrolling for the journal
-Feature #1850: NiBSPArrayController
-Task #768: On windows, settings folder should be "OpenMW", not "openmw"
-Task #908: Share keyframe data
-Task #1716: Remove defunct option for building without FFmpeg
+------
+
+    Bug #1132: Unable to jump when facing a wall
+    Bug #1341: Summoned Creatures do not immediately disappear when killed.
+    Bug #1430: CharGen Revamped script does not compile
+    Bug #1451: NPCs shouldn't equip weapons prior to fighting
+    Bug #1461: Stopped start scripts do not restart on load
+    Bug #1473: Dead NPC standing and in 2 pieces
+    Bug #1482: Abilities are depleted when interrupted during casting
+    Bug #1503: Behaviour of NPCs facing the player
+    Bug #1506: Missing character, French edition: three-points
+    Bug #1528: Inventory very slow after 2 hours
+    Bug #1540: Extra arguments should be ignored for script functions
+    Bug #1541: Helseth's Champion: Tribunal
+    Bug #1570: Journal cannot be opened while in inventory screen
+    Bug #1573: PC joins factions at random
+    Bug #1576: NPCs aren't switching their weapons when out of ammo
+    Bug #1579: Guards detect creatures in far distance, instead on sight
+    Bug #1588: The Siege of the Skaal Village: bloodmoon
+    Bug #1593: The script compiler isn't recognising some names that contain a -
+    Bug #1606: Books: Question marks instead of quotation marks
+    Bug #1608: Dead bodies prevent door from opening/closing.
+    Bug #1609: Imperial guards in Sadrith Mora are not using their spears
+    Bug #1610: The bounty number is not displayed properly with high numbers
+    Bug #1620: Implement correct formula for auto-calculated NPC spells
+    Bug #1630: Boats standing vertically in Vivec
+    Bug #1635: Arrest dialogue is executed second time after I select "Go to jail"
+    Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults?
+    Bug #1641: Persuasion dialog remains after loading, possibly resulting in crash
+    Bug #1644: "Goodbye" and similar options on dialogues prevents escape working properly.
+    Bug #1646: PC skill stats are not updated immediately when changing equipment
+    Bug #1652: Non-aggressive creature
+    Bug #1653: Quickloading while the container window is open crashes the game
+    Bug #1654: Priority of checks in organic containers
+    Bug #1656: Inventory items merge issue when repairing
+    Bug #1657: Attacked state of NPCs is not saved properly
+    Bug #1660: Rank dialogue condition ignored
+    Bug #1668: Game starts on day 2 instead of day 1
+    Bug #1669: Critical Strikes while fighting a target who is currently fighting me
+    Bug #1672: OpenCS doesn't save the projects
+    Bug #1673: Fatigue decreasing by only one point when running
+    Bug #1675: Minimap and localmap graphic glitches
+    Bug #1676: Pressing the OK button on the travel menu cancels the travel and exits the menu
+    Bug #1677: Sleeping in a rented bed is considered a crime
+    Bug #1685: NPCs turn towards player even if invisible/sneaking
+    Bug #1686: UI bug: cursor is clicking "world/local" map button while inventory window is closed?
+    Bug #1690: Double clicking on a inventory window header doesn't close it.
+    Bug #1693: Spell Absorption does not absorb shrine blessings
+    Bug #1694: journal displays learned topics as quests
+    Bug #1700: Sideways scroll of text boxes
+    Bug #1701: Player enchanting requires player hold money, always 100% sucessful.
+    Bug #1704: self-made Fortify Intelligence/Drain willpower potions are broken
+    Bug #1707: Pausing the game through the esc menu will silence rain, pausing it by opening the inventory will not.
+    Bug #1709: Remesa Othril is hostile to Hlaalu members
+    Bug #1713: Crash on load after death
+    Bug #1719: Blind effect has slight border at the edge of the screen where it is ineffective.
+    Bug #1722: Crash after creating enchanted item, reloading saved game
+    Bug #1723: Content refs that are stacked share the same index after unstacking
+    Bug #1726: Can't finish Aengoth the Jeweler's quest : Retrieve the Scrap Metal
+    Bug #1727: Targets almost always resist soultrap scrolls
+    Bug #1728: Casting a soultrap spell on invalid target yields no message
+    Bug #1729: Chop attack doesn't work if walking diagonally
+    Bug #1732: Error handling for missing script function arguments produces weird message
+    Bug #1736: Alt-tabbing removes detail from overworld map.
+    Bug #1737: Going through doors with (high magnitude?) leviation will put the player high up, possibly even out of bounds.
+    Bug #1739: Setting a variable on an NPC from another NPC's dialogue result sets the wrong variable
+    Bug #1741: The wait dialogue doesn't black the screen out properly during waiting.
+    Bug #1742: ERROR: Object 'sDifficulty' not found (const)
+    Bug #1744: Night sky in Skies V.IV (& possibly v3) by SWG rendered incorrectly
+    Bug #1746: Bow/marksman weapon condition does not degrade with use
+    Bug #1749: Constant Battle Music
+    Bug #1752: Alt-Tabbing in the character menus makes the paper doll disappear temporarily
+    Bug #1753: Cost of training is not added to merchant's inventory
+    Bug #1755: Disposition changes do not persist if the conversation menu is closed by purchasing training.
+    Bug #1756: Caught Blight after being cured of Corprus
+    Bug #1758: Crash Upon Loading New Cell
+    Bug #1760: Player's Magicka is not recalculated upon drained or boosted intelligence
+    Bug #1761: Equiped torches lost on reload
+    Bug #1762: Your spell did not get a target. Soul trap. Gorenea Andrano
+    Bug #1763: Custom Spell Magicka Cost
+    Bug #1765: Azuras Star breaks on recharging item
+    Bug #1767: GetPCRank did not handle ignored explicit references
+    Bug #1772: Dark Brotherhood Assassins never use their Carved Ebony Dart, sticking to their melee weapon.
+    Bug #1774: String table overflow also occurs when loading TheGloryRoad.esm
+    Bug #1776: dagoth uthol runs in slow motion
+    Bug #1778: Incorrect values in spellmaking window
+    Bug #1779: Icon of Master Propylon Index is not visible
+    Bug #1783: Invisible NPC after looting corpse
+    Bug #1787: Health Calculation
+    Bug #1788: Skeletons, ghosts etc block doors when we try to open
+    Bug #1791: [MOD: LGNPC Foreign Quarter] NPC in completely the wrong place.
+    Bug #1792: Potions should show more effects
+    Bug #1793: Encumbrance while bartering
+    Bug #1794: Fortify attribute not affecting fatigue
+    Bug #1795: Too much magicka
+    Bug #1796: "Off by default" torch burning
+    Bug #1797: Fish too slow
+    Bug #1798: Rest until healed shouldn't show with full health and magicka
+    Bug #1802: Mark location moved
+    Bug #1804: stutter with recent builds
+    Bug #1810: attack gothens dremora doesnt agro the others.
+    Bug #1811: Regression: Crash Upon Loading New Cell
+    Bug #1812: Mod: "QuickChar" weird button placement
+    Bug #1815: Keys show value and weight, Vanilla Morrowind's keys dont.
+    Bug #1817: Persuasion results do not show using unpatched MW ESM
+    Bug #1818: Quest B3_ZainabBride moves to stage 47 upon loading save while Falura Llervu is following
+    Bug #1823: AI response to theft incorrect - only guards react, in vanilla everyone does.
+    Bug #1829: On-Target Spells Rendered Behind Water Surface Effects
+    Bug #1830: Galsa Gindu's house is on fire
+    Bug #1832: Fatal Error: OGRE Exception(2:InvalidParametersException)
+    Bug #1836: Attacked Guards open "fine/jail/resist"-dialogue after killing you
+    Bug #1840: Infinite recursion in ActionTeleport
+    Bug #1843: Escorted people change into player's cell after completion of escort stage
+    Bug #1845: Typing 'j' into 'Name' fields opens the journal
+    Bug #1846: Text pasted into the console still appears twice (Windows)
+    Bug #1847: "setfatigue 0" doesn't render NPC unconscious
+    Bug #1848: I can talk to unconscious actors
+    Bug #1866: Crash when player gets killed by a creature summoned by him
+    Bug #1868: Memory leaking when openmw window is minimized
+    Feature #47: Magic Effects
+    Feature #642: Control NPC mouth movement using current Say sound
+    Feature #939: Editor: Resources tables
+    Feature #961: AI Combat for magic (spells, potions and enchanted items)
+    Feature #1111: Collision script instructions (used e.g. by Lava)
+    Feature #1120: Command creature/humanoid magic effects
+    Feature #1121: Elemental shield magic effects
+    Feature #1122: Light magic effect
+    Feature #1139: AI: Friendly hits
+    Feature #1141: AI: combat party
+    Feature #1326: Editor: Add tooltips to all graphical buttons
+    Feature #1489: Magic effect Get/Mod/Set functions
+    Feature #1505: Difficulty slider
+    Feature #1538: Targeted scripts
+    Feature #1571: Allow creating custom markers on the local map
+    Feature #1615: Determine local variables from compiled scripts instead of the values in the script record
+    Feature #1616: Editor: Body part record verifier
+    Feature #1651: Editor: Improved keyboard navigation for scene toolbar
+    Feature #1666: Script blacklisting
+    Feature #1711: Including the Git revision number from the command line "--version" switch.
+    Feature #1721: NPC eye blinking
+    Feature #1740: Scene toolbar buttons for selecting which type of elements are rendered
+    Feature #1790: Mouse wheel scrolling for the journal
+    Feature #1850: NiBSPArrayController
+    Task #768: On windows, settings folder should be "OpenMW", not "openmw"
+    Task #908: Share keyframe data
+    Task #1716: Remove defunct option for building without FFmpeg
 
 0.31.0
-
-Bug #245: Cloud direction and weather systems differ from Morrowind
-Bug #275: Local Map does not always show objects that span multiple cells
-Bug #538: Update CenterOnCell (COC) function behavior
-Bug #618: Local and World Map Textures are sometimes Black
-Bug #640: Water behaviour at night
-Bug #668: OpenMW doesn't support non-latin paths on Windows
-Bug #746: OpenMW doesn't check if the background music was already played
-Bug #747: Door is stuck if cell is left before animation finishes
-Bug #772: Disabled statics are visible on map
-Bug #829: OpenMW uses up all available vram, when playing for extended time
-Bug #869: Dead bodies don't collide with anything
-Bug #894: Various character creation issues
-Bug #897/#1369: opencs Segmentation Fault after "new" or "load"
-Bug #899: Various jumping issues
-Bug #952: Reflection effects are one frame delayed
-Bug #993: Able to interact with world during Wait/Rest dialog
-Bug #995: Dropped items can be placed inside the wall
-Bug #1008: Corpses always face up upon reentering the cell
-Bug #1035: Random colour patterns appearing in automap
-Bug #1037: Footstep volume issues
-Bug #1047: Creation of wrong links in dialogue window
-Bug #1129: Summoned creature time life duration seems infinite
-Bug #1134: Crimes can be committed against hostile NPCs
-Bug #1136: Creature run speed formula is incorrect
-Bug #1150: Weakness to Fire doesn't apply to Fire Damage in the same spell
-Bug #1155: NPCs killing each other
-Bug #1166: Bittercup script still does not work
-Bug #1178: .bsa file names are case sensitive.
-Bug #1179: Crash after trying to load game after being killed
-Bug #1180: Changing footstep sound location
-Bug #1196: Jumping not disabled when showing messageboxes
-Bug #1202: "strange" keys are not shown in binding menu, and are not saved either, but works
-Bug #1216: Broken dialog topics in russian Morrowind
-Bug #1217: Container content changes based on the current position of the mouse
-Bug #1234: Loading/saving issues with dynamic records
-Bug #1277: Text pasted into the console appears twice
-Bug #1284: Crash on New Game
-Bug #1303: It's possible to skip the chargen
-Bug #1304: Slaughterfish should not detect the player unless the player is in the water
-Bug #1311: Editor: deleting Record Filter line does not reset the filter
-Bug #1324: ERROR: ESM Error: String table overflow when loading Animated Morrowind.esp
-Bug #1328: Editor: Bogus Filter created when dragging multiple records to filter bar of non-applicable table
-Bug #1331: Walking/running sound persist after killing NPC`s that are walking/running.
-Bug #1334: Previously equipped items not shown as unequipped after attempting to sell them.
-Bug #1335: Actors ignore vertical axis when deciding to attack
-Bug #1338: Unknown toggle option for shadows
-Bug #1339: "Ashlands Region" is visible when beginning new game during "Loading Area" process
-Bug #1340: Guards prompt Player with punishment options after resisting arrest with another guard.
-Bug #1348: Regression: Bug #1098 has returned with a vengeance
-Bug #1349: [TR] TR_Data mesh tr_ex_imp_gatejamb01 cannot be activated
-Bug #1352: Disabling an ESX file does not disable dependent ESX files
-Bug #1355: CppCat Checks OpenMW
-Bug #1356: Incorrect voice type filtering for sleep interrupts
-Bug #1357: Restarting the game clears saves
-Bug #1360: Seyda Neen silk rider dialog problem
-Bug #1361: Some lights don't work
-Bug #1364: It is difficult to bind "Mouse 1" to an action in the options menu
-Bug #1370: Animation compilation mod does not work properly
-Bug #1371: SL_Pick01.nif from third party fails to load in openmw, but works in Vanilla
-Bug #1373: When stealing in front of Sellus Gravius cannot exit the dialog
-Bug #1378: Installs to /usr/local are not working
-Bug #1380: Loading a save file fail if one of the content files is disabled
-Bug #1382: "getHExact() size mismatch" crash on loading official plugin "Siege at Firemoth.esp"
-Bug #1386: Arkngthand door will not open
-Bug #1388: Segfault when modifying View Distance in Menu options
-Bug #1389: Crash when loading a save after dying
-Bug #1390: Apostrophe characters not displayed [French version]
-Bug #1391: Custom made icon background texture for magical weapons and stuff isn't scaled properly on GUI.
-Bug #1393: Coin icon during the level up dialogue are off of the background
-Bug #1394: Alt+F4 doesn't work on Win version
-Bug #1395: Changing rings switches only the last one put on
-Bug #1396: Pauldron parts aren't showing when the robe is equipped
-Bug #1402: Dialogue of some shrines have wrong button orientation
-Bug #1403: Items are floating in the air when they're dropped onto dead bodies.
-Bug #1404: Forearms are not rendered on Argonian females
-Bug #1407: Alchemy allows making potions from two of the same item
-Bug #1408: "Max sale" button gives you all the items AND all the trader's gold
-Bug #1409: Rest "Until Healed" broken for characters with stunted magicka.
-Bug #1412: Empty travel window opens while playing through start game
-Bug #1413: Save game ignores missing writing permission
-Bug #1414: The Underground 2 ESM Error
-Bug #1416: Not all splash screens in the Splash directory are used
-Bug #1417: Loading saved game does not terminate
-Bug #1419: Skyrim: Home of the Nords error
-Bug #1422: ClearInfoActor
-Bug #1423: ForceGreeting closes existing dialogue windows
-Bug #1425: Cannot load save game
-Bug #1426: Read skill books aren't stored in savegame
-Bug #1427: Useless items can be set under hotkeys
-Bug #1429: Text variables in journal
-Bug #1432: When attacking friendly NPC, the crime is reported and bounty is raised after each swing
-Bug #1435: Stealing priceless items is without punishment
-Bug #1437: Door marker at Jobasha's Rare Books is spawning PC in the air
-Bug #1440: Topic selection menu should be wider
-Bug #1441: Dropping items on the rug makes them inaccessible
-Bug #1442: When dropping and taking some looted items, bystanders consider that as a crime
-Bug #1444: Arrows and bolts are not dropped where the cursor points
-Bug #1445: Security trainers offering acrobatics instead
-Bug #1447: Character dash not displayed, French edition
-Bug #1448: When the player is killed by the guard while having a bounty on his head, the guard dialogue opens over and over instead of loading dialogue
-Bug #1454: Script error in SkipTutorial
-Bug #1456: Bad lighting when using certain Morrowind.ini generated by MGE
-Bug #1457: Heart of Lorkan comes after you when attacking it
-Bug #1458: Modified Keybindings are not remembered
-Bug #1459: Dura Gra-Bol doesn't respond to PC attack
-Bug #1462: Interior cells not loaded with Morrowind Patch active
-Bug #1469: Item tooltip should show the base value, not real value
-Bug #1477: Death count is not stored in savegame
-Bug #1478: AiActivate does not trigger activate scripts
-Bug #1481: Weapon not rendered when partially submerged in water
-Bug #1483: Enemies are attacking even while dying
-Bug #1486: ESM Error: Don't know what to do with INFO
-Bug #1490: Arrows shot at PC can end up in inventory
-Bug #1492: Monsters respawn on top of one another
-Bug #1493: Dialogue box opens with follower NPC even if NPC is dead
-Bug #1494: Paralysed cliffracers remain airbourne
-Bug #1495: Dialogue box opens with follower NPC even the game is paused
-Bug #1496: GUI messages are not cleared when loading another saved game
-Bug #1499: Underwater sound sometimes plays when transitioning from interior.
-Bug #1500: Targetted spells and water.
-Bug #1502: Console error message on info refusal
-Bug #1507: Bloodmoon MQ The Ritual of Beasts: Can't remove the arrow
-Bug #1508: Bloodmoon: Fort Frostmoth, cant talk with Carnius Magius
-Bug #1516: PositionCell doesn't move actors to current cell
-Bug #1518: ForceGreeting broken for explicit references
-Bug #1522: Crash after attempting to play non-music file
-Bug #1523: World map empty after loading interior save
-Bug #1524: Arrows in waiting/resting dialog act like minimum and maximum buttons
-Bug #1525: Werewolf: Killed NPC's don't fill werewolfs hunger for blood
-Bug #1527: Werewolf: Detect life detects wrong type of actor
-Bug #1529: OpenMW crash during "the shrine of the dead" mission (tribunal)
-Bug #1530: Selected text in the console has the same color as the background
-Bug #1539: Barilzar's Mazed Band: Tribunal
-Bug #1542: Looping taunts from NPC`s after death: Tribunal
-Bug #1543: OpenCS crash when using drag&drop in script editor
-Bug #1547: Bamz-Amschend: Centurion Archers combat problem
-Bug #1548: The Missing Hand: Tribunal
-Bug #1549: The Mad God: Tribunal, Dome of Serlyn
-Bug #1557: A bounty is calculated from actual item cost
-Bug #1562: Invisible terrain on top of Red Mountain
-Bug #1564: Cave of the hidden music: Bloodmoon
-Bug #1567: Editor: Deleting of referenceables does not work
-Bug #1568: Picking up a stack of items and holding the enter key and moving your mouse around paints a bunch of garbage on screen.
-Bug #1574: Solstheim: Drauger cant inflict damage on player
-Bug #1578: Solstheim: Bonewolf running animation not working
-Bug #1585: Particle effects on PC are stopped when paralyzed
-Bug #1589: Tribunal: Crimson Plague quest does not update when Gedna Relvel is killed
-Bug #1590: Failed to save game: compile error
-Bug #1598: Segfault when making Drain/Fortify Skill spells
-Bug #1599: Unable to switch to fullscreen
-Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed
-Bug #1618: Death notice fails to show up
-Bug #1628: Alt+Tab Segfault
-Feature #32: Periodic Cleanup/Refill
-Feature #41: Precipitation and weather particles
-Feature #568: Editor: Configuration setup
-Feature #649: Editor: Threaded loading
-Feature #930: Editor: Cell record saving
-Feature #934: Editor: Body part table
-Feature #935: Editor: Enchantment effect table
-Feature #1162: Dialogue merging
-Feature #1174: Saved Game: add missing creature state
-Feature #1177: Saved Game: fog of war state
-Feature #1312: Editor: Combat/Magic/Stealth values for creatures are not displayed
-Feature #1314: Make NPCs and creatures fight each other
-Feature #1315: Crime: Murder
-Feature #1321: Sneak skill enhancements
-Feature #1323: Handle restocking items
-Feature #1332: Saved Game: levelled creatures
-Feature #1347: modFactionReaction script instruction
-Feature #1362: Animated main menu support
-Feature #1433: Store walk/run toggle
-Feature #1449: Use names instead of numbers for saved game files and folders
-Feature #1453: Adding Delete button to the load menu
-Feature #1460: Enable Journal screen while in dialogue
-Feature #1480: Play Battle music when in combat
-Feature #1501: Followers unable to fast travel with you
-Feature #1520: Disposition and distance-based aggression/ShouldAttack
-Feature #1595: Editor: Object rendering in cells
-Task #940: Move license to locations where applicable
-Task #1333: Remove cmake git tag reading
-Task #1566: Editor: Object rendering refactoring
+------
+
+    Bug #245: Cloud direction and weather systems differ from Morrowind
+    Bug #275: Local Map does not always show objects that span multiple cells
+    Bug #538: Update CenterOnCell (COC) function behavior
+    Bug #618: Local and World Map Textures are sometimes Black
+    Bug #640: Water behaviour at night
+    Bug #668: OpenMW doesn't support non-latin paths on Windows
+    Bug #746: OpenMW doesn't check if the background music was already played
+    Bug #747: Door is stuck if cell is left before animation finishes
+    Bug #772: Disabled statics are visible on map
+    Bug #829: OpenMW uses up all available vram, when playing for extended time
+    Bug #869: Dead bodies don't collide with anything
+    Bug #894: Various character creation issues
+    Bug #897/#1369: opencs Segmentation Fault after "new" or "load"
+    Bug #899: Various jumping issues
+    Bug #952: Reflection effects are one frame delayed
+    Bug #993: Able to interact with world during Wait/Rest dialog
+    Bug #995: Dropped items can be placed inside the wall
+    Bug #1008: Corpses always face up upon reentering the cell
+    Bug #1035: Random colour patterns appearing in automap
+    Bug #1037: Footstep volume issues
+    Bug #1047: Creation of wrong links in dialogue window
+    Bug #1129: Summoned creature time life duration seems infinite
+    Bug #1134: Crimes can be committed against hostile NPCs
+    Bug #1136: Creature run speed formula is incorrect
+    Bug #1150: Weakness to Fire doesn't apply to Fire Damage in the same spell
+    Bug #1155: NPCs killing each other
+    Bug #1166: Bittercup script still does not work
+    Bug #1178: .bsa file names are case sensitive.
+    Bug #1179: Crash after trying to load game after being killed
+    Bug #1180: Changing footstep sound location
+    Bug #1196: Jumping not disabled when showing messageboxes
+    Bug #1202: "strange" keys are not shown in binding menu, and are not saved either, but works
+    Bug #1216: Broken dialog topics in russian Morrowind
+    Bug #1217: Container content changes based on the current position of the mouse
+    Bug #1234: Loading/saving issues with dynamic records
+    Bug #1277: Text pasted into the console appears twice
+    Bug #1284: Crash on New Game
+    Bug #1303: It's possible to skip the chargen
+    Bug #1304: Slaughterfish should not detect the player unless the player is in the water
+    Bug #1311: Editor: deleting Record Filter line does not reset the filter
+    Bug #1324: ERROR: ESM Error: String table overflow when loading Animated Morrowind.esp
+    Bug #1328: Editor: Bogus Filter created when dragging multiple records to filter bar of non-applicable table
+    Bug #1331: Walking/running sound persist after killing NPC`s that are walking/running.
+    Bug #1334: Previously equipped items not shown as unequipped after attempting to sell them.
+    Bug #1335: Actors ignore vertical axis when deciding to attack
+    Bug #1338: Unknown toggle option for shadows
+    Bug #1339: "Ashlands Region" is visible when beginning new game during "Loading Area" process
+    Bug #1340: Guards prompt Player with punishment options after resisting arrest with another guard.
+    Bug #1348: Regression: Bug #1098 has returned with a vengeance
+    Bug #1349: [TR] TR_Data mesh tr_ex_imp_gatejamb01 cannot be activated
+    Bug #1352: Disabling an ESX file does not disable dependent ESX files
+    Bug #1355: CppCat Checks OpenMW
+    Bug #1356: Incorrect voice type filtering for sleep interrupts
+    Bug #1357: Restarting the game clears saves
+    Bug #1360: Seyda Neen silk rider dialog problem
+    Bug #1361: Some lights don't work
+    Bug #1364: It is difficult to bind "Mouse 1" to an action in the options menu
+    Bug #1370: Animation compilation mod does not work properly
+    Bug #1371: SL_Pick01.nif from third party fails to load in openmw, but works in Vanilla
+    Bug #1373: When stealing in front of Sellus Gravius cannot exit the dialog
+    Bug #1378: Installs to /usr/local are not working
+    Bug #1380: Loading a save file fail if one of the content files is disabled
+    Bug #1382: "getHExact() size mismatch" crash on loading official plugin "Siege at Firemoth.esp"
+    Bug #1386: Arkngthand door will not open
+    Bug #1388: Segfault when modifying View Distance in Menu options
+    Bug #1389: Crash when loading a save after dying
+    Bug #1390: Apostrophe characters not displayed [French version]
+    Bug #1391: Custom made icon background texture for magical weapons and stuff isn't scaled properly on GUI.
+    Bug #1393: Coin icon during the level up dialogue are off of the background
+    Bug #1394: Alt+F4 doesn't work on Win version
+    Bug #1395: Changing rings switches only the last one put on
+    Bug #1396: Pauldron parts aren't showing when the robe is equipped
+    Bug #1402: Dialogue of some shrines have wrong button orientation
+    Bug #1403: Items are floating in the air when they're dropped onto dead bodies.
+    Bug #1404: Forearms are not rendered on Argonian females
+    Bug #1407: Alchemy allows making potions from two of the same item
+    Bug #1408: "Max sale" button gives you all the items AND all the trader's gold
+    Bug #1409: Rest "Until Healed" broken for characters with stunted magicka.
+    Bug #1412: Empty travel window opens while playing through start game
+    Bug #1413: Save game ignores missing writing permission
+    Bug #1414: The Underground 2 ESM Error
+    Bug #1416: Not all splash screens in the Splash directory are used
+    Bug #1417: Loading saved game does not terminate
+    Bug #1419: Skyrim: Home of the Nords error
+    Bug #1422: ClearInfoActor
+    Bug #1423: ForceGreeting closes existing dialogue windows
+    Bug #1425: Cannot load save game
+    Bug #1426: Read skill books aren't stored in savegame
+    Bug #1427: Useless items can be set under hotkeys
+    Bug #1429: Text variables in journal
+    Bug #1432: When attacking friendly NPC, the crime is reported and bounty is raised after each swing
+    Bug #1435: Stealing priceless items is without punishment
+    Bug #1437: Door marker at Jobasha's Rare Books is spawning PC in the air
+    Bug #1440: Topic selection menu should be wider
+    Bug #1441: Dropping items on the rug makes them inaccessible
+    Bug #1442: When dropping and taking some looted items, bystanders consider that as a crime
+    Bug #1444: Arrows and bolts are not dropped where the cursor points
+    Bug #1445: Security trainers offering acrobatics instead
+    Bug #1447: Character dash not displayed, French edition
+    Bug #1448: When the player is killed by the guard while having a bounty on his head, the guard dialogue opens over and over instead of loading dialogue
+    Bug #1454: Script error in SkipTutorial
+    Bug #1456: Bad lighting when using certain Morrowind.ini generated by MGE
+    Bug #1457: Heart of Lorkan comes after you when attacking it
+    Bug #1458: Modified Keybindings are not remembered
+    Bug #1459: Dura Gra-Bol doesn't respond to PC attack
+    Bug #1462: Interior cells not loaded with Morrowind Patch active
+    Bug #1469: Item tooltip should show the base value, not real value
+    Bug #1477: Death count is not stored in savegame
+    Bug #1478: AiActivate does not trigger activate scripts
+    Bug #1481: Weapon not rendered when partially submerged in water
+    Bug #1483: Enemies are attacking even while dying
+    Bug #1486: ESM Error: Don't know what to do with INFO
+    Bug #1490: Arrows shot at PC can end up in inventory
+    Bug #1492: Monsters respawn on top of one another
+    Bug #1493: Dialogue box opens with follower NPC even if NPC is dead
+    Bug #1494: Paralysed cliffracers remain airbourne
+    Bug #1495: Dialogue box opens with follower NPC even the game is paused
+    Bug #1496: GUI messages are not cleared when loading another saved game
+    Bug #1499: Underwater sound sometimes plays when transitioning from interior.
+    Bug #1500: Targetted spells and water.
+    Bug #1502: Console error message on info refusal
+    Bug #1507: Bloodmoon MQ The Ritual of Beasts: Can't remove the arrow
+    Bug #1508: Bloodmoon: Fort Frostmoth, cant talk with Carnius Magius
+    Bug #1516: PositionCell doesn't move actors to current cell
+    Bug #1518: ForceGreeting broken for explicit references
+    Bug #1522: Crash after attempting to play non-music file
+    Bug #1523: World map empty after loading interior save
+    Bug #1524: Arrows in waiting/resting dialog act like minimum and maximum buttons
+    Bug #1525: Werewolf: Killed NPC's don't fill werewolfs hunger for blood
+    Bug #1527: Werewolf: Detect life detects wrong type of actor
+    Bug #1529: OpenMW crash during "the shrine of the dead" mission (tribunal)
+    Bug #1530: Selected text in the console has the same color as the background
+    Bug #1539: Barilzar's Mazed Band: Tribunal
+    Bug #1542: Looping taunts from NPC`s after death: Tribunal
+    Bug #1543: OpenCS crash when using drag&drop in script editor
+    Bug #1547: Bamz-Amschend: Centurion Archers combat problem
+    Bug #1548: The Missing Hand: Tribunal
+    Bug #1549: The Mad God: Tribunal, Dome of Serlyn
+    Bug #1557: A bounty is calculated from actual item cost
+    Bug #1562: Invisible terrain on top of Red Mountain
+    Bug #1564: Cave of the hidden music: Bloodmoon
+    Bug #1567: Editor: Deleting of referenceables does not work
+    Bug #1568: Picking up a stack of items and holding the enter key and moving your mouse around paints a bunch of garbage on screen.
+    Bug #1574: Solstheim: Drauger cant inflict damage on player
+    Bug #1578: Solstheim: Bonewolf running animation not working
+    Bug #1585: Particle effects on PC are stopped when paralyzed
+    Bug #1589: Tribunal: Crimson Plague quest does not update when Gedna Relvel is killed
+    Bug #1590: Failed to save game: compile error
+    Bug #1598: Segfault when making Drain/Fortify Skill spells
+    Bug #1599: Unable to switch to fullscreen
+    Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed
+    Bug #1618: Death notice fails to show up
+    Bug #1628: Alt+Tab Segfault
+    Feature #32: Periodic Cleanup/Refill
+    Feature #41: Precipitation and weather particles
+    Feature #568: Editor: Configuration setup
+    Feature #649: Editor: Threaded loading
+    Feature #930: Editor: Cell record saving
+    Feature #934: Editor: Body part table
+    Feature #935: Editor: Enchantment effect table
+    Feature #1162: Dialogue merging
+    Feature #1174: Saved Game: add missing creature state
+    Feature #1177: Saved Game: fog of war state
+    Feature #1312: Editor: Combat/Magic/Stealth values for creatures are not displayed
+    Feature #1314: Make NPCs and creatures fight each other
+    Feature #1315: Crime: Murder
+    Feature #1321: Sneak skill enhancements
+    Feature #1323: Handle restocking items
+    Feature #1332: Saved Game: levelled creatures
+    Feature #1347: modFactionReaction script instruction
+    Feature #1362: Animated main menu support
+    Feature #1433: Store walk/run toggle
+    Feature #1449: Use names instead of numbers for saved game files and folders
+    Feature #1453: Adding Delete button to the load menu
+    Feature #1460: Enable Journal screen while in dialogue
+    Feature #1480: Play Battle music when in combat
+    Feature #1501: Followers unable to fast travel with you
+    Feature #1520: Disposition and distance-based aggression/ShouldAttack
+    Feature #1595: Editor: Object rendering in cells
+    Task #940: Move license to locations where applicable
+    Task #1333: Remove cmake git tag reading
+    Task #1566: Editor: Object rendering refactoring
 
 0.30.0
-
-Bug #416: Extreme shaking can occur during cell transitions while moving
-Bug #1003: Province Cyrodiil: Ogre Exception in Stirk
-Bug #1071: Crash when given a non-existent content file
-Bug #1080: OpenMW allows resting/using a bed while in combat
-Bug #1097: Wrong punishment for stealing in Census and Excise Office at the start of a new game
-Bug #1098: Unlocked evidence chests should get locked after new evidence is put into them
-Bug #1099: NPCs that you attacked still fight you after you went to jail/paid your fine
-Bug #1100: Taking items from a corpse is considered stealing
-Bug #1126: Some creatures can't get close enough to attack
-Bug #1144: Killed creatures seem to die again each time player transitions indoors/outdoors
-Bug #1181: loading a saved game does not reset the player control status
-Bug #1185: Collision issues in Addamasartus
-Bug #1187: Athyn Sarethi mission, rescuing varvur sarethi from the doesnt end the mission
-Bug #1189: Crash when entering interior cell "Gnisis, Arvs-Drelen"
-Bug #1191: Picking up papers without inventory in new game
-Bug #1195: NPCs do not equip torches in certain interiors
-Bug #1197: mouse wheel makes things scroll too fast
-Bug #1200: door blocked by monsters
-Bug #1201: item's magical charges are only refreshed when they are used
-Bug #1203: Scribs do not defend themselves
-Bug #1204: creatures life is not empty when they are dead
-Bug #1205: armor experience does not progress when hits are taken
-Bug #1206: blood particules always red. Undeads and mechanicals should have a different one.
-Bug #1209: Tarhiel never falls
-Bug #1210: journal adding script is ran again after having saved/loaded
-Bug #1224: Names of custom classes are not properly handled in save games
-Bug #1227: Editor: Fixed case handling for broken localised versions of Morrowind.esm
-Bug #1235: Indoors walk stutter
-Bug #1236: Aborting intro movie brings up the menu
-Bug #1239: NPCs get stuck when walking past each other
-Bug #1240: BTB - Settings 14.1 and Health Bar.
-Bug #1241: BTB - Character and Khajiit Prejudice
-Bug #1248: GUI Weapon icon is changed to hand-to-hand after save load
-Bug #1254: Guild ranks do not show in dialogue
-Bug #1255: When opening a container and selecting "Take All", the screen flashes blue
-Bug #1260: Level Up menu doesn't show image when using a custom class
-Bug #1265: Quit Menu Has Misaligned Buttons
-Bug #1270: Active weapon icon is not updated when weapon is repaired
-Bug #1271: NPC Stuck in hovering "Jumping" animation
-Bug #1272: Crash when attempting to load Big City esm file.
-Bug #1276: Editor: Dropping a region into the filter of a cell subview fails
-Bug #1286: Dialogue topic list clips with window frame
-Bug #1291: Saved game: store faction membership
-Bug #1293: Pluginless Khajiit Head Pack by ashiraniir makes OpenMW close.
-Bug #1294: Pasting in console adds text to end, not at cursor
-Bug #1295: Conversation loop when asking about "specific place" in Vivec
-Bug #1296: Caius doesn't leave at start of quest "Mehra Milo and the Lost Prophecies"
-Bug #1297: Saved game: map markers
-Bug #1302: ring_keley script causes vector::_M_range_check exception
-Bug #1309: Bug on "You violated the law" dialog
-Bug #1319: Creatures sometimes rendered incorrectly
-Feature #50: Ranged Combat
-Feature #58: Sneaking Skill
-Feature #73: Crime and Punishment
-Feature #135: Editor: OGRE integration
-Feature #541: Editor: Dialogue Sub-Views
-Feature #853: Editor: Rework User Settings
-Feature #944: Editor: lighting modes
-Feature #945: Editor: Camera navigation mode
-Feature #953: Trader gold
-Feature #1140: AI: summoned creatures
-Feature #1142: AI follow: Run stance
-Feature #1154: Not all NPCs get aggressive when one is attacked
-Feature #1169: Terrain threading
-Feature #1172: Loading screen and progress bars during saved/loading game
-Feature #1173: Saved Game: include weather state
-Feature #1207: Class creation form does not remember
-Feature #1220: Editor: Preview Subview
-Feature #1223: Saved Game: Local Variables
-Feature #1229: Quicksave, quickload, autosave
-Feature #1230: Deleting saves
-Feature #1233: Bribe gold is placed into NPCs inventory
-Feature #1252: Saved Game: quick key bindings
-Feature #1273: Editor: Region Map context menu
-Feature #1274: Editor: Region Map drag & drop
-Feature #1275: Editor: Scene subview drop
-Feature #1282: Non-faction member crime recognition.
-Feature #1289: NPCs return to default position
-Task #941: Remove unused cmake files
+------
+
+    Bug #416: Extreme shaking can occur during cell transitions while moving
+    Bug #1003: Province Cyrodiil: Ogre Exception in Stirk
+    Bug #1071: Crash when given a non-existent content file
+    Bug #1080: OpenMW allows resting/using a bed while in combat
+    Bug #1097: Wrong punishment for stealing in Census and Excise Office at the start of a new game
+    Bug #1098: Unlocked evidence chests should get locked after new evidence is put into them
+    Bug #1099: NPCs that you attacked still fight you after you went to jail/paid your fine
+    Bug #1100: Taking items from a corpse is considered stealing
+    Bug #1126: Some creatures can't get close enough to attack
+    Bug #1144: Killed creatures seem to die again each time player transitions indoors/outdoors
+    Bug #1181: loading a saved game does not reset the player control status
+    Bug #1185: Collision issues in Addamasartus
+    Bug #1187: Athyn Sarethi mission, rescuing varvur sarethi from the doesnt end the mission
+    Bug #1189: Crash when entering interior cell "Gnisis, Arvs-Drelen"
+    Bug #1191: Picking up papers without inventory in new game
+    Bug #1195: NPCs do not equip torches in certain interiors
+    Bug #1197: mouse wheel makes things scroll too fast
+    Bug #1200: door blocked by monsters
+    Bug #1201: item's magical charges are only refreshed when they are used
+    Bug #1203: Scribs do not defend themselves
+    Bug #1204: creatures life is not empty when they are dead
+    Bug #1205: armor experience does not progress when hits are taken
+    Bug #1206: blood particules always red. Undeads and mechanicals should have a different one.
+    Bug #1209: Tarhiel never falls
+    Bug #1210: journal adding script is ran again after having saved/loaded
+    Bug #1224: Names of custom classes are not properly handled in save games
+    Bug #1227: Editor: Fixed case handling for broken localised versions of Morrowind.esm
+    Bug #1235: Indoors walk stutter
+    Bug #1236: Aborting intro movie brings up the menu
+    Bug #1239: NPCs get stuck when walking past each other
+    Bug #1240: BTB - Settings 14.1 and Health Bar.
+    Bug #1241: BTB - Character and Khajiit Prejudice
+    Bug #1248: GUI Weapon icon is changed to hand-to-hand after save load
+    Bug #1254: Guild ranks do not show in dialogue
+    Bug #1255: When opening a container and selecting "Take All", the screen flashes blue
+    Bug #1260: Level Up menu doesn't show image when using a custom class
+    Bug #1265: Quit Menu Has Misaligned Buttons
+    Bug #1270: Active weapon icon is not updated when weapon is repaired
+    Bug #1271: NPC Stuck in hovering "Jumping" animation
+    Bug #1272: Crash when attempting to load Big City esm file.
+    Bug #1276: Editor: Dropping a region into the filter of a cell subview fails
+    Bug #1286: Dialogue topic list clips with window frame
+    Bug #1291: Saved game: store faction membership
+    Bug #1293: Pluginless Khajiit Head Pack by ashiraniir makes OpenMW close.
+    Bug #1294: Pasting in console adds text to end, not at cursor
+    Bug #1295: Conversation loop when asking about "specific place" in Vivec
+    Bug #1296: Caius doesn't leave at start of quest "Mehra Milo and the Lost Prophecies"
+    Bug #1297: Saved game: map markers
+    Bug #1302: ring_keley script causes vector::_M_range_check exception
+    Bug #1309: Bug on "You violated the law" dialog
+    Bug #1319: Creatures sometimes rendered incorrectly
+    Feature #50: Ranged Combat
+    Feature #58: Sneaking Skill
+    Feature #73: Crime and Punishment
+    Feature #135: Editor: OGRE integration
+    Feature #541: Editor: Dialogue Sub-Views
+    Feature #853: Editor: Rework User Settings
+    Feature #944: Editor: lighting modes
+    Feature #945: Editor: Camera navigation mode
+    Feature #953: Trader gold
+    Feature #1140: AI: summoned creatures
+    Feature #1142: AI follow: Run stance
+    Feature #1154: Not all NPCs get aggressive when one is attacked
+    Feature #1169: Terrain threading
+    Feature #1172: Loading screen and progress bars during saved/loading game
+    Feature #1173: Saved Game: include weather state
+    Feature #1207: Class creation form does not remember
+    Feature #1220: Editor: Preview Subview
+    Feature #1223: Saved Game: Local Variables
+    Feature #1229: Quicksave, quickload, autosave
+    Feature #1230: Deleting saves
+    Feature #1233: Bribe gold is placed into NPCs inventory
+    Feature #1252: Saved Game: quick key bindings
+    Feature #1273: Editor: Region Map context menu
+    Feature #1274: Editor: Region Map drag & drop
+    Feature #1275: Editor: Scene subview drop
+    Feature #1282: Non-faction member crime recognition.
+    Feature #1289: NPCs return to default position
+    Task #941: Remove unused cmake files
 
 0.29.0
-
-Bug #556: Video soundtrack not played when music volume is set to zero
-Bug #829: OpenMW uses up all available vram, when playing for extended time
-Bug #848: Wrong amount of footsteps playing in 1st person
-Bug #888: Ascended Sleepers have movement issues
-Bug #892: Explicit references are allowed on all script functions
-Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly
-Bug #1009: Lake Fjalding AI related slowdown.
-Bug #1041: Music playback issues on OS X >= 10.9
-Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window
-Bug #1060: Some message boxes are cut off at the bottom
-Bug #1062: Bittercup script does not work ('end' variable)
-Bug #1074: Inventory paperdoll obscures armour rating
-Bug #1077: Message after killing an essential NPC disappears too fast
-Bug #1078: "Clutterbane" shows empty charge bar
-Bug #1083: UndoWerewolf fails
-Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered
-Bug #1090: Start scripts fail when going to a non-predefined cell
-Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed.
-Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior
-Bug #1105: Magicka is depleted when using uncastable spells
-Bug #1106: Creatures should be able to run
-Bug #1107: TR cliffs have way too huge collision boxes in OpenMW
-Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded.
-Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop)
-Bug #1115: Memory leak when spying on Fargoth
-Bug #1137: Script execution fails (drenSlaveOwners script)
-Bug #1143: Mehra Milo quest (vivec informants) is broken
-Bug #1145: Issues with moving gold between inventory and containers
-Bug #1146: Issues with picking up stacks of gold
-Bug #1147: Dwemer Crossbows are held incorrectly
-Bug #1158: Armor rating should always stay below inventory mannequin
-Bug #1159: Quick keys can be set during character generation
-Bug #1160: Crash on equip lockpick when
-Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file
-Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file
-Feature #30: Loading/Saving (still missing a few parts)
-Feature #101: AI Package: Activate
-Feature #103: AI Package: Follow, FollowCell
-Feature #138: Editor: Drag & Drop
-Feature #428: Player death
-Feature #505: Editor: Record Cloning
-Feature #701: Levelled creatures
-Feature #708: Improved Local Variable handling
-Feature #709: Editor: Script verifier
-Feature #764: Missing journal backend features
-Feature #777: Creature weapons/shields
-Feature #789: Editor: Referenceable record verifier
-Feature #924: Load/Save GUI (still missing loading screen and progress bars)
-Feature #946: Knockdown
-Feature #947: Decrease fatigue when running, swimming and attacking
-Feature #956: Melee Combat: Blocking
-Feature #957: Area magic
-Feature #960: Combat/AI combat for creatures
-Feature #962: Combat-Related AI instructions
-Feature #1075: Damage/Restore skill/attribute magic effects
-Feature #1076: Soultrap magic effect
-Feature #1081: Disease contraction
-Feature #1086: Blood particles
-Feature #1092: Interrupt resting
-Feature #1101: Inventory equip scripts
-Feature #1116: Version/Build number in Launcher window
-Feature #1119: Resistance/weakness to normal weapons magic effect
-Feature #1123: Slow Fall magic effect
-Feature #1130: Auto-calculate spells
-Feature #1164: Editor: Case-insensitive sorting in tables
+------
+
+    Bug #556: Video soundtrack not played when music volume is set to zero
+    Bug #829: OpenMW uses up all available vram, when playing for extended time
+    Bug #848: Wrong amount of footsteps playing in 1st person
+    Bug #888: Ascended Sleepers have movement issues
+    Bug #892: Explicit references are allowed on all script functions
+    Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly
+    Bug #1009: Lake Fjalding AI related slowdown.
+    Bug #1041: Music playback issues on OS X >= 10.9
+    Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window
+    Bug #1060: Some message boxes are cut off at the bottom
+    Bug #1062: Bittercup script does not work ('end' variable)
+    Bug #1074: Inventory paperdoll obscures armour rating
+    Bug #1077: Message after killing an essential NPC disappears too fast
+    Bug #1078: "Clutterbane" shows empty charge bar
+    Bug #1083: UndoWerewolf fails
+    Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered
+    Bug #1090: Start scripts fail when going to a non-predefined cell
+    Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed.
+    Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior
+    Bug #1105: Magicka is depleted when using uncastable spells
+    Bug #1106: Creatures should be able to run
+    Bug #1107: TR cliffs have way too huge collision boxes in OpenMW
+    Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded.
+    Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop)
+    Bug #1115: Memory leak when spying on Fargoth
+    Bug #1137: Script execution fails (drenSlaveOwners script)
+    Bug #1143: Mehra Milo quest (vivec informants) is broken
+    Bug #1145: Issues with moving gold between inventory and containers
+    Bug #1146: Issues with picking up stacks of gold
+    Bug #1147: Dwemer Crossbows are held incorrectly
+    Bug #1158: Armor rating should always stay below inventory mannequin
+    Bug #1159: Quick keys can be set during character generation
+    Bug #1160: Crash on equip lockpick when
+    Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file
+    Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file
+    Feature #30: Loading/Saving (still missing a few parts)
+    Feature #101: AI Package: Activate
+    Feature #103: AI Package: Follow, FollowCell
+    Feature #138: Editor: Drag & Drop
+    Feature #428: Player death
+    Feature #505: Editor: Record Cloning
+    Feature #701: Levelled creatures
+    Feature #708: Improved Local Variable handling
+    Feature #709: Editor: Script verifier
+    Feature #764: Missing journal backend features
+    Feature #777: Creature weapons/shields
+    Feature #789: Editor: Referenceable record verifier
+    Feature #924: Load/Save GUI (still missing loading screen and progress bars)
+    Feature #946: Knockdown
+    Feature #947: Decrease fatigue when running, swimming and attacking
+    Feature #956: Melee Combat: Blocking
+    Feature #957: Area magic
+    Feature #960: Combat/AI combat for creatures
+    Feature #962: Combat-Related AI instructions
+    Feature #1075: Damage/Restore skill/attribute magic effects
+    Feature #1076: Soultrap magic effect
+    Feature #1081: Disease contraction
+    Feature #1086: Blood particles
+    Feature #1092: Interrupt resting
+    Feature #1101: Inventory equip scripts
+    Feature #1116: Version/Build number in Launcher window
+    Feature #1119: Resistance/weakness to normal weapons magic effect
+    Feature #1123: Slow Fall magic effect
+    Feature #1130: Auto-calculate spells
+    Feature #1164: Editor: Case-insensitive sorting in tables
 
 0.28.0
-
-Bug #399: Inventory changes are not visible immediately
-Bug #417: Apply weather instantly when teleporting
-Bug #566: Global Map position marker not updated for interior cells
-Bug #712: Looting corpse delay
-Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod
-Bug #805: Two TR meshes appear black (v0.24RC)
-Bug #841: Third-person activation distance taken from camera rather than head
-Bug #845: NPCs hold torches during the day
-Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24
-Bug #856: Maormer race by Mac Kom - The heads are way up
-Bug #864: Walk locks during loading in 3rd person
-Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog
-Bug #882: Hircine's Ring doesn't always work
-Bug #909: [Tamriel Rebuilt] crashes in Akamora
-Bug #922: Launcher writing merged openmw.cfg files
-Bug #943: Random magnitude should be calculated per effect
-Bug #948: Negative fatigue level should be allowed
-Bug #949: Particles in world space
-Bug #950: Hard crash on x64 Linux running --new-game (on startup)
-Bug #951: setMagicka and setFatigue have no effect
-Bug #954: Problem with equipping inventory items when using a keyboard shortcut
-Bug #955: Issues with equipping torches
-Bug #966: Shield is visible when casting spell
-Bug #967: Game crashes when equipping silver candlestick
-Bug #970: Segmentation fault when starting at Bal Isra
-Bug #977: Pressing down key in console doesn't go forward in history
-Bug #979: Tooltip disappears when changing inventory
-Bug #980: Barter: item category is remembered, but not shown
-Bug #981: Mod: replacing model has wrong position/orientation
-Bug #982: Launcher: Addon unchecking is not saved
-Bug #983: Fix controllers to affect objects attached to the base node
-Bug #985: Player can talk to NPCs who are in combat
-Bug #989: OpenMW crashes when trying to include mod with capital .ESP
-Bug #991: Merchants equip items with harmful constant effect enchantments
-Bug #994: Don't cap skills/attributes when set via console
-Bug #998: Setting the max health should also set the current health
-Bug #1005: Torches are visible when casting spells and during hand to hand combat.
-Bug #1006: Many NPCs have 0 skill
-Bug #1007: Console fills up with text
-Bug #1013: Player randomly loses health or dies
-Bug #1014: Persuasion window is not centered in maximized window
-Bug #1015: Player status window scroll state resets on status change
-Bug #1016: Notification window not big enough for all skill level ups
-Bug #1020: Saved window positions are not rescaled appropriately on resolution change
-Bug #1022: Messages stuck permanently on screen when they pile up
-Bug #1023: Journals doesn't open
-Bug #1026: Game loses track of torch usage.
-Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level
-Bug #1029: Quick keys menu: Select compatible replacement when tool used up
-Bug #1042: TES3 header data wrong encoding
-Bug #1045: OS X: deployed OpenCS won't launch
-Bug #1046: All damaged weaponry is worth 1 gold
-Bug #1048: Links in "locked" dialogue are still clickable
-Bug #1052: Using color codes when naming your character actually changes the name's color
-Bug #1054: Spell effects not visible in front of water
-Bug #1055: Power-Spell animation starts even though you already casted it that day
-Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability
-Bug #1063: Crash upon checking out game start ship area in Seyda Neen
-Bug #1064: openmw binaries link to unnecessary libraries
-Bug #1065: Landing from a high place in water still causes fall damage
-Bug #1072: Drawing weapon increases torch brightness
-Bug #1073: Merchants sell stacks of gold
-Feature #43: Visuals for Magic Effects
-Feature #51: Ranged Magic
-Feature #52: Touch Range Magic
-Feature #53: Self Range Magic
-Feature #54: Spell Casting
-Feature #70: Vampirism
-Feature #100: Combat AI
-Feature #171: Implement NIF record NiFlipController
-Feature #410: Window to restore enchanted item charge
-Feature #647: Enchanted item glow
-Feature #723: Invisibility/Chameleon magic effects
-Feature #737: Resist Magicka magic effect
-Feature #758: GetLOS
-Feature #926: Editor: Info-Record tables
-Feature #958: Material controllers
-Feature #959: Terrain bump, specular, & parallax mapping
-Feature #990: Request: unlock mouse when in any menu
-Feature #1018: Do not allow view mode switching while performing an action
-Feature #1027: Vertex morph animation (NiGeomMorpherController)
-Feature #1031: Handle NiBillboardNode
-Feature #1051: Implement NIF texture slot DarkTexture
-Task #873: Unify OGRE initialisation
+------
+
+    Bug #399: Inventory changes are not visible immediately
+    Bug #417: Apply weather instantly when teleporting
+    Bug #566: Global Map position marker not updated for interior cells
+    Bug #712: Looting corpse delay
+    Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod
+    Bug #805: Two TR meshes appear black (v0.24RC)
+    Bug #841: Third-person activation distance taken from camera rather than head
+    Bug #845: NPCs hold torches during the day
+    Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24
+    Bug #856: Maormer race by Mac Kom - The heads are way up
+    Bug #864: Walk locks during loading in 3rd person
+    Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog
+    Bug #882: Hircine's Ring doesn't always work
+    Bug #909: [Tamriel Rebuilt] crashes in Akamora
+    Bug #922: Launcher writing merged openmw.cfg files
+    Bug #943: Random magnitude should be calculated per effect
+    Bug #948: Negative fatigue level should be allowed
+    Bug #949: Particles in world space
+    Bug #950: Hard crash on x64 Linux running --new-game (on startup)
+    Bug #951: setMagicka and setFatigue have no effect
+    Bug #954: Problem with equipping inventory items when using a keyboard shortcut
+    Bug #955: Issues with equipping torches
+    Bug #966: Shield is visible when casting spell
+    Bug #967: Game crashes when equipping silver candlestick
+    Bug #970: Segmentation fault when starting at Bal Isra
+    Bug #977: Pressing down key in console doesn't go forward in history
+    Bug #979: Tooltip disappears when changing inventory
+    Bug #980: Barter: item category is remembered, but not shown
+    Bug #981: Mod: replacing model has wrong position/orientation
+    Bug #982: Launcher: Addon unchecking is not saved
+    Bug #983: Fix controllers to affect objects attached to the base node
+    Bug #985: Player can talk to NPCs who are in combat
+    Bug #989: OpenMW crashes when trying to include mod with capital .ESP
+    Bug #991: Merchants equip items with harmful constant effect enchantments
+    Bug #994: Don't cap skills/attributes when set via console
+    Bug #998: Setting the max health should also set the current health
+    Bug #1005: Torches are visible when casting spells and during hand to hand combat.
+    Bug #1006: Many NPCs have 0 skill
+    Bug #1007: Console fills up with text
+    Bug #1013: Player randomly loses health or dies
+    Bug #1014: Persuasion window is not centered in maximized window
+    Bug #1015: Player status window scroll state resets on status change
+    Bug #1016: Notification window not big enough for all skill level ups
+    Bug #1020: Saved window positions are not rescaled appropriately on resolution change
+    Bug #1022: Messages stuck permanently on screen when they pile up
+    Bug #1023: Journals doesn't open
+    Bug #1026: Game loses track of torch usage.
+    Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level
+    Bug #1029: Quick keys menu: Select compatible replacement when tool used up
+    Bug #1042: TES3 header data wrong encoding
+    Bug #1045: OS X: deployed OpenCS won't launch
+    Bug #1046: All damaged weaponry is worth 1 gold
+    Bug #1048: Links in "locked" dialogue are still clickable
+    Bug #1052: Using color codes when naming your character actually changes the name's color
+    Bug #1054: Spell effects not visible in front of water
+    Bug #1055: Power-Spell animation starts even though you already casted it that day
+    Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability
+    Bug #1063: Crash upon checking out game start ship area in Seyda Neen
+    Bug #1064: openmw binaries link to unnecessary libraries
+    Bug #1065: Landing from a high place in water still causes fall damage
+    Bug #1072: Drawing weapon increases torch brightness
+    Bug #1073: Merchants sell stacks of gold
+    Feature #43: Visuals for Magic Effects
+    Feature #51: Ranged Magic
+    Feature #52: Touch Range Magic
+    Feature #53: Self Range Magic
+    Feature #54: Spell Casting
+    Feature #70: Vampirism
+    Feature #100: Combat AI
+    Feature #171: Implement NIF record NiFlipController
+    Feature #410: Window to restore enchanted item charge
+    Feature #647: Enchanted item glow
+    Feature #723: Invisibility/Chameleon magic effects
+    Feature #737: Resist Magicka magic effect
+    Feature #758: GetLOS
+    Feature #926: Editor: Info-Record tables
+    Feature #958: Material controllers
+    Feature #959: Terrain bump, specular, & parallax mapping
+    Feature #990: Request: unlock mouse when in any menu
+    Feature #1018: Do not allow view mode switching while performing an action
+    Feature #1027: Vertex morph animation (NiGeomMorpherController)
+    Feature #1031: Handle NiBillboardNode
+    Feature #1051: Implement NIF texture slot DarkTexture
+    Task #873: Unify OGRE initialisation
 
 0.27.0
-
-Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp
-Bug #794: incorrect display of decimal numbers
-Bug #840: First-person sneaking camera height
-Bug #887: Ambient sounds playing while paused
-Bug #902: Problems with Polish character encoding
-Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key
-Bug #910: Some CDs not working correctly with Unshield installer
-Bug #917: Quick character creation plugin does not work
-Bug #918: Fatigue does not refill
-Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2)
-Feature #57: Acrobatics Skill
-Feature #462: Editor: Start Dialogue
-Feature #546: Modify ESX selector to handle new content file scheme
-Feature #588: Editor: Adjust name/path of edited content files
-Feature #644: Editor: Save
-Feature #710: Editor: Configure script compiler context
-Feature #790: God Mode
-Feature #881: Editor: Allow only one instance of OpenCS
-Feature #889: Editor: Record filtering
-Feature #895: Extinguish torches
-Feature #898: Breath meter enhancements
-Feature #901: Editor: Default record filter
-Feature #913: Merge --master and --plugin switches
+------
+
+    Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp
+    Bug #794: incorrect display of decimal numbers
+    Bug #840: First-person sneaking camera height
+    Bug #887: Ambient sounds playing while paused
+    Bug #902: Problems with Polish character encoding
+    Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key
+    Bug #910: Some CDs not working correctly with Unshield installer
+    Bug #917: Quick character creation plugin does not work
+    Bug #918: Fatigue does not refill
+    Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2)
+    Feature #57: Acrobatics Skill
+    Feature #462: Editor: Start Dialogue
+    Feature #546: Modify ESX selector to handle new content file scheme
+    Feature #588: Editor: Adjust name/path of edited content files
+    Feature #644: Editor: Save
+    Feature #710: Editor: Configure script compiler context
+    Feature #790: God Mode
+    Feature #881: Editor: Allow only one instance of OpenCS
+    Feature #889: Editor: Record filtering
+    Feature #895: Extinguish torches
+    Feature #898: Breath meter enhancements
+    Feature #901: Editor: Default record filter
+    Feature #913: Merge --master and --plugin switches
 
 0.26.0
-
-Bug #274: Inconsistencies in the terrain
-Bug #557: Already-dead NPCs do not equip clothing/items.
-Bug #592: Window resizing
-Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren)
-Bug #664: Heart of lorkhan acts like a dead body (container)
-Bug #767: Wonky ramp physics & water
-Bug #780: Swimming out of water
-Bug #792: Wrong ground alignment on actors when no clipping
-Bug #796: Opening and closing door sound issue
-Bug #797: No clipping hinders opening and closing of doors
-Bug #799: sliders in enchanting window
-Bug #838: Pressing key during startup procedure freezes the game
-Bug #839: Combat/magic stances during character creation
-Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment
-Bug #844: Resting "until healed" option given even with full stats
-Bug #846: Equipped torches are invisible.
-Bug #847: Incorrect formula for autocalculated NPC initial health
-Bug #850: Shealt weapon sound plays when leaving magic-ready stance
-Bug #852: Some boots do not produce footstep sounds
-Bug #860: FPS bar misalignment
-Bug #861: Unable to print screen
-Bug #863: No sneaking and jumping at the same time
-Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start.
-Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance.
-Bug #868: Idle animations are repeated
-Bug #874: Underwater swimming close to the ground is jerky
-Bug #875: Animation problem while swimming on the surface and looking up
-Bug #876: Always a starting upper case letter in the inventory
-Bug #878: Active spell effects don't update the layout properly when ended
-Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load
-Bug #896: New game sound issue
-Feature #49: Melee Combat
-Feature #71: Lycanthropy
-Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList
-Feature #622: Multiple positions for inventory window
-Feature #627: Drowning
-Feature #786: Allow the 'Activate' key to close the countdialog window
-Feature #798: Morrowind installation via Launcher (Linux/Max OS only)
-Feature #851: First/Third person transitions with mouse wheel
-Task #689: change PhysicActor::enableCollisions
-Task #707: Reorganise Compiler
+------
+
+    Bug #274: Inconsistencies in the terrain
+    Bug #557: Already-dead NPCs do not equip clothing/items.
+    Bug #592: Window resizing
+    Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren)
+    Bug #664: Heart of lorkhan acts like a dead body (container)
+    Bug #767: Wonky ramp physics & water
+    Bug #780: Swimming out of water
+    Bug #792: Wrong ground alignment on actors when no clipping
+    Bug #796: Opening and closing door sound issue
+    Bug #797: No clipping hinders opening and closing of doors
+    Bug #799: sliders in enchanting window
+    Bug #838: Pressing key during startup procedure freezes the game
+    Bug #839: Combat/magic stances during character creation
+    Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment
+    Bug #844: Resting "until healed" option given even with full stats
+    Bug #846: Equipped torches are invisible.
+    Bug #847: Incorrect formula for autocalculated NPC initial health
+    Bug #850: Shealt weapon sound plays when leaving magic-ready stance
+    Bug #852: Some boots do not produce footstep sounds
+    Bug #860: FPS bar misalignment
+    Bug #861: Unable to print screen
+    Bug #863: No sneaking and jumping at the same time
+    Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start.
+    Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance.
+    Bug #868: Idle animations are repeated
+    Bug #874: Underwater swimming close to the ground is jerky
+    Bug #875: Animation problem while swimming on the surface and looking up
+    Bug #876: Always a starting upper case letter in the inventory
+    Bug #878: Active spell effects don't update the layout properly when ended
+    Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load
+    Bug #896: New game sound issue
+    Feature #49: Melee Combat
+    Feature #71: Lycanthropy
+    Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList
+    Feature #622: Multiple positions for inventory window
+    Feature #627: Drowning
+    Feature #786: Allow the 'Activate' key to close the countdialog window
+    Feature #798: Morrowind installation via Launcher (Linux/Max OS only)
+    Feature #851: First/Third person transitions with mouse wheel
+    Task #689: change PhysicActor::enableCollisions
+    Task #707: Reorganise Compiler
 
 0.25.0
-
-Bug #411: Launcher crash on OS X < 10.8
-Bug #604: Terrible performance drop in the Census and Excise Office.
-Bug #676: Start Scripts fail to load
-Bug #677: OpenMW does not accept script names with -
-Bug #766: Extra space in front of topic links
-Bug #793: AIWander Isn't Being Passed The Repeat Parameter
-Bug #795: Sound playing with drawn weapon and crossing cell-border
-Bug #800: can't select weapon for enchantment
-Bug #801: Player can move while over-encumbered
-Bug #802: Dead Keys not working
-Bug #808: mouse capture
-Bug #809: ini Importer does not work without an existing cfg file
-Bug #812: Launcher will run OpenMW with no ESM or ESP selected
-Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected
-Bug #817: Dead NPCs and Creatures still have collision boxes
-Bug #820: Incorrect sorting of answers (Dialogue)
-Bug #826: mwinimport dumps core when given an unknown parameter
-Bug #833: getting stuck in door
-Bug #835: Journals/books not showing up properly.
-Feature #38: SoundGen
-Feature #105: AI Package: Wander
-Feature #230: 64-bit compatibility for OS X
-Feature #263: Hardware mouse cursors
-Feature #449: Allow mouse outside of window while paused
-Feature #736: First person animations
-Feature #750: Using mouse wheel in third person mode
-Feature #822: Autorepeat for slider buttons
+------
+
+    Bug #411: Launcher crash on OS X < 10.8
+    Bug #604: Terrible performance drop in the Census and Excise Office.
+    Bug #676: Start Scripts fail to load
+    Bug #677: OpenMW does not accept script names with -
+    Bug #766: Extra space in front of topic links
+    Bug #793: AIWander Isn't Being Passed The Repeat Parameter
+    Bug #795: Sound playing with drawn weapon and crossing cell-border
+    Bug #800: can't select weapon for enchantment
+    Bug #801: Player can move while over-encumbered
+    Bug #802: Dead Keys not working
+    Bug #808: mouse capture
+    Bug #809: ini Importer does not work without an existing cfg file
+    Bug #812: Launcher will run OpenMW with no ESM or ESP selected
+    Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected
+    Bug #817: Dead NPCs and Creatures still have collision boxes
+    Bug #820: Incorrect sorting of answers (Dialogue)
+    Bug #826: mwinimport dumps core when given an unknown parameter
+    Bug #833: getting stuck in door
+    Bug #835: Journals/books not showing up properly.
+    Feature #38: SoundGen
+    Feature #105: AI Package: Wander
+    Feature #230: 64-bit compatibility for OS X
+    Feature #263: Hardware mouse cursors
+    Feature #449: Allow mouse outside of window while paused
+    Feature #736: First person animations
+    Feature #750: Using mouse wheel in third person mode
+    Feature #822: Autorepeat for slider buttons
 
 0.24.0
-
-Bug #284: Book's text misalignment
-Bug #445: Camera able to get slightly below floor / terrain
-Bug #582: Seam issue in Red Mountain
-Bug #632: Journal Next Button shows white square
-Bug #653: IndexedStore ignores index
-Bug #694: Parser does not recognize float values starting with .
-Bug #699: Resource handling broken with Ogre 1.9 trunk
-Bug #718: components/esm/loadcell is using the mwworld subsystem
-Bug #729: Levelled item list tries to add nonexistent item
-Bug #730: Arrow buttons in the settings menu do not work.
-Bug #732: Erroneous behavior when binding keys
-Bug #733: Unclickable dialogue topic
-Bug #734: Book empty line problem
-Bug #738: OnDeath only works with implicit references
-Bug #740: Script compiler fails on scripts with special names
-Bug #742: Wait while no clipping
-Bug #743: Problem with changeweather console command
-Bug #744: No wait dialogue after starting a new game
-Bug #748: Player is not able to unselect objects with the console
-Bug #751: AddItem should only spawn a message box when called from dialogue
-Bug #752: The enter button has several functions in trade and looting that is not impelemted.
-Bug #753: Fargoth's Ring Quest Strange Behavior
-Bug #755: Launcher writes duplicate lines into settings.cfg
-Bug #759: Second quest in mages guild does not work
-Bug #763: Enchantment cast cost is wrong
-Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly
-Bug #773: AIWander Isn't Being Passed The Correct idle Values
-Bug #778: The journal can be opened at the start of a new game
-Bug #779: Divayth Fyr starts as dead
-Bug #787: "Batch count" on detailed FPS counter gets cut-off
-Bug #788: chargen scroll layout does not match vanilla
-Feature #60: Atlethics Skill
-Feature #65: Security Skill
-Feature #74: Interaction with non-load-doors
-Feature #98: Render Weapon and Shield
-Feature #102: AI Package: Escort, EscortCell
-Feature #182: Advanced Journal GUI
-Feature #288: Trading enhancements
-Feature #405: Integrate "new game" into the menu
-Feature #537: Highlight dialogue topic links
-Feature #658: Rotate, RotateWorld script instructions and local rotations
-Feature #690: Animation Layering
-Feature #722: Night Eye/Blind magic effects
-Feature #735: Move, MoveWorld script instructions.
-Feature #760: Non-removable corpses
+------
+
+    Bug #284: Book's text misalignment
+    Bug #445: Camera able to get slightly below floor / terrain
+    Bug #582: Seam issue in Red Mountain
+    Bug #632: Journal Next Button shows white square
+    Bug #653: IndexedStore ignores index
+    Bug #694: Parser does not recognize float values starting with .
+    Bug #699: Resource handling broken with Ogre 1.9 trunk
+    Bug #718: components/esm/loadcell is using the mwworld subsystem
+    Bug #729: Levelled item list tries to add nonexistent item
+    Bug #730: Arrow buttons in the settings menu do not work.
+    Bug #732: Erroneous behavior when binding keys
+    Bug #733: Unclickable dialogue topic
+    Bug #734: Book empty line problem
+    Bug #738: OnDeath only works with implicit references
+    Bug #740: Script compiler fails on scripts with special names
+    Bug #742: Wait while no clipping
+    Bug #743: Problem with changeweather console command
+    Bug #744: No wait dialogue after starting a new game
+    Bug #748: Player is not able to unselect objects with the console
+    Bug #751: AddItem should only spawn a message box when called from dialogue
+    Bug #752: The enter button has several functions in trade and looting that is not impelemted.
+    Bug #753: Fargoth's Ring Quest Strange Behavior
+    Bug #755: Launcher writes duplicate lines into settings.cfg
+    Bug #759: Second quest in mages guild does not work
+    Bug #763: Enchantment cast cost is wrong
+    Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly
+    Bug #773: AIWander Isn't Being Passed The Correct idle Values
+    Bug #778: The journal can be opened at the start of a new game
+    Bug #779: Divayth Fyr starts as dead
+    Bug #787: "Batch count" on detailed FPS counter gets cut-off
+    Bug #788: chargen scroll layout does not match vanilla
+    Feature #60: Atlethics Skill
+    Feature #65: Security Skill
+    Feature #74: Interaction with non-load-doors
+    Feature #98: Render Weapon and Shield
+    Feature #102: AI Package: Escort, EscortCell
+    Feature #182: Advanced Journal GUI
+    Feature #288: Trading enhancements
+    Feature #405: Integrate "new game" into the menu
+    Feature #537: Highlight dialogue topic links
+    Feature #658: Rotate, RotateWorld script instructions and local rotations
+    Feature #690: Animation Layering
+    Feature #722: Night Eye/Blind magic effects
+    Feature #735: Move, MoveWorld script instructions.
+    Feature #760: Non-removable corpses
 
 0.23.0
-
-Bug #522: Player collides with placeable items
-Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open
-Bug #561: Tooltip word wrapping delay
-Bug #578: Bribing works incorrectly
-Bug #601: PositionCell fails on negative coordinates
-Bug #606: Some NPCs hairs not rendered with Better Heads addon
-Bug #609: Bad rendering of bone boots
-Bug #613: Messagebox causing assert to fail
-Bug #631: Segfault on shutdown
-Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard
-Bug #635: Scale NPCs depending on race
-Bug #643: Dialogue Race select function is inverted
-Bug #646: Twohanded weapons don't work properly
-Bug #654: Crash when dropping objects without a collision shape
-Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell
-Bug #660: "g" in "change" cut off in Race Menu
-Bug #661: Arrille sells me the key to his upstairs room
-Bug #662: Day counter starts at 2 instead of 1
-Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur
-Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window.
-Bug #666: Looking up/down problem
-Bug #667: Active effects border visible during loading
-Bug #669: incorrect player position at new game start
-Bug #670: race selection menu: sex, face and hair left button not totally clickable
-Bug #671: new game: player is naked
-Bug #674: buying or selling items doesn't change amount of gold
-Bug #675: fatigue is not set to its maximum when starting a new game
-Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly
-Bug #680: different gold coins in Tel Mara
-Bug #682: Race menu ignores playable flag for some hairs and faces
-Bug #685: Script compiler does not accept ":" after a function name
-Bug #688: dispose corpse makes cross-hair to disappear
-Bug #691: Auto equipping ignores equipment conditions
-Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder
-Bug #696: Draugr incorrect head offset
-Bug #697: Sail transparency issue
-Bug #700: "On the rocks" mod does not load its UV coordinates correctly.
-Bug #702: Some race mods don't work
-Bug #711: Crash during character creation
-Bug #715: Growing Tauryon
-Bug #725: Auto calculate stats
-Bug #728: Failure to open container and talk dialogue
-Bug #731: Crash with Mush-Mere's "background" topic
-Feature #55/657: Item Repairing
-Feature #62/87: Enchanting
-Feature #99: Pathfinding
-Feature #104: AI Package: Travel
-Feature #129: Levelled items
-Feature #204: Texture animations
-Feature #239: Fallback-Settings
-Feature #535: Console object selection improvements
-Feature #629: Add levelup description in levelup layout dialog
-Feature #630: Optional format subrecord in (tes3) header
-Feature #641: Armor rating
-Feature #645: OnDeath script function
-Feature #683: Companion item UI
-Feature #698: Basic Particles
-Task #648: Split up components/esm/loadlocks
-Task #695: mwgui cleanup
+------
+
+    Bug #522: Player collides with placeable items
+    Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open
+    Bug #561: Tooltip word wrapping delay
+    Bug #578: Bribing works incorrectly
+    Bug #601: PositionCell fails on negative coordinates
+    Bug #606: Some NPCs hairs not rendered with Better Heads addon
+    Bug #609: Bad rendering of bone boots
+    Bug #613: Messagebox causing assert to fail
+    Bug #631: Segfault on shutdown
+    Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard
+    Bug #635: Scale NPCs depending on race
+    Bug #643: Dialogue Race select function is inverted
+    Bug #646: Twohanded weapons don't work properly
+    Bug #654: Crash when dropping objects without a collision shape
+    Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell
+    Bug #660: "g" in "change" cut off in Race Menu
+    Bug #661: Arrille sells me the key to his upstairs room
+    Bug #662: Day counter starts at 2 instead of 1
+    Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur
+    Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window.
+    Bug #666: Looking up/down problem
+    Bug #667: Active effects border visible during loading
+    Bug #669: incorrect player position at new game start
+    Bug #670: race selection menu: sex, face and hair left button not totally clickable
+    Bug #671: new game: player is naked
+    Bug #674: buying or selling items doesn't change amount of gold
+    Bug #675: fatigue is not set to its maximum when starting a new game
+    Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly
+    Bug #680: different gold coins in Tel Mara
+    Bug #682: Race menu ignores playable flag for some hairs and faces
+    Bug #685: Script compiler does not accept ":" after a function name
+    Bug #688: dispose corpse makes cross-hair to disappear
+    Bug #691: Auto equipping ignores equipment conditions
+    Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder
+    Bug #696: Draugr incorrect head offset
+    Bug #697: Sail transparency issue
+    Bug #700: "On the rocks" mod does not load its UV coordinates correctly.
+    Bug #702: Some race mods don't work
+    Bug #711: Crash during character creation
+    Bug #715: Growing Tauryon
+    Bug #725: Auto calculate stats
+    Bug #728: Failure to open container and talk dialogue
+    Bug #731: Crash with Mush-Mere's "background" topic
+    Feature #55/657: Item Repairing
+    Feature #62/87: Enchanting
+    Feature #99: Pathfinding
+    Feature #104: AI Package: Travel
+    Feature #129: Levelled items
+    Feature #204: Texture animations
+    Feature #239: Fallback-Settings
+    Feature #535: Console object selection improvements
+    Feature #629: Add levelup description in levelup layout dialog
+    Feature #630: Optional format subrecord in (tes3) header
+    Feature #641: Armor rating
+    Feature #645: OnDeath script function
+    Feature #683: Companion item UI
+    Feature #698: Basic Particles
+    Task #648: Split up components/esm/loadlocks
+    Task #695: mwgui cleanup
 
 0.22.0
-
-Bug #311: Potential infinite recursion in script compiler
-Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit.
-Bug #382: Weird effect in 3rd person on water
-Bug #387: Always use detailed shape for physics raycasts
-Bug #420: Potion/ingredient effects do not stack
-Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips
-Bug #434/Bug #605: Object movement between cells not properly implemented
-Bug #502: Duplicate player collision model at origin
-Bug #509: Dialogue topic list shifts inappropriately
-Bug #513: Sliding stairs
-Bug #515: Launcher does not support non-latin strings
-Bug #525: Race selection preview camera wrong position
-Bug #526: Attributes / skills should not go below zero
-Bug #529: Class and Birthsign menus options should be preselected
-Bug #530: Lock window button graphic missing
-Bug #532: Missing map menu graphics
-Bug #545: ESX selector does not list ESM files properly
-Bug #547: Global variables of type short are read incorrectly
-Bug #550: Invisible meshes collision and tooltip
-Bug #551: Performance drop when loading multiple ESM files
-Bug #552: Don't list CG in options if it is not available
-Bug #555: Character creation windows "OK" button broken
-Bug #558: Segmentation fault when Alt-tabbing with console opened
-Bug #559: Dialog window should not be available before character creation is finished
-Bug #560: Tooltip borders should be stretched
-Bug #562: Sound should not be played when an object cannot be picked up
-Bug #565: Water animation speed + timescale
-Bug #572: Better Bodies' textures don't work
-Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod)
-Bug #574: Moving left/right should not cancel auto-run
-Bug #575: Crash entering the Chamber of Song
-Bug #576: Missing includes
-Bug #577: Left Gloves Addon causes ESMReader exception
-Bug #579: Unable to open container "Kvama Egg Sack"
-Bug #581: Mimicking vanilla Morrowind water
-Bug #583: Gender not recognized
-Bug #586: Wrong char gen behaviour
-Bug #587: "End" script statements with spaces don't work
-Bug #589: Closing message boxes by pressing the activation key
-Bug #590: Ugly Dagoth Ur rendering
-Bug #591: Race selection issues
-Bug #593: Persuasion response should be random
-Bug #595: Footless guard
-Bug #599: Waterfalls are invisible from a certain distance
-Bug #600: Waterfalls rendered incorrectly, cut off by water
-Bug #607: New beast bodies mod crashes
-Bug #608: Crash in cell "Mournhold, Royal Palace"
-Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt
-Bug #613: Messagebox causing assert to fail
-Bug #615: Meshes invisible from above water
-Bug #617: Potion effects should be hidden until discovered
-Bug #619: certain moss hanging from tree has rendering bug
-Bug #621: Batching bloodmoon's trees
-Bug #623: NiMaterialProperty alpha unhandled
-Bug #628: Launcher in latest master crashes the game
-Bug #633: Crash on startup: Better Heads
-Bug #636: Incorrect Char Gen Menu Behavior
-Feature #29: Allow ESPs and multiple ESMs
-Feature #94: Finish class selection-dialogue
-Feature #149: Texture Alphas
-Feature #237: Run Morrowind-ini importer from launcher
-Feature #286: Update Active Spell Icons
-Feature #334: Swimming animation
-Feature #335: Walking animation
-Feature #360: Proper collision shapes for NPCs and creatures
-Feature #367: Lights that behave more like original morrowind implementation
-Feature #477: Special local scripting variables
-Feature #528: Message boxes should close when enter is pressed under certain conditions.
-Feature #543: Add bsa files to the settings imported by the ini importer
-Feature #594: coordinate space and utility functions
-Feature #625: Zoom in vanity mode
-Task #464: Refactor launcher ESX selector into a re-usable component
-Task #624: Unified implementation of type-variable sub-records
+------
+
+    Bug #311: Potential infinite recursion in script compiler
+    Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit.
+    Bug #382: Weird effect in 3rd person on water
+    Bug #387: Always use detailed shape for physics raycasts
+    Bug #420: Potion/ingredient effects do not stack
+    Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips
+    Bug #434/Bug #605: Object movement between cells not properly implemented
+    Bug #502: Duplicate player collision model at origin
+    Bug #509: Dialogue topic list shifts inappropriately
+    Bug #513: Sliding stairs
+    Bug #515: Launcher does not support non-latin strings
+    Bug #525: Race selection preview camera wrong position
+    Bug #526: Attributes / skills should not go below zero
+    Bug #529: Class and Birthsign menus options should be preselected
+    Bug #530: Lock window button graphic missing
+    Bug #532: Missing map menu graphics
+    Bug #545: ESX selector does not list ESM files properly
+    Bug #547: Global variables of type short are read incorrectly
+    Bug #550: Invisible meshes collision and tooltip
+    Bug #551: Performance drop when loading multiple ESM files
+    Bug #552: Don't list CG in options if it is not available
+    Bug #555: Character creation windows "OK" button broken
+    Bug #558: Segmentation fault when Alt-tabbing with console opened
+    Bug #559: Dialog window should not be available before character creation is finished
+    Bug #560: Tooltip borders should be stretched
+    Bug #562: Sound should not be played when an object cannot be picked up
+    Bug #565: Water animation speed + timescale
+    Bug #572: Better Bodies' textures don't work
+    Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod)
+    Bug #574: Moving left/right should not cancel auto-run
+    Bug #575: Crash entering the Chamber of Song
+    Bug #576: Missing includes
+    Bug #577: Left Gloves Addon causes ESMReader exception
+    Bug #579: Unable to open container "Kvama Egg Sack"
+    Bug #581: Mimicking vanilla Morrowind water
+    Bug #583: Gender not recognized
+    Bug #586: Wrong char gen behaviour
+    Bug #587: "End" script statements with spaces don't work
+    Bug #589: Closing message boxes by pressing the activation key
+    Bug #590: Ugly Dagoth Ur rendering
+    Bug #591: Race selection issues
+    Bug #593: Persuasion response should be random
+    Bug #595: Footless guard
+    Bug #599: Waterfalls are invisible from a certain distance
+    Bug #600: Waterfalls rendered incorrectly, cut off by water
+    Bug #607: New beast bodies mod crashes
+    Bug #608: Crash in cell "Mournhold, Royal Palace"
+    Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt
+    Bug #613: Messagebox causing assert to fail
+    Bug #615: Meshes invisible from above water
+    Bug #617: Potion effects should be hidden until discovered
+    Bug #619: certain moss hanging from tree has rendering bug
+    Bug #621: Batching bloodmoon's trees
+    Bug #623: NiMaterialProperty alpha unhandled
+    Bug #628: Launcher in latest master crashes the game
+    Bug #633: Crash on startup: Better Heads
+    Bug #636: Incorrect Char Gen Menu Behavior
+    Feature #29: Allow ESPs and multiple ESMs
+    Feature #94: Finish class selection-dialogue
+    Feature #149: Texture Alphas
+    Feature #237: Run Morrowind-ini importer from launcher
+    Feature #286: Update Active Spell Icons
+    Feature #334: Swimming animation
+    Feature #335: Walking animation
+    Feature #360: Proper collision shapes for NPCs and creatures
+    Feature #367: Lights that behave more like original morrowind implementation
+    Feature #477: Special local scripting variables
+    Feature #528: Message boxes should close when enter is pressed under certain conditions.
+    Feature #543: Add bsa files to the settings imported by the ini importer
+    Feature #594: coordinate space and utility functions
+    Feature #625: Zoom in vanity mode
+    Task #464: Refactor launcher ESX selector into a re-usable component
+    Task #624: Unified implementation of type-variable sub-records
 
 0.21.0
-
-Bug #253: Dialogs don't work for Russian version of Morrowind
-Bug #267: Activating creatures without dialogue can still activate the dialogue GUI
-Bug #354: True flickering lights
-Bug #386: The main menu's first entry is wrong (in french)
-Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations
-Bug #495: Activation Range
-Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned
-Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available
-Bug #500: Disposition for most NPCs is 0/100
-Bug #501: Getdisposition command wrongly returns base disposition
-Bug #506: Journal UI doesn't update anymore
-Bug #507: EnableRestMenu is not a valid command - change it to EnableRest
-Bug #508: Crash in Ald Daedroth Shrine
-Bug #517: Wrong price calculation when untrading an item
-Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin
-Bug #524: Beast races are able to wear shoes
-Bug #527: Background music fails to play
-Bug #533: The arch at Gnisis entrance is not displayed
-Bug #534: Terrain gets its correct shape only some time after the cell is loaded
-Bug #536: The same entry can be added multiple times to the journal
-Bug #539: Race selection is broken
-Bug #544: Terrain normal map corrupt when the map is rendered
-Feature #39: Video Playback
-Feature #151: ^-escape sequences in text output
-Feature #392: Add AI related script functions
-Feature #456: Determine required ini fallback values and adjust the ini importer accordingly
-Feature #460: Experimental DirArchives improvements
-Feature #540: Execute scripts of objects in containers/inventories in active cells
-Task #401: Review GMST fixing
-Task #453: Unify case smashing/folding
-Task #512: Rewrite utf8 component
+------
+
+    Bug #253: Dialogs don't work for Russian version of Morrowind
+    Bug #267: Activating creatures without dialogue can still activate the dialogue GUI
+    Bug #354: True flickering lights
+    Bug #386: The main menu's first entry is wrong (in french)
+    Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations
+    Bug #495: Activation Range
+    Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned
+    Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available
+    Bug #500: Disposition for most NPCs is 0/100
+    Bug #501: Getdisposition command wrongly returns base disposition
+    Bug #506: Journal UI doesn't update anymore
+    Bug #507: EnableRestMenu is not a valid command - change it to EnableRest
+    Bug #508: Crash in Ald Daedroth Shrine
+    Bug #517: Wrong price calculation when untrading an item
+    Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin
+    Bug #524: Beast races are able to wear shoes
+    Bug #527: Background music fails to play
+    Bug #533: The arch at Gnisis entrance is not displayed
+    Bug #534: Terrain gets its correct shape only some time after the cell is loaded
+    Bug #536: The same entry can be added multiple times to the journal
+    Bug #539: Race selection is broken
+    Bug #544: Terrain normal map corrupt when the map is rendered
+    Feature #39: Video Playback
+    Feature #151: ^-escape sequences in text output
+    Feature #392: Add AI related script functions
+    Feature #456: Determine required ini fallback values and adjust the ini importer accordingly
+    Feature #460: Experimental DirArchives improvements
+    Feature #540: Execute scripts of objects in containers/inventories in active cells
+    Task #401: Review GMST fixing
+    Task #453: Unify case smashing/folding
+    Task #512: Rewrite utf8 component
 
 0.20.0
-
-Bug #366: Changing the player's race during character creation does not change the look of the player character
-Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell
-Bug #437: Stop animations when paused
-Bug #438: Time displays as "0 a.m." when it should be "12 a.m."
-Bug #439: Text in "name" field of potion/spell creation window is persistent
-Bug #440: Starting date at a new game is off by one day
-Bug #442: Console window doesn't close properly sometimes
-Bug #448: Do not break container window formatting when item names are very long
-Bug #458: Topics sometimes not automatically added to known topic list
-Bug #476: Auto-Moving allows player movement after using DisablePlayerControls
-Bug #478: After sleeping in a bed the rest dialogue window opens automtically again
-Bug #492: On creating potions the ingredients are removed twice
-Feature #63: Mercantile skill
-Feature #82: Persuasion Dialogue
-Feature #219: Missing dialogue filters/functions
-Feature #369: Add a FailedAction
-Feature #377: Select head/hair on character creation
-Feature #391: Dummy AI package classes
-Feature #435: Global Map, 2nd Layer
-Feature #450: Persuasion
-Feature #457: Add more script instructions
-Feature #474: update the global variable pcrace when the player's race is changed
-Task #158: Move dynamically generated classes from Player class to World Class
-Task #159: ESMStore rework and cleanup
-Task #163: More Component Namespace Cleanup
-Task #402: Move player data from MWWorld::Player to the player's NPC record
-Task #446: Fix no namespace in BulletShapeLoader
+------
+
+    Bug #366: Changing the player's race during character creation does not change the look of the player character
+    Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell
+    Bug #437: Stop animations when paused
+    Bug #438: Time displays as "0 a.m." when it should be "12 a.m."
+    Bug #439: Text in "name" field of potion/spell creation window is persistent
+    Bug #440: Starting date at a new game is off by one day
+    Bug #442: Console window doesn't close properly sometimes
+    Bug #448: Do not break container window formatting when item names are very long
+    Bug #458: Topics sometimes not automatically added to known topic list
+    Bug #476: Auto-Moving allows player movement after using DisablePlayerControls
+    Bug #478: After sleeping in a bed the rest dialogue window opens automtically again
+    Bug #492: On creating potions the ingredients are removed twice
+    Feature #63: Mercantile skill
+    Feature #82: Persuasion Dialogue
+    Feature #219: Missing dialogue filters/functions
+    Feature #369: Add a FailedAction
+    Feature #377: Select head/hair on character creation
+    Feature #391: Dummy AI package classes
+    Feature #435: Global Map, 2nd Layer
+    Feature #450: Persuasion
+    Feature #457: Add more script instructions
+    Feature #474: update the global variable pcrace when the player's race is changed
+    Task #158: Move dynamically generated classes from Player class to World Class
+    Task #159: ESMStore rework and cleanup
+    Task #163: More Component Namespace Cleanup
+    Task #402: Move player data from MWWorld::Player to the player's NPC record
+    Task #446: Fix no namespace in BulletShapeLoader
 
 0.19.0
-
-Bug #374: Character shakes in 3rd person mode near the origin
-Bug #404: Gamma correct rendering
-Bug #407: Shoes of St. Rilm do not work
-Bug #408: Rugs has collision even if they are not supposed to
-Bug #412: Birthsign menu sorted incorrectly
-Bug #413: Resolutions presented multiple times in launcher
-Bug #414: launcher.cfg file stored in wrong directory
-Bug #415: Wrong esm order in openmw.cfg
-Bug #418: Sound listener position updates incorrectly
-Bug #423: wrong usage of "Version" entry in openmw.desktop
-Bug #426: Do not use hardcoded splash images
-Bug #431: Don't use markers for raycast
-Bug #432: Crash after picking up items from an NPC
-Feature #21/#95: Sleeping/resting
-Feature #61: Alchemy Skill
-Feature #68: Death
-Feature #69/#86: Spell Creation
-Feature #72/#84: Travel
-Feature #76: Global Map, 1st Layer
-Feature #120: Trainer Window
-Feature #152: Skill Increase from Skill Books
-Feature #160: Record Saving
-Task #400: Review GMST access
+------
+
+    Bug #374: Character shakes in 3rd person mode near the origin
+    Bug #404: Gamma correct rendering
+    Bug #407: Shoes of St. Rilm do not work
+    Bug #408: Rugs has collision even if they are not supposed to
+    Bug #412: Birthsign menu sorted incorrectly
+    Bug #413: Resolutions presented multiple times in launcher
+    Bug #414: launcher.cfg file stored in wrong directory
+    Bug #415: Wrong esm order in openmw.cfg
+    Bug #418: Sound listener position updates incorrectly
+    Bug #423: wrong usage of "Version" entry in openmw.desktop
+    Bug #426: Do not use hardcoded splash images
+    Bug #431: Don't use markers for raycast
+    Bug #432: Crash after picking up items from an NPC
+    Feature #21/#95: Sleeping/resting
+    Feature #61: Alchemy Skill
+    Feature #68: Death
+    Feature #69/#86: Spell Creation
+    Feature #72/#84: Travel
+    Feature #76: Global Map, 1st Layer
+    Feature #120: Trainer Window
+    Feature #152: Skill Increase from Skill Books
+    Feature #160: Record Saving
+    Task #400: Review GMST access
 
 0.18.0
-
-Bug #310: Button of the "preferences menu" are too small
-Bug #361: Hand-to-hand skill is always 100
-Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to
-Bug #372: playSound3D uses original coordinates instead of current coordinates.
-Bug #373: Static OGRE build faulty
-Bug #375: Alt-tab toggle view
-Bug #376: Screenshots are disable
-Bug #378: Exception when drinking self-made potions
-Bug #380: Cloth visibility problem
-Bug #384: Weird character on doors tooltip.
-Bug #398: Some objects do not collide in MW, but do so in OpenMW
-Feature #22: Implement level-up
-Feature #36: Hide Marker
-Feature #88: Hotkey Window
-Feature #91: Level-Up Dialogue
-Feature #118: Keyboard and Mouse-Button bindings
-Feature #119: Spell Buying Window
-Feature #133: Handle resources across multiple data directories
-Feature #134: Generate a suitable default-value for --data-local
-Feature #292: Object Movement/Creation Script Instructions
-Feature #340: AIPackage data structures
-Feature #356: Ingredients use
-Feature #358: Input system rewrite
-Feature #370: Target handling in actions
-Feature #379: Door markers on the local map
-Feature #389: AI framework
-Feature #395: Using keys to open doors / containers
-Feature #396: Loading screens
-Feature #397: Inventory avatar image and race selection head preview
-Task #339: Move sounds into Action
+------
+
+    Bug #310: Button of the "preferences menu" are too small
+    Bug #361: Hand-to-hand skill is always 100
+    Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to
+    Bug #372: playSound3D uses original coordinates instead of current coordinates.
+    Bug #373: Static OGRE build faulty
+    Bug #375: Alt-tab toggle view
+    Bug #376: Screenshots are disable
+    Bug #378: Exception when drinking self-made potions
+    Bug #380: Cloth visibility problem
+    Bug #384: Weird character on doors tooltip.
+    Bug #398: Some objects do not collide in MW, but do so in OpenMW
+    Feature #22: Implement level-up
+    Feature #36: Hide Marker
+    Feature #88: Hotkey Window
+    Feature #91: Level-Up Dialogue
+    Feature #118: Keyboard and Mouse-Button bindings
+    Feature #119: Spell Buying Window
+    Feature #133: Handle resources across multiple data directories
+    Feature #134: Generate a suitable default-value for --data-local
+    Feature #292: Object Movement/Creation Script Instructions
+    Feature #340: AIPackage data structures
+    Feature #356: Ingredients use
+    Feature #358: Input system rewrite
+    Feature #370: Target handling in actions
+    Feature #379: Door markers on the local map
+    Feature #389: AI framework
+    Feature #395: Using keys to open doors / containers
+    Feature #396: Loading screens
+    Feature #397: Inventory avatar image and race selection head preview
+    Task #339: Move sounds into Action
 
 0.17.0
-
-Bug #225: Valgrind reports about 40MB of leaked memory
-Bug #241: Some physics meshes still don't match
-Bug #248: Some textures are too dark
-Bug #300: Dependency on proprietary CG toolkit
-Bug #302: Some objects don't collide although they should
-Bug #308: Freeze in Balmora, Meldor: Armorer
-Bug #313: openmw without a ~/.config/openmw folder segfault.
-Bug #317: adding non-existing spell via console locks game
-Bug #318: Wrong character normals
-Bug #341: Building with Ogre Debug libraries does not use debug version of plugins
-Bug #347: Crash when running openmw with --start="XYZ"
-Bug #353: FindMyGUI.cmake breaks path on Windows
-Bug #359: WindowManager throws exception at destruction
-Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation
-Feature #33: Allow objects to cross cell-borders
-Feature #59: Dropping Items (replaced stopgap implementation with a proper one)
-Feature #93: Main Menu
-Feature #96/329/330/331/332/333: Player Control
-Feature #180: Object rotation and scaling.
-Feature #272: Incorrect NIF material sharing
-Feature #314: Potion usage
-Feature #324: Skill Gain
-Feature #342: Drain/fortify dynamic stats/attributes magic effects
-Feature #350: Allow console only script instructions
-Feature #352: Run scripts in console on startup
-Task #107: Refactor mw*-subsystems
-Task #325: Make CreatureStats into a class
-Task #345: Use Ogre's animation system
-Task #351: Rewrite Action class to support automatic sound playing
+------
+
+    Bug #225: Valgrind reports about 40MB of leaked memory
+    Bug #241: Some physics meshes still don't match
+    Bug #248: Some textures are too dark
+    Bug #300: Dependency on proprietary CG toolkit
+    Bug #302: Some objects don't collide although they should
+    Bug #308: Freeze in Balmora, Meldor: Armorer
+    Bug #313: openmw without a ~/.config/openmw folder segfault.
+    Bug #317: adding non-existing spell via console locks game
+    Bug #318: Wrong character normals
+    Bug #341: Building with Ogre Debug libraries does not use debug version of plugins
+    Bug #347: Crash when running openmw with --start="XYZ"
+    Bug #353: FindMyGUI.cmake breaks path on Windows
+    Bug #359: WindowManager throws exception at destruction
+    Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation
+    Feature #33: Allow objects to cross cell-borders
+    Feature #59: Dropping Items (replaced stopgap implementation with a proper one)
+    Feature #93: Main Menu
+    Feature #96/329/330/331/332/333: Player Control
+    Feature #180: Object rotation and scaling.
+    Feature #272: Incorrect NIF material sharing
+    Feature #314: Potion usage
+    Feature #324: Skill Gain
+    Feature #342: Drain/fortify dynamic stats/attributes magic effects
+    Feature #350: Allow console only script instructions
+    Feature #352: Run scripts in console on startup
+    Task #107: Refactor mw*-subsystems
+    Task #325: Make CreatureStats into a class
+    Task #345: Use Ogre's animation system
+    Task #351: Rewrite Action class to support automatic sound playing
 
 0.16.0
-
-Bug #250: OpenMW launcher erratic behaviour
-Bug #270: Crash because of underwater effect on OS X
-Bug #277: Auto-equipping in some cells not working
-Bug #294: Container GUI ignores disabled inventory menu
-Bug #297: Stats review dialog shows all skills and attribute values as 0
-Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses
-Bug #299: Crash in World::disable
-Bug #306: Non-existent ~/.config/openmw "crash" the launcher.
-Bug #307: False "Data Files" location make the launcher "crash"
-Feature #81: Spell Window
-Feature #85: Alchemy Window
-Feature #181: Support for x.y script syntax
-Feature #242: Weapon and Spell icons
-Feature #254: Ingame settings window
-Feature #293: Allow "stacking" game modes
-Feature #295: Class creation dialog tooltips
-Feature #296: Clicking on the HUD elements should show/hide the respective window
-Feature #301: Direction after using a Teleport Door
-Feature #303: Allow object selection in the console
-Feature #305: Allow the use of = as a synonym for ==
-Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts
-Task #176: Restructure enabling/disabling of MW-references
-Task #283: Integrate ogre.cfg file in settings file
-Task #290: Auto-Close MW-reference related GUI windows
+------
+
+    Bug #250: OpenMW launcher erratic behaviour
+    Bug #270: Crash because of underwater effect on OS X
+    Bug #277: Auto-equipping in some cells not working
+    Bug #294: Container GUI ignores disabled inventory menu
+    Bug #297: Stats review dialog shows all skills and attribute values as 0
+    Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses
+    Bug #299: Crash in World::disable
+    Bug #306: Non-existent ~/.config/openmw "crash" the launcher.
+    Bug #307: False "Data Files" location make the launcher "crash"
+    Feature #81: Spell Window
+    Feature #85: Alchemy Window
+    Feature #181: Support for x.y script syntax
+    Feature #242: Weapon and Spell icons
+    Feature #254: Ingame settings window
+    Feature #293: Allow "stacking" game modes
+    Feature #295: Class creation dialog tooltips
+    Feature #296: Clicking on the HUD elements should show/hide the respective window
+    Feature #301: Direction after using a Teleport Door
+    Feature #303: Allow object selection in the console
+    Feature #305: Allow the use of = as a synonym for ==
+    Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts
+    Task #176: Restructure enabling/disabling of MW-references
+    Task #283: Integrate ogre.cfg file in settings file
+    Task #290: Auto-Close MW-reference related GUI windows
 
 0.15.0
-
-Bug #5: Physics reimplementation (fixes various issues)
-Bug #258: Resizing arrow's background is not transparent
-Bug #268: Widening the stats window in X direction causes layout problems
-Bug #269: Topic pane in dialgoue window is too small for some longer topics
-Bug #271: Dialog choices are sorted incorrectly
-Bug #281: The single quote character is not rendered on dialog windows
-Bug #285: Terrain not handled properly in cells that are not predefined
-Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs
-Feature #15: Collision with Terrain
-Feature #17: Inventory-, Container- and Trade-Windows
-Feature #44: Floating Labels above Focussed Objects
-Feature #80: Tooltips
-Feature #83: Barter Dialogue
-Feature #90: Book and Scroll Windows
-Feature #156: Item Stacking in Containers
-Feature #213: Pulsating lights
-Feature #218: Feather & Burden
-Feature #256: Implement magic effect bookkeeping
-Feature #259: Add missing information to Stats window
-Feature #260: Correct case for dialogue topics
-Feature #280: GUI texture atlasing
-Feature #291: Ability to use GMST strings from GUI layout files
-Task #255: Make MWWorld::Environment into a singleton
+------
+
+    Bug #5: Physics reimplementation (fixes various issues)
+    Bug #258: Resizing arrow's background is not transparent
+    Bug #268: Widening the stats window in X direction causes layout problems
+    Bug #269: Topic pane in dialgoue window is too small for some longer topics
+    Bug #271: Dialog choices are sorted incorrectly
+    Bug #281: The single quote character is not rendered on dialog windows
+    Bug #285: Terrain not handled properly in cells that are not predefined
+    Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs
+    Feature #15: Collision with Terrain
+    Feature #17: Inventory-, Container- and Trade-Windows
+    Feature #44: Floating Labels above Focussed Objects
+    Feature #80: Tooltips
+    Feature #83: Barter Dialogue
+    Feature #90: Book and Scroll Windows
+    Feature #156: Item Stacking in Containers
+    Feature #213: Pulsating lights
+    Feature #218: Feather & Burden
+    Feature #256: Implement magic effect bookkeeping
+    Feature #259: Add missing information to Stats window
+    Feature #260: Correct case for dialogue topics
+    Feature #280: GUI texture atlasing
+    Feature #291: Ability to use GMST strings from GUI layout files
+    Task #255: Make MWWorld::Environment into a singleton
 
 0.14.0
-
-Bug #1: Meshes rendered with wrong orientation
-Bug #6/Task #220: Picking up small objects doesn't always work
-Bug #127: tcg doesn't work
-Bug #178: Compablity problems with Ogre 1.8.0 RC 1
-Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI
-Bug #227: Terrain crashes when moving away from predefined cells
-Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces
-Bug #235: TGA texture loading problem
-Bug #246: wireframe mode does not work in water
-Feature #8/#232: Water Rendering
-Feature #13: Terrain Rendering
-Feature #37: Render Path Grid
-Feature #66: Factions
-Feature #77: Local Map
-Feature #78: Compass/Mini-Map
-Feature #97: Render Clothing/Armour
-Feature #121: Window Pinning
-Feature #205: Auto equip
-Feature #217: Contiainer should track changes to its content
-Feature #221: NPC Dialogue Window Enhancements
-Feature #233: Game settings manager
-Feature #240: Spell List and selected spell (no GUI yet)
-Feature #243: Draw State
-Task #113: Morrowind.ini Importer
-Task #215: Refactor the sound code
-Task #216: Update MyGUI
+------
+
+    Bug #1: Meshes rendered with wrong orientation
+    Bug #6/Task #220: Picking up small objects doesn't always work
+    Bug #127: tcg doesn't work
+    Bug #178: Compablity problems with Ogre 1.8.0 RC 1
+    Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI
+    Bug #227: Terrain crashes when moving away from predefined cells
+    Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces
+    Bug #235: TGA texture loading problem
+    Bug #246: wireframe mode does not work in water
+    Feature #8/#232: Water Rendering
+    Feature #13: Terrain Rendering
+    Feature #37: Render Path Grid
+    Feature #66: Factions
+    Feature #77: Local Map
+    Feature #78: Compass/Mini-Map
+    Feature #97: Render Clothing/Armour
+    Feature #121: Window Pinning
+    Feature #205: Auto equip
+    Feature #217: Contiainer should track changes to its content
+    Feature #221: NPC Dialogue Window Enhancements
+    Feature #233: Game settings manager
+    Feature #240: Spell List and selected spell (no GUI yet)
+    Feature #243: Draw State
+    Task #113: Morrowind.ini Importer
+    Task #215: Refactor the sound code
+    Task #216: Update MyGUI
 
 0.13.0
-
-Bug #145: Fixed sound problems after cell change
-Bug #179: Pressing space in console triggers activation
-Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux
-Bug #189: ASCII 16 character added to console on it's activation on Mac OS X
-Bug #190: Case Folding fails with music files
-Bug #192: Keypresses write Text into Console no matter which gui element is active
-Bug #196: Collision shapes out of place
-Bug #202: ESMTool doesn't not work with localised ESM files anymore
-Bug #203: Torch lights only visible on short distance
-Bug #207: Ogre.log not written
-Bug #209: Sounds do not play
-Bug #210: Ogre crash at Dren plantation
-Bug #214: Unsupported file format version
-Bug #222: Launcher is writing openmw.cfg file to wrong location
-Feature #9: NPC Dialogue Window
-Feature #16/42: New sky/weather implementation
-Feature #40: Fading
-Feature #48: NPC Dialogue System
-Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet)
-Feature #161: Load REC_PGRD records
-Feature #195: Wireframe-mode
-Feature #198/199: Various sound effects
-Feature #206: Allow picking data path from launcher if non is set
-Task #108: Refactor window manager class
-Task #172: Sound Manager Cleanup
-Task #173: Create OpenEngine systems in the appropriate manager classes
-Task #184: Adjust MSVC and gcc warning levels
-Task #185: RefData rewrite
-Task #201: Workaround for transparency issues
-Task #208: silenced esm_reader.hpp warning
+------
+
+    Bug #145: Fixed sound problems after cell change
+    Bug #179: Pressing space in console triggers activation
+    Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux
+    Bug #189: ASCII 16 character added to console on it's activation on Mac OS X
+    Bug #190: Case Folding fails with music files
+    Bug #192: Keypresses write Text into Console no matter which gui element is active
+    Bug #196: Collision shapes out of place
+    Bug #202: ESMTool doesn't not work with localised ESM files anymore
+    Bug #203: Torch lights only visible on short distance
+    Bug #207: Ogre.log not written
+    Bug #209: Sounds do not play
+    Bug #210: Ogre crash at Dren plantation
+    Bug #214: Unsupported file format version
+    Bug #222: Launcher is writing openmw.cfg file to wrong location
+    Feature #9: NPC Dialogue Window
+    Feature #16/42: New sky/weather implementation
+    Feature #40: Fading
+    Feature #48: NPC Dialogue System
+    Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet)
+    Feature #161: Load REC_PGRD records
+    Feature #195: Wireframe-mode
+    Feature #198/199: Various sound effects
+    Feature #206: Allow picking data path from launcher if non is set
+    Task #108: Refactor window manager class
+    Task #172: Sound Manager Cleanup
+    Task #173: Create OpenEngine systems in the appropriate manager classes
+    Task #184: Adjust MSVC and gcc warning levels
+    Task #185: RefData rewrite
+    Task #201: Workaround for transparency issues
+    Task #208: silenced esm_reader.hpp warning
 
 0.12.0
-
-Bug #154: FPS Drop
-Bug #169: Local scripts continue running if associated object is deleted
-Bug #174: OpenMW fails to start if the config directory doesn't exist
-Bug #187: Missing lighting
-Bug #188: Lights without a mesh are not rendered
-Bug #191: Taking screenshot causes crash when running installed
-Feature #28: Sort out the cell load problem
-Feature #31: Allow the player to move away from pre-defined cells
-Feature #35: Use alternate storage location for modified object position
-Feature #45: NPC animations
-Feature #46: Creature Animation
-Feature #89: Basic Journal Window
-Feature #110: Automatically pick up the path of existing MW-installations
-Feature #183: More FPS display settings
-Task #19: Refactor engine class
-Task #109/Feature #162: Automate Packaging
-Task #112: Catch exceptions thrown in input handling functions
-Task #128/#168: Cleanup Configuration File Handling
-Task #131: NPC Activation doesn't work properly
-Task #144: MWRender cleanup
-Task #155: cmake cleanup
+------
+
+    Bug #154: FPS Drop
+    Bug #169: Local scripts continue running if associated object is deleted
+    Bug #174: OpenMW fails to start if the config directory doesn't exist
+    Bug #187: Missing lighting
+    Bug #188: Lights without a mesh are not rendered
+    Bug #191: Taking screenshot causes crash when running installed
+    Feature #28: Sort out the cell load problem
+    Feature #31: Allow the player to move away from pre-defined cells
+    Feature #35: Use alternate storage location for modified object position
+    Feature #45: NPC animations
+    Feature #46: Creature Animation
+    Feature #89: Basic Journal Window
+    Feature #110: Automatically pick up the path of existing MW-installations
+    Feature #183: More FPS display settings
+    Task #19: Refactor engine class
+    Task #109/Feature #162: Automate Packaging
+    Task #112: Catch exceptions thrown in input handling functions
+    Task #128/#168: Cleanup Configuration File Handling
+    Task #131: NPC Activation doesn't work properly
+    Task #144: MWRender cleanup
+    Task #155: cmake cleanup
 
 0.11.1
-
-Bug #2: Resources loading doesn't work outside of bsa files
-Bug #3: GUI does not render non-English characters
-Bug #7: openmw.cfg location doesn't match
-Bug #124: The TCL alias for ToggleCollision is missing.
-Bug #125: Some command line options can't be used from a .cfg file
-Bug #126: Toggle-type script instructions are less verbose compared with original MW
-Bug #130: NPC-Record Loading fails for some NPCs
-Bug #167: Launcher sets invalid parameters in ogre config
-Feature #10: Journal
-Feature #12: Rendering Optimisations
-Feature #23: Change Launcher GUI to a tabbed interface
-Feature #24: Integrate the OGRE settings window into the launcher
-Feature #25: Determine openmw.cfg location (Launcher)
-Feature #26: Launcher Profiles
-Feature #79: MessageBox
-Feature #116: Tab-Completion in Console
-Feature #132: --data-local and multiple --data
-Feature #143: Non-Rendering Performance-Optimisations
-Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs
-Feature #157: Version Handling
-Task #14: Replace tabs with 4 spaces
-Task #18: Move components from global namespace into their own namespace
-Task #123: refactor header files in components/esm
+------
+
+    Bug #2: Resources loading doesn't work outside of bsa files
+    Bug #3: GUI does not render non-English characters
+    Bug #7: openmw.cfg location doesn't match
+    Bug #124: The TCL alias for ToggleCollision is missing.
+    Bug #125: Some command line options can't be used from a .cfg file
+    Bug #126: Toggle-type script instructions are less verbose compared with original MW
+    Bug #130: NPC-Record Loading fails for some NPCs
+    Bug #167: Launcher sets invalid parameters in ogre config
+    Feature #10: Journal
+    Feature #12: Rendering Optimisations
+    Feature #23: Change Launcher GUI to a tabbed interface
+    Feature #24: Integrate the OGRE settings window into the launcher
+    Feature #25: Determine openmw.cfg location (Launcher)
+    Feature #26: Launcher Profiles
+    Feature #79: MessageBox
+    Feature #116: Tab-Completion in Console
+    Feature #132: --data-local and multiple --data
+    Feature #143: Non-Rendering Performance-Optimisations
+    Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs
+    Feature #157: Version Handling
+    Task #14: Replace tabs with 4 spaces
+    Task #18: Move components from global namespace into their own namespace
+    Task #123: refactor header files in components/esm
 
 0.10.0
+------
 
 * NPC dialogue window (not functional yet)
 * Collisions with objects
@@ -1384,6 +1409,7 @@ Task #123: refactor header files in components/esm
 * Mac OS X support is back!
 
 0.9.0
+-----
 
 * Exterior cells loading, unloading and management
 * Character Creation GUI
@@ -1393,6 +1419,7 @@ Task #123: refactor header files in components/esm
 * NPCs rendering
 
 0.8.0
+-----
 
 * GUI
 * Complete and working script engine
@@ -1402,12 +1429,14 @@ Task #123: refactor header files in components/esm
 * Tons of smaller stuff
 
 0.7.0
+-----
 
 * This release is a complete rewrite in C++.
 * All D code has been culled, and all modules have been rewritten.
 * The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase.
 
 0.6.0
+-----
 
 * Coded a GUI system using MyGUI
 * Skinned MyGUI to look like Morrowind (work in progress)
@@ -1417,6 +1446,7 @@ Task #123: refactor header files in components/esm
 * Fixed Windows sound problems (replaced old openal32.dll)
 
 0.5.0
+-----
 
 * Collision detection with Bullet
 * Experimental walk & fall character physics
@@ -1428,6 +1458,7 @@ Task #123: refactor header files in components/esm
 * Various minor changes and updates
 
 0.4.0
+-----
 
 * Switched from Audiere to OpenAL
 * * (BIG thanks to Chris Robinson)
@@ -1440,6 +1471,7 @@ Task #123: refactor header files in components/esm
 * Cosmetic changes to placate gdc Wall
 
 0.3.0
+-----
 
 * Built and tested on Windows XP
 * Partial support for FreeBSD (exceptions do not work)
@@ -1454,12 +1486,13 @@ Task #123: refactor header files in components/esm
 * confirmed to work against OIS 1.0.0 (Ubuntu repository package)
 
 0.2.0
+-----
 
 * Compiles with gdc
 * Switched to DSSS for building D code
 * Includes the program esmtool
 
 0.1.0
+-----
 
 first release
-
From bc51fe4ec2a3db5df1d78f51364bdbf7ece02e86 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 17:22:12 +0100 Subject: [PATCH 281/404] Use indent instead of
 in AUTHORS.md (looks
 nicer in plaintext mode)

---
 AUTHORS.md | 331 ++++++++++++++++++++++++++---------------------------
 1 file changed, 165 insertions(+), 166 deletions(-)

diff --git a/AUTHORS.md b/AUTHORS.md
index f6da26e77d..d2c5cdc044 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -9,184 +9,183 @@ If you feel your name is missing from this list, please notify a developer.
 
 Programmers
 -----------
-
-Marc Zinnschlag (Zini) - Lead Programmer/Project Manager
-
-Adam Hogan (aurix)
-Aleksandar Jovanov
-Alex Haddad (rainChu)
-Alex McKibben (WeirdSexy)
-Alexander Nadeau (wareya)
-Alexander Olofsson (Ace)
-Artem Kotsynyak (greye)
-Arthur Moore (EmperorArthur)
-athile
-Bret Curtis (psi29a)
-Britt Mathis (galdor557)
-cc9cii
-Chris Boyce (slothlife)
-Chris Robinson (KittyCat)
-Cory F. Cohen (cfcohen)
-Cris Mihalache (Mirceam)
-darkf
-Dmitry Shkurskiy (endorph)
-Douglas Diniz (Dgdiniz)
-Douglas Mencken (dougmencken)
-dreamer-dead
-dteviot
-Edmondo Tommasina (edmondo)
-Eduard Cot (trombonecot)
-Eli2
-Emanuel Guével (potatoesmaster)
-eroen
-Fil Krynicki (filkry)
-Gašper Sedej
-gugus/gus
-Hallfaer Tuilinn
-Internecine
-Jacob Essex (Yacoby)
-Jannik Heller (scrawl)
-Jason Hooks (jhooks)
-jeaye
-Jeffrey Haines (Jyby)
-Jengerer
-Joel Graff (graffy)
-John Blomberg (fstp)
-Jordan Ayers
-Jordan Milne
-Julien Voisin (jvoisin/ap0)
-Karl-Felix Glatzer (k1ll)
-Kevin Poitra (PuppyKevin)
-Lars Söderberg (Lazaroth)
-lazydev
-Leon Saunders (emoose)
-Lukasz Gromanowski (lgro)
-Manuel Edelmann (vorenon)
-Marc Bouvier (CramitDeFrog)
-Marcin Hulist (Gohan)
-Mark Siewert (mark76)
-Marco Melletti (mellotanica)
-Marco Schulze
-Mateusz Kołaczek (PL_kolek)
-megaton
-Michael Hogan (Xethik)
-Michael Mc Donnell
-Michael Papageorgiou (werdanith)
-Michał Bień (Glorf)
-Miroslav Puda (pakanek)
-MiroslavR
-Narmo
-Nathan Jeffords (blunted2night)
-Nikolay Kasyanov (corristo)
-nobrakal
-Nolan Poe (nopoe)
-Paul McElroy (Greendogo)
-Pieter van der Kloet (pvdk)
-Radu-Marius Popovici (rpopovici)
-riothamus
-Robert MacGregor (Ragora)
-Rohit Nirmal
-Roman Melnik (Kromgart)
-Roman Proskuryakov (humbug)
-sandstranger
-Sandy Carter (bwrsandman)
-Scott Howard
-Sebastian Wick (swick)
-Sergey Shambir
-sir_herrbatka
-Stefan Galowicz (bogglez)
-Stanislav Bobrov (Jiub)
-Sylvain Thesnieres (Garvek)
-terrorfisch
-Thomas Luppi (Digmaster)
-Tom Mason (wheybags)
-Torben Leif Carrington (TorbenC)
-viadanna
-Vincent Heuken
-vocollapse
-
+ + Marc Zinnschlag (Zini) - Lead Programmer/Project Manager + + Adam Hogan (aurix) + Aleksandar Jovanov + Alex Haddad (rainChu) + Alex McKibben (WeirdSexy) + Alexander Nadeau (wareya) + Alexander Olofsson (Ace) + Artem Kotsynyak (greye) + Arthur Moore (EmperorArthur) + athile + Bret Curtis (psi29a) + Britt Mathis (galdor557) + cc9cii + Chris Boyce (slothlife) + Chris Robinson (KittyCat) + Cory F. Cohen (cfcohen) + Cris Mihalache (Mirceam) + darkf + Dmitry Shkurskiy (endorph) + Douglas Diniz (Dgdiniz) + Douglas Mencken (dougmencken) + dreamer-dead + dteviot + Edmondo Tommasina (edmondo) + Eduard Cot (trombonecot) + Eli2 + Emanuel Guével (potatoesmaster) + eroen + Fil Krynicki (filkry) + Gašper Sedej + gugus/gus + Hallfaer Tuilinn + Internecine + Jacob Essex (Yacoby) + Jannik Heller (scrawl) + Jason Hooks (jhooks) + jeaye + Jeffrey Haines (Jyby) + Jengerer + Joel Graff (graffy) + John Blomberg (fstp) + Jordan Ayers + Jordan Milne + Julien Voisin (jvoisin/ap0) + Karl-Felix Glatzer (k1ll) + Kevin Poitra (PuppyKevin) + Lars Söderberg (Lazaroth) + lazydev + Leon Saunders (emoose) + Lukasz Gromanowski (lgro) + Manuel Edelmann (vorenon) + Marc Bouvier (CramitDeFrog) + Marcin Hulist (Gohan) + Mark Siewert (mark76) + Marco Melletti (mellotanica) + Marco Schulze + Mateusz Kołaczek (PL_kolek) + megaton + Michael Hogan (Xethik) + Michael Mc Donnell + Michael Papageorgiou (werdanith) + Michał Bień (Glorf) + Miroslav Puda (pakanek) + MiroslavR + Narmo + Nathan Jeffords (blunted2night) + Nikolay Kasyanov (corristo) + nobrakal + Nolan Poe (nopoe) + Paul McElroy (Greendogo) + Pieter van der Kloet (pvdk) + Radu-Marius Popovici (rpopovici) + riothamus + Robert MacGregor (Ragora) + Rohit Nirmal + Roman Melnik (Kromgart) + Roman Proskuryakov (humbug) + sandstranger + Sandy Carter (bwrsandman) + Scott Howard + Sebastian Wick (swick) + Sergey Shambir + sir_herrbatka + Stefan Galowicz (bogglez) + Stanislav Bobrov (Jiub) + Sylvain Thesnieres (Garvek) + terrorfisch + Thomas Luppi (Digmaster) + Tom Mason (wheybags) + Torben Leif Carrington (TorbenC) + viadanna + Vincent Heuken + vocollapse + Packagers --------- -
-Alexander Olofsson (Ace) - Windows
-Bret Curtis (psi29a) - Ubuntu Linux
-Edmondo Tommasina (edmondo) - Gentoo Linux
-Julian Ospald (hasufell) - Gentoo Linux
-Karl-Felix Glatzer (k1ll) - Linux Binaries
-Kenny Armstrong (artorius) - Fedora Linux
-Nikolay Kasyanov (corristo) - Mac OS X
-Sandy Carter (bwrsandman) - Arch Linux
-
+ + Alexander Olofsson (Ace) - Windows + Bret Curtis (psi29a) - Ubuntu Linux + Edmondo Tommasina (edmondo) - Gentoo Linux + Julian Ospald (hasufell) - Gentoo Linux + Karl-Felix Glatzer (k1ll) - Linux Binaries + Kenny Armstrong (artorius) - Fedora Linux + Nikolay Kasyanov (corristo) - Mac OS X + Sandy Carter (bwrsandman) - Arch Linux + Public Relations and Translations --------------------------------- -
-Alex McKibben (WeirdSexy) - Podcaster
-Artem Kotsynyak (greye) - Russian News Writer
-Jim Clauwaert (Zedd) - Public Outreach
-Julien Voisin (jvoisin/ap0) - French News Writer
-Tom Koenderink (Okulo) - English News Writer
-Lukasz Gromanowski (lgro) - English News Writer
-Mickey Lyle (raevol) - Release Manager
-Pithorn - Chinese News Writer
-sir_herrbatka - Polish News Writer
-Dawid Lakomy (Vedyimyn) - Polish News Writer
-
+ + Alex McKibben (WeirdSexy) - Podcaster + Artem Kotsynyak (greye) - Russian News Writer + Jim Clauwaert (Zedd) - Public Outreach + Julien Voisin (jvoisin/ap0) - French News Writer + Tom Koenderink (Okulo) - English News Writer + Lukasz Gromanowski (lgro) - English News Writer + Mickey Lyle (raevol) - Release Manager + Pithorn - Chinese News Writer + sir_herrbatka - Polish News Writer + Dawid Lakomy (Vedyimyn) - Polish News Writer + Website ------- -
-Lukasz Gromanowski (Lgro) - Website Administrator
-Ryan Sardonic (Wry) - Wiki Editor
-sir_herrbatka - Forum Administrator
-
+ + Lukasz Gromanowski (Lgro) - Website Administrator + Ryan Sardonic (Wry) - Wiki Editor + sir_herrbatka - Forum Administrator + Formula Research ---------------- -
-Hrnchamd
-Epsilon
-fragonard
-Greendogo
-HiPhish
-modred11
-Myckel
-natirips
-Sadler
-
+ + Hrnchamd + Epsilon + fragonard + Greendogo + HiPhish + modred11 + Myckel + natirips + Sadler + Artwork ------- -
-Necrod - OpenMW Logo
-Mickey Lyle (raevol) - Wordpress Theme
-Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel - OpenMW Editor Icons
-
+ + Necrod - OpenMW Logo + Mickey Lyle (raevol) - Wordpress Theme + Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel - OpenMW Editor Icons + Inactive Contributors --------------------- -
-Ardekantur
-Armin Preiml
-Berulacks
-Carl Maxwell
-Diggory Hardy
-Dmitry Marakasov (AMDmi3)
-ElderTroll
-guidoj
-Jan-Peter Nilsson (peppe)
-Jan Borsodi
-Josua Grawitter
-juanmnzsk8
-Kingpix
-Lordrea
-Michal Sciubidlo
-Nicolay Korslund
-Nekochan
-pchan3
-penguinroad
-psi29a
-sergoz
-spyboot
-Star-Demon
-Thoronador
-Yuri Krupenin
-
+ + Ardekantur + Armin Preiml + Berulacks + Carl Maxwell + Diggory Hardy + Dmitry Marakasov (AMDmi3) + ElderTroll + guidoj + Jan-Peter Nilsson (peppe) + Jan Borsodi + Josua Grawitter + juanmnzsk8 + Kingpix + Lordrea + Michal Sciubidlo + Nicolay Korslund + Nekochan + pchan3 + penguinroad + psi29a + sergoz + spyboot + Star-Demon + Thoronador + Yuri Krupenin Additional Credits ------------------ From 38c6859281ede92eb33fb5254f68b4b2c0386788 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 17:27:38 +0100 Subject: [PATCH 282/404] Add some Getting Started links from the wiki to Readme --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ff5185b5df..8bc3c02800 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,16 @@ Morrowind by Bethesda Softworks. You need to own and install the original game f Font Licenses: * DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information) -Installation -============ - -Head to [Downloads](http://openmw.org/downloads/) to find a packaged release for your platform of choice. - -Build from source -================= - -https://wiki.openmw.org/index.php?title=Development_Environment_Setup +Getting Started +=============== + +* [Official forums](https://forum.openmw.org/) +* [Installation instructions](https://wiki.openmw.org/index.php?title=Installation_Instructions) +* [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup) +* [Testing the game](https://wiki.openmw.org/index.php?title=Testing) +* [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted) +* [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](before submitting your first bug!) +* [Known issues] (http://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker) The data path ============= From d4740b7533fe06bad16db365cd1e63be51e7ca3e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 17:36:36 +0100 Subject: [PATCH 283/404] Formatting, link fix --- README.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 8bc3c02800..168684e8d7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -OpenMW: A reimplementation of The Elder Scrolls III: Morrowind -============================================================== +OpenMW +====== [![Build Status](https://travis-ci.org/OpenMW/openmw.svg?branch=coverity_scan)](https://travis-ci.org/OpenMW/openmw) @@ -17,23 +17,23 @@ Font Licenses: * DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information) Getting Started -=============== +--------------- * [Official forums](https://forum.openmw.org/) * [Installation instructions](https://wiki.openmw.org/index.php?title=Installation_Instructions) * [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup) * [Testing the game](https://wiki.openmw.org/index.php?title=Testing) * [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted) -* [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](before submitting your first bug!) +* [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) before submitting your first bug! * [Known issues] (http://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker) The data path -============= +------------- The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install). Command line options -==================== +--------------------
 Syntax: openmw 
@@ -91,8 +91,3 @@ Allowed options:
   --no-grab                             Don't grab mouse cursor
   --activate-dist arg (=-1)             activation distance override
 
- -Changelog -========= - -See CHANGELOG.md From fcbf7d7deb602456f7140ebe68be428903d9371f Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 11 Jan 2015 08:33:33 +1300 Subject: [PATCH 284/404] Fixed string formatting minor error found by Mingun. --- apps/launcher/datafilespage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8429aaadc5..2456654213 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -302,7 +302,7 @@ bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text) msgBox.setWindowTitle(tr("Delete Content List")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Cancel); - msgBox.setText(tr("Are you sure you want to delete %0?").arg(text)); + msgBox.setText(tr("Are you sure you want to delete %1?").arg(text)); QAbstractButton *deleteButton = msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); From 91571f51be4f6ecc339ffb2354e44cb35b55b5c3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 15:27:03 +0100 Subject: [PATCH 285/404] Ogre crash workaround (Fixes #1745) --- apps/openmw/mwrender/animation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ea40721cb0..bf161442e7 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -169,6 +169,9 @@ struct AddGlow instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true))); instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z))); + // Workaround for crash in Ogre (https://bitbucket.org/sinbad/ogre/pull-request/447/fix-shadows-crash-for-textureunitstates/diff) + // Remove when the fix is merged + instance->getMaterial()->setShadowCasterMaterial("openmw_shadowcaster_noalpha"); } }; From 579f5d232f5ea87716f3bfb44272052325a7ba75 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 23:21:39 +0100 Subject: [PATCH 286/404] Move interactive messageBox to separate function --- apps/openmw/mwbase/windowmanager.hpp | 4 ++- apps/openmw/mwdialogue/journalimp.cpp | 4 +-- apps/openmw/mwgui/windowmanagerimp.cpp | 35 +++++++++++++------- apps/openmw/mwgui/windowmanagerimp.hpp | 5 ++- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +- apps/openmw/mwmechanics/npcstats.cpp | 6 ++-- apps/openmw/mwscript/containerextensions.cpp | 6 ++-- apps/openmw/mwscript/interpretercontext.cpp | 5 ++- apps/openmw/mwstate/statemanagerimp.cpp | 6 ++-- apps/openmw/mwworld/worldimp.cpp | 2 +- 10 files changed, 44 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index fb35157e85..3e957ef148 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -240,9 +240,11 @@ namespace MWBase /** No guarentee of actually closing the window **/ virtual void exitCurrentGuiMode() = 0; - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; + virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; virtual void staticMessageBox(const std::string& message) = 0; virtual void removeStaticMessageBox() = 0; + virtual void interactiveMessageBox (const std::string& message, + const std::vector& buttons = std::vector(), bool block=false) = 0; /// returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual int readPressedButton() = 0; diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 9497347e3a..e8d5375e1f 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -92,9 +92,7 @@ namespace MWDialogue quest.addEntry (entry); // we are doing slicing on purpose here - std::vector empty; - std::string notification = "#{sJournalEntry}"; - MWBase::Environment::get().getWindowManager()->messageBox (notification, empty); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sJournalEntry}"); } void Journal::setJournalIndex (const std::string& id, int index) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5d737ec413..7d9e10d8fd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -805,19 +805,30 @@ namespace MWGui } } - void WindowManager::messageBox (const std::string& message, const std::vector& buttons, enum MWGui::ShowInDialogueMode showInDialogueMode) - { - if (buttons.empty()) { - /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ - if (getMode() == GM_Dialogue && showInDialogueMode != MWGui::ShowInDialogueMode_Never) { - mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); - } else if (showInDialogueMode != MWGui::ShowInDialogueMode_Only) { - mMessageBoxManager->createMessageBox(message); + void WindowManager::interactiveMessageBox(const std::string &message, const std::vector &buttons, bool block) + { + mMessageBoxManager->createInteractiveMessageBox(message, buttons); + MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); + updateVisible(); + + if (block) + { + while (mMessageBoxManager->readPressedButton() == -1 + && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) + { + MWBase::Environment::get().getInputManager()->update(0, true, false); + + mRendering->getWindow()->update(); } - } else { - mMessageBoxManager->createInteractiveMessageBox(message, buttons); - MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); - updateVisible(); + } + } + + void WindowManager::messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode) + { + if (getMode() == GM_Dialogue && showInDialogueMode != MWGui::ShowInDialogueMode_Never) { + mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); + } else if (showInDialogueMode != MWGui::ShowInDialogueMode_Only) { + mMessageBoxManager->createMessageBox(message); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 015f200d58..44d5819c24 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -238,9 +238,12 @@ namespace MWGui ///Gracefully attempts to exit the topmost GUI mode virtual void exitCurrentGuiMode(); - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible); + virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible); virtual void staticMessageBox(const std::string& message); virtual void removeStaticMessageBox(); + virtual void interactiveMessageBox (const std::string& message, + const std::vector& buttons = std::vector(), bool block=false); + virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual void onFrame (float frameDuration); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ece189c752..846cce6a2b 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -743,8 +743,7 @@ namespace MWInput { mEngine.screenshot(); - std::vector empty; - MWBase::Environment::get().getWindowManager()->messageBox ("Screenshot saved", empty); + MWBase::Environment::get().getWindowManager()->messageBox ("Screenshot saved"); } void InputManager::toggleInventory() diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ec7b232a20..ba01caf95e 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -257,18 +257,16 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas /// \todo check if character is the player, if levelling is ever implemented for NPCs MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1); - std::vector noButtons; - std::stringstream message; message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); - MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), noButtons, MWGui::ShowInDialogueMode_Never); + MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never); if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) { // levelup is possible now - MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", noButtons, MWGui::ShowInDialogueMode_Never); + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never); } getSkill (skillIndex).setBase (base); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 96e30e3966..58b1b375b4 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -71,8 +71,7 @@ namespace MWScript msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage61}"); msgBox = boost::str(boost::format(msgBox) % count % itemName); } - std::vector noButtons; - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, MWGui::ShowInDialogueMode_Only); + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, MWGui::ShowInDialogueMode_Only); } } }; @@ -143,8 +142,7 @@ namespace MWScript msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage62}"); msgBox = boost::str (boost::format(msgBox) % itemName); } - std::vector noButtons; - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, MWGui::ShowInDialogueMode_Only); + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, MWGui::ShowInDialogueMode_Only); } } }; diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 5d52033a88..5cc4d69b65 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -200,7 +200,10 @@ namespace MWScript void InterpreterContext::messageBox (const std::string& message, const std::vector& buttons) { - MWBase::Environment::get().getWindowManager()->messageBox (message, buttons); + if (buttons.empty()) + MWBase::Environment::get().getWindowManager()->messageBox (message); + else + MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); } void InterpreterContext::report (const std::string& message) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 94b1ab4dc6..8ae7fb3554 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -118,7 +118,7 @@ void MWState::StateManager::askLoadRecent() std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); size_t pos = message.find(tag); message.replace(pos, tag.length(), lastSave.mProfile.mDescription); - MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); mAskLoadRecent = true; } } @@ -259,7 +259,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot std::vector buttons; buttons.push_back("#{sOk}"); - MWBase::Environment::get().getWindowManager()->messageBox(error.str(), buttons); + MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); // If no file was written, clean up the slot if (slot && !boost::filesystem::exists(slot->mPath)) @@ -459,7 +459,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str std::vector buttons; buttons.push_back("#{sOk}"); - MWBase::Environment::get().getWindowManager()->messageBox(error.str(), buttons); + MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f9642226bb..810b7f80d4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3047,7 +3047,7 @@ namespace MWWorld std::vector buttons; buttons.push_back("#{sOk}"); - MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); } } From f9227beedd787e2386ce85310b11c663b07c6770 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 10 Jan 2015 23:58:55 +0100 Subject: [PATCH 287/404] Add warning when loading a savegame that depends on non-existing content files (Fixes #2261) --- apps/openmw/mwgui/messagebox.cpp | 5 +-- apps/openmw/mwgui/messagebox.hpp | 3 +- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +-- apps/openmw/mwstate/statemanagerimp.cpp | 33 +++++++++++++++++++ apps/openmw/mwstate/statemanagerimp.hpp | 2 ++ .../openmw_interactive_messagebox.layout | 2 +- files/mygui/openmw_layers.xml | 1 + 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index c45136eb38..9cb778ea2d 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -146,10 +146,11 @@ namespace MWGui return false; } - int MessageBoxManager::readPressedButton () + int MessageBoxManager::readPressedButton (bool reset) { int pressed = mLastButtonPressed; - mLastButtonPressed = -1; + if (reset) + mLastButtonPressed = -1; return pressed; } diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 59804e097c..48a92c844b 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -33,7 +33,8 @@ namespace MWGui bool removeMessageBox (MessageBox *msgbox); - int readPressedButton (); + /// @param reset Reset the pressed button to -1 after reading it. + int readPressedButton (bool reset=true); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7d9e10d8fd..077ca4fe33 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -607,7 +607,7 @@ namespace MWGui // GM_Loading uses a texture of the last rendered frame so everything previously visible will be rendered. mHud->setVisible(false); mToolTips->setVisible(false); - setCursorVisible(false); + setCursorVisible(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()); break; default: // Unsupported mode, switch back to game @@ -813,9 +813,10 @@ namespace MWGui if (block) { - while (mMessageBoxManager->readPressedButton() == -1 + while (mMessageBoxManager->readPressedButton(false) == -1 && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { + mMessageBoxManager->onFrame(0.f); MWBase::Environment::get().getInputManager()->update(0, true, false); mRendering->getWindow()->update(); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8ae7fb3554..f756ee42ae 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -353,6 +353,12 @@ void MWState::StateManager::loadGame (const Character *character, const std::str { ESM::SavedGame profile; profile.load(reader); + if (!verifyProfile(profile)) + { + cleanup (true); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + return; + } mTimePlayed = profile.mTimePlayed; } break; @@ -517,3 +523,30 @@ void MWState::StateManager::update (float duration) } } } + +bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const +{ + const std::vector& selectedContentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); + bool notFound = false; + for (std::vector::const_iterator it = profile.mContentFiles.begin(); + it != profile.mContentFiles.end(); ++it) + { + if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it) + == selectedContentFiles.end()) + { + notFound = true; + break; + } + } + if (notFound) + { + std::vector buttons; + buttons.push_back("#{sYes}"); + buttons.push_back("#{sNo}"); + MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{sMissingMastersMsg}", buttons, true); + int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); + if (selectedButton == 1 || selectedButton == -1) + return false; + } + return true; +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 956a2d73c4..37f38f8df7 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -23,6 +23,8 @@ namespace MWState void cleanup (bool force = false); + bool verifyProfile (const ESM::SavedGame& profile) const; + std::map buildContentFileIndexMap (const ESM::ESMReader& reader) const; public: diff --git a/files/mygui/openmw_interactive_messagebox.layout b/files/mygui/openmw_interactive_messagebox.layout index a1dbc5aa83..a72bebf3a9 100644 --- a/files/mygui/openmw_interactive_messagebox.layout +++ b/files/mygui/openmw_interactive_messagebox.layout @@ -1,7 +1,7 @@ - + diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 38a98d133f..50f83aaa2c 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -11,6 +11,7 @@ + From 7aa0f887c0d992c96fa260e06b40234a50ca76b8 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 11 Jan 2015 12:20:22 +1300 Subject: [PATCH 288/404] Minor changes to ESM::RefNum 1. Changed mIndex to unsigned, to solve potential implementation defined behavior with right shift. 2. Refactoring to minimize use of magic number -1 to indicate "no Content File". --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 2 +- apps/openmw/mwworld/cellref.cpp | 8 ++++++-- apps/openmw/mwworld/cellref.hpp | 3 +++ apps/openmw/mwworld/cellreflist.hpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 10 +++++----- apps/openmw/mwworld/manualref.hpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 4 ++-- components/esm/cellref.cpp | 3 +-- components/esm/cellref.hpp | 8 ++++++-- 11 files changed, 28 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index e21676f108..9eb5b1b690 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -872,7 +872,7 @@ namespace MWClass { // Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell. // This also means we cannot respawn dynamically placed references with no content file connection. - if (ptr.getCellRef().getRefNum().mContentFile != -1) + if (ptr.getCellRef().hasContentFile()) { if (ptr.getRefData().getCount() == 0) ptr.getRefData().setCount(1); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 22263d820d..596b56acd7 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1331,7 +1331,7 @@ namespace MWClass { // Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell. // This also means we cannot respawn dynamically placed references with no content file connection. - if (ptr.getCellRef().getRefNum().mContentFile != -1) + if (ptr.getCellRef().hasContentFile()) { if (ptr.getRefData().getCount() == 0) ptr.getRefData().setCount(1); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index f20c9967df..d4431016a7 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1035,7 +1035,7 @@ namespace MWScript msg << "Content file: "; - if (ptr.getCellRef().getRefNum().mContentFile == -1) + if (!ptr.getCellRef().hasContentFile()) msg << "[None]" << std::endl; else { diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 3ea3ed8bf7..aa6627de53 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -10,10 +10,14 @@ namespace MWWorld return mCellRef.mRefNum; } + bool CellRef::hasContentFile() const + { + return getRefNum().hasContentFile(); + } + void CellRef::unsetRefNum() { - mCellRef.mRefNum.mContentFile = -1; - mCellRef.mRefNum.mIndex = 0; + getRefNum().unset(); } std::string CellRef::getRefId() const diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index d7c0ce2219..a7ffbe08b5 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -28,6 +28,9 @@ namespace MWWorld // Set RefNum to its default state. void unsetRefNum(); + /// Does the RefNum have a content file? + bool hasContentFile() const; + // Id of object being referenced std::string getRefId() const; diff --git a/apps/openmw/mwworld/cellreflist.hpp b/apps/openmw/mwworld/cellreflist.hpp index 037de8645e..2c5e01aaa3 100644 --- a/apps/openmw/mwworld/cellreflist.hpp +++ b/apps/openmw/mwworld/cellreflist.hpp @@ -28,7 +28,7 @@ namespace MWWorld { for (typename List::iterator iter (mList.begin()); iter!=mList.end(); ++iter) if (!iter->mData.isDeletedByContentFile() - && (iter->mRef.getRefNum().mContentFile != -1 || iter->mData.getCount() > 0) + && (iter->mRef.hasContentFile() || iter->mData.getCount() > 0) && iter->mRef.getRefId() == name) return &*iter; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index f1a8451ea3..1c0e7e3859 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -72,12 +72,12 @@ namespace iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { - if (!iter->mData.hasChanged() && !iter->mRef.hasChanged() && iter->mRef.getRefNum().mContentFile != -1) + if (!iter->mData.hasChanged() && !iter->mRef.hasChanged() && iter->mRef.hasContentFile()) { // Reference that came from a content file and has not been changed -> ignore continue; } - if (iter->mData.getCount()==0 && iter->mRef.getRefNum().mContentFile==-1) + if (iter->mData.getCount()==0 && !iter->mRef.hasContentFile()) { // Deleted reference that did not come from a content file -> ignore continue; @@ -102,7 +102,7 @@ namespace state.load (reader); // If the reference came from a content file, make sure this content file is loaded - if (state.mRef.mRefNum.mContentFile != -1) + if (state.mRef.mRefNum.hasContentFile()) { std::map::const_iterator iter = contentFileMap.find (state.mRef.mRefNum.mContentFile); @@ -121,7 +121,7 @@ namespace if (!record) return; - if (state.mRef.mRefNum.mContentFile != -1) + if (state.mRef.mRefNum.hasContentFile()) { for (typename MWWorld::CellRefList::List::iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) @@ -487,7 +487,7 @@ namespace MWWorld mCell->restore (esm[index], i); ESM::CellRef ref; - ref.mRefNum.mContentFile = -1; + ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; // Get each reference in turn bool deleted = false; diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 0becd75242..4eb93543ba 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -24,8 +24,7 @@ namespace MWWorld const T* base = list.find(name); ESM::CellRef cellRef; - cellRef.mRefNum.mIndex = 0; - cellRef.mRefNum.mContentFile = -1; + cellRef.mRefNum.unset(); cellRef.mRefID = name; cellRef.mScale = 1; cellRef.mFactionRank = 0; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f9642226bb..7dfd414085 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1089,7 +1089,7 @@ namespace MWWorld void World::undeleteObject(const Ptr& ptr) { - if (ptr.getCellRef().getRefNum().mContentFile == -1) + if (!ptr.getCellRef().hasContentFile()) return; if (ptr.getRefData().isDeleted()) { @@ -3206,7 +3206,7 @@ namespace MWWorld { // Can't reset actors that were moved to a different cell, because we don't know what cell they came from. // This could be fixed once we properly track actor cell changes, but may not be desirable behaviour anyhow. - if (ptr.getClass().isActor() && ptr.getCellRef().getRefNum().mContentFile != -1) + if (ptr.getClass().isActor() && ptr.getCellRef().hasContentFile()) { const ESM::Position& origPos = ptr.getCellRef().getPosition(); MWBase::Environment::get().getWorld()->moveObject(ptr, origPos.pos[0], origPos.pos[1], origPos.pos[2]); diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 29d26d013f..f93fe1535c 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -129,8 +129,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons void ESM::CellRef::blank() { - mRefNum.mIndex = 0; - mRefNum.mContentFile = -1; + mRefNum.unset(); mRefID.clear(); mScale = 1; mOwner.clear(); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 9c57061b00..f3986ccf39 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -13,8 +13,12 @@ namespace ESM struct RefNum { - int mIndex; - int mContentFile; // -1 no content file + unsigned int mIndex; + int mContentFile; + + enum { RefNum_NoContentFile = -1 }; + inline bool hasContentFile() const { return mContentFile != RefNum_NoContentFile; } + inline void unset() { mIndex = 0; mContentFile = RefNum_NoContentFile; } }; /* Cell reference. This represents ONE object (of many) inside the From 458b82c308ec62156e0c250f75dce75f3760901a Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 11 Jan 2015 14:25:46 +1300 Subject: [PATCH 289/404] Centralized "fish can't attack non-swimmer" logic. --- apps/openmw/mwmechanics/actors.cpp | 7 ++++--- apps/openmw/mwmechanics/aicombat.cpp | 9 +++------ apps/openmw/mwmechanics/combat.cpp | 20 ++++++++++++++++++++ apps/openmw/mwmechanics/combat.hpp | 4 ++++ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a25ddd42e..a902771603 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -37,6 +37,7 @@ #include "actor.hpp" #include "summoning.hpp" +#include "combat.hpp" namespace { @@ -309,10 +310,10 @@ namespace MWMechanics // pure water creatures won't try to fight with the target on the ground // except that creature is already hostile if ((againstPlayer || !creatureStats.getAiSequence().isInCombat()) - && ((actor1.getClass().isPureWaterCreature(actor1) - && !MWBase::Environment::get().getWorld()->isWading(actor2)) - || (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target + && !MWMechanics::isEnvironmentCompatible(actor1, actor2)) // creature can't swim to target + { return; + } bool aggressive; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ce5963a917..f7e285ff5b 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -23,6 +23,7 @@ #include "character.hpp" // fixme: for getActiveWeapon #include "aicombataction.hpp" +#include "combat.hpp" namespace { @@ -206,12 +207,8 @@ namespace MWMechanics const MWWorld::Class& actorClass = actor.getClass(); MWBase::World* world = MWBase::Environment::get().getWorld(); - if (!actorClass.isNpc() && - // 1. pure water creature and Player moved out of water - ((target == world->getPlayerPtr() && - actorClass.isPureWaterCreature(actor) && !world->isWading(target)) - // 2. creature can't swim to target - || (!actorClass.canSwim(actor) && world->isSwimming(target)))) + // can't fight if attacker can't go where target is. E.g. A fish can't attack person on land. + if (!actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target)) { actorClass.getCreatureStats(actor).setAttackingOrSpell(false); return true; diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 459ff8aefb..ad4d3aabff 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -367,4 +367,24 @@ namespace MWMechanics else sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f); } + + bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim) + { + const MWWorld::Class& attackerClass = attacker.getClass(); + MWBase::World* world = MWBase::Environment::get().getWorld(); + + // If attacker is fish, victim must be in water + if (attackerClass.isPureWaterCreature(attacker)) + { + return world->isWading(victim); + } + + // If attacker can't swim, victim must not be in water + if (!attackerClass.canSwim(attacker)) + { + return !world->isSwimming(victim); + } + + return true; + } } diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index c6fc9ff927..41dd2d1a4b 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -34,6 +34,10 @@ void adjustWeaponDamage (float& damage, const MWWorld::Ptr& weapon); void getHandToHandDamage (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, float& damage, bool& healthdmg); +/// Can attacker operate in victim's environment? +/// e.g. If attacker is a fish, is victim in water? Or, if attacker can't swim, is victim on land? +bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); + } #endif From 1795fdaf034928b9e995eb64b98c6e0be8be3dd8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 13:50:50 +0100 Subject: [PATCH 290/404] Command line options readme fix --- README.md | 104 ++++++++++++++++++++++++++---------------------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 168684e8d7..bb59b7e02c 100644 --- a/README.md +++ b/README.md @@ -35,59 +35,57 @@ The data path tells OpenMW where to find your Morrowind files. If you run the la Command line options -------------------- -
-Syntax: openmw 
-Allowed options:
-  --help                                print help message
-  --version                             print version information and quit
-  --data arg (=data)                    set data directories (later directories
-                                        have higher priority)
-  --data-local arg                      set local data directory (highest
-                                        priority)
-  --fallback-archive arg (=fallback-archive)
-                                        set fallback BSA archives (later
-                                        archives have higher priority)
-  --resources arg (=resources)          set resources directory
-  --start arg (=Beshara)                set initial cell
-  --content arg                         content file(s): esm/esp, or
-                                        omwgame/omwaddon
-  --anim-verbose [=arg(=1)] (=0)        output animation indices files
-  --no-sound [=arg(=1)] (=0)            disable all sounds
-  --script-verbose [=arg(=1)] (=0)      verbose script output
-  --script-all [=arg(=1)] (=0)          compile all scripts (excluding dialogue
-                                        scripts) at startup
-  --script-console [=arg(=1)] (=0)      enable console-only script
-                                        functionality
-  --script-run arg                      select a file containing a list of
-                                        console commands that is executed on
-                                        startup
-   --script-warn [=arg(=1)] (=1)        handling of warnings when compiling
-                                        scripts
-                                        0 - ignore warning
-                                        1 - show warning but consider script as
-                                        correctly compiled anyway
-                                        2 - treat warnings as errors
-  --skip-menu [=arg(=1)] (=0)           skip main menu on game startup
-  --new-game [=arg(=1)] (=0)            run new game sequence (ignored if
-                                        skip-menu=0)
-  --fs-strict [=arg(=1)] (=0)           strict file system handling (no case
-                                        folding)
-  --encoding arg (=win1252)             Character encoding used in OpenMW game
-                                        messages:
+    Syntax: openmw 
+    Allowed options:
+      --help                                print help message
+      --version                             print version information and quit
+      --data arg (=data)                    set data directories (later directories
+                                            have higher priority)
+      --data-local arg                      set local data directory (highest
+                                            priority)
+      --fallback-archive arg (=fallback-archive)
+                                            set fallback BSA archives (later
+                                            archives have higher priority)
+      --resources arg (=resources)          set resources directory
+      --start arg (=Beshara)                set initial cell
+      --content arg                         content file(s): esm/esp, or
+                                            omwgame/omwaddon
+      --anim-verbose [=arg(=1)] (=0)        output animation indices files
+      --no-sound [=arg(=1)] (=0)            disable all sounds
+      --script-verbose [=arg(=1)] (=0)      verbose script output
+      --script-all [=arg(=1)] (=0)          compile all scripts (excluding dialogue
+                                            scripts) at startup
+      --script-console [=arg(=1)] (=0)      enable console-only script
+                                            functionality
+      --script-run arg                      select a file containing a list of
+                                            console commands that is executed on
+                                            startup
+       --script-warn [=arg(=1)] (=1)        handling of warnings when compiling
+                                            scripts
+                                            0 - ignore warning
+                                            1 - show warning but consider script as
+                                            correctly compiled anyway
+                                            2 - treat warnings as errors
+      --skip-menu [=arg(=1)] (=0)           skip main menu on game startup
+      --new-game [=arg(=1)] (=0)            run new game sequence (ignored if
+                                            skip-menu=0)
+      --fs-strict [=arg(=1)] (=0)           strict file system handling (no case
+                                            folding)
+      --encoding arg (=win1252)             Character encoding used in OpenMW game
+                                            messages:
 
-                                        win1250 - Central and Eastern European
-                                        such as Polish, Czech, Slovak,
-                                        Hungarian, Slovene, Bosnian, Croatian,
-                                        Serbian (Latin script), Romanian and
-                                        Albanian languages
+                                            win1250 - Central and Eastern European
+                                            such as Polish, Czech, Slovak,
+                                            Hungarian, Slovene, Bosnian, Croatian,
+                                            Serbian (Latin script), Romanian and
+                                            Albanian languages
 
-                                        win1251 - Cyrillic alphabet such as
-                                        Russian, Bulgarian, Serbian Cyrillic
-                                        and other languages
+                                            win1251 - Cyrillic alphabet such as
+                                            Russian, Bulgarian, Serbian Cyrillic
+                                            and other languages
 
-                                        win1252 - Western European (Latin)
-                                        alphabet, used by default
-  --fallback arg                        fallback values
-  --no-grab                             Don't grab mouse cursor
-  --activate-dist arg (=-1)             activation distance override
-
+ win1252 - Western European (Latin) + alphabet, used by default + --fallback arg fallback values + --no-grab Don't grab mouse cursor + --activate-dist arg (=-1) activation distance override From e2fe9693f3a9be047eb785fae49aa4e5c06ece93 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 00:17:55 +0100 Subject: [PATCH 291/404] Add missing tooltips in edit effect dialog --- files/mygui/openmw_edit_effect.layout | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/files/mygui/openmw_edit_effect.layout b/files/mygui/openmw_edit_effect.layout index 5dc53e5057..6123f90a73 100644 --- a/files/mygui/openmw_edit_effect.layout +++ b/files/mygui/openmw_edit_effect.layout @@ -12,9 +12,12 @@ - + + + + @@ -22,9 +25,12 @@ - + + + + @@ -49,9 +55,12 @@ - + + + + @@ -66,9 +75,12 @@ - + + + + From 716e14a37a8cd86d8711bd2ba5e1ac8e597d0d66 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 15:12:47 +0100 Subject: [PATCH 292/404] Print missing savegame dependencies on the console --- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f756ee42ae..77e3452e38 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -534,8 +534,8 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it) == selectedContentFiles.end()) { + std::cerr << "Savegame dependency " << *it << " is missing." << std::endl; notFound = true; - break; } } if (notFound) From b108fbe9866d97c853489defff91b41996cb39e0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 15:47:56 +0100 Subject: [PATCH 293/404] Remove incorrect implementation of fGreetDistanceReset This can't be right. A reset distance of 512 is smaller than the maximum greeting distance for certain NPCs, which would then say greetings non-stop. --- apps/openmw/mwmechanics/aiwander.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 94c4542f81..30e17c7f43 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -477,10 +477,8 @@ namespace MWMechanics if (greetingState == MWMechanics::AiWander::Greet_Done) { - static float fGreetDistanceReset = MWBase::Environment::get().getWorld()->getStore() - .get().find("fGreetDistanceReset")->getFloat(); - - if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset) + float resetDist = 2*helloDistance; + if (playerDistSqr >= resetDist*resetDist) greetingState = Greet_None; } } From 4d4f2fc475a2f42bd07bf6d235debb7a09c83375 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 16:51:24 +0100 Subject: [PATCH 294/404] Add maximum distance for teleporting followers on using a door --- apps/openmw/mwworld/actionteleport.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 7fd6ba0246..8bbb080086 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -37,7 +37,11 @@ namespace MWWorld getFollowers(actor, followers); for(std::set::iterator it = followers.begin();it != followers.end();++it) { - teleport(*it); + MWWorld::Ptr follower = *it; + if (Ogre::Vector3(follower.getRefData().getPosition().pos).squaredDistance( + Ogre::Vector3( actor.getRefData().getPosition().pos)) + <= 800*800) + teleport(*it); } teleport(actor); From 1780bcc238e06230815f4090cca3e96cdd88d036 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 16:52:04 +0100 Subject: [PATCH 295/404] Print RefNum in BetaComment --- apps/openmw/mwscript/miscextensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index d4431016a7..37bb4235e2 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1042,6 +1042,7 @@ namespace MWScript std::vector contentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); msg << contentFiles.at (ptr.getCellRef().getRefNum().mContentFile) << std::endl; + msg << "RefNum: " << ptr.getCellRef().getRefNum().mIndex << std::endl; } msg << "RefID: " << ptr.getCellRef().getRefId() << std::endl; From 0ec018f7f1f77c22750bb7f6a6649808c4ec1e1a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 17:54:15 +0100 Subject: [PATCH 296/404] More accurate savegame loading progress bar, uses position in the file (Bug #2259) --- apps/openmw/mwstate/statemanagerimp.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 77e3452e38..e9b549dc99 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -337,11 +337,13 @@ void MWState::StateManager::loadGame (const Character *character, const std::str Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - listener.setProgressRange(reader.getRecordCount()); + listener.setProgressRange(100); listener.setLabel("#{sLoadingMessage14}"); Loading::ScopedLoad load(&listener); + size_t total = reader.getFileSize(); + int currentPercent = 0; while (reader.hasMoreRecs()) { ESM::NAME n = reader.getRecName(); @@ -423,7 +425,12 @@ void MWState::StateManager::loadGame (const Character *character, const std::str std::cerr << "Ignoring unknown record: " << n.name << std::endl; reader.skipRecord(); } - listener.increaseProgress(); + int progressPercent = static_cast(float(reader.getFileOffset())/total*100); + if (progressPercent > currentPercent) + { + listener.increaseProgress(progressPercent-currentPercent); + currentPercent = progressPercent; + } } mCharacterManager.setCurrentCharacter(character); @@ -446,7 +453,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false); - // Vanilla MW will restart startup scripts when a save game is loaded. This is unintuive, + // Vanilla MW will restart startup scripts when a save game is loaded. This is unintuitive, // but some mods may be using it as a reload detector. MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); @@ -472,8 +479,11 @@ void MWState::StateManager::loadGame (const Character *character, const std::str void MWState::StateManager::quickLoad() { if (Character* mCurrentCharacter = getCurrentCharacter (false)) - if (const MWState::Slot* slot = &*mCurrentCharacter->begin()) //Get newest save - loadGame (mCurrentCharacter, slot->mPath.string()); + { + if (mCurrentCharacter->begin() == mCurrentCharacter->end()) + return; + loadGame (mCurrentCharacter, mCurrentCharacter->begin()->mPath.string()); //Get newest save + } } void MWState::StateManager::deleteGame(const MWState::Character *character, const MWState::Slot *slot) From c3f3f8b3d0fd4f8845c54fa0666d4b208fb33ee2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 18:01:06 +0100 Subject: [PATCH 297/404] Use only Cell records for saving progress bar (Fixes #2259) --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 1 - apps/openmw/mwdialogue/journalimp.cpp | 4 ---- apps/openmw/mwgui/mapwindow.cpp | 1 - apps/openmw/mwgui/windowmanagerimp.cpp | 3 --- apps/openmw/mwmechanics/actors.cpp | 2 -- apps/openmw/mwrender/globalmap.cpp | 1 - apps/openmw/mwscript/globalscripts.cpp | 1 - apps/openmw/mwstate/statemanagerimp.cpp | 4 ++-- apps/openmw/mwworld/cells.cpp | 4 ++-- apps/openmw/mwworld/esmstore.cpp | 1 - apps/openmw/mwworld/globals.cpp | 1 - apps/openmw/mwworld/player.cpp | 2 -- apps/openmw/mwworld/projectilemanager.cpp | 4 ---- apps/openmw/mwworld/store.hpp | 1 - apps/openmw/mwworld/weather.cpp | 1 - apps/openmw/mwworld/worldimp.cpp | 8 +++++--- apps/openmw/mwworld/worldimp.hpp | 1 + 18 files changed, 11 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 7432db734b..db0f0a7634 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -104,6 +104,7 @@ namespace MWBase virtual void clear() = 0; virtual int countSavedGameRecords() const = 0; + virtual int countSavedGameCells() const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index a42a486962..dd0fa21fae 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -645,7 +645,6 @@ namespace MWDialogue writer.startRecord (ESM::REC_DIAS); state.save (writer); writer.endRecord (ESM::REC_DIAS); - progress.increaseProgress(); } void DialogueManager::readRecord (ESM::ESMReader& reader, int32_t type) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index e8d5375e1f..1d17134dff 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -187,7 +187,6 @@ namespace MWDialogue writer.startRecord (ESM::REC_QUES); state.save (writer); writer.endRecord (ESM::REC_QUES); - progress.increaseProgress(); for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter) { @@ -198,7 +197,6 @@ namespace MWDialogue writer.startRecord (ESM::REC_JOUR); entry.save (writer); writer.endRecord (ESM::REC_JOUR); - progress.increaseProgress(); } } @@ -210,7 +208,6 @@ namespace MWDialogue writer.startRecord (ESM::REC_JOUR); entry.save (writer); writer.endRecord (ESM::REC_JOUR); - progress.increaseProgress(); } for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter) @@ -226,7 +223,6 @@ namespace MWDialogue writer.startRecord (ESM::REC_JOUR); entry.save (writer); writer.endRecord (ESM::REC_JOUR); - progress.increaseProgress(); } } } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 0f9fa2775c..4de9af1086 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -903,7 +903,6 @@ namespace MWGui writer.startRecord(ESM::REC_GMAP); map.save(writer); writer.endRecord(ESM::REC_GMAP); - progress.increaseProgress(); } void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 077ca4fe33..d5a71bc775 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1604,14 +1604,12 @@ namespace MWGui mMap->write(writer, progress); mQuickKeysMenu->write(writer); - progress.increaseProgress(); if (!mSelectedSpell.empty()) { writer.startRecord(ESM::REC_ASPL); writer.writeHNString("ID__", mSelectedSpell); writer.endRecord(ESM::REC_ASPL); - progress.increaseProgress(); } for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) @@ -1619,7 +1617,6 @@ namespace MWGui writer.startRecord(ESM::REC_MARK); (*it).save(writer); writer.endRecord(ESM::REC_MARK); - progress.increaseProgress(); } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a902771603..adabff0925 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1460,8 +1460,6 @@ namespace MWMechanics writer.writeHNT ("COUN", it->second); } writer.endRecord(ESM::REC_DCOU); - - listener.increaseProgress(1); } void Actors::readRecord (ESM::ESMReader& reader, int32_t type) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 5546c401ba..9816b0f41e 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -157,7 +157,6 @@ namespace MWRender data[texelY * mWidth * 3 + texelX * 3+2] = b; } } - loadingListener->increaseProgress(1); } } diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index b409a304c9..c7371100f7 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -142,7 +142,6 @@ namespace MWScript writer.startRecord (ESM::REC_GSCR); script.save (writer); writer.endRecord (ESM::REC_GSCR); - progress.increaseProgress(); } } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index e9b549dc99..9bd2dbd3e8 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -221,7 +221,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.save (stream); Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - listener.setProgressRange(recordCount); + // Using only Cells for progress information, since they typically have the largest records by far + listener.setProgressRange(MWBase::Environment::get().getWorld()->countSavedGameCells()); listener.setLabel("#{sNotifyMessage4}"); Loading::ScopedLoad load(&listener); @@ -229,7 +230,6 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.startRecord (ESM::REC_SAVE); slot->mProfile.save (writer); writer.endRecord (ESM::REC_SAVE); - listener.increaseProgress(); MWBase::Environment::get().getJournal()->write (writer, listener); MWBase::Environment::get().getDialogueManager()->write (writer, listener); diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index ef3d299a92..3a8b7d5c44 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -287,7 +287,7 @@ void MWWorld::Cells::write (ESM::ESMWriter& writer, Loading::Listener& progress) if (iter->second.hasState()) { writeCell (writer, iter->second); - progress.increaseProgress(); // Assumes that each cell writes one record + progress.increaseProgress(); } for (std::map::iterator iter (mInteriors.begin()); @@ -295,7 +295,7 @@ void MWWorld::Cells::write (ESM::ESMWriter& writer, Loading::Listener& progress) if (iter->second.hasState()) { writeCell (writer, iter->second); - progress.increaseProgress(); // Assumes that each cell writes one record + progress.increaseProgress(); } } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 8e0f58a6b0..b096f4b90d 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -161,7 +161,6 @@ void ESMStore::setUp() writer.writeT(mDynamicCount); writer.endRecord("COUN"); writer.endRecord(ESM::REC_DYNA); - progress.increaseProgress(); mPotions.write (writer, progress); mArmors.write (writer, progress); diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 15ba274980..302a05824a 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -85,7 +85,6 @@ namespace MWWorld writer.writeHNString ("NAME", iter->first); iter->second.write (writer, ESM::Variant::Format_Global); writer.endRecord (ESM::REC_GLOB); - progress.increaseProgress(); } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index b3996f7566..a57934bd32 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -225,8 +225,6 @@ namespace MWWorld writer.startRecord (ESM::REC_PLAY); player.save (writer); writer.endRecord (ESM::REC_PLAY); - - progress.increaseProgress(); } bool Player::readRecord (ESM::ESMReader& reader, int32_t type) diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 100ff0ba9b..e4b2d5a1e9 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -318,8 +318,6 @@ namespace MWWorld state.save(writer); writer.endRecord(ESM::REC_PROJ); - - progress.increaseProgress(); } for (std::vector::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) @@ -342,8 +340,6 @@ namespace MWWorld state.save(writer); writer.endRecord(ESM::REC_MPRJ); - - progress.increaseProgress(); } } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index d9e4285fe8..adca81adcb 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -325,7 +325,6 @@ namespace MWWorld writer.writeHNString ("NAME", iter->second.mId); iter->second.save (writer); writer.endRecord (T::sRecordId); - progress.increaseProgress(); } } diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 3f9b7d5623..cd05cb798f 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -760,7 +760,6 @@ void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) writer.startRecord(ESM::REC_WTHR); state.save(writer); writer.endRecord(ESM::REC_WTHR); - progress.increaseProgress(); } bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5461e84054..4c34f2a04c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -309,6 +309,11 @@ namespace MWWorld +1; // camera } + int World::countSavedGameCells() const + { + return mCells.countSavedGameRecords(); + } + void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { // Active cells could have a dirty fog of war, sync it to the CellStore first @@ -320,7 +325,6 @@ namespace MWWorld } MWMechanics::CreatureStats::writeActorIdCounter(writer); - progress.increaseProgress(); mStore.write (writer, progress); // dynamic Store must be written (and read) before Cells, so that // references to custom made records will be recognized @@ -334,12 +338,10 @@ namespace MWWorld writer.writeHNT("TELE", mTeleportEnabled); writer.writeHNT("LEVT", mLevitationEnabled); writer.endRecord(ESM::REC_ENAB); - progress.increaseProgress(); writer.startRecord(ESM::REC_CAM_); writer.writeHNT("FIRS", isFirstPerson()); writer.endRecord(ESM::REC_CAM_); - progress.increaseProgress(); } void World::readRecord (ESM::ESMReader& reader, int32_t type, diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index dfaa9f7894..f050c498d0 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -166,6 +166,7 @@ namespace MWWorld virtual void clear(); virtual int countSavedGameRecords() const; + virtual int countSavedGameCells() const; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; From e95b513cfcdbe1dedb95fffaa12a0ba736b6761f Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 11 Jan 2015 16:33:03 +1300 Subject: [PATCH 298/404] Use icon to show files with problem (Fixes #2268) Launcher now indicates files with problem using an icon. Using red text for files with load order issue removed because it doesn't work well with dark themes. --- components/contentselector/model/contentmodel.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 6c5614f13a..5760a327eb 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "components/esm/esmreader.hpp" @@ -177,14 +178,9 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int switch (role) { - case Qt::ForegroundRole: + case Qt::DecorationRole: { - if (isLoadOrderError(file)) - { - QBrush redBackground(Qt::red, Qt::SolidPattern); - return redBackground; - } - break; + return isLoadOrderError(file) ? QIcon::fromTheme("edit-delete") : QVariant(); } case Qt::EditRole: @@ -618,7 +614,7 @@ QString ContentSelectorModel::ContentModel::toolTip(const EsmFile *file) const { if (isLoadOrderError(file)) { - QString text(""); + QString text(""); int index = indexFromItem(item(file->filePath())).row(); foreach(const LoadOrderError& error, checkForLoadOrderErrors(file, index)) { @@ -626,7 +622,7 @@ QString ContentSelectorModel::ContentModel::toolTip(const EsmFile *file) const text += error.toolTip(); text += "

"; } - text += ("
"); + text += (""); text += file->toolTip(); return text; } From 82eaa9f1bc7af41be5193db065e67dbd26d4132a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 20:07:17 +0100 Subject: [PATCH 299/404] Fix loading crash (don't apply viewmode before player is set up) (Fixes #2272) --- apps/openmw/mwstate/statemanagerimp.cpp | 11 +++++++++-- apps/openmw/mwworld/worldimp.cpp | 6 ------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9bd2dbd3e8..54c3726bd7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -342,6 +342,8 @@ void MWState::StateManager::loadGame (const Character *character, const std::str Loading::ScopedLoad load(&listener); + bool firstPersonCam = false; + size_t total = reader.getFileSize(); int currentPercent = 0; while (reader.hasMoreRecs()) @@ -396,9 +398,11 @@ void MWState::StateManager::loadGame (const Character *character, const std::str case ESM::REC_ENAB: case ESM::REC_LEVC: case ESM::REC_LEVI: - case ESM::REC_CAM_: + MWBase::Environment::get().getWorld()->readRecord(reader, n.val, contentFileMap); + break; - MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); + case ESM::REC_CAM_: + reader.getHNT(firstPersonCam, "FIRS"); break; case ESM::REC_GSCR: @@ -446,6 +450,9 @@ void MWState::StateManager::loadGame (const Character *character, const std::str MWBase::Environment::get().getWindowManager()->updatePlayer(); MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson()) + MWBase::Environment::get().getWorld()->togglePOV(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4c34f2a04c..5e773f5d30 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -356,12 +356,6 @@ namespace MWWorld reader.getHNT(mTeleportEnabled, "TELE"); reader.getHNT(mLevitationEnabled, "LEVT"); return; - case ESM::REC_CAM_: - bool firstperson; - reader.getHNT(firstperson, "FIRS"); - if (firstperson != isFirstPerson()) - togglePOV(); - break; default: if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && From 04d6cead3b3738fbf4aeebb355a71592797f043d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 11 Jan 2015 20:34:26 +0100 Subject: [PATCH 300/404] Autogenerated collision should only be disabled if RootCollisionNode is a child of the root node (Fixes #2133) --- components/nifbullet/bulletnifloader.cpp | 20 ++++++++------------ components/nifbullet/bulletnifloader.hpp | 2 +- libs/openengine/bullet/BulletShapeLoader.cpp | 2 +- libs/openengine/bullet/BulletShapeLoader.h | 5 ++--- libs/openengine/bullet/physic.cpp | 2 +- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 3abe0c1716..6c56fbba8b 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -95,7 +95,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) return; } - mShape->mHasCollisionNode = hasRootCollisionNode(node); + mShape->mAutogenerated = hasAutoGeneratedCollision(node); //do a first pass handleNode(node,0,false,false,false); @@ -152,12 +152,9 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mShape->mRaycastingShape = new TriangleMeshShape(mStaticMesh,true); } -bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) +bool ManualBulletShapeLoader::hasAutoGeneratedCollision(Nif::Node const * rootNode) { - if(node->recType == Nif::RC_RootCollisionNode) - return true; - - const Nif::NiNode *ninode = dynamic_cast(node); + const Nif::NiNode *ninode = dynamic_cast(rootNode); if(ninode) { const Nif::NodeList &list = ninode->children; @@ -165,13 +162,12 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) { if(!list[i].empty()) { - if(hasRootCollisionNode(list[i].getPtr())) - return true; + if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode) + return false; } } } - - return false; + return true; } void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, @@ -230,8 +226,8 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, } } - if ( (isCollisionNode || (!mShape->mHasCollisionNode && !raycasting)) - && (!isMarker || (mShape->mHasCollisionNode && !raycasting))) + if ( (isCollisionNode || (mShape->mAutogenerated && !raycasting)) + && (!isMarker || (!mShape->mAutogenerated && !raycasting))) { // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // It must be ignored completely. diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index a9ee968b96..8a77e9e9e7 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -112,7 +112,7 @@ private: /** *Helper function */ - bool hasRootCollisionNode(const Nif::Node *node); + bool hasAutoGeneratedCollision(const Nif::Node *rootNode); /** *convert a NiTriShape to a bullet trishape. diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp index 5e3eeec96d..fd9204b441 100644 --- a/libs/openengine/bullet/BulletShapeLoader.cpp +++ b/libs/openengine/bullet/BulletShapeLoader.cpp @@ -19,7 +19,7 @@ Ogre::Resource(creator, name, handle, group, isManual, loader) */ mCollisionShape = NULL; mRaycastingShape = NULL; - mHasCollisionNode = false; + mAutogenerated = true; mCollide = true; createParamDictionary("BulletShape"); } diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index a95640cf18..472a9861f7 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -41,9 +41,8 @@ public: btCollisionShape* mCollisionShape; btCollisionShape* mRaycastingShape; - // Whether or not a NiRootCollisionNode was present in the .nif. If there is none, the collision behaviour - // depends on object type, so we need to expose this variable. - bool mHasCollisionNode; + // Does this .nif have an autogenerated collision mesh? + bool mAutogenerated; Ogre::Vector3 mBoxTranslation; Ogre::Quaternion mBoxRotation; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 0b08e28d95..94bd397886 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -456,7 +456,7 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); - if (placeable && !raycasting && shape->mCollisionShape && !shape->mHasCollisionNode) + if (placeable && !raycasting && shape->mCollisionShape && shape->mAutogenerated) return NULL; if (!shape->mCollisionShape && !raycasting) From 871d59de6ce9f910c5792aacb00e0cbd546b45e5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 12 Jan 2015 12:09:26 +0100 Subject: [PATCH 301/404] Reduce Idle voice chance (Fixes #1964) --- apps/openmw/mwmechanics/aiwander.cpp | 43 ++++++++++++++++------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 30e17c7f43..c3278a5ad3 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -9,6 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" @@ -301,28 +302,32 @@ namespace MWMechanics playIdle(actor, playedIdle); chooseAction = false; idleNow = true; - - // Play idle voiced dialogue entries randomly - int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); - if (hello > 0) - { - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - // Don't bother if the player is out of hearing range - static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() - .get().find("fVoiceIdleOdds")->getFloat(); - - // Only say Idle voices when player is in LOS - // A bit counterintuitive, likely vanilla did this to reduce the appearance of - // voices going through walls? - if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500 - && MWBase::Environment::get().getWorld()->getLOS(player, actor)) - MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); - } } } + // Play idle voiced dialogue entries randomly + int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); + if (hello > 0 && !MWBase::Environment::get().getWorld()->isSwimming(actor) + && actor.getRefData().getPosition().pos[2] < 3000 && + MWBase::Environment::get().getSoundManager()->sayDone(actor)) + { + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 10000; // [0, 9999] + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + // Don't bother if the player is out of hearing range + static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() + .get().find("fVoiceIdleOdds")->getFloat(); + + float x = fVoiceIdleOdds * MWBase::Environment::get().getFrameDuration(); + + // Only say Idle voices when player is in LOS + // A bit counterintuitive, likely vanilla did this to reduce the appearance of + // voices going through walls? + if (roll < x && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500 + && MWBase::Environment::get().getWorld()->getLOS(player, actor)) + MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); + } + float& lastReaction = storage.mReaction; lastReaction += duration; if(lastReaction < REACTION_INTERVAL) From cda2eea906531709599a7a0768db87bb7ee975ed Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 12 Jan 2015 19:51:05 +0100 Subject: [PATCH 302/404] Enchanting: use fEnchantmentConstantDurationMult and fEffectCostMult --- apps/openmw/mwmechanics/enchanting.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 96afe2e2a9..31ad4a01a0 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -176,23 +176,25 @@ namespace MWMechanics int magMax = (it->mMagnMax == 0) ? 1 : it->mMagnMax; int area = (it->mArea == 0) ? 1 : it->mArea; - float magnitudeCost = 0; + float magnitudeCost = (magMin + magMax) * baseCost * 0.05; if (mCastStyle == ESM::Enchantment::ConstantEffect) { - magnitudeCost = (magMin + magMax) * baseCost * 2.5; + magnitudeCost *= store.get().find("fEnchantmentConstantDurationMult")->getFloat(); } else { - magnitudeCost = (magMin + magMax) * it->mDuration * baseCost * 0.025; + magnitudeCost *= it->mDuration; if(it->mRange == ESM::RT_Target) magnitudeCost *= 1.5; } - float areaCost = area * 0.025 * baseCost; + float areaCost = area * 0.05 * baseCost; if (it->mRange == ESM::RT_Target) areaCost *= 1.5; - enchantmentCost += (magnitudeCost + areaCost) * effectsLeftCnt; + const float fEffectCostMult = store.get().find("fEffectCostMult")->getFloat(); + + enchantmentCost += (magnitudeCost + areaCost) * fEffectCostMult * effectsLeftCnt; --effectsLeftCnt; } From de23ad5c8d6ef3440ef9a7b4fe3a86dacf4e0b34 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 12 Jan 2015 19:57:54 +0100 Subject: [PATCH 303/404] Change dynamic_cast to static_cast to make coverity happy --- apps/openmw/mwgui/bookpage.cpp | 6 +++--- apps/openmw/mwmechanics/actors.cpp | 10 +++++----- apps/openmw/mwmechanics/aisequence.cpp | 14 +++++++------- apps/openmw/mwrender/globalmap.cpp | 2 +- apps/openmw/mwrender/localmap.cpp | 2 +- components/nifogre/mesh.cpp | 4 ++-- components/nifogre/ogrenifloader.cpp | 6 +++--- libs/openengine/bullet/physic.cpp | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 0f2df494a2..3b1d40fc39 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -246,7 +246,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter Style* createHotStyle (Style* baseStyle, Colour normalColour, Colour hoverColour, Colour activeColour, InteractiveId id, bool unique) { - StyleImpl* BaseStyle = dynamic_cast (baseStyle); + StyleImpl* BaseStyle = static_cast (baseStyle); if (!unique) for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) @@ -268,7 +268,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { Range range = mBook->addContent (text); - writeImpl (dynamic_cast (style), range.first, range.second); + writeImpl (static_cast (style), range.first, range.second); } intptr_t addContent (Utf8Span text, bool select) @@ -295,7 +295,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter Utf8Point begin_ = &mCurrentContent->front () + begin; Utf8Point end_ = &mCurrentContent->front () + end ; - writeImpl (dynamic_cast (style), begin_, end_); + writeImpl (static_cast (style), begin_, end_); } void lineBreak (float margin) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index adabff0925..a454d816a4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -131,7 +131,7 @@ void adjustCommandedActor (const MWWorld::Ptr& actor) for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && - dynamic_cast(*it)->isCommanded()) + static_cast(*it)->isCommanded()) { hasCommandPackage = true; break; @@ -355,7 +355,7 @@ namespace MWMechanics { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) { - MWWorld::Ptr followTarget = dynamic_cast(*it)->getTarget(); + MWWorld::Ptr followTarget = static_cast(*it)->getTarget(); if (followTarget.isEmpty()) continue; @@ -1389,7 +1389,7 @@ namespace MWMechanics { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) { - MWWorld::Ptr followTarget = dynamic_cast(*it)->getTarget(); + MWWorld::Ptr followTarget = static_cast(*it)->getTarget(); if (followTarget.isEmpty()) continue; if (followTarget == actor) @@ -1419,11 +1419,11 @@ namespace MWMechanics { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) { - MWWorld::Ptr followTarget = dynamic_cast(*it)->getTarget(); + MWWorld::Ptr followTarget = static_cast(*it)->getTarget(); if (followTarget.isEmpty()) continue; if (followTarget == actor) - list.push_back(dynamic_cast(*it)->getFollowIndex()); + list.push_back(static_cast(*it)->getFollowIndex()); else break; } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 13d09af7ef..364e6effe2 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -342,49 +342,49 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) case ESM::AiSequence::Ai_Wander: { MWMechanics::AiWander* wander = new AiWander( - dynamic_cast(it->mPackage)); + static_cast(it->mPackage)); mPackages.push_back(wander); break; } case ESM::AiSequence::Ai_Travel: { MWMechanics::AiTravel* travel = new AiTravel( - dynamic_cast(it->mPackage)); + static_cast(it->mPackage)); mPackages.push_back(travel); break; } case ESM::AiSequence::Ai_Escort: { MWMechanics::AiEscort* escort = new AiEscort( - dynamic_cast(it->mPackage)); + static_cast(it->mPackage)); mPackages.push_back(escort); break; } case ESM::AiSequence::Ai_Follow: { MWMechanics::AiFollow* follow = new AiFollow( - dynamic_cast(it->mPackage)); + static_cast(it->mPackage)); mPackages.push_back(follow); break; } case ESM::AiSequence::Ai_Activate: { MWMechanics::AiActivate* activate = new AiActivate( - dynamic_cast(it->mPackage)); + static_cast(it->mPackage)); mPackages.push_back(activate); break; } case ESM::AiSequence::Ai_Combat: { MWMechanics::AiCombat* combat = new AiCombat( - dynamic_cast(it->mPackage)); + static_cast(it->mPackage)); mPackages.push_back(combat); break; } case ESM::AiSequence::Ai_Pursue: { MWMechanics::AiPursue* pursue = new AiPursue( - dynamic_cast(it->mPackage)); + static_cast(it->mPackage)); mPackages.push_back(pursue); break; } diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 9816b0f41e..a4460c59d2 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -245,7 +245,7 @@ namespace MWRender void GlobalMap::loadResource(Ogre::Resource *resource) { - Ogre::Texture* tex = dynamic_cast(resource); + Ogre::Texture* tex = static_cast(resource); Ogre::ConstImagePtrList list; list.push_back(&mOverlayImage); tex->_loadImages(list); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 059d83e79f..c152d15136 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -494,7 +494,7 @@ void LocalMap::loadResource(Ogre::Resource* resource) std::vector& buffer = mBuffers[resourceName]; - Ogre::Texture* tex = dynamic_cast(resource); + Ogre::Texture* tex = static_cast(resource); tex->createInternalResources(); memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); tex->getBuffer()->unlock(); diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index 4932dd0098..85c3a7b65c 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -382,7 +382,7 @@ NIFMeshLoader::NIFMeshLoader(const std::string &name, const std::string &group, void NIFMeshLoader::loadResource(Ogre::Resource *resource) { - Ogre::Mesh *mesh = dynamic_cast(resource); + Ogre::Mesh *mesh = static_cast(resource); OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!"); Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(mName); @@ -395,7 +395,7 @@ void NIFMeshLoader::loadResource(Ogre::Resource *resource) } const Nif::Record *record = nif->getRecord(mShapeIndex); - createSubMesh(mesh, dynamic_cast(record)); + createSubMesh(mesh, static_cast(record)); } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 9c5f4a0168..7ec2c2c242 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -799,7 +799,7 @@ class NIFObjectLoader { if (ctrls->recType == Nif::RC_NiAlphaController) { - const Nif::NiAlphaController *alphaCtrl = dynamic_cast(ctrls.getPtr()); + const Nif::NiAlphaController *alphaCtrl = static_cast(ctrls.getPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW AlphaController::Value(movable, alphaCtrl->data.getPtr(), &scene->mMaterialControllerMgr)); AlphaController::Function* function = OGRE_NEW AlphaController::Function(alphaCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); @@ -808,7 +808,7 @@ class NIFObjectLoader } else if (ctrls->recType == Nif::RC_NiMaterialColorController) { - const Nif::NiMaterialColorController *matCtrl = dynamic_cast(ctrls.getPtr()); + const Nif::NiMaterialColorController *matCtrl = static_cast(ctrls.getPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW MaterialColorController::Value(movable, matCtrl->data.getPtr(), &scene->mMaterialControllerMgr)); MaterialColorController::Function* function = OGRE_NEW MaterialColorController::Function(matCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); @@ -826,7 +826,7 @@ class NIFObjectLoader { if (ctrls->recType == Nif::RC_NiFlipController) { - const Nif::NiFlipController *flipCtrl = dynamic_cast(ctrls.getPtr()); + const Nif::NiFlipController *flipCtrl = static_cast(ctrls.getPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW FlipController::Value( diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 94bd397886..ed6d74b9bf 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -35,7 +35,7 @@ btCollisionShape *duplicateCollisionShape(btCollisionShape *shape) if(btBvhTriangleMeshShape *trishape = dynamic_cast(shape)) { - btTriangleMesh* oldMesh = dynamic_cast(trishape->getMeshInterface()); + btTriangleMesh* oldMesh = static_cast(trishape->getMeshInterface()); btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh); NifBullet::TriangleMeshShape *newShape = new NifBullet::TriangleMeshShape(newMesh, true); From 08d8dd287cb59b4759764b34c06dbba672015dc0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 12 Jan 2015 20:25:06 +0100 Subject: [PATCH 304/404] I think this was committed by accident --- apps/openmw/mwrender/localmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index c152d15136..638a086239 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -200,7 +200,7 @@ void LocalMap::requestMap(MWWorld::CellStore* cell, // Get the cell's NorthMarker rotation. This is used to rotate the entire map. const Vector2& north = MWBase::Environment::get().getWorld()->getNorthVector(cell); - Radian angle = Ogre::Math::ATan2 (north.x, north.y) + Ogre::Degree(2); + Radian angle = Ogre::Math::ATan2 (north.x, north.y); mAngle = angle.valueRadians(); // Rotate the cell and merge the rotated corners to the bounding box From bb718f216db6216ed886172bd23bf7a0b6d5ef4b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 12 Jan 2015 20:25:25 +0100 Subject: [PATCH 305/404] Initialize ENAMstruct in SpellCreationDialog (Coverity) --- apps/openmw/mwgui/spellcreationdialog.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index a00afdab8c..f4c9d021a5 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -36,6 +36,18 @@ namespace return gmst.find(ESM::MagicEffect::effectIdToString (id1))->getString() < gmst.find(ESM::MagicEffect::effectIdToString (id2))->getString(); } + + void init(ESM::ENAMstruct& effect) + { + effect.mArea = 0; + effect.mDuration = 0; + effect.mEffectID = -1; + effect.mMagnMax = 0; + effect.mMagnMin = 0; + effect.mRange = 0; + effect.mSkill = -1; + effect.mAttribute = -1; + } } namespace MWGui @@ -47,6 +59,9 @@ namespace MWGui , mMagicEffect(NULL) , mConstantEffect(false) { + init(mEffect); + init(mOldEffect); + getWidget(mCancelButton, "CancelButton"); getWidget(mOkButton, "OkButton"); getWidget(mDeleteButton, "DeleteButton"); From 4ed3e7bbb7714d93771689114d6867494385b30a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 12 Jan 2015 23:28:52 +0100 Subject: [PATCH 306/404] Enchanting code cleanup --- apps/openmw/mwmechanics/enchanting.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 31ad4a01a0..002831accb 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -171,7 +171,6 @@ namespace MWMechanics for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { float baseCost = (store.get().find(it->mEffectID))->mData.mBaseCost; - // To reflect vanilla behavior int magMin = (it->mMagnMin == 0) ? 1 : it->mMagnMin; int magMax = (it->mMagnMax == 0) ? 1 : it->mMagnMax; int area = (it->mArea == 0) ? 1 : it->mArea; @@ -184,17 +183,18 @@ namespace MWMechanics else { magnitudeCost *= it->mDuration; - if(it->mRange == ESM::RT_Target) - magnitudeCost *= 1.5; } float areaCost = area * 0.05 * baseCost; - if (it->mRange == ESM::RT_Target) - areaCost *= 1.5; const float fEffectCostMult = store.get().find("fEffectCostMult")->getFloat(); - enchantmentCost += (magnitudeCost + areaCost) * fEffectCostMult * effectsLeftCnt; + float cost = (magnitudeCost + areaCost) * fEffectCostMult; + if (it->mRange == ESM::RT_Target) + cost *= 1.5; + + enchantmentCost += cost * effectsLeftCnt; + enchantmentCost = std::max(1.f, enchantmentCost); --effectsLeftCnt; } From 41b3a9dba9206d24778140d668c7a4290273a4a7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 12 Jan 2015 11:29:56 +0100 Subject: [PATCH 307/404] Rewrite animated collision shape support (Fixes #2123) --- apps/openmw/mwclass/activator.cpp | 10 ++- apps/openmw/mwclass/activator.hpp | 4 +- apps/openmw/mwclass/apparatus.cpp | 8 +-- apps/openmw/mwclass/apparatus.hpp | 4 +- apps/openmw/mwclass/armor.cpp | 8 +-- apps/openmw/mwclass/armor.hpp | 4 +- apps/openmw/mwclass/book.cpp | 8 +-- apps/openmw/mwclass/book.hpp | 4 +- apps/openmw/mwclass/clothing.cpp | 8 +-- apps/openmw/mwclass/clothing.hpp | 4 +- apps/openmw/mwclass/container.cpp | 10 ++- apps/openmw/mwclass/container.hpp | 4 +- apps/openmw/mwclass/creature.cpp | 9 ++- apps/openmw/mwclass/creature.hpp | 4 +- apps/openmw/mwclass/door.cpp | 10 ++- apps/openmw/mwclass/door.hpp | 4 +- apps/openmw/mwclass/ingredient.cpp | 8 +-- apps/openmw/mwclass/ingredient.hpp | 4 +- apps/openmw/mwclass/light.cpp | 10 ++- apps/openmw/mwclass/light.hpp | 4 +- apps/openmw/mwclass/lockpick.cpp | 8 +-- apps/openmw/mwclass/lockpick.hpp | 4 +- apps/openmw/mwclass/misc.cpp | 8 +-- apps/openmw/mwclass/misc.hpp | 4 +- apps/openmw/mwclass/npc.cpp | 6 +- apps/openmw/mwclass/npc.hpp | 4 +- apps/openmw/mwclass/potion.cpp | 8 +-- apps/openmw/mwclass/potion.hpp | 4 +- apps/openmw/mwclass/probe.cpp | 8 +-- apps/openmw/mwclass/probe.hpp | 4 +- apps/openmw/mwclass/repair.cpp | 8 +-- apps/openmw/mwclass/repair.hpp | 4 +- apps/openmw/mwclass/static.cpp | 8 +-- apps/openmw/mwclass/static.hpp | 4 +- apps/openmw/mwclass/weapon.cpp | 8 +-- apps/openmw/mwclass/weapon.hpp | 4 +- apps/openmw/mwrender/activatoranimation.cpp | 8 +-- apps/openmw/mwrender/activatoranimation.hpp | 2 +- apps/openmw/mwrender/actors.cpp | 10 +-- apps/openmw/mwrender/actors.hpp | 4 +- apps/openmw/mwrender/animation.cpp | 46 ++++++------- apps/openmw/mwrender/animation.hpp | 4 ++ apps/openmw/mwrender/creatureanimation.cpp | 10 ++- apps/openmw/mwrender/creatureanimation.hpp | 4 +- apps/openmw/mwrender/npcanimation.cpp | 15 +++-- apps/openmw/mwrender/renderingmanager.cpp | 4 +- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/class.cpp | 4 +- apps/openmw/mwworld/class.hpp | 4 +- apps/openmw/mwworld/physicssystem.cpp | 33 ++++----- apps/openmw/mwworld/physicssystem.hpp | 4 +- apps/openmw/mwworld/scene.cpp | 14 ++-- apps/openmw/mwworld/worldimp.cpp | 5 +- components/misc/resourcehelpers.cpp | 17 +++++ components/misc/resourcehelpers.hpp | 2 + components/nifbullet/bulletnifloader.cpp | 75 +++++++++++++++++++-- components/nifbullet/bulletnifloader.hpp | 2 + libs/openengine/bullet/BulletShapeLoader.h | 6 +- libs/openengine/bullet/physic.cpp | 13 ++-- libs/openengine/bullet/physic.hpp | 4 +- 60 files changed, 286 insertions(+), 229 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 46b23e942d..457b0cec10 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -31,20 +31,18 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertActivator(ptr); + actors.insertActivator(ptr, model); } } - void Activator::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Activator::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr, model); MWBase::Environment::get().getMechanicsManager()->add(ptr); } diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 3e4bc3de41..e79318a55e 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -16,10 +16,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index c466cbc33e..316ba3ab6e 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -26,19 +26,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Apparatus::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Apparatus::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 5cdda8f26a..2ab0a47e3b 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -18,10 +18,10 @@ namespace MWClass virtual float getWeight (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 2fa6602c4d..64043157dc 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -31,19 +31,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Armor::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Armor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Armor::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 8b7804c63f..8c8e74cf41 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -17,10 +17,10 @@ namespace MWClass virtual float getWeight (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index b99d71a06f..a9c96e7c7d 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -28,19 +28,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Book::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Book::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Book::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Book::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index 49d21e8bf7..05ff88bb2e 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -15,10 +15,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index eb2dec0ab2..0fa686dda4 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -28,19 +28,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Clothing::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Clothing::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Clothing::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 99ce61ece3..5700543481 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -15,10 +15,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index b05837cb6d..c6a7bbf741 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -85,20 +85,18 @@ namespace MWClass store.restock(list, ptr, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank()); } - void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertActivator(ptr); + actors.insertActivator(ptr, model); } } - void Container::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Container::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr, model); MWBase::Environment::get().getMechanicsManager()->add(ptr); } diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index e926a71fe3..52873374e4 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -18,10 +18,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9eb5b1b690..a3614af968 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -160,20 +160,19 @@ namespace MWClass MWBase::Environment::get().getWorld()->adjustPosition(ptr, force); } - void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { MWWorld::LiveCellRef *ref = ptr.get(); MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertCreature(ptr, ref->mBase->mFlags & ESM::Creature::Weapon); + actors.insertCreature(ptr, model, ref->mBase->mFlags & ESM::Creature::Weapon); } - void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Creature::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) { - physics.addActor(ptr); + physics.addActor(ptr, model); if (getCreatureStats(ptr).isDead()) MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false); } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 4b58864489..e11529b2e8 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -44,10 +44,10 @@ namespace MWClass virtual std::string getId (const MWWorld::Ptr& ptr) const; ///< Return ID of \a ptr - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const; ///< Adjust position to stand on ground. Must be called post model load diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index ee3993fc5a..84c6c66fda 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -49,20 +49,18 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertActivator(ptr); + actors.insertActivator(ptr, model); } } - void Door::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Door::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr, model); // Resume the door's opening/closing animation if it wasn't finished if (ptr.getRefData().getCustomData()) diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 23e11d3368..c5f258d3e0 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -19,10 +19,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 610a0b478c..9f662a60ea 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -32,19 +32,17 @@ namespace MWClass return ref->mBase->mId; } - void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Ingredient::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Ingredient::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 690dd601a7..a4681f4621 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -15,10 +15,10 @@ namespace MWClass virtual std::string getId (const MWWorld::Ptr& ptr) const; ///< Return ID of \a ptr - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 7ad81232d5..669b8187cf 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -54,26 +54,24 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Light::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { MWWorld::LiveCellRef *ref = ptr.get(); // Insert even if model is empty, so that the light is added MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertActivator(ptr, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)); + actors.insertActivator(ptr, model, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)); } - void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->mBase != NULL); - const std::string &model = ref->mBase->mModel; - if(!model.empty()) - physics.addObject(ptr,ref->mBase->mData.mFlags & ESM::Light::Carry); + physics.addObject(ptr, model, ref->mBase->mData.mFlags & ESM::Light::Carry); if (!ref->mBase->mSound.empty() && !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)) MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index a3b8412611..bbca30113c 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -17,10 +17,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 9df5870248..e78c43eee3 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -27,19 +27,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Lockpick::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Lockpick::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index d4bdf3fa69..293a40be11 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -15,10 +15,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 1b4719c6e7..271a7510e3 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -43,19 +43,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 53a8e050bd..23160d41c6 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -15,10 +15,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 596b56acd7..ac41827340 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -428,14 +428,14 @@ namespace MWClass MWBase::Environment::get().getWorld()->adjustPosition(ptr, force); } - void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { renderingInterface.getActors().insertNPC(ptr); } - void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Npc::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - physics.addActor(ptr); + physics.addActor(ptr, model); MWBase::Environment::get().getMechanicsManager()->add(ptr); if (getCreatureStats(ptr).isDead()) MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 8c89686af3..56931a4199 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -50,10 +50,10 @@ namespace MWClass virtual std::string getId (const MWWorld::Ptr& ptr) const; ///< Return ID of \a ptr - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const; ///< Adjust position to stand on ground. Must be called post model load diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 814d903ffb..bd06f89fc8 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -30,19 +30,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Potion::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Potion::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Potion::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 4c407d161c..32e3901156 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -15,10 +15,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 82f4879864..a11725f267 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -27,19 +27,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Probe::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Probe::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Probe::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 047cb8ed4f..bb90ac1531 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -15,10 +15,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 4b18aced0d..e9c4ac9b19 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -26,19 +26,17 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Repair::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Repair::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Repair::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index f89258234d..2589a4af35 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -15,10 +15,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index c241935ab2..dbbe7e43a7 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -17,22 +17,20 @@ namespace MWClass return ptr.get()->mBase->mId; } - void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Static::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { MWWorld::LiveCellRef *ref = ptr.get(); - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model, !ref->mBase->mPersistent); } } - void Static::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Static::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr, model); } std::string Static::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index 2ac2e86825..a94dff394d 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -15,10 +15,10 @@ namespace MWClass /// Return ID of \a ptr virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 8456c72e68..122c8eeaef 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -30,19 +30,17 @@ namespace MWClass return ref->mBase->mId; } - void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - const std::string model = getModel(ptr); if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } - void Weapon::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Weapon::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const { - const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr,true); + physics.addObject(ptr, model, true); } std::string Weapon::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 97ee102911..47f1c52514 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -15,10 +15,10 @@ namespace MWClass virtual std::string getId (const MWWorld::Ptr& ptr) const; ///< Return ID of \a ptr - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 4c63e2cf29..1ef68f619c 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -5,10 +5,6 @@ #include -#include "../mwbase/world.hpp" - -#include "../mwworld/class.hpp" - #include "renderconst.hpp" namespace MWRender @@ -18,11 +14,9 @@ ActivatorAnimation::~ActivatorAnimation() { } -ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) +ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr, const std::string& model) : Animation(ptr, ptr.getRefData().getBaseNode()) { - const std::string& model = mPtr.getClass().getModel(mPtr); - if(!model.empty()) { setObjectRoot(model, false); diff --git a/apps/openmw/mwrender/activatoranimation.hpp b/apps/openmw/mwrender/activatoranimation.hpp index ec0114ccd2..a234defe7a 100644 --- a/apps/openmw/mwrender/activatoranimation.hpp +++ b/apps/openmw/mwrender/activatoranimation.hpp @@ -13,7 +13,7 @@ namespace MWRender class ActivatorAnimation : public Animation { public: - ActivatorAnimation(const MWWorld::Ptr& ptr); + ActivatorAnimation(const MWWorld::Ptr& ptr, const std::string &model); virtual ~ActivatorAnimation(); void addLight(const ESM::Light *light); diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index db666e890a..3da6c40c83 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -71,22 +71,22 @@ void Actors::insertNPC(const MWWorld::Ptr& ptr) mAllActors[ptr] = anim; mRendering->addWaterRippleEmitter (ptr); } -void Actors::insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields) +void Actors::insertCreature (const MWWorld::Ptr& ptr, const std::string &model, bool weaponsShields) { insertBegin(ptr); Animation* anim = NULL; if (weaponsShields) - anim = new CreatureWeaponAnimation(ptr); + anim = new CreatureWeaponAnimation(ptr, model); else - anim = new CreatureAnimation(ptr); + anim = new CreatureAnimation(ptr, model); delete mAllActors[ptr]; mAllActors[ptr] = anim; mRendering->addWaterRippleEmitter (ptr); } -void Actors::insertActivator (const MWWorld::Ptr& ptr, bool addLight) +void Actors::insertActivator (const MWWorld::Ptr& ptr, const std::string &model, bool addLight) { insertBegin(ptr); - ActivatorAnimation* anim = new ActivatorAnimation(ptr); + ActivatorAnimation* anim = new ActivatorAnimation(ptr, model); if(ptr.getTypeName() == typeid(ESM::Light).name()) { diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index f81082e41b..4f6c1bec28 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -40,8 +40,8 @@ namespace MWRender void setRootNode(Ogre::SceneNode* root); void insertNPC(const MWWorld::Ptr& ptr); - void insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields); - void insertActivator (const MWWorld::Ptr& ptr, bool addLight=false); + void insertCreature (const MWWorld::Ptr& ptr, const std::string& model, bool weaponsShields); + void insertActivator (const MWWorld::Ptr& ptr, const std::string& model, bool addLight=false); bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index bf161442e7..117830c093 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -86,6 +86,12 @@ Animation::~Animation() mAnimSources.clear(); } +std::string Animation::getObjectRootName() const +{ + if (mSkelBase) + return mSkelBase->getMesh()->getName(); + return std::string(); +} void Animation::setObjectRoot(const std::string &model, bool baseonly) { @@ -97,22 +103,8 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) if(model.empty()) return; - std::string mdlname = Misc::StringUtils::lowerCase(model); - std::string::size_type p = mdlname.rfind('\\'); - if(p == std::string::npos) - p = mdlname.rfind('/'); - if(p != std::string::npos) - mdlname.insert(mdlname.begin()+p+1, 'x'); - else - mdlname.insert(mdlname.begin(), 'x'); - if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(mdlname)) - { - mdlname = model; - Misc::StringUtils::toLower(mdlname); - } - - mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : - NifOgre::Loader::createObjectBase(mInsert, mdlname)); + mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) : + NifOgre::Loader::createObjectBase(mInsert, model)); if(mObjectRoot->mSkelBase) { @@ -255,14 +247,8 @@ void Animation::addAnimSource(const std::string &model) if(!mSkelBase) return; - std::string kfname = Misc::StringUtils::lowerCase(model); - std::string::size_type p = kfname.rfind('\\'); - if(p == std::string::npos) - p = kfname.rfind('/'); - if(p != std::string::npos) - kfname.insert(kfname.begin()+p+1, 'x'); - else - kfname.insert(kfname.begin(), 'x'); + std::string kfname = model; + Misc::StringUtils::toLower(kfname); if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) kfname.replace(kfname.size()-4, 4, ".kf"); @@ -417,7 +403,7 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScene } -Ogre::Node *Animation::getNode(const std::string &name) +Ogre::Node* Animation::getNode(const std::string &name) { if(mSkelBase) { @@ -428,6 +414,16 @@ Ogre::Node *Animation::getNode(const std::string &name) return NULL; } +Ogre::Node* Animation::getNode(int handle) +{ + if (mSkelBase) + { + Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton(); + return skel->getBone(handle); + } + return NULL; +} + NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname) { NifOgre::TextKeyMap::const_iterator iter(keys.begin()); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 73d10fd06c..dab8cfebb7 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -206,6 +206,9 @@ public: Ogre::uint8 transqueue, Ogre::Real dist=0.0f, bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); + /// Returns the name of the .nif file that makes up this animation's base skeleton. + /// If there is no skeleton, returns "". + std::string getObjectRootName() const; Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); virtual ~Animation(); @@ -334,6 +337,7 @@ public: Ogre::AxisAlignedBox getWorldBounds(); Ogre::Node *getNode(const std::string &name); + Ogre::Node *getNode(int handle); // Attaches the given object to a bone on this object's base skeleton. If the bone doesn't // exist, the object isn't attached and NULL is returned. The returned TagPoint is only diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index c1957d7a8f..2bdf8a499a 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -16,39 +16,37 @@ namespace MWRender { -CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) +CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr, const std::string& model) : Animation(ptr, ptr.getRefData().getBaseNode()) { MWWorld::LiveCellRef *ref = mPtr.get(); - std::string model = ptr.getClass().getModel(ptr); if(!model.empty()) { setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) - addAnimSource("meshes\\base_anim.nif"); + addAnimSource("meshes\\xbase_anim.nif"); addAnimSource(model); } } -CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr) +CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const std::string& model) : Animation(ptr, ptr.getRefData().getBaseNode()) , mShowWeapons(false) , mShowCarriedLeft(false) { MWWorld::LiveCellRef *ref = mPtr.get(); - std::string model = ptr.getClass().getModel(ptr); if(!model.empty()) { setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) - addAnimSource("meshes\\base_anim.nif"); + addAnimSource("meshes\\xbase_anim.nif"); addAnimSource(model); mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr); diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index d6cd8a5179..6201c7af4a 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -15,7 +15,7 @@ namespace MWRender class CreatureAnimation : public Animation { public: - CreatureAnimation(const MWWorld::Ptr& ptr); + CreatureAnimation(const MWWorld::Ptr& ptr, const std::string &model); virtual ~CreatureAnimation() {} }; @@ -25,7 +25,7 @@ namespace MWRender class CreatureWeaponAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener { public: - CreatureWeaponAnimation(const MWWorld::Ptr& ptr); + CreatureWeaponAnimation(const MWWorld::Ptr& ptr, const std::string &model); virtual ~CreatureWeaponAnimation() {} virtual void equipmentChanged() { updateParts(); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 32c1e7e059..5e73a95f23 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -12,6 +12,8 @@ #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" @@ -282,6 +284,7 @@ void NpcAnimation::updateNpcBase() (!isWerewolf ? !isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif" : "meshes\\wolf\\skin.1st.nif"); + smodel = Misc::ResourceHelpers::correctActorModelPath(smodel); setObjectRoot(smodel, true); if(mViewMode != VM_FirstPerson) @@ -290,11 +293,11 @@ void NpcAnimation::updateNpcBase() if(!isWerewolf) { if(Misc::StringUtils::lowerCase(mNpc->mRace).find("argonian") != std::string::npos) - addAnimSource("meshes\\argonian_swimkna.nif"); + addAnimSource("meshes\\xargonian_swimkna.nif"); else if(!mNpc->isMale() && !isBeast) - addAnimSource("meshes\\base_anim_female.nif"); + addAnimSource("meshes\\xbase_anim_female.nif"); if(mNpc->mModel.length() > 0) - addAnimSource("meshes\\"+mNpc->mModel); + addAnimSource("meshes\\x"+mNpc->mModel); } } else @@ -306,11 +309,11 @@ void NpcAnimation::updateNpcBase() /* A bit counter-intuitive, but unlike third-person anims, it seems * beast races get both base_anim.1st.nif and base_animkna.1st.nif. */ - addAnimSource("meshes\\base_anim.1st.nif"); + addAnimSource("meshes\\xbase_anim.1st.nif"); if(isBeast) - addAnimSource("meshes\\base_animkna.1st.nif"); + addAnimSource("meshes\\xbase_animkna.1st.nif"); if(!mNpc->isMale() && !isBeast) - addAnimSource("meshes\\base_anim_female.1st.nif"); + addAnimSource("meshes\\xbase_anim_female.1st.nif"); } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 4a87104adb..8e8b18cd12 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -256,10 +256,10 @@ void RenderingManager::cellAdded (MWWorld::CellStore *store) mDebugging->cellAdded(store); } -void RenderingManager::addObject (const MWWorld::Ptr& ptr){ +void RenderingManager::addObject (const MWWorld::Ptr& ptr, const std::string& model){ const MWWorld::Class& class_ = ptr.getClass(); - class_.insertObjectRendering(ptr, *this); + class_.insertObjectRendering(ptr, model, *this); } void RenderingManager::removeObject (const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index c3eedce7b5..d4b85133d9 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -111,7 +111,7 @@ public: /// Write current fog of war for this cell to the CellStore void writeFog (MWWorld::CellStore* store); - void addObject (const MWWorld::Ptr& ptr); + void addObject (const MWWorld::Ptr& ptr, const std::string& model); void removeObject (const MWWorld::Ptr& ptr); void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 939b72ddb7..edb3f37883 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -38,12 +38,12 @@ namespace MWWorld throw std::runtime_error ("class does not support ID retrieval"); } - void Class::insertObjectRendering (const Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Class::insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const { } - void Class::insertObject(const Ptr& ptr, MWWorld::PhysicsSystem& physics) const + void Class::insertObject(const Ptr& ptr, const std::string& mesh, MWWorld::PhysicsSystem& physics) const { } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 7e605d6a22..3c44abe668 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -81,8 +81,8 @@ namespace MWWorld ///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) - virtual void insertObjectRendering (const Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; - virtual void insertObject(const Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObject(const Ptr& ptr, const std::string& mesh, MWWorld::PhysicsSystem& physics) const; ///< Add reference into a cell for rendering (default implementation: don't render anything). virtual std::string getName (const Ptr& ptr) const = 0; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 5b712648c2..7375fa4f83 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -17,6 +17,8 @@ #include #include +#include +#include #include @@ -51,25 +53,22 @@ void animateCollisionShapes (std::mapgetAnimation(ptr); - if (!animation) // Shouldn't happen either, since keyframe-controlled objects are not batched in StaticGeometry - throw std::runtime_error("can't find Animation for " + ptr.getCellRef().getRefId()); + if (!animation) + continue; OEngine::Physic::AnimatedShapeInstance& instance = it->second; - std::map& shapes = instance.mAnimatedShapes; - for (std::map::iterator shapeIt = shapes.begin(); + std::map& shapes = instance.mAnimatedShapes; + for (std::map::iterator shapeIt = shapes.begin(); shapeIt != shapes.end(); ++shapeIt) { - Ogre::Node* bone; - if (shapeIt->first.empty()) - // HACK: see NifSkeletonLoader::buildBones - bone = animation->getNode(" "); - else - bone = animation->getNode(shapeIt->first); + const std::string& mesh = animation->getObjectRootName(); + int boneHandle = NifOgre::NIFSkeletonLoader::lookupOgreBoneHandle(mesh, shapeIt->first); + Ogre::Node* bone = animation->getNode(boneHandle); if (bone == NULL) - throw std::runtime_error("can't find bone"); + continue; btCompoundShape* compound = dynamic_cast(instance.mCompound); @@ -689,9 +688,8 @@ namespace MWWorld mEngine->removeHeightField(x, y); } - void PhysicsSystem::addObject (const Ptr& ptr, bool placeable) + void PhysicsSystem::addObject (const Ptr& ptr, const std::string& mesh, bool placeable) { - std::string mesh = ptr.getClass().getModel(ptr); Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); handleToMesh[node->getName()] = mesh; mEngine->createAndAdjustRigidBody( @@ -700,9 +698,8 @@ namespace MWWorld mesh, node->getName(), ptr.getCellRef().getScale(), node->getPosition(), node->getOrientation(), 0, 0, true, placeable); } - void PhysicsSystem::addActor (const Ptr& ptr) + void PhysicsSystem::addActor (const Ptr& ptr, const std::string& mesh) { - std::string mesh = ptr.getClass().getModel(ptr); Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); //TODO:optimize this. Searching the std::map isn't very efficient i think. mEngine->addCharacter(node->getName(), mesh, node->getPosition(), node->getScale().x, node->getOrientation()); @@ -773,13 +770,16 @@ namespace MWWorld const std::string &handle = node->getName(); if(handleToMesh.find(handle) != handleToMesh.end()) { + std::string model = ptr.getClass().getModel(ptr); + model = Misc::ResourceHelpers::correctActorModelPath(model); // FIXME: scaling shouldn't require model + bool placeable = false; if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle,true)) placeable = body->mPlaceable; else if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle,false)) placeable = body->mPlaceable; removeObject(handle); - addObject(ptr, placeable); + addObject(ptr, model, placeable); } if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) @@ -820,6 +820,7 @@ namespace MWWorld bool PhysicsSystem::getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max) { std::string model = ptr.getClass().getModel(ptr); + model = Misc::ResourceHelpers::correctActorModelPath(model); if (model.empty()) { return false; } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 7dc8acaa19..c1046aacb4 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -38,9 +38,9 @@ namespace MWWorld void setWaterHeight(float height); void disableWater(); - void addObject (const MWWorld::Ptr& ptr, bool placeable=false); + void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, bool placeable=false); - void addActor (const MWWorld::Ptr& ptr); + void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); void addHeightField (float* heights, int x, int y, float yoffset, diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index efe88c406e..d7548018f3 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -79,8 +80,9 @@ namespace { try { - mRendering.addObject (ptr); - ptr.getClass().insertObject (ptr, mPhysics); + const std::string& model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); + mRendering.addObject(ptr, model); + ptr.getClass().insertObject (ptr, model, mPhysics); updateObjectLocalRotation(ptr, mPhysics, mRendering); if (ptr.getRefData().getBaseNode()) @@ -486,8 +488,7 @@ namespace MWWorld // Sky system MWBase::Environment::get().getWorld()->adjustSky(); - mCellChanged = true; - MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); + mCellChanged = true; MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); @@ -529,8 +530,9 @@ namespace MWWorld void Scene::addObjectToScene (const Ptr& ptr) { - mRendering.addObject(ptr); - ptr.getClass().insertObject(ptr, *mPhysics); + const std::string& model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); + mRendering.addObject(ptr, model); + ptr.getClass().insertObject (ptr, model, *mPhysics); MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5e773f5d30..011f6c3307 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -2092,7 +2093,9 @@ namespace MWWorld // so we should make sure not to use a "stale" controller for that. MWBase::Environment::get().getMechanicsManager()->add(mPlayer->getPlayer()); - mPhysics->addActor(mPlayer->getPlayer()); + std::string model = getPlayerPtr().getClass().getModel(getPlayerPtr()); + model = Misc::ResourceHelpers::correctActorModelPath(model); + mPhysics->addActor(mPlayer->getPlayer(), model); } int World::canRest () diff --git a/components/misc/resourcehelpers.cpp b/components/misc/resourcehelpers.cpp index ee911c566e..dc08b352a6 100644 --- a/components/misc/resourcehelpers.cpp +++ b/components/misc/resourcehelpers.cpp @@ -121,3 +121,20 @@ std::string Misc::ResourceHelpers::correctBookartPath(const std::string &resPath return image; } + +std::string Misc::ResourceHelpers::correctActorModelPath(const std::string &resPath) +{ + std::string mdlname = resPath; + std::string::size_type p = mdlname.rfind('\\'); + if(p == std::string::npos) + p = mdlname.rfind('/'); + if(p != std::string::npos) + mdlname.insert(mdlname.begin()+p+1, 'x'); + else + mdlname.insert(mdlname.begin(), 'x'); + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(mdlname)) + { + return resPath; + } + return mdlname; +} diff --git a/components/misc/resourcehelpers.hpp b/components/misc/resourcehelpers.hpp index 3cf0f4c279..2ce3dce1e9 100644 --- a/components/misc/resourcehelpers.hpp +++ b/components/misc/resourcehelpers.hpp @@ -13,6 +13,8 @@ namespace Misc std::string correctIconPath(const std::string &resPath); std::string correctBookartPath(const std::string &resPath); std::string correctBookartPath(const std::string &resPath, int width, int height); + /// Uses "xfoo.nif" instead of "foo.nif" if available + std::string correctActorModelPath(const std::string &resPath); } } diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 6c56fbba8b..1307877979 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -48,6 +48,57 @@ http://www.gnu.org/licenses/ . typedef unsigned char ubyte; +// Extract a list of keyframe-controlled nodes from a .kf file +// FIXME: this is a similar copy of OgreNifLoader::loadKf +void extractControlledNodes(Nif::NIFFilePtr kfFile, std::set& controlled) +{ + if(kfFile->numRoots() < 1) + { + kfFile->warn("Found no root nodes in "+kfFile->getFilename()+"."); + return; + } + + const Nif::Record *r = kfFile->getRoot(0); + assert(r != NULL); + + if(r->recType != Nif::RC_NiSequenceStreamHelper) + { + kfFile->warn("First root was not a NiSequenceStreamHelper, but a "+ + r->recName+"."); + return; + } + const Nif::NiSequenceStreamHelper *seq = static_cast(r); + + Nif::ExtraPtr extra = seq->extra; + if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) + { + kfFile->warn("First extra data was not a NiTextKeyExtraData, but a "+ + (extra.empty() ? std::string("nil") : extra->recName)+"."); + return; + } + + extra = extra->extra; + Nif::ControllerPtr ctrl = seq->controller; + for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) + { + if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) + { + kfFile->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName); + continue; + } + + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + + const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + + if(key->data.empty()) + continue; + controlled.insert(strdata->string); + } +} + namespace NifBullet { @@ -72,10 +123,6 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mCompoundShape = NULL; mStaticMesh = NULL; - // Load the NIF. TODO: Wrap this in a try-catch block once we're out - // of the early stages of development. Right now we WANT to catch - // every error as early and intrusively as possible, as it's most - // likely a sign of incomplete code rather than faulty input. Nif::NIFFilePtr pnif (Nif::Cache::getInstance().load(mResourceName.substr(0, mResourceName.length()-7))); Nif::NIFFile & nif = *pnif.get (); if (nif.numRoots() < 1) @@ -84,6 +131,19 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) return; } + // Have to load controlled nodes from the .kf + // FIXME: the .kf has to be loaded both for rendering and physics, ideally it should be opened once and then reused + mControlledNodes.clear(); + std::string kfname = mResourceName.substr(0, mResourceName.length()-7); + Misc::StringUtils::toLower(kfname); + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + kfname.replace(kfname.size()-4, 4, ".kf"); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(kfname)) + { + Nif::NIFFilePtr kf (Nif::Cache::getInstance().load(kfname)); + extractControlledNodes(kf, mControlledNodes); + } + Nif::Record *r = nif.getRoot(0); assert(r != NULL); @@ -182,6 +242,9 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, && (node->controller->flags & Nif::NiNode::ControllerFlag_Active)) isAnimated = true; + if (mControlledNodes.find(node->name) != mControlledNodes.end()) + isAnimated = true; + if (!raycasting) isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); else @@ -318,9 +381,9 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int btTransform trans(btQuaternion(q.x, q.y, q.z, q.w), btVector3(v.x, v.y, v.z)); if (raycasting) - mShape->mAnimatedRaycastingShapes.insert(std::make_pair(shape->name, mCompoundShape->getNumChildShapes())); + mShape->mAnimatedRaycastingShapes.insert(std::make_pair(shape->recIndex, mCompoundShape->getNumChildShapes())); else - mShape->mAnimatedShapes.insert(std::make_pair(shape->name, mCompoundShape->getNumChildShapes())); + mShape->mAnimatedShapes.insert(std::make_pair(shape->recIndex, mCompoundShape->getNumChildShapes())); mCompoundShape->addChildShape(trans, childShape); } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 8a77e9e9e7..f95bdfccfe 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -128,6 +128,8 @@ private: btTriangleMesh* mStaticMesh; btBoxShape *mBoundingBox; + + std::set mControlledNodes; }; diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index 472a9861f7..31ee3cc7d8 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -33,10 +33,10 @@ public: // Stores animated collision shapes. If any collision nodes in the NIF are animated, then mCollisionShape // will be a btCompoundShape (which consists of one or more child shapes). // In this map, for each animated collision shape, - // we store the bone name mapped to the child index of the shape in the btCompoundShape. - std::map mAnimatedShapes; + // we store the node's record index mapped to the child index of the shape in the btCompoundShape. + std::map mAnimatedShapes; - std::map mAnimatedRaycastingShapes; + std::map mAnimatedRaycastingShapes; btCollisionShape* mCollisionShape; btCollisionShape* mRaycastingShape; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index ed6d74b9bf..a3ec25e22a 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -1,16 +1,21 @@ #include "physic.hpp" + #include #include #include + +#include +#include + +#include + #include -#include "OgreRoot.h" +#include + #include "BtOgrePG.h" #include "BtOgreGP.h" #include "BtOgreExtras.h" -#include -#include - namespace { diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index fb6ae0cebe..f497150f9c 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -182,8 +182,8 @@ namespace Physic { btCollisionShape* mCompound; - // Maps bone name to child index in the compound shape - std::map mAnimatedShapes; + // Maps node record index to child index in the compound shape + std::map mAnimatedShapes; }; /** From f7bac58b39125c6466cb187578291cb75cf20a52 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Jan 2015 01:00:33 +0100 Subject: [PATCH 308/404] Terrain: change index buffer flags to unsigned --- components/terrain/buffercache.cpp | 2 +- components/terrain/buffercache.hpp | 2 +- components/terrain/quadtreenode.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp index a3e67af5bd..c26e475535 100644 --- a/components/terrain/buffercache.cpp +++ b/components/terrain/buffercache.cpp @@ -39,7 +39,7 @@ namespace Terrain return buffer; } - Ogre::HardwareIndexBufferSharedPtr BufferCache::getIndexBuffer(int flags) + Ogre::HardwareIndexBufferSharedPtr BufferCache::getIndexBuffer(unsigned int flags) { unsigned int verts = mNumVerts; diff --git a/components/terrain/buffercache.hpp b/components/terrain/buffercache.hpp index f0aea9bfd8..51c0a61af2 100644 --- a/components/terrain/buffercache.hpp +++ b/components/terrain/buffercache.hpp @@ -17,7 +17,7 @@ namespace Terrain /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) - Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (int flags); + Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (unsigned int flags); Ogre::HardwareVertexBufferSharedPtr getUVBuffer (); diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 57379a334d..755c45c219 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -415,7 +415,7 @@ void QuadTreeNode::updateIndexBuffers() // Fetch a suitable index buffer (which may be shared) size_t ourLod = getActualLodLevel(); - int flags = 0; + unsigned int flags = 0; for (int i=0; i<4; ++i) { @@ -436,7 +436,7 @@ void QuadTreeNode::updateIndexBuffers() if (lod > 0) { assert (lod - ourLod < (1 << 4)); - flags |= int(lod - ourLod) << (4*i); + flags |= static_cast(lod - ourLod) << (4*i); } } flags |= 0 /*((int)mAdditionalLod)*/ << (4*4); From 261da8dd0ac6ddb64aa4154a863c950087a1e05a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Jan 2015 01:47:42 +0100 Subject: [PATCH 309/404] Terrain: use 32-bit indices if necessary --- components/terrain/buffercache.cpp | 305 +++++++++++++++-------------- 1 file changed, 161 insertions(+), 144 deletions(-) diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp index c26e475535..3d0b359104 100644 --- a/components/terrain/buffercache.cpp +++ b/components/terrain/buffercache.cpp @@ -4,6 +4,162 @@ #include "defs.hpp" +namespace +{ + +template +Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigned int verts, Ogre::HardwareIndexBuffer::IndexType type) +{ + // LOD level n means every 2^n-th vertex is kept + size_t lodLevel = (flags >> (4*4)); + + size_t lodDeltas[4]; + for (int i=0; i<4; ++i) + lodDeltas[i] = (flags >> (4*i)) & (0xf); + + bool anyDeltas = (lodDeltas[Terrain::North] || lodDeltas[Terrain::South] || lodDeltas[Terrain::West] || lodDeltas[Terrain::East]); + + size_t increment = 1 << lodLevel; + assert(increment < verts); + std::vector indices; + indices.reserve((verts-1)*(verts-1)*2*3 / increment); + + size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; + // If any edge needs stitching we'll skip all edges at this point, + // mainly because stitching one edge would have an effect on corners and on the adjacent edges + if (anyDeltas) + { + colStart += increment; + colEnd -= increment; + rowEnd -= increment; + rowStart += increment; + } + for (size_t row = rowStart; row < rowEnd; row += increment) + { + for (size_t col = colStart; col < colEnd; col += increment) + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row+increment); + + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col+increment)+row+increment); + } + } + + size_t innerStep = increment; + if (anyDeltas) + { + // Now configure LOD transitions at the edges - this is pretty tedious, + // and some very long and boring code, but it works great + + // South + size_t row = 0; + size_t outerStep = 1 << (lodDeltas[Terrain::South] + lodLevel); + for (size_t col = 0; col < verts-1; col += outerStep) + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+outerStep)+row); + // Make sure not to touch the right edge + if (col+outerStep == verts-1) + indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); + else + indices.push_back(verts*(col+outerStep)+row+innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == verts-1-innerStep) + continue; + indices.push_back(verts*(col)+row); + indices.push_back(verts*(col+i+innerStep)+row+innerStep); + indices.push_back(verts*(col+i)+row+innerStep); + } + } + + // North + row = verts-1; + outerStep = size_t(1) << (lodDeltas[Terrain::North] + lodLevel); + for (size_t col = 0; col < verts-1; col += outerStep) + { + indices.push_back(verts*(col+outerStep)+row); + indices.push_back(verts*col+row); + // Make sure not to touch the left edge + if (col == 0) + indices.push_back(verts*(col+innerStep)+row-innerStep); + else + indices.push_back(verts*col+row-innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == verts-1-innerStep) + continue; + indices.push_back(verts*(col+i)+row-innerStep); + indices.push_back(verts*(col+i+innerStep)+row-innerStep); + indices.push_back(verts*(col+outerStep)+row); + } + } + + // West + size_t col = 0; + outerStep = size_t(1) << (lodDeltas[Terrain::West] + lodLevel); + for (size_t row = 0; row < verts-1; row += outerStep) + { + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*col+row); + // Make sure not to touch the top edge + if (row+outerStep == verts-1) + indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); + else + indices.push_back(verts*(col+innerStep)+row+outerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == verts-1-innerStep) + continue; + indices.push_back(verts*col+row); + indices.push_back(verts*(col+innerStep)+row+i); + indices.push_back(verts*(col+innerStep)+row+i+innerStep); + } + } + + // East + col = verts-1; + outerStep = size_t(1) << (lodDeltas[Terrain::East] + lodLevel); + for (size_t row = 0; row < verts-1; row += outerStep) + { + indices.push_back(verts*col+row); + indices.push_back(verts*col+row+outerStep); + // Make sure not to touch the bottom edge + if (row == 0) + indices.push_back(verts*(col-innerStep)+row+innerStep); + else + indices.push_back(verts*(col-innerStep)+row); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == verts-1-innerStep) + continue; + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*(col-innerStep)+row+i+innerStep); + indices.push_back(verts*(col-innerStep)+row+i); + } + } + } + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(type, + indices.size(), Ogre::HardwareBuffer::HBU_STATIC); + buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); + return buffer; +} + +} + namespace Terrain { @@ -48,151 +204,12 @@ namespace Terrain return mIndexBufferMap[flags]; } - // LOD level n means every 2^n-th vertex is kept - size_t lodLevel = (flags >> (4*4)); - - size_t lodDeltas[4]; - for (int i=0; i<4; ++i) - lodDeltas[i] = (flags >> (4*i)) & (0xf); - - bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); + Ogre::HardwareIndexBufferSharedPtr buffer; + if (verts*verts > (0xffffu)) + buffer = createIndexBuffer(flags, verts, Ogre::HardwareIndexBuffer::IT_32BIT); + else + buffer = createIndexBuffer(flags, verts, Ogre::HardwareIndexBuffer::IT_16BIT); - size_t increment = 1 << lodLevel; - assert(increment < verts); - std::vector indices; - indices.reserve((verts-1)*(verts-1)*2*3 / increment); - - size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; - // If any edge needs stitching we'll skip all edges at this point, - // mainly because stitching one edge would have an effect on corners and on the adjacent edges - if (anyDeltas) - { - colStart += increment; - colEnd -= increment; - rowEnd -= increment; - rowStart += increment; - } - for (size_t row = rowStart; row < rowEnd; row += increment) - { - for (size_t col = colStart; col < colEnd; col += increment) - { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row+increment); - indices.push_back(verts*col+row+increment); - - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row); - indices.push_back(verts*(col+increment)+row+increment); - } - } - - size_t innerStep = increment; - if (anyDeltas) - { - // Now configure LOD transitions at the edges - this is pretty tedious, - // and some very long and boring code, but it works great - - // South - size_t row = 0; - size_t outerStep = 1 << (lodDeltas[South] + lodLevel); - for (size_t col = 0; col < verts-1; col += outerStep) - { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+outerStep)+row); - // Make sure not to touch the right edge - if (col+outerStep == verts-1) - indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); - else - indices.push_back(verts*(col+outerStep)+row+innerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == verts-1-innerStep) - continue; - indices.push_back(verts*(col)+row); - indices.push_back(verts*(col+i+innerStep)+row+innerStep); - indices.push_back(verts*(col+i)+row+innerStep); - } - } - - // North - row = verts-1; - outerStep = size_t(1) << (lodDeltas[North] + lodLevel); - for (size_t col = 0; col < verts-1; col += outerStep) - { - indices.push_back(verts*(col+outerStep)+row); - indices.push_back(verts*col+row); - // Make sure not to touch the left edge - if (col == 0) - indices.push_back(verts*(col+innerStep)+row-innerStep); - else - indices.push_back(verts*col+row-innerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == verts-1-innerStep) - continue; - indices.push_back(verts*(col+i)+row-innerStep); - indices.push_back(verts*(col+i+innerStep)+row-innerStep); - indices.push_back(verts*(col+outerStep)+row); - } - } - - // West - size_t col = 0; - outerStep = size_t(1) << (lodDeltas[West] + lodLevel); - for (size_t row = 0; row < verts-1; row += outerStep) - { - indices.push_back(verts*col+row+outerStep); - indices.push_back(verts*col+row); - // Make sure not to touch the top edge - if (row+outerStep == verts-1) - indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); - else - indices.push_back(verts*(col+innerStep)+row+outerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == verts-1-innerStep) - continue; - indices.push_back(verts*col+row); - indices.push_back(verts*(col+innerStep)+row+i); - indices.push_back(verts*(col+innerStep)+row+i+innerStep); - } - } - - // East - col = verts-1; - outerStep = size_t(1) << (lodDeltas[East] + lodLevel); - for (size_t row = 0; row < verts-1; row += outerStep) - { - indices.push_back(verts*col+row); - indices.push_back(verts*col+row+outerStep); - // Make sure not to touch the bottom edge - if (row == 0) - indices.push_back(verts*(col-innerStep)+row+innerStep); - else - indices.push_back(verts*(col-innerStep)+row); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == verts-1-innerStep) - continue; - indices.push_back(verts*col+row+outerStep); - indices.push_back(verts*(col-innerStep)+row+i+innerStep); - indices.push_back(verts*(col-innerStep)+row+i); - } - } - } - - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, - indices.size(), Ogre::HardwareBuffer::HBU_STATIC); - buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); mIndexBufferMap[flags] = buffer; return buffer; } From fafc14d5a08f9a38ab4e3924e6d44f0aa2064b02 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Jan 2015 03:11:29 +0100 Subject: [PATCH 310/404] Add proper air movement mechanics (Fixes #2077) --- apps/openmw/mwmechanics/character.cpp | 10 ++++---- apps/openmw/mwworld/physicssystem.cpp | 33 ++++++++------------------- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6632f02753..fa7e52af22 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1496,12 +1496,12 @@ void CharacterController::update(float duration) forcestateupdate = (mJumpState != JumpState_InAir); mJumpState = JumpState_InAir; - // This is a guess. All that seems to be known is that "While the player is in the - // air, fJumpMoveBase and fJumpMoveMult governs air control". What does fJumpMoveMult do? static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat(); - - vec.x *= fJumpMoveBase; - vec.y *= fJumpMoveBase; + static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat(); + float factor = fJumpMoveBase + fJumpMoveMult * mPtr.getClass().getSkill(mPtr, ESM::Skill::Acrobatics)/100.f; + factor = std::min(1.f, factor); + vec.x *= factor; + vec.y *= factor; vec.z = 0.0f; } else if(vec.z > 0.0f && mJumpState == JumpState_None) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7375fa4f83..77c5b4460e 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -289,7 +289,7 @@ namespace MWWorld // Reset per-frame data physicActor->setWalkingOnWater(false); - /* Anything to collide with? */ + // Anything to collide with? if(!physicActor->getCollisionMode()) { return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * @@ -317,12 +317,11 @@ namespace MWWorld */ OEngine::Physic::ActorTracer tracer; - Ogre::Vector3 inertia(0.0f); + Ogre::Vector3 inertia = physicActor->getInertialForce(); Ogre::Vector3 velocity; - if(position.z < waterlevel || isFlying) // under water by 3/4 or can fly + if(position.z < waterlevel || isFlying) { - // TODO: Shouldn't water have higher drag in calculating velocity? velocity = (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)* Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement; } @@ -330,25 +329,12 @@ namespace MWWorld { velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement; - // not in water nor can fly, so need to deal with gravity - if(!physicActor->getOnGround()) // if current OnGround status is false, must be falling or jumping + if (velocity.z > 0.f) + inertia = velocity; + if(!physicActor->getOnGround()) { - // If falling or jumping up, add part of the incoming velocity with the current inertia, - // but don't allow increasing inertia beyond actor's speed (except on the initial jump impulse) - float actorSpeed = ptr.getClass().getSpeed(ptr); - float cap = std::max(actorSpeed, Ogre::Vector2(physicActor->getInertialForce().x, physicActor->getInertialForce().y).length()); - Ogre::Vector3 newVelocity = velocity + physicActor->getInertialForce(); - if (Ogre::Vector2(newVelocity.x, newVelocity.y).squaredLength() > cap*cap) - { - velocity = newVelocity; - float speedXY = Ogre::Vector2(velocity.x, velocity.y).length(); - velocity.x *= cap / speedXY; - velocity.y *= cap / speedXY; - } - else - velocity = newVelocity; + velocity = velocity + physicActor->getInertialForce(); } - inertia = velocity; // NOTE: velocity is for z axis only in this code block } ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; @@ -371,7 +357,6 @@ namespace MWWorld float remainingTime = time; for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) { - // NOTE: velocity is either z axis only or x & z axis Ogre::Vector3 nextpos = newPosition + velocity * remainingTime; // If not able to fly, don't allow to swim up into the air @@ -482,7 +467,7 @@ namespace MWWorld // so that we do not stay suspended in air indefinitely. if (tracer.mFraction < 1.0f && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Actor) { - if (Ogre::Vector3(inertia.x, inertia.y, 0).squaredLength() < 100.f*100.f) + if (Ogre::Vector3(velocity.x, velocity.y, 0).squaredLength() < 100.f*100.f) { btVector3 aabbMin, aabbMax; tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax); @@ -508,7 +493,7 @@ namespace MWWorld } physicActor->setOnGround(isOnGround); - newPosition.z -= halfExtents.z; // remove what was added at the beggining + newPosition.z -= halfExtents.z; // remove what was added at the beginning return newPosition; } }; From 7b4665c6234f7190e085396a524c40220a50a3ed Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Jan 2015 03:30:14 +0100 Subject: [PATCH 311/404] Terrain: documentation update --- components/esmterrain/storage.hpp | 2 ++ components/terrain/storage.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index d25f7552b7..e184cbc4cd 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -38,6 +38,8 @@ namespace ESMTerrain /// Fill vertex buffers for a terrain chunk. /// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue. + /// @note Vertices should be written in row-major order (a row is defined as parallel to the x-axis). + /// The specified positions should be in local space, i.e. relative to the center of the terrain chunk. /// @param lodLevel LOD level, 0 = most detailed /// @param size size of the terrain chunk in cell units /// @param center center of the chunk in cell units diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index d3770ea57a..aa52ffc4bc 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -30,6 +30,8 @@ namespace Terrain /// Fill vertex buffers for a terrain chunk. /// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue. + /// @note Vertices should be written in row-major order (a row is defined as parallel to the x-axis). + /// The specified positions should be in local space, i.e. relative to the center of the terrain chunk. /// @param lodLevel LOD level, 0 = most detailed /// @param size size of the terrain chunk in cell units /// @param center center of the chunk in cell units From 3ce22d31d807447860234ee5580db398321884ba Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Jan 2015 04:53:49 +0100 Subject: [PATCH 312/404] Fix cursor issue when loading savegame from within mouselook-mode and a warning pops up --- apps/openmw/mwinput/inputmanagerimp.cpp | 38 +++++++++++++++---------- apps/openmw/mwinput/inputmanagerimp.hpp | 2 ++ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 846cce6a2b..fed864dfcc 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -302,22 +302,8 @@ namespace MWInput } } - void InputManager::update(float dt, bool disableControls, bool disableEvents) + void InputManager::updateCursorMode() { - mControlsDisabled = disableControls; - - mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); - - mInputManager->capture(disableEvents); - // inject some fake mouse movement to force updating MyGUI's widget states - MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); - - if (mControlsDisabled) - return; - - // update values of channels (as a result of pressed keys) - mInputBinder->update(dt); - bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console; @@ -337,6 +323,28 @@ namespace MWInput { mInputManager->warpMouse(mMouseX, mMouseY); } + } + + void InputManager::update(float dt, bool disableControls, bool disableEvents) + { + mControlsDisabled = disableControls; + + mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); + + mInputManager->capture(disableEvents); + // inject some fake mouse movement to force updating MyGUI's widget states + MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); + + if (mControlsDisabled) + { + updateCursorMode(); + return; + } + + // update values of channels (as a result of pressed keys) + mInputBinder->update(dt); + + updateCursorMode(); // Disable movement in Gui mode if (!(MWBase::Environment::get().getWindowManager()->isGuiMode() diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 346e02ff95..851f249715 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -176,6 +176,8 @@ namespace MWInput void setPlayerControlsEnabled(bool enabled); + void updateCursorMode(); + private: void toggleMainMenu(); void toggleSpell(); From 883f7ec7ce0b62c2b04019898f4419f176433169 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Jan 2015 17:14:54 +0100 Subject: [PATCH 313/404] Move workaround for hiding markers from NIF loader to Scene --- apps/openmw/mwworld/scene.cpp | 21 ++++++++++++++------- components/nifbullet/bulletnifloader.cpp | 18 +++++------------- components/nifbullet/bulletnifloader.hpp | 2 +- components/nifogre/ogrenifloader.cpp | 5 ----- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index d7548018f3..d054cd5604 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -21,6 +21,18 @@ namespace { + + void addObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, + MWRender::RenderingManager& rendering) + { + std::string model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); + std::string id = ptr.getClass().getId(ptr); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + model = ""; + rendering.addObject(ptr, model); + ptr.getClass().insertObject (ptr, model, physics); + } + void updateObjectLocalRotation (const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, MWRender::RenderingManager& rendering) { @@ -80,10 +92,7 @@ namespace { try { - const std::string& model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); - mRendering.addObject(ptr, model); - ptr.getClass().insertObject (ptr, model, mPhysics); - + addObject(ptr, mPhysics, mRendering); updateObjectLocalRotation(ptr, mPhysics, mRendering); if (ptr.getRefData().getBaseNode()) { @@ -530,9 +539,7 @@ namespace MWWorld void Scene::addObjectToScene (const Ptr& ptr) { - const std::string& model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); - mRendering.addObject(ptr, model); - ptr.getClass().insertObject (ptr, model, *mPhysics); + addObject(ptr, *mPhysics, mRendering); MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); } diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 1307877979..8cb531f55f 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -158,7 +158,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mShape->mAutogenerated = hasAutoGeneratedCollision(node); //do a first pass - handleNode(node,0,false,false,false); + handleNode(node,0,false,false); if(mBoundingBox != NULL) { @@ -232,7 +232,7 @@ bool ManualBulletShapeLoader::hasAutoGeneratedCollision(Nif::Node const * rootNo void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, bool isCollisionNode, - bool raycasting, bool isMarker, bool isAnimated) + bool raycasting, bool isAnimated) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. @@ -254,13 +254,6 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, if(node->recType == Nif::RC_AvoidNode) flags |= 0x800; - // Marker objects - /// \todo don't do this in the editor - std::string nodename = node->name; - Misc::StringUtils::toLower(nodename); - if (nodename.find("marker") != std::string::npos) - isMarker = true; - // Check for extra data Nif::Extra const *e = node; while (!e->extra.empty()) @@ -285,12 +278,11 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, // Marker objects. These are only visible in the // editor. Until and unless we add an editor component to // the engine, just skip this entire node. - isMarker = true; + return; } } - if ( (isCollisionNode || (mShape->mAutogenerated && !raycasting)) - && (!isMarker || (!mShape->mAutogenerated && !raycasting))) + if (isCollisionNode || (mShape->mAutogenerated && !raycasting)) { // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // It must be ignored completely. @@ -319,7 +311,7 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker, isAnimated); + handleNode(list[i].getPtr(), flags, isCollisionNode, raycasting, isAnimated); } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index f95bdfccfe..8895997195 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -107,7 +107,7 @@ private: *Parse a node. */ void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, - bool raycasting, bool isMarker, bool isAnimated=false); + bool raycasting, bool isAnimated=false); /** *Helper function diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 7ec2c2c242..51e5d10da2 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1176,11 +1176,6 @@ class NIFObjectLoader if(node->recType == Nif::RC_RootCollisionNode) isRootCollisionNode = true; - // Marker objects: just skip the entire node branch - /// \todo don't do this in the editor - if (node->name.find("marker") != std::string::npos) - return; - if(node->recType == Nif::RC_NiBSAnimationNode) animflags |= node->flags; else if(node->recType == Nif::RC_NiBSParticleNode) From f11ec653d0f2ab00431f1af6abc9435d7a93ae9a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Jan 2015 17:29:25 +0100 Subject: [PATCH 314/404] Add setting for showing MRK nodes to NIF loaders This makes marker objects show up in OpenCS. --- apps/opencs/editor.cpp | 4 +++- apps/opencs/view/world/physicssystem.cpp | 2 +- components/nifbullet/bulletnifloader.cpp | 5 ++--- components/nifbullet/bulletnifloader.hpp | 5 ++++- components/nifogre/ogrenifloader.cpp | 19 ++++++++++++++++++- components/nifogre/ogrenifloader.hpp | 8 ++++++++ 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 826619b5d0..77644665f1 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -15,7 +15,7 @@ #include #include - +#include #include #include "model/doc/document.hpp" @@ -35,6 +35,8 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit) ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string()); + NifOgre::Loader::setShowMarkers(true); + mOverlaySystem.reset (new CSVRender::OverlaySystem); Bsa::registerResources (Files::Collections (config.first, !mFsStrict), config.second, true, diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp index 57909f4d38..2cbe17dcfa 100644 --- a/apps/opencs/view/world/physicssystem.cpp +++ b/apps/opencs/view/world/physicssystem.cpp @@ -16,7 +16,7 @@ namespace CSVWorld PhysicsSystem::PhysicsSystem() { // Create physics. shapeLoader is deleted by the physic engine - NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); + NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(true); mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); } diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 8cb531f55f..372716b680 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -274,10 +274,9 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, // No collision. Use an internal flag setting to mark this. flags |= 0x800; } - else if (sd->string == "MRK") + else if (sd->string == "MRK" && !mShowMarkers) // Marker objects. These are only visible in the - // editor. Until and unless we add an editor component to - // the engine, just skip this entire node. + // editor. return; } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 8895997195..56d98834da 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -67,11 +67,12 @@ struct TriangleMeshShape : public btBvhTriangleMeshShape class ManualBulletShapeLoader : public OEngine::Physic::BulletShapeLoader { public: - ManualBulletShapeLoader() + ManualBulletShapeLoader(bool showMarkers=false) : mShape(NULL) , mStaticMesh(NULL) , mCompoundShape(NULL) , mBoundingBox(NULL) + , mShowMarkers(showMarkers) { } @@ -130,6 +131,8 @@ private: btBoxShape *mBoundingBox; std::set mControlledNodes; + + bool mShowMarkers; }; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 51e5d10da2..17df7a3cdc 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -689,6 +689,14 @@ public: */ class NIFObjectLoader { + static bool sShowMarkers; +public: + static void setShowMarkers(bool show) + { + sShowMarkers = show; + } +private: + static void warn(const std::string &msg) { std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl; @@ -1208,7 +1216,7 @@ class NIFObjectLoader const Nif::NiStringExtraData *sd = static_cast(e.getPtr()); // String markers may contain important information // affecting the entire subtree of this obj - if(sd->string == "MRK") + if(sd->string == "MRK" && !sShowMarkers) { // Marker objects. These meshes are only visible in the // editor. @@ -1471,5 +1479,14 @@ void Loader::createKfControllers(Ogre::Entity *skelBase, NIFObjectLoader::loadKf(skelBase->getSkeleton(), name, textKeys, ctrls); } +bool Loader::sShowMarkers = false; +bool NIFObjectLoader::sShowMarkers = false; + +void Loader::setShowMarkers(bool show) +{ + sShowMarkers = show; + NIFObjectLoader::setShowMarkers(show); +} + } // namespace NifOgre diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index fd5ddca7d0..c135326448 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -110,10 +110,18 @@ public: std::string name, const std::string &group="General"); + /// Set whether or not nodes marked as "MRK" should be shown. + /// These should be hidden ingame, but visible in the editior. + /// Default: false. + static void setShowMarkers(bool show); + static void createKfControllers(Ogre::Entity *skelBase, const std::string &name, TextKeyMap &textKeys, std::vector > &ctrls); + +private: + static bool sShowMarkers; }; // FIXME: Should be with other general Ogre extensions. From 2ac4a74a34d150cb135d8c9545fe117734f21b9c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Jan 2015 18:09:10 +0100 Subject: [PATCH 315/404] Fix running AI for dead actors --- apps/openmw/mwmechanics/actors.cpp | 20 ++++++++++++++------ apps/openmw/mwmechanics/aisequence.cpp | 3 +-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a454d816a4..64434a3045 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -42,6 +42,12 @@ namespace { +bool isConscious(const MWWorld::Ptr& ptr) +{ + const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + return !stats.isDead() && !stats.getKnockedDown(); +} + void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& actor) { if (bound) @@ -1101,12 +1107,12 @@ namespace MWMechanics updateCrimePersuit(iter->first, duration); if (iter->first != player) - iter->first.getClass().getCreatureStats(iter->first).getAiSequence().execute(iter->first,iter->second->getAiState(), duration); - - CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); - if(!stats.isDead()) { - if (stats.getAiSequence().isInCombat()) hostilesCount++; + CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); + if (isConscious(iter->first)) + stats.getAiSequence().execute(iter->first,iter->second->getAiState(), duration); + + if (stats.getAiSequence().isInCombat() && !stats.isDead()) hostilesCount++; } } @@ -1515,7 +1521,9 @@ namespace MWMechanics for (PtrActorMap::iterator it = map.begin(); it != map.end(); ++it) { MWWorld::Ptr ptr = it->first; - if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr() + || !isConscious(ptr) + || ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) continue; MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); seq.fastForward(ptr, it->second->getAiState()); diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 364e6effe2..533bcd17cc 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -152,8 +152,7 @@ bool AiSequence::isPackageDone() const void AiSequence::execute (const MWWorld::Ptr& actor, AiState& state,float duration) { - if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr() - && !actor.getClass().getCreatureStats(actor).getKnockedDown()) + if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr()) { if (!mPackages.empty()) { From a3c861b7fa1c68b20a20ff9edc927dc406956e80 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 Jan 2015 00:07:14 +0100 Subject: [PATCH 316/404] Idle voice fix --- apps/openmw/mwmechanics/aiwander.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c3278a5ad3..73f0661406 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -311,14 +311,18 @@ namespace MWMechanics && actor.getRefData().getPosition().pos[2] < 3000 && MWBase::Environment::get().getSoundManager()->sayDone(actor)) { - float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 10000; // [0, 9999] MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - // Don't bother if the player is out of hearing range static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() .get().find("fVoiceIdleOdds")->getFloat(); - float x = fVoiceIdleOdds * MWBase::Environment::get().getFrameDuration(); + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 10000; + + // In vanilla MW the chance was FPS dependent, and did not allow proper changing of fVoiceIdleOdds + // due to the roll being an integer. + // Our implementation does not have these issues, so needs to be recalibrated. We chose to + // use the chance MW would have when run at 60 FPS with the default value of the GMST for calibration. + float x = fVoiceIdleOdds * 0.6 * (MWBase::Environment::get().getFrameDuration() / 0.1); // Only say Idle voices when player is in LOS // A bit counterintuitive, likely vanilla did this to reduce the appearance of From 52ed3d92a8c86224bc4ab0dc24aeb7ad797a8c96 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 Jan 2015 17:59:04 +0100 Subject: [PATCH 317/404] Fix btCompoundShape scaling (Fixes #1683) --- apps/openmw/mwworld/physicssystem.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 77c5b4460e..993e1aa256 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -73,10 +73,12 @@ void animateCollisionShapes (std::map(instance.mCompound); btTransform trans; - trans.setOrigin(BtOgre::Convert::toBullet(bone->_getDerivedPosition())); + trans.setOrigin(BtOgre::Convert::toBullet(bone->_getDerivedPosition()) * compound->getLocalScaling()); trans.setRotation(BtOgre::Convert::toBullet(bone->_getDerivedOrientation())); - compound->getChildShape(shapeIt->second)->setLocalScaling(BtOgre::Convert::toBullet(bone->_getDerivedScale())); + compound->getChildShape(shapeIt->second)->setLocalScaling( + compound->getLocalScaling() * + BtOgre::Convert::toBullet(bone->_getDerivedScale())); compound->updateChildTransform(shapeIt->second, trans); } From d387c207d19aa4b0685207e9b0a48d841ac68899 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 Jan 2015 18:43:42 +0100 Subject: [PATCH 318/404] Fix initial scaling for btCompoundShape children (Fixes #2234) --- components/nifbullet/bulletnifloader.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 372716b680..35d5527268 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -365,10 +365,17 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int TriangleMeshShape* childShape = new TriangleMeshShape(childMesh,true); - childShape->setLocalScaling(btVector3(transform[0][0], transform[1][1], transform[2][2])); - + float scale = shape->trafo.scale; + const Nif::Node* parent = shape; + while (parent->parent) + { + parent = parent->parent; + scale *= parent->trafo.scale; + } Ogre::Quaternion q = transform.extractQuaternion(); Ogre::Vector3 v = transform.getTrans(); + childShape->setLocalScaling(btVector3(scale, scale, scale)); + btTransform trans(btQuaternion(q.x, q.y, q.z, q.w), btVector3(v.x, v.y, v.z)); if (raycasting) From fb6dd736cfae19269ac2a98e82c2c7d2339697d2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 Jan 2015 20:43:15 +0100 Subject: [PATCH 319/404] Reducing number of jobs in .travis.yml to see if this fixes out of memory issues with coverity build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 079e84a8e1..9e302b144d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ addons: description: "" notification_email: scrawl@baseoftrash.de build_command_prepend: "cmake ." - build_command: "make -j4" + build_command: "make -j2" branch_pattern: coverity_scan before_install: From 03c3e3e1fffcea07b970d448c91e83fcb968fa47 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 00:33:20 +0100 Subject: [PATCH 320/404] 3 jobs --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9e302b144d..f9c917bbbf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ addons: description: "" notification_email: scrawl@baseoftrash.de build_command_prepend: "cmake ." - build_command: "make -j2" + build_command: "make -j3" branch_pattern: coverity_scan before_install: From cef72385d4f8bfeea46bdc986abf9a37ed7799d2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 00:58:12 +0100 Subject: [PATCH 321/404] Fix extreme frame drop when running into certain corners (Fixes #2023) --- apps/openmw/mwworld/physicssystem.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 993e1aa256..d17d5e0898 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -350,6 +350,8 @@ namespace MWWorld velocity *= 1.f-(fStromWalkMult * (angle.valueDegrees()/180.f)); } + Ogre::Vector3 origVelocity = velocity; + Ogre::Vector3 newPosition = position; /* * A loop to find newPosition using tracer, if successful different from the starting position. @@ -427,10 +429,18 @@ namespace MWWorld else { // Can't move this way, try to find another spot along the plane - Ogre::Real movelen = velocity.normalise(); + Ogre::Vector3 direction = velocity; + Ogre::Real movelen = direction.normalise(); Ogre::Vector3 reflectdir = velocity.reflect(tracer.mPlaneNormal); reflectdir.normalise(); - velocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; + + Ogre::Vector3 newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; + if ((newVelocity-velocity).squaredLength() < 0.01) + break; + if (velocity.dotProduct(origVelocity) <= 0.f) + break; + + velocity = newVelocity; // Do not allow sliding upward if there is gravity. Stepping will have taken // care of that. From edbac30a57e20d5d742640f38ee7226aa1af4554 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 01:23:58 +0100 Subject: [PATCH 322/404] Change another dynamic_cast to static_cast (coverity) --- apps/openmw/mwworld/physicssystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index d17d5e0898..0090ca0106 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -70,7 +70,7 @@ void animateCollisionShapes (std::map(instance.mCompound); + btCompoundShape* compound = static_cast(instance.mCompound); btTransform trans; trans.setOrigin(BtOgre::Convert::toBullet(bone->_getDerivedPosition()) * compound->getLocalScaling()); From 6b2df951677802b138e662b606c83a01105de051 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 02:03:27 +0100 Subject: [PATCH 323/404] Fix for some coverity defects --- apps/openmw/mwworld/worldimp.cpp | 13 +++++++++++-- extern/oics/ICSInputControlSystem.cpp | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 011f6c3307..fbb0b58699 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2107,6 +2107,9 @@ namespace MWWorld Ogre::Vector3 playerPos(refdata.getPosition().pos); const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + if (!physactor) + return 0; // shouldn't happen + if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) return 2; if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || @@ -2326,9 +2329,15 @@ namespace MWWorld if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode()) return false; // not in active cell - Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents(); + OEngine::Physic::PhysicActor* actor1 = mPhysEngine->getCharacter(actor.getRefData().getHandle()); + OEngine::Physic::PhysicActor* actor2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle()); + + if (!actor1 || !actor2) + return false; + + Ogre::Vector3 halfExt1 = actor1->getHalfExtents(); const float* pos1 = actor.getRefData().getPosition().pos; - Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle())->getHalfExtents(); + Ogre::Vector3 halfExt2 = actor2->getHalfExtents(); const float* pos2 = targetActor.getRefData().getPosition().pos; btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z*2*0.9); // eye level diff --git a/extern/oics/ICSInputControlSystem.cpp b/extern/oics/ICSInputControlSystem.cpp index 7dc026c7ef..e1bd587288 100644 --- a/extern/oics/ICSInputControlSystem.cpp +++ b/extern/oics/ICSInputControlSystem.cpp @@ -36,6 +36,9 @@ namespace ICS , mDetectingBindingControl(NULL) , mLog(log) , mXmouseAxisBinded(false), mYmouseAxisBinded(false) + , mClientHeight(1) + , mClientWidth(1) + , mDetectingBindingDirection(Control::STOP) { ICS_LOG(" - Creating InputControlSystem - "); From b39cc0c8c837f4c01e2d98dc13721f72f52d0de7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 02:49:54 +0100 Subject: [PATCH 324/404] Fix OpenCS window opening issue when config file doesn't exist It would attempt to create a zero-sized window (or even negative-sized, after subtracting the frame dimensions). --- apps/opencs/view/doc/view.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 0d2b6060ef..9117a6d034 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -375,17 +375,20 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews) { - QString width = CSMSettings::UserSettings::instance().settingValue - ("window/default-width"); + int width = CSMSettings::UserSettings::instance().settingValue + ("window/default-width").toInt(); - QString height = CSMSettings::UserSettings::instance().settingValue - ("window/default-height"); + int height = CSMSettings::UserSettings::instance().settingValue + ("window/default-height").toInt(); + + width = std::max(width, 300); + height = std::max(height, 300); // trick to get the window decorations and their sizes show(); hide(); - resize (width.toInt() - (frameGeometry().width() - geometry().width()), - height.toInt() - (frameGeometry().height() - geometry().height())); + resize (width - (frameGeometry().width() - geometry().width()), + height - (frameGeometry().height() - geometry().height())); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); From bf915d929a3fcc8a5cb06c88c1e8986b4c4ef7b8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 03:35:46 +0100 Subject: [PATCH 325/404] Update idle voices according to research --- apps/openmw/mwmechanics/aiwander.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 73f0661406..fbb147b341 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -308,8 +308,7 @@ namespace MWMechanics // Play idle voiced dialogue entries randomly int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); if (hello > 0 && !MWBase::Environment::get().getWorld()->isSwimming(actor) - && actor.getRefData().getPosition().pos[2] < 3000 && - MWBase::Environment::get().getSoundManager()->sayDone(actor)) + && MWBase::Environment::get().getSoundManager()->sayDone(actor)) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -327,7 +326,8 @@ namespace MWMechanics // Only say Idle voices when player is in LOS // A bit counterintuitive, likely vanilla did this to reduce the appearance of // voices going through walls? - if (roll < x && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500 + if (roll < x && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) + < 3000*3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead && MWBase::Environment::get().getWorld()->getLOS(player, actor)) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } From 375d426dd0e87173c668b9abea0f5f958ebc3989 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 15 Jan 2015 11:35:17 +0100 Subject: [PATCH 326/404] check for premature end of scripts more consistently --- components/compiler/scanner.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 14ab99c211..83d4359621 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -387,10 +387,9 @@ namespace Compiler if (get (c)) { /// \todo hack to allow a space in comparison operators (add option to disable) - if (c==' ') - get (c); - - if (c=='=') + if (c==' ' && !get (c)) + special = S_cmpEQ; + else if (c=='=') special = S_cmpEQ; else { @@ -471,10 +470,9 @@ namespace Compiler if (get (c)) { /// \todo hack to allow a space in comparison operators (add option to disable) - if (c==' ') - get (c); - - if (c=='=') + if (c==' ' && !get (c)) + special = S_cmpLT; + else if (c=='=') { special = S_cmpLE; @@ -495,10 +493,9 @@ namespace Compiler if (get (c)) { /// \todo hack to allow a space in comparison operators (add option to disable) - if (c==' ') - get (c); - - if (c=='=') + if (c==' ' && !get (c)) + special = S_cmpGT; + else if (c=='=') { special = S_cmpGE; From f3c7532660c0d7f5bd806366802be4a61c0b730c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 15 Jan 2015 12:01:59 +0100 Subject: [PATCH 327/404] cleaned up some enum confusion --- apps/opencs/model/filter/valuenode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/filter/valuenode.cpp b/apps/opencs/model/filter/valuenode.cpp index 66b6282d77..26b9824418 100644 --- a/apps/opencs/model/filter/valuenode.cpp +++ b/apps/opencs/model/filter/valuenode.cpp @@ -27,7 +27,7 @@ bool CSMFilter::ValueNode::test (const CSMWorld::IdTableBase& table, int row, QVariant data = table.data (index); if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int && - data.type()!=QVariant::UInt && data.type()!=static_cast (QMetaType::Float)) + data.type()!=QVariant::UInt) return false; double value = data.toDouble(); From 7b8e6f9dda0e278ec8961d02f42f0ad615a95ec5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 15 Jan 2015 12:04:23 +0100 Subject: [PATCH 328/404] addressed potential 0-pointer issue --- apps/opencs/view/render/object.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index d92b4aaa2d..3cf256e116 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -156,11 +156,13 @@ CSVRender::Object::~Object() { clear(); - if(mPhysics) // preview may not have physics enabled - mPhysics->removeObject(mBase->getName()); - if (mBase) + { + if(mPhysics) // preview may not have physics enabled + mPhysics->removeObject(mBase->getName()); + mBase->getCreator()->destroySceneNode (mBase); + } } bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft, From 706df3f881ed974e3372e4e5644a39f1c8adcd58 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 15 Jan 2015 12:13:53 +0100 Subject: [PATCH 329/404] silenced a coverity warning --- apps/opencs/editor.cpp | 3 ++- apps/opencs/view/world/tablesubview.cpp | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 826619b5d0..21e1270f30 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -75,7 +75,8 @@ CS::Editor::~Editor () mPidFile.close(); if(mServer && boost::filesystem::exists(mPid)) - remove(mPid.string().c_str()); // ignore any error + static_cast ( // silence coverity warning + remove(mPid.string().c_str())); // ignore any error // cleanup global resources used by OEngine delete OEngine::Physic::BulletShapeManager::getSingletonPtr(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 54518023b4..729b6b8d77 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -138,17 +138,19 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) { if (event->type() == QEvent::Drop) { - QDropEvent* drop = dynamic_cast(event); - const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); - if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped - return false; - - bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); - if (handled) + if (QDropEvent* drop = dynamic_cast(event)) { - mFilterBox->setRecordFilter(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); + if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped + return false; + + bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); + if (handled) + { + mFilterBox->setRecordFilter(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + } + return handled; } - return handled; } return false; } From c55e9b9c586b4687c16fab04cc91a4f9061f3ae4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 15 Jan 2015 15:00:16 +0100 Subject: [PATCH 330/404] one more potential 0-pointer fix --- apps/opencs/view/render/object.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 3cf256e116..3607fb415d 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -35,7 +35,8 @@ void CSVRender::Object::clear() { mObject.setNull(); - clearSceneNode (mBase); + if (mBase) + clearSceneNode (mBase); } void CSVRender::Object::update() From fc6aa256bfd1118496db8f6f09dc6397824ecea1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 16:05:25 +0100 Subject: [PATCH 331/404] Add comment --- apps/openmw/mwworld/scene.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index d054cd5604..286721df42 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -28,7 +28,7 @@ namespace std::string model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); std::string id = ptr.getClass().getId(ptr); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") - model = ""; + model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player rendering.addObject(ptr, model); ptr.getClass().insertObject (ptr, model, physics); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fbb0b58699..104605e653 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2108,7 +2108,7 @@ namespace MWWorld const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); if (!physactor) - return 0; // shouldn't happen + throw std::runtime_error("can't find player"); if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) return 2; From 714b19015cb43e5a5829020730de145b15d3c7f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 20:31:08 +0100 Subject: [PATCH 332/404] Fix unknown record error message --- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 54c3726bd7..87b303b6fb 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -426,7 +426,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str default: // ignore invalid records - std::cerr << "Ignoring unknown record: " << n.name << std::endl; + std::cerr << "Ignoring unknown record: " << n.toString() << std::endl; reader.skipRecord(); } int progressPercent = static_cast(float(reader.getFileOffset())/total*100); From 1869d37cfc3b1dfb786f51a7995818d3225a4f15 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Jan 2015 04:55:42 +0100 Subject: [PATCH 333/404] Remove unused mLastDrowningHit --- apps/openmw/mwmechanics/npcstats.cpp | 3 --- apps/openmw/mwmechanics/npcstats.hpp | 2 -- components/esm/npcstats.cpp | 8 +++----- components/esm/npcstats.hpp | 1 - 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ba01caf95e..6d9388408f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -33,7 +33,6 @@ MWMechanics::NpcStats::NpcStats() , mWerewolfKills (0) , mProfit(0) , mTimeToStartDrowning(20.0) -, mLastDrowningHit(0) { mSkillIncreases.resize (ESM::Attribute::Length, 0); } @@ -522,7 +521,6 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds)); state.mTimeToStartDrowning = mTimeToStartDrowning; - state.mLastDrowningHit = mLastDrowningHit; } void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) @@ -573,5 +571,4 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) mUsedIds.insert (*iter); mTimeToStartDrowning = state.mTimeToStartDrowning; - mLastDrowningHit = state.mLastDrowningHit; } diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index ee897033bf..579455a756 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -46,8 +46,6 @@ namespace MWMechanics /// Countdown to getting damage while underwater float mTimeToStartDrowning; - /// time since last hit from drowning - float mLastDrowningHit; public: diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 3e6aed99dd..4ba0ce7e3f 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -75,8 +75,9 @@ void ESM::NpcStats::load (ESMReader &esm) mTimeToStartDrowning = 0; esm.getHNOT (mTimeToStartDrowning, "DRTI"); - mLastDrowningHit = 0; - esm.getHNOT (mLastDrowningHit, "DRLH"); + // No longer used + float lastDrowningHit = 0; + esm.getHNOT (lastDrowningHit, "DRLH"); // No longer used float levelHealthBonus = 0; @@ -146,9 +147,6 @@ void ESM::NpcStats::save (ESMWriter &esm) const if (mTimeToStartDrowning) esm.writeHNT ("DRTI", mTimeToStartDrowning); - if (mLastDrowningHit) - esm.writeHNT ("DRLH", mLastDrowningHit); - if (mCrimeId != -1) esm.writeHNT ("CRID", mCrimeId); } diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index 0061fc05f4..f6e9c1e588 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -45,7 +45,6 @@ namespace ESM int mSkillIncrease[8]; std::vector mUsedIds; float mTimeToStartDrowning; - float mLastDrowningHit; int mCrimeId; void load (ESMReader &esm); From b77558726a99427b62d59256db75ace243d4bef7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Jan 2015 14:59:42 +0100 Subject: [PATCH 334/404] Disable collision for placeable objects (Fixes #1634) --- libs/openengine/bullet/physic.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index a3ec25e22a..1ef00a0e13 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -461,7 +461,9 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); - if (placeable && !raycasting && shape->mCollisionShape && shape->mAutogenerated) + // TODO: add option somewhere to enable collision for placeable meshes + + if (placeable && !raycasting && shape->mCollisionShape) return NULL; if (!shape->mCollisionShape && !raycasting) From 14aacf9a726e2b1d94b3339d1403af1ecabab13c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Jan 2015 17:56:19 +0100 Subject: [PATCH 335/404] Add comment --- apps/openmw/mwmechanics/npcstats.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 579455a756..4594492e10 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -108,8 +108,10 @@ namespace MWMechanics /// Called at character creation. void flagAsUsed (const std::string& id); + ///< @note Id must be lower-case bool hasBeenUsed (const std::string& id) const; + ///< @note Id must be lower-case int getBounty() const; From fc663addfa336032dab9f02a026474f91e3c80c7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Jan 2015 23:10:57 +0100 Subject: [PATCH 336/404] Fix null character issue in ESMReader::getString --- components/esm/esmreader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 6facee381c..7cf0de1a99 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -299,8 +299,7 @@ std::string ESMReader::getString(int size) char *ptr = &mBuffer[0]; getExact(ptr, size); - if (size>0 && ptr[size-1]==0) - --size; + size = strnlen(ptr, size); // Convert to UTF8 and return if (mEncoder) From 031eec45509c3b157dd4fce9e366ac4fcfffe571 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Jan 2015 00:11:36 +0100 Subject: [PATCH 337/404] Starting ESS importer for Morrowind save files --- CMakeLists.txt | 11 + apps/essimporter/CMakeLists.txt | 25 +++ apps/essimporter/converter.cpp | 40 ++++ apps/essimporter/converter.hpp | 262 +++++++++++++++++++++++ apps/essimporter/importacdt.hpp | 25 +++ apps/essimporter/importcellref.cpp | 28 +++ apps/essimporter/importcellref.hpp | 33 +++ apps/essimporter/importcrec.cpp | 13 ++ apps/essimporter/importcrec.hpp | 22 ++ apps/essimporter/importer.cpp | 306 +++++++++++++++++++++++++++ apps/essimporter/importer.hpp | 25 +++ apps/essimporter/importercontext.cpp | 0 apps/essimporter/importercontext.hpp | 65 ++++++ apps/essimporter/importnpcc.cpp | 18 ++ apps/essimporter/importnpcc.hpp | 29 +++ apps/essimporter/importplayer.cpp | 59 ++++++ apps/essimporter/importplayer.hpp | 58 +++++ apps/essimporter/main.cpp | 60 ++++++ components/esm/esmreader.hpp | 1 + components/esm/loadcell.cpp | 5 + components/esm/loadnpcc.hpp | 29 +-- components/esm/loadscpt.cpp | 26 +++ components/esm/loadtes3.cpp | 19 ++ components/esm/loadtes3.hpp | 14 ++ 24 files changed, 1161 insertions(+), 12 deletions(-) create mode 100644 apps/essimporter/CMakeLists.txt create mode 100644 apps/essimporter/converter.cpp create mode 100644 apps/essimporter/converter.hpp create mode 100644 apps/essimporter/importacdt.hpp create mode 100644 apps/essimporter/importcellref.cpp create mode 100644 apps/essimporter/importcellref.hpp create mode 100644 apps/essimporter/importcrec.cpp create mode 100644 apps/essimporter/importcrec.hpp create mode 100644 apps/essimporter/importer.cpp create mode 100644 apps/essimporter/importer.hpp create mode 100644 apps/essimporter/importercontext.cpp create mode 100644 apps/essimporter/importercontext.hpp create mode 100644 apps/essimporter/importnpcc.cpp create mode 100644 apps/essimporter/importnpcc.hpp create mode 100644 apps/essimporter/importplayer.cpp create mode 100644 apps/essimporter/importplayer.hpp create mode 100644 apps/essimporter/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bbce7ad42..6e971b8fb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ option(BUILD_BSATOOL "build BSA extractor" ON) option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_LAUNCHER "build Launcher" ON) option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) +option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON) option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_WIZARD "build Installation Wizard" ON) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) @@ -414,6 +415,9 @@ IF(NOT WIN32 AND NOT APPLE) IF(BUILD_MWINIIMPORTER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_ESSIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-essimporter" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_ESSIMPORTER) IF(BUILD_OPENCS) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) ENDIF(BUILD_OPENCS) @@ -472,6 +476,9 @@ if(WIN32) IF(BUILD_MWINIIMPORTER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" DESTINATION ".") ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_ESSIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".") + ENDIF(BUILD_ESSIMPORTER) IF(BUILD_OPENCS) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/opencs.exe" DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".") @@ -582,6 +589,10 @@ if (BUILD_MWINIIMPORTER) add_subdirectory( apps/mwiniimporter ) endif() +if (BUILD_ESSIMPORTER) + add_subdirectory (apps/essimporter ) +endif() + if (BUILD_OPENCS) add_subdirectory (apps/opencs) endif() diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt new file mode 100644 index 0000000000..231e31fd84 --- /dev/null +++ b/apps/essimporter/CMakeLists.txt @@ -0,0 +1,25 @@ +set(ESSIMPORTER_FILES + main.cpp + importer.cpp + importplayer.cpp + importnpcc.cpp + importcrec.cpp + importcellref.cpp + importacdt.hpp + importercontext.cpp + converter.cpp +) + +add_executable(openmw-essimporter + ${ESSIMPORTER_FILES} +) + +target_link_libraries(openmw-essimporter + ${Boost_LIBRARIES} + components +) + +if (BUILD_WITH_CODE_COVERAGE) + add_definitions (--coverage) + target_link_libraries(openmw-essimporter gcov) +endif() diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp new file mode 100644 index 0000000000..4f36f13ee3 --- /dev/null +++ b/apps/essimporter/converter.cpp @@ -0,0 +1,40 @@ +#include "converter.hpp" + +#include + +namespace +{ + + void convertImage(char* data, int size, int width, int height, Ogre::PixelFormat pf, const std::string& out) + { + Ogre::Image screenshot; + Ogre::DataStreamPtr stream (new Ogre::MemoryDataStream(data, size)); + screenshot.loadRawData(stream, width, height, 1, pf); + screenshot.save(out); + } + +} + +namespace ESSImport +{ + + + struct MAPH + { + unsigned int size; + unsigned int value; + }; + + void ConvertFMAP::read(ESM::ESMReader &esm) + { + MAPH maph; + esm.getHNT(maph, "MAPH"); + std::vector data; + esm.getSubNameIs("MAPD"); + esm.getSubHeader(); + data.resize(esm.getSubSize()); + esm.getExact(&data[0], data.size()); + convertImage(&data[0], data.size(), maph.size, maph.size, Ogre::PF_BYTE_RGB, "map.tga"); + } + +} diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp new file mode 100644 index 0000000000..2662f8c752 --- /dev/null +++ b/apps/essimporter/converter.hpp @@ -0,0 +1,262 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTER_H +#define OPENMW_ESSIMPORT_CONVERTER_H + +#include +#include + +#include +#include +#include +#include +#include "importcrec.hpp" + +#include "importercontext.hpp" +#include "importcellref.hpp" + +namespace ESSImport +{ + +class Converter +{ +public: + /// @return the order for writing this converter's records to the output file, in relation to other converters + virtual int getStage() { return 1; } + + virtual ~Converter() {} + + void setContext(Context& context) { mContext = &context; } + + virtual void read(ESM::ESMReader& esm) + { + } + + /// Called after the input file has been read in completely, which may be necessary + /// if the conversion process relies on information in other records + virtual void write(ESM::ESMWriter& esm) + { + + } + +protected: + Context* mContext; +}; + +/// Default converter: simply reads the record and writes it unmodified to the output +template +class DefaultConverter : public Converter +{ +public: + virtual int getStage() { return 0; } + + virtual void read(ESM::ESMReader& esm) + { + std::string id = esm.getHNString("NAME"); + T record; + record.load(esm); + mRecords[id] = record; + } + + virtual void write(ESM::ESMWriter& esm) + { + for (typename std::map::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it) + { + esm.startRecord(T::sRecordId); + esm.writeHNString("NAME", it->first); + it->second.save(esm); + esm.endRecord(T::sRecordId); + } + } + +protected: + std::map mRecords; +}; + +class ConvertNPC : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + // this is always the player + ESM::NPC npc; + std::string id = esm.getHNString("NAME"); + assert (id == "player"); + npc.load(esm); + mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; + mContext->mPlayerBase = npc; + std::map empty; + for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) + mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + } +}; + +class ConvertGlobal : public DefaultConverter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + ESM::Global global; + global.load(esm); + if (Misc::StringUtils::ciEqual(id, "gamehour")) + mContext->mHour = global.mValue.getFloat(); + if (Misc::StringUtils::ciEqual(id, "day")) + mContext->mDay = global.mValue.getInteger(); + if (Misc::StringUtils::ciEqual(id, "month")) + mContext->mMonth = global.mValue.getInteger(); + if (Misc::StringUtils::ciEqual(id, "year")) + mContext->mYear = global.mValue.getInteger(); + mRecords[id] = global; + } +}; + +class ConvertClass : public DefaultConverter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + ESM::Class class_; + class_.load(esm); + + if (id == "NEWCLASSID_CHARGEN") + mContext->mCustomPlayerClassName = class_.mName; + + mRecords[id] = class_; + } +}; + +class ConvertBook : public DefaultConverter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + ESM::Book book; + book.load(esm); + if (book.mData.mSkillID == -1) + mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(id)); + + mRecords[id] = book; + } +}; + +class ConvertNPCC : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + NPCC npcc; + npcc.load(esm); + if (id == "PlayerSaveGame") + { + mContext->mPlayer.mObject.mNpcStats.mReputation = npcc.mNPDT.mReputation; + } + } +}; + +class ConvertREFR : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + REFR refr; + refr.load(esm); + assert(refr.mRefID == "PlayerSaveGame"); + mContext->mPlayer.mObject.mPosition = refr.mPos; + + ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats; + for (int i=0; i<3; ++i) + { + int writeIndex = translateDynamicIndex(i); + cStats.mDynamic[writeIndex].mBase = refr.mACDT.mDynamic[i][1]; + cStats.mDynamic[writeIndex].mMod = refr.mACDT.mDynamic[i][1]; + cStats.mDynamic[writeIndex].mCurrent = refr.mACDT.mDynamic[i][0]; + } + for (int i=0; i<8; ++i) + { + cStats.mAttributes[i].mBase = refr.mACDT.mAttributes[i][1]; + cStats.mAttributes[i].mMod = refr.mACDT.mAttributes[i][0]; + cStats.mAttributes[i].mCurrent = refr.mACDT.mAttributes[i][0]; + } + ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats; + for (int i=0; imPlayer.mBirthsign = pcdt.mBirthsign; + mContext->mPlayer.mObject.mNpcStats.mBounty = pcdt.mBounty; + for (std::vector::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it) + { + ESM::NpcStats::Faction faction; + faction.mExpelled = it->mFlags & 0x2; + faction.mRank = it->mRank; + faction.mReputation = it->mReputation; + mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction; + } + + } +}; + +class ConvertCREC : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + CREC crec; + crec.load(esm); + } +}; + +class ConvertFMAP : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm); +}; + +class ConvertCell : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + ESM::Cell cell; + std::string id = esm.getHNString("NAME"); + cell.load(esm, false); + CellRef ref; + while (esm.hasMoreSubs()) + { + ref.load (esm); + if (esm.isNextSub("DELE")) + std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + } + } + +}; + +} + +#endif diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp new file mode 100644 index 0000000000..1d79f899e3 --- /dev/null +++ b/apps/essimporter/importacdt.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_ESSIMPORT_ACDT_H +#define OPENMW_ESSIMPORT_ACDT_H + +namespace ESSImport +{ + + /// Actor data, shared by (at least) REFR and CellRef + struct ACDT + { + unsigned char mUnknown1[40]; + float mDynamic[3][2]; + unsigned char mUnknown2[16]; + float mAttributes[8][2]; + unsigned char mUnknown3[120]; + }; + + /// Unknown, shared by (at least) REFR and CellRef + struct ACSC + { + unsigned char unknown[112]; + }; + +} + +#endif diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp new file mode 100644 index 0000000000..f59358d8b8 --- /dev/null +++ b/apps/essimporter/importcellref.cpp @@ -0,0 +1,28 @@ +#include "importcellref.hpp" + +#include + +namespace ESSImport +{ + + void CellRef::load(ESM::ESMReader &esm) + { + esm.getHNT(mRefNum.mIndex, "FRMR"); // TODO: adjust RefNum + + mIndexedRefId = esm.getHNString("NAME"); + + esm.getHNT(mACDT, "ACDT"); + + ACSC acsc; + esm.getHNOT(acsc, "ACSC"); + esm.getHNOT(acsc, "ACSL"); + + if (esm.isNextSub("CRED")) + esm.skipHSub(); + + if (esm.isNextSub("ND3D")) + esm.skipHSub(); + esm.getHNOT(mPos, "DATA", 24); + } + +} diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp new file mode 100644 index 0000000000..f06278ba50 --- /dev/null +++ b/apps/essimporter/importcellref.hpp @@ -0,0 +1,33 @@ +#ifndef OPENMW_ESSIMPORT_CELLREF_H +#define OPENMW_ESSIMPORT_CELLREF_H + +#include + +#include + +#include "importacdt.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + // Not sure if we can share any code with ESM::CellRef here + struct CellRef + { + std::string mIndexedRefId; + ESM::RefNum mRefNum; + + ACDT mACDT; + + ESM::Position mPos; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importcrec.cpp b/apps/essimporter/importcrec.cpp new file mode 100644 index 0000000000..c770b2386a --- /dev/null +++ b/apps/essimporter/importcrec.cpp @@ -0,0 +1,13 @@ +#include "importcrec.hpp" + +#include + +namespace ESSImport +{ + + void CREC::load(ESM::ESMReader &esm) + { + esm.getHNT(mIndex, "INDX"); + } + +} diff --git a/apps/essimporter/importcrec.hpp b/apps/essimporter/importcrec.hpp new file mode 100644 index 0000000000..62881c582a --- /dev/null +++ b/apps/essimporter/importcrec.hpp @@ -0,0 +1,22 @@ +#ifndef OPENMW_ESSIMPORT_CREC_H +#define OPENMW_ESSIMPORT_CREC_H + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + /// Creature changes + struct CREC + { + int mIndex; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp new file mode 100644 index 0000000000..96b79e029f --- /dev/null +++ b/apps/essimporter/importer.cpp @@ -0,0 +1,306 @@ +#include "importer.hpp" + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "importercontext.hpp" + +#include "converter.hpp" + +namespace +{ + + void writeScreenshot(const ESM::Header& fileHeader, ESM::SavedGame& out) + { + Ogre::Image screenshot; + std::vector screenshotData = fileHeader.mSCRS; // MemoryDataStream doesn't work with const data :( + Ogre::DataStreamPtr screenshotStream (new Ogre::MemoryDataStream(&screenshotData[0], screenshotData.size())); + screenshot.loadRawData(screenshotStream, 128, 128, 1, Ogre::PF_BYTE_BGRA); + Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); + out.mScreenshot.resize(encoded->size()); + encoded->read(&out.mScreenshot[0], encoded->size()); + } + +} + +namespace ESSImport +{ + + Importer::Importer(const std::string &essfile, const std::string &outfile) + : mEssFile(essfile) + , mOutFile(outfile) + { + + } + + struct File + { + struct Subrecord + { + std::string mName; + size_t mFileOffset; + std::vector mData; + }; + + struct Record + { + std::string mName; + size_t mFileOffset; + std::vector mSubrecords; + }; + + std::vector mRecords; + }; + + void read(const std::string& filename, File& file) + { + ESM::ESMReader esm; + esm.open(filename); + + while (esm.hasMoreRecs()) + { + ESM::NAME n = esm.getRecName(); + esm.getRecHeader(); + + File::Record rec; + rec.mName = n.toString(); + rec.mFileOffset = esm.getFileOffset(); + while (esm.hasMoreSubs()) + { + File::Subrecord sub; + esm.getSubName(); + esm.getSubHeader(); + sub.mFileOffset = esm.getFileOffset(); + sub.mName = esm.retSubName().toString(); + sub.mData.resize(esm.getSubSize()); + esm.getExact(&sub.mData[0], sub.mData.size()); + rec.mSubrecords.push_back(sub); + } + file.mRecords.push_back(rec); + } + } + + void Importer::compare() + { + // data that always changes should be blacklisted + std::set > blacklist; + blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour + blacklist.insert(std::make_pair("REFR", "DATA")); // player position + + File file1; + read(mEssFile, file1); + File file2; + read(mOutFile, file2); // todo rename variable + + for (unsigned int i=0; i= file2.mRecords.size()) + { + std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset; + return; + } + + File::Record rec2 = file2.mRecords[i]; + + if (rec.mName != rec2.mName) + { + std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl; + return; // TODO: try to recover + } + + // FIXME: use max(size1, size2) + for (unsigned int j=0; j= rec2.mSubrecords.size()) + { + std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset; + return; + } + + File::Subrecord sub2 = rec2.mSubrecords[j]; + + if (sub.mName != sub2.mName) + { + std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset + << " (2) 0x" << sub2.mFileOffset << std::endl; + return; // TODO: try to recover + } + + if (sub.mData != sub2.mData) + { + if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end()) + continue; + + std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset + << " (2) 0x" << sub2.mFileOffset << std::endl; + + std::cout << "Data 1:" << std::endl; + for (unsigned int k=0; k globals; + + const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; + const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; + const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; + + std::map > converters; + converters[ESM::REC_GLOB] = boost::shared_ptr(new ConvertGlobal()); + converters[ESM::REC_BOOK] = boost::shared_ptr(new ConvertBook()); + converters[ESM::REC_NPC_] = boost::shared_ptr(new ConvertNPC()); + converters[ESM::REC_NPCC] = boost::shared_ptr(new ConvertNPCC()); + converters[ESM::REC_CREC] = boost::shared_ptr(new ConvertCREC()); + converters[recREFR] = boost::shared_ptr(new ConvertREFR()); + converters[recPCDT] = boost::shared_ptr(new ConvertPCDT()); + converters[recFMAP] = boost::shared_ptr(new ConvertFMAP()); + converters[ESM::REC_CELL] = boost::shared_ptr(new ConvertCell()); + converters[ESM::REC_ALCH] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_CLAS] = boost::shared_ptr(new ConvertClass()); + converters[ESM::REC_SPEL] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_ARMO] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_WEAP] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_CLOT] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_ENCH] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_WEAP] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); + + + for (std::map >::const_iterator it = converters.begin(); + it != converters.end(); ++it) + { + it->second->setContext(context); + } + + while (esm.hasMoreRecs()) + { + ESM::NAME n = esm.getRecName(); + esm.getRecHeader(); + + std::map >::iterator it = converters.find(n.val); + if (it != converters.end()) + { + it->second->read(esm); + } + else + { + std::cerr << "unknown record " << n.toString() << std::endl; + esm.skipRecord(); + } + } + + const ESM::Header& header = esm.getHeader(); + + ESM::ESMWriter writer; + + writer.setFormat (ESM::Header::CurrentFormat); + + std::ofstream stream(mOutFile.c_str(), std::ios::binary); + // all unused + writer.setVersion(0); + writer.setType(0); + writer.setAuthor(""); + writer.setDescription(""); + writer.setRecordCount (0); + writer.save (stream); + + ESM::SavedGame profile; + for (std::vector::const_iterator it = header.mMaster.begin(); + it != header.mMaster.end(); ++it) + { + profile.mContentFiles.push_back(it->name); + } + profile.mDescription = esm.getDesc(); + profile.mInGameTime.mDay = context.mDay; + profile.mInGameTime.mGameHour = context.mHour; + profile.mInGameTime.mMonth = context.mMonth; + profile.mInGameTime.mYear = context.mYear; + profile.mPlayerCell = header.mGameData.mCurrentCell.toString(); + if (context.mPlayerBase.mClass == "NEWCLASSID_CHARGEN") + profile.mPlayerClassName = context.mCustomPlayerClassName; + else + profile.mPlayerClassId = context.mPlayerBase.mClass; + profile.mPlayerLevel = context.mPlayerBase.mNpdt52.mLevel; + profile.mPlayerName = header.mGameData.mPlayerName.toString(); + + writeScreenshot(header, profile); + + writer.startRecord (ESM::REC_SAVE); + profile.save (writer); + writer.endRecord (ESM::REC_SAVE); + + // Writing order should be Dynamic Store -> Cells -> Player, + // so that references to dynamic records can be recognized when loading + for (std::map >::const_iterator it = converters.begin(); + it != converters.end(); ++it) + { + if (it->second->getStage() != 0) + continue; + it->second->write(writer); + } + + writer.startRecord(ESM::REC_NPC_); + writer.writeHNString("NAME", "player"); + context.mPlayerBase.save(writer); + writer.endRecord(ESM::REC_NPC_); + + for (std::map >::const_iterator it = converters.begin(); + it != converters.end(); ++it) + { + if (it->second->getStage() != 1) + continue; + it->second->write(writer); + } + + writer.startRecord(ESM::REC_PLAY); + context.mPlayer.save(writer); + writer.endRecord(ESM::REC_PLAY); + } + + +} diff --git a/apps/essimporter/importer.hpp b/apps/essimporter/importer.hpp new file mode 100644 index 0000000000..eb199b6dfb --- /dev/null +++ b/apps/essimporter/importer.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_ESSIMPORTER_IMPORTER_H +#define OPENMW_ESSIMPORTER_IMPORTER_H + +#include + +namespace ESSImport +{ + + class Importer + { + public: + Importer(const std::string& essfile, const std::string& outfile); + + void run(); + + void compare(); + + private: + std::string mEssFile; + std::string mOutFile; + }; + +} + +#endif diff --git a/apps/essimporter/importercontext.cpp b/apps/essimporter/importercontext.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp new file mode 100644 index 0000000000..e9af776788 --- /dev/null +++ b/apps/essimporter/importercontext.hpp @@ -0,0 +1,65 @@ +#ifndef OPENMW_ESSIMPORT_CONTEXT_H +#define OPENMW_ESSIMPORT_CONTEXT_H + +#include + +#include "importnpcc.hpp" +#include "importplayer.hpp" + +#include + +namespace ESSImport +{ + + struct Context + { + ESM::Player mPlayer; + ESM::NPC mPlayerBase; + std::string mCustomPlayerClassName; + + int mDay, mMonth, mYear; + float mHour; + + Context() + { + mPlayer.mAutoMove = 0; + ESM::CellId playerCellId; + playerCellId.mPaged = true; + playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; + mPlayer.mCellId = playerCellId; + //mPlayer.mLastKnownExteriorPosition + mPlayer.mHasMark = 0; // TODO + mPlayer.mCurrentCrimeId = 0; // TODO + mPlayer.mObject.mCount = 1; + mPlayer.mObject.mEnabled = 1; + mPlayer.mObject.mHasLocals = false; + mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame + mPlayer.mObject.mCreatureStats.mHasAiSettings = true; + mPlayer.mObject.mCreatureStats.mDead = false; + mPlayer.mObject.mCreatureStats.mDied = false; + mPlayer.mObject.mCreatureStats.mKnockdown = false; + mPlayer.mObject.mCreatureStats.mKnockdownOneFrame = false; + mPlayer.mObject.mCreatureStats.mKnockdownOverOneFrame = false; + mPlayer.mObject.mCreatureStats.mHitRecovery = false; + mPlayer.mObject.mCreatureStats.mBlock = false; + mPlayer.mObject.mCreatureStats.mMovementFlags = 0; + mPlayer.mObject.mCreatureStats.mAttackStrength = 0.f; + mPlayer.mObject.mCreatureStats.mFallHeight = 0.f; + mPlayer.mObject.mCreatureStats.mRecalcDynamicStats = false; + mPlayer.mObject.mCreatureStats.mDrawState = 0; + mPlayer.mObject.mCreatureStats.mDeathAnimation = 0; + mPlayer.mObject.mNpcStats.mIsWerewolf = false; + mPlayer.mObject.mNpcStats.mTimeToStartDrowning = 20; + mPlayer.mObject.mNpcStats.mLevelProgress = 0; + mPlayer.mObject.mNpcStats.mDisposition = 0; + mPlayer.mObject.mNpcStats.mTimeToStartDrowning = 20; + mPlayer.mObject.mNpcStats.mReputation = 0; + mPlayer.mObject.mNpcStats.mCrimeId = -1; + mPlayer.mObject.mNpcStats.mWerewolfKills = 0; + mPlayer.mObject.mNpcStats.mProfit = 0; + } + }; + +} + +#endif diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp new file mode 100644 index 0000000000..192fe5da41 --- /dev/null +++ b/apps/essimporter/importnpcc.cpp @@ -0,0 +1,18 @@ +#include "importnpcc.hpp" + +#include + +namespace ESSImport +{ + + void NPCC::load(ESM::ESMReader &esm) + { + esm.getHNT(mNPDT, "NPDT"); + + // container: + // XIDX + // XHLT - condition + // WIDX - equipping? + } + +} diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp new file mode 100644 index 0000000000..12135850f6 --- /dev/null +++ b/apps/essimporter/importnpcc.hpp @@ -0,0 +1,29 @@ +#ifndef OPENMW_ESSIMPORT_NPCC_H +#define OPENMW_ESSIMPORT_NPCC_H + +#include +#include + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + struct NPCC : public ESM::NPC + { + struct NPDT + { + unsigned char unknown[2]; + unsigned char mReputation; + unsigned char unknown2[5]; + } mNPDT; + + void load(ESM::ESMReader &esm); + }; + +} + +#endif diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp new file mode 100644 index 0000000000..81f81e764e --- /dev/null +++ b/apps/essimporter/importplayer.cpp @@ -0,0 +1,59 @@ +#include "importplayer.hpp" + +#include + +namespace ESSImport +{ + + void REFR::load(ESM::ESMReader &esm) + { + esm.getHNT(mRefNum.mIndex, "FRMR"); + + mRefID = esm.getHNString("NAME"); + + if (esm.isNextSub("STPR")) + esm.skipHSub(); // ESS TODO + + esm.getHNT(mACDT, "ACDT"); + + ACSC acsc; + esm.getHNOT(acsc, "ACSC"); + esm.getHNOT(acsc, "ACSL"); + + esm.getHNExact(mSkills, 27*2*sizeof(int), "CHRD"); + + if (esm.isNextSub("ND3D")) + esm.skipHSub(); // ESS TODO (1 byte) + + esm.getHNOT(mPos, "DATA", 24); + } + + void PCDT::load(ESM::ESMReader &esm) + { + if (esm.isNextSub("PNAM")) + esm.skipHSub(); + if (esm.isNextSub("SNAM")) + esm.skipHSub(); + if (esm.isNextSub("NAM9")) + esm.skipHSub(); + + mBounty = 0; + esm.getHNOT(mBounty, "CNAM"); + + mBirthsign = esm.getHNOString("BNAM"); + + if (esm.isNextSub("ENAM")) + esm.skipHSub(); + + while (esm.isNextSub("FNAM")) + { + FNAM fnam; + esm.getHT(fnam); + mFactions.push_back(fnam); + } + + if (esm.isNextSub("KNAM")) + esm.skipHSub(); + } + +} diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp new file mode 100644 index 0000000000..8a0a087a53 --- /dev/null +++ b/apps/essimporter/importplayer.hpp @@ -0,0 +1,58 @@ +#ifndef OPENMW_ESSIMPORT_PLAYER_H +#define OPENMW_ESSIMPORT_PLAYER_H + +#include +#include + +#include +#include +#include + +#include "importacdt.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + +/// Player-agnostic player data +struct REFR +{ + ACDT mACDT; + + std::string mRefID; + ESM::Position mPos; + ESM::RefNum mRefNum; + + int mSkills[27][2]; + float mAttributes[8][2]; + + void load(ESM::ESMReader& esm); +}; + +/// Other player data +struct PCDT +{ + int mBounty; + std::string mBirthsign; + + struct FNAM + { + unsigned char mRank; + unsigned char mUnknown1[3]; + int mReputation; + unsigned char mFlags; // 0x1: unknown, 0x2: expelled + unsigned char mUnknown2[3]; + ESM::NAME32 mFactionName; + }; + std::vector mFactions; + + void load(ESM::ESMReader& esm); +}; + +} + +#endif diff --git a/apps/essimporter/main.cpp b/apps/essimporter/main.cpp new file mode 100644 index 0000000000..0e5c65e951 --- /dev/null +++ b/apps/essimporter/main.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include "importer.hpp" + +namespace bpo = boost::program_options; +namespace bfs = boost::filesystem; + + + +int main(int argc, const char** argv) +{ + try + { + bpo::options_description desc("Syntax: openmw-essimporter infile.ess outfile.omwsave\nAllowed options"); + bpo::positional_options_description p_desc; + desc.add_options() + ("help,h", "produce help message") + ("mwsave,m", bpo::value(), "morrowind .ess save file") + ("output,o", bpo::value(), "output file (.omwsave)") + ("compare,c", "compare two .ess files") + ; + p_desc.add("mwsave", 1).add("output", 1); + + bpo::variables_map vm; + + bpo::parsed_options parsed = bpo::command_line_parser(argc, argv) + .options(desc) + .positional(p_desc) + .run(); + + bpo::store(parsed, vm); + + if(vm.count("help") || !vm.count("mwsave") || !vm.count("output")) { + std::cout << desc; + return 0; + } + + bpo::notify(vm); + + std::string essFile = vm["mwsave"].as(); + std::string outputFile = vm["output"].as(); + + ESSImport::Importer importer(essFile, outputFile); + + if (vm.count("compare")) + importer.compare(); + else + importer.run(); + } + catch (std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 1549e15f53..642ec4168e 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -36,6 +36,7 @@ public: const std::string getAuthor() const { return mHeader.mData.author.toString(); } const std::string getDesc() const { return mHeader.mData.desc.toString(); } const std::vector &getGameFiles() const { return mHeader.mMaster; } + const Header& getHeader() const { return mHeader; } int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index e4f847dec2..f6649d94ab 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -111,6 +111,11 @@ void Cell::loadData(ESMReader &esm) } esm.getHNT(mData, "DATA", 12); + + // ess only, unknown + char nam8[32]; + if (esm.isNextSub("NAM8")) + esm.getHExact(nam8, 32); } void Cell::postLoad(ESMReader &esm) diff --git a/components/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp index c87c2545f7..6c087fd017 100644 --- a/components/esm/loadnpcc.hpp +++ b/components/esm/loadnpcc.hpp @@ -17,26 +17,29 @@ class ESMWriter; * * Some general observations about savegames: * - * Magical items/potions/spells/etc are added normally as new ALCH, - * SPEL, etc. records, with unique numeric identifiers. - * - * Books with ability enhancements are listed in the save if they have - * been read. - * - * GLOB records set global variables. - * * SCPT records do not define new scripts, but assign values to the * variables of existing ones. * * STLN - stolen items, ONAM is the owner * - * GAME - contains a GMDT (game data) of unknown format - * - * VFXM, SPLM, KLST - no clue + * GAME - weather data + * struct GMDT + { + char mCellName[64]; + int mFogColour; + float mFogDensity; + int mCurrentWeather, mNextWeather; + int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage + float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition + int masserPhase, secundaPhase; // top 3 bytes may be garbage + }; + * + * VFXM, SPLM - no clue + * KLST - kill counter * * PCDT - seems to contain a lot of DNAMs, strings? * - * FMAP - MAPH and MAPD, probably map data. + * FMAP - MAPH and MAPD, global map image. * * JOUR - the entire journal in html * @@ -44,6 +47,8 @@ class ESMWriter; * ones you have done or begun. * * REGN - lists all regions in the game, even unvisited ones. + * notable differences to Regions in ESM files: mMapColor may be missing, and includes an unknown WNAM subrecord. + * * * The DIAL/INFO blocks contain changes to characters' dialog status. * diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 07561c2eaf..f2f13d0864 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -92,6 +92,32 @@ void Script::load(ESMReader &esm) if (esm.isNextSub("SCVR")) { esm.skipHSub(); } + + // ESS only, TODO: move + if (esm.isNextSub("SLCS")) + { + esm.getSubHeader(); + char unknown[12]; + esm.getExact(unknown, 12); + } + if (esm.isNextSub("SLSD")) + { + float read; + esm.getSubHeader(); + // values of variables? + for (int i=0; i mSCRD; // Used in .ess savegames only, screenshot? + std::vector mSCRS; // Used in .ess savegames only, screenshot? + Data mData; int mFormat; std::vector mMaster; From cbf56dbb4750822650dfc5335e78cc577a596c31 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Jan 2015 03:07:24 +0100 Subject: [PATCH 338/404] ESSImport: work on cell fog of war --- apps/essimporter/converter.cpp | 50 ++++++++++++++++++++++++++++++++++ apps/essimporter/converter.hpp | 35 ++++++++++++++++++------ apps/essimporter/importer.cpp | 6 ++-- components/esm/loadcell.cpp | 5 ---- 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 4f36f13ee3..5f73666258 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -37,4 +37,54 @@ namespace ESSImport convertImage(&data[0], data.size(), maph.size, maph.size, Ogre::PF_BYTE_RGB, "map.tga"); } + void ConvertCell::read(ESM::ESMReader &esm) + { + ESM::Cell cell; + std::string id = esm.getHNString("NAME"); + cell.load(esm, false); + + Cell newcell; + newcell.mCell = cell; + + // fog of war + // seems to be a 1-bit pixel format, 16*16 pixels + // TODO: add bleeding of FOW into neighbouring cells (openmw handles this by writing to the textures, + // MW handles it when rendering only) + unsigned char nam8[32]; + if (esm.isNextSub("NAM8")) + { + esm.getHExact(nam8, 32); + + newcell.mFogOfWar.reserve(16*16); + for (int x=0; x<16; ++x) + { + for (int y=0; y<16; ++y) + { + size_t pos = x*16+y; + size_t bytepos = pos/8; + assert(bytepos<32); + int bit = pos%8; + newcell.mFogOfWar.push_back(((nam8[bytepos] >> bit) & (0x1)) ? 0xffffffff : 0x000000ff); + } + } + + std::ostringstream filename; + filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga"; + + convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, Ogre::PF_BYTE_RGBA, filename.str()); + } + + std::vector cellrefs; + while (esm.hasMoreSubs()) + { + CellRef ref; + ref.load (esm); + if (esm.isNextSub("DELE")) + std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + } + + newcell.mRefs = cellrefs; + mCells[id] = newcell; + } + } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 2662f8c752..011508f36d 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "importcrec.hpp" #include "importercontext.hpp" @@ -241,20 +242,36 @@ public: class ConvertCell : public Converter { public: - virtual void read(ESM::ESMReader& esm) + virtual void read(ESM::ESMReader& esm); + + virtual void write(ESM::ESMWriter& esm) { - ESM::Cell cell; - std::string id = esm.getHNString("NAME"); - cell.load(esm, false); - CellRef ref; - while (esm.hasMoreSubs()) + for (std::map::const_iterator it = mCells.begin(); it != mCells.end(); ++it) { - ref.load (esm); - if (esm.isNextSub("DELE")) - std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + const ESM::Cell& cell = it->second.mCell; + esm.startRecord(ESM::REC_CSTA); + ESM::CellState csta; + csta.mHasFogOfWar = 0; + csta.mId = cell.getCellId(); + csta.mId.save(esm); + // TODO csta.mLastRespawn; + // shouldn't be needed if we respawn on global schedule like in original MW + csta.mWaterLevel = cell.mWater; + csta.save(esm); + esm.endRecord(ESM::REC_CSTA); } } +private: + struct Cell + { + ESM::Cell mCell; + std::vector mRefs; + std::vector mFogOfWar; + }; + + std::map mCells; + }; } diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 96b79e029f..b511dfe5e3 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -99,16 +99,18 @@ namespace ESSImport void Importer::compare() { - // data that always changes should be blacklisted + // data that always changes (and/or is already fully decoded) should be blacklisted std::set > blacklist; blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour blacklist.insert(std::make_pair("REFR", "DATA")); // player position + blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war File file1; read(mEssFile, file1); File file2; read(mOutFile, file2); // todo rename variable + // FIXME: use max(size1, size2) for (unsigned int i=0; i globals; - const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index f6649d94ab..e4f847dec2 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -111,11 +111,6 @@ void Cell::loadData(ESMReader &esm) } esm.getHNT(mData, "DATA", 12); - - // ess only, unknown - char nam8[32]; - if (esm.isNextSub("NAM8")) - esm.getHExact(nam8, 32); } void Cell::postLoad(ESMReader &esm) From b66937d6307d07823f890c5a78a6c5c567221be0 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sat, 17 Jan 2015 15:35:58 +1300 Subject: [PATCH 339/404] add #include Required to get build to work on my configuration. --- libs/openengine/ogre/renderer.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 1b18a7b0bf..33a42f8cd8 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -6,6 +6,7 @@ */ #include +#include #include From 017e4cd4effb26b0b1a4a9c64b47c17f4d4e0633 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sat, 17 Jan 2015 18:11:03 +1300 Subject: [PATCH 340/404] OpenCS shows plug-ins with load order issues. 1. FileDiaog in OpenCS now shows warning icon beside .esm./.esp files with load order problems. 2. omwlaucher -> replaced "stop" icon with "warning" icon for files with load order problems. --- components/contentselector/model/contentmodel.cpp | 7 +++---- components/contentselector/model/contentmodel.hpp | 11 ++++++----- components/contentselector/view/contentselector.cpp | 4 +++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 5760a327eb..4a1a69169e 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -6,13 +6,12 @@ #include #include #include -#include -#include #include "components/esm/esmreader.hpp" -ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : +ContentSelectorModel::ContentModel::ContentModel(QObject *parent, QIcon warningIcon) : QAbstractTableModel(parent), + mWarningIcon(warningIcon), mMimeType ("application/omwcontent"), mMimeTypes (QStringList() << mMimeType), mColumnCount (1), @@ -180,7 +179,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int { case Qt::DecorationRole: { - return isLoadOrderError(file) ? QIcon::fromTheme("edit-delete") : QVariant(); + return isLoadOrderError(file) ? mWarningIcon : QVariant(); } case Qt::EditRole: diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 153800dcaa..2ced40f047 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -4,7 +4,7 @@ #include #include #include - +#include #include "loadordererror.hpp" namespace ContentSelectorModel @@ -23,7 +23,7 @@ namespace ContentSelectorModel { Q_OBJECT public: - explicit ContentModel(QObject *parent = 0); + explicit ContentModel(QObject *parent, QIcon warningIcon); ~ContentModel(); void setEncoding(const QString &encoding); @@ -57,6 +57,9 @@ namespace ContentSelectorModel void refreshModel(); + /// Checks all plug-ins for load order errors and updates mPluginsWithLoadOrderError with plug-ins with issues + void checkForLoadOrderErrors(); + private: void addFile(EsmFile *file); @@ -65,9 +68,6 @@ namespace ContentSelectorModel void sortFiles(); - /// Checks all plug-ins for load order errors and updates mPluginsWithLoadOrderError with plug-ins with issues - void checkForLoadOrderErrors(); - /// Checks a specific plug-in for load order errors /// \return all errors found for specific plug-in QList checkForLoadOrderErrors(const EsmFile *file, int row) const; @@ -82,6 +82,7 @@ namespace ContentSelectorModel QSet mPluginsWithLoadOrderError; QTextCodec *mCodec; QString mEncoding; + QIcon mWarningIcon; public: diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 770d45c862..521462d922 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -24,7 +24,8 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : void ContentSelectorView::ContentSelector::buildContentModel() { - mContentModel = new ContentSelectorModel::ContentModel(this); + QIcon warningIcon(ui.addonView->style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(QSize(16, 15))); + mContentModel = new ContentSelectorModel::ContentModel(this, warningIcon); } void ContentSelectorView::ContentSelector::buildGameFileView() @@ -161,6 +162,7 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i oldIndex = index; model->setData(model->index(index, 0), true, Qt::UserRole + 1); + mContentModel->checkForLoadOrderErrors(); } if (proxy) From 71700d2cb119934122dc7fc9f8ffae725cb2fce1 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 18 Jan 2015 11:55:58 +1300 Subject: [PATCH 341/404] Bugfix: OpenCS segfault when plug-in dependency not found. When a file that a plug-in depends on cannot be found, the OpenCS file dialog crashes. Similar problem exists in omwlauncher's "Data Files" dialog. --- components/contentselector/model/contentmodel.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 4a1a69169e..3f1feac619 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -597,13 +597,16 @@ QList ContentSelectorModel::ContentModel:: { errors.append(LoadOrderError(LoadOrderError::ErrorCode_MissingDependency, dependentfileName)); } - if (!isChecked(dependentFile->filePath())) - { - errors.append(LoadOrderError(LoadOrderError::ErrorCode_InactiveDependency, dependentfileName)); - } - if (row < indexFromItem(dependentFile).row()) + else { - errors.append(LoadOrderError(LoadOrderError::ErrorCode_LoadOrder, dependentfileName)); + if (!isChecked(dependentFile->filePath())) + { + errors.append(LoadOrderError(LoadOrderError::ErrorCode_InactiveDependency, dependentfileName)); + } + if (row < indexFromItem(dependentFile).row()) + { + errors.append(LoadOrderError(LoadOrderError::ErrorCode_LoadOrder, dependentfileName)); + } } } return errors; From c8ed24cc84f5112d7784d957afd1f6e745225c87 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 Jan 2015 16:13:52 +0100 Subject: [PATCH 342/404] ESSImport: creature CellRefs work, need probing to find ref type --- apps/essimporter/converter.cpp | 60 ++++++++++++++++++++++++++++ apps/essimporter/converter.hpp | 22 ++-------- apps/essimporter/importcellref.cpp | 9 +++++ apps/essimporter/importcellref.hpp | 2 + apps/essimporter/importercontext.hpp | 10 ++++- 5 files changed, 84 insertions(+), 19 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 5f73666258..1ad60d7ba7 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -2,6 +2,8 @@ #include +#include + namespace { @@ -81,10 +83,68 @@ namespace ESSImport ref.load (esm); if (esm.isNextSub("DELE")) std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + cellrefs.push_back(ref); } newcell.mRefs = cellrefs; mCells[id] = newcell; } + void ConvertCell::write(ESM::ESMWriter &esm) + { + for (std::map::const_iterator it = mCells.begin(); it != mCells.end(); ++it) + { + const ESM::Cell& cell = it->second.mCell; + esm.startRecord(ESM::REC_CSTA); + ESM::CellState csta; + csta.mHasFogOfWar = 0; + csta.mId = cell.getCellId(); + csta.mId.save(esm); + // TODO csta.mLastRespawn; + // shouldn't be needed if we respawn on global schedule like in original MW + csta.mWaterLevel = cell.mWater; + csta.save(esm); + + for (std::vector::const_iterator refIt = it->second.mRefs.begin(); refIt != it->second.mRefs.end(); ++refIt) + { + const CellRef& cellref = *refIt; + ESM::CellRef out; + out.blank(); + + if (cellref.mIndexedRefId.size() < 8) + { + std::cerr << "CellRef with no index?" << std::endl; + continue; + } + std::stringstream stream; + stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); + int refIndex; + stream >> refIndex; + + out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); + + std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (crecIt != mContext->mCreatureChanges.end()) + { + std::cerr << "Can't' find CREC for " << refIndex << " " << out.mRefID << std::endl; + continue; + } + + ESM::CreatureState objstate; + objstate.mCount = 1; + objstate.mEnabled = cellref.mEnabled; + objstate.mHasLocals = 0; + objstate.mLocalRotation[0] = objstate.mLocalRotation[1] = objstate.mLocalRotation[2] = 0; + objstate.mPosition = cellref.mPos; + objstate.mRef = out; + objstate.mRef.mRefNum = cellref.mRefNum; + esm.writeHNT ("OBJE", ESM::REC_CREA); + objstate.save(esm); + } + + esm.endRecord(ESM::REC_CSTA); + } + } + } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 011508f36d..71eb3ab9fd 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -153,6 +153,7 @@ public: { mContext->mPlayer.mObject.mNpcStats.mReputation = npcc.mNPDT.mReputation; } + //mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), crec)); } }; @@ -230,6 +231,8 @@ public: std::string id = esm.getHNString("NAME"); CREC crec; crec.load(esm); + + mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec)); } }; @@ -243,24 +246,7 @@ class ConvertCell : public Converter { public: virtual void read(ESM::ESMReader& esm); - - virtual void write(ESM::ESMWriter& esm) - { - for (std::map::const_iterator it = mCells.begin(); it != mCells.end(); ++it) - { - const ESM::Cell& cell = it->second.mCell; - esm.startRecord(ESM::REC_CSTA); - ESM::CellState csta; - csta.mHasFogOfWar = 0; - csta.mId = cell.getCellId(); - csta.mId.save(esm); - // TODO csta.mLastRespawn; - // shouldn't be needed if we respawn on global schedule like in original MW - csta.mWaterLevel = cell.mWater; - csta.save(esm); - esm.endRecord(ESM::REC_CSTA); - } - } + virtual void write(ESM::ESMWriter& esm); private: struct Cell diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index f59358d8b8..51e771081c 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -9,6 +9,11 @@ namespace ESSImport { esm.getHNT(mRefNum.mIndex, "FRMR"); // TODO: adjust RefNum + // this is required since openmw supports more than 255 content files + int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24; + mRefNum.mContentFile = pluginIndex-1; + mRefNum.mIndex &= 0x00ffffff; + mIndexedRefId = esm.getHNString("NAME"); esm.getHNT(mACDT, "ACDT"); @@ -22,6 +27,10 @@ namespace ESSImport if (esm.isNextSub("ND3D")) esm.skipHSub(); + + mEnabled = true; + esm.getHNOT(mEnabled, "ZNAM"); + esm.getHNOT(mPos, "DATA", 24); } diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp index f06278ba50..72c5af2570 100644 --- a/apps/essimporter/importcellref.hpp +++ b/apps/essimporter/importcellref.hpp @@ -25,6 +25,8 @@ namespace ESSImport ESM::Position mPos; + bool mEnabled; + void load(ESM::ESMReader& esm); }; diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index e9af776788..6f26e8329d 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -1,12 +1,16 @@ #ifndef OPENMW_ESSIMPORT_CONTEXT_H #define OPENMW_ESSIMPORT_CONTEXT_H +#include + #include +#include #include "importnpcc.hpp" +#include "importcrec.hpp" #include "importplayer.hpp" -#include + namespace ESSImport { @@ -20,6 +24,10 @@ namespace ESSImport int mDay, mMonth, mYear; float mHour; + // key + std::map, CREC> mCreatureChanges; + std::map, NPCC> mNpcChanges; + Context() { mPlayer.mAutoMove = 0; From 08ad4d73bbf9873887a8c6716c37b641e2603026 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 Jan 2015 19:59:29 +0100 Subject: [PATCH 343/404] ESSImport: player is placed in correct cell, npc cellrefs work --- apps/essimporter/CMakeLists.txt | 4 ++- apps/essimporter/convertacdt.cpp | 42 ++++++++++++++++++++++ apps/essimporter/convertacdt.hpp | 22 ++++++++++++ apps/essimporter/converter.cpp | 54 +++++++++++++++++++++------- apps/essimporter/converter.hpp | 40 +++++---------------- apps/essimporter/convertnpcc.cpp | 22 ++++++++++++ apps/essimporter/convertnpcc.hpp | 15 ++++++++ apps/essimporter/importacdt.cpp | 28 +++++++++++++++ apps/essimporter/importacdt.hpp | 24 +++++++++++++ apps/essimporter/importcellref.cpp | 19 +++++----- apps/essimporter/importcellref.hpp | 4 ++- apps/essimporter/importer.cpp | 19 ++++++++-- apps/essimporter/importercontext.hpp | 30 +++------------- apps/essimporter/importnpcc.cpp | 25 ++++++++++--- apps/essimporter/importnpcc.hpp | 12 +++++-- apps/essimporter/importplayer.cpp | 14 +++----- apps/essimporter/importplayer.hpp | 5 +-- components/esm/creaturestate.cpp | 8 ++++- components/esm/creaturestate.hpp | 3 ++ components/esm/creaturestats.cpp | 36 +++++++++++++++++-- components/esm/creaturestats.hpp | 3 ++ components/esm/npcstate.cpp | 9 ++++- components/esm/npcstate.hpp | 3 ++ components/esm/npcstats.cpp | 15 ++++++++ components/esm/npcstats.hpp | 3 ++ components/esm/objectstate.cpp | 16 ++++++++- components/esm/objectstate.hpp | 5 ++- 27 files changed, 370 insertions(+), 110 deletions(-) create mode 100644 apps/essimporter/convertacdt.cpp create mode 100644 apps/essimporter/convertacdt.hpp create mode 100644 apps/essimporter/convertnpcc.cpp create mode 100644 apps/essimporter/convertnpcc.hpp create mode 100644 apps/essimporter/importacdt.cpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 231e31fd84..257a466ee2 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -5,9 +5,11 @@ set(ESSIMPORTER_FILES importnpcc.cpp importcrec.cpp importcellref.cpp - importacdt.hpp + importacdt.cpp importercontext.cpp converter.cpp + convertacdt.cpp + convertnpcc.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/convertacdt.cpp b/apps/essimporter/convertacdt.cpp new file mode 100644 index 0000000000..81ab61084f --- /dev/null +++ b/apps/essimporter/convertacdt.cpp @@ -0,0 +1,42 @@ +#include "convertacdt.hpp" + +namespace ESSImport +{ + + int translateDynamicIndex(int mwIndex) + { + if (mwIndex == 1) + return 2; + else if (mwIndex == 2) + return 1; + return mwIndex; + } + + void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats) + { + for (int i=0; i<3; ++i) + { + int writeIndex = translateDynamicIndex(i); + cStats.mDynamic[writeIndex].mBase = acdt.mDynamic[i][1]; + cStats.mDynamic[writeIndex].mMod = acdt.mDynamic[i][1]; + cStats.mDynamic[writeIndex].mCurrent = acdt.mDynamic[i][0]; + } + for (int i=0; i<8; ++i) + { + cStats.mAttributes[i].mBase = acdt.mAttributes[i][1]; + cStats.mAttributes[i].mMod = acdt.mAttributes[i][0]; + cStats.mAttributes[i].mCurrent = acdt.mAttributes[i][0]; + } + } + + void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats) + { + for (int i=0; i +#include +#include + +#include "importacdt.hpp" + +namespace ESSImport +{ + + // OpenMW uses Health,Magicka,Fatigue, MW uses Health,Fatigue,Magicka + int translateDynamicIndex(int mwIndex); + + + void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats); + + void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats); +} + +#endif diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 1ad60d7ba7..2e7a954e08 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -43,8 +43,15 @@ namespace ESSImport { ESM::Cell cell; std::string id = esm.getHNString("NAME"); + cell.mName = id; cell.load(esm, false); + // note if the player is in a nameless exterior cell, we will assign the cellId later based on player position + if (id == mContext->mPlayerCellName) + { + mContext->mPlayer.mCellId = cell.getCellId(); + } + Cell newcell; newcell.mCell = cell; @@ -55,7 +62,12 @@ namespace ESSImport unsigned char nam8[32]; if (esm.isNextSub("NAM8")) { - esm.getHExact(nam8, 32); + esm.getSubHeader(); + // FIXME: different size in interior cells for some reason + if (esm.getSubSize() == 36) + esm.skip(4); + + esm.getExact(nam8, 32); newcell.mFogOfWar.reserve(16*16); for (int x=0; x<16; ++x) @@ -87,6 +99,8 @@ namespace ESSImport } newcell.mRefs = cellrefs; + + // FIXME: map by ID for exterior cells mCells[id] = newcell; } @@ -127,20 +141,36 @@ namespace ESSImport std::make_pair(refIndex, out.mRefID)); if (crecIt != mContext->mCreatureChanges.end()) { - std::cerr << "Can't' find CREC for " << refIndex << " " << out.mRefID << std::endl; + ESM::CreatureState objstate; + objstate.blank(); + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + objstate.mEnabled = cellref.mEnabled; + objstate.mPosition = cellref.mPos; + objstate.mRef = out; + objstate.mRef.mRefNum = cellref.mRefNum; + esm.writeHNT ("OBJE", ESM::REC_CREA); + objstate.save(esm); + continue; + } + + std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (npccIt != mContext->mNpcChanges.end()) + { + ESM::NpcState objstate; + objstate.blank(); + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertNpcData(cellref.mActorData, objstate.mNpcStats); + objstate.mEnabled = cellref.mEnabled; + objstate.mPosition = cellref.mPos; + objstate.mRef = out; + objstate.mRef.mRefNum = cellref.mRefNum; + esm.writeHNT ("OBJE", ESM::REC_NPC_); + objstate.save(esm); continue; } - ESM::CreatureState objstate; - objstate.mCount = 1; - objstate.mEnabled = cellref.mEnabled; - objstate.mHasLocals = 0; - objstate.mLocalRotation[0] = objstate.mLocalRotation[1] = objstate.mLocalRotation[2] = 0; - objstate.mPosition = cellref.mPos; - objstate.mRef = out; - objstate.mRef.mRefNum = cellref.mRefNum; - esm.writeHNT ("OBJE", ESM::REC_CREA); - objstate.save(esm); + std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl; } esm.endRecord(ESM::REC_CSTA); diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 71eb3ab9fd..7e91669022 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -14,6 +14,9 @@ #include "importercontext.hpp" #include "importcellref.hpp" +#include "convertacdt.hpp" +#include "convertnpcc.hpp" + namespace ESSImport { @@ -151,9 +154,10 @@ public: npcc.load(esm); if (id == "PlayerSaveGame") { - mContext->mPlayer.mObject.mNpcStats.mReputation = npcc.mNPDT.mReputation; + convertNPCC(npcc, mContext->mPlayer.mObject); } - //mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), crec)); + else + mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), npcc)); } }; @@ -168,36 +172,10 @@ public: mContext->mPlayer.mObject.mPosition = refr.mPos; ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats; - for (int i=0; i<3; ++i) - { - int writeIndex = translateDynamicIndex(i); - cStats.mDynamic[writeIndex].mBase = refr.mACDT.mDynamic[i][1]; - cStats.mDynamic[writeIndex].mMod = refr.mACDT.mDynamic[i][1]; - cStats.mDynamic[writeIndex].mCurrent = refr.mACDT.mDynamic[i][0]; - } - for (int i=0; i<8; ++i) - { - cStats.mAttributes[i].mBase = refr.mACDT.mAttributes[i][1]; - cStats.mAttributes[i].mMod = refr.mACDT.mAttributes[i][0]; - cStats.mAttributes[i].mCurrent = refr.mACDT.mAttributes[i][0]; - } - ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats; - for (int i=0; imPlayer.mObject.mNpcStats; + convertNpcData(refr.mActorData, npcStats); } }; diff --git a/apps/essimporter/convertnpcc.cpp b/apps/essimporter/convertnpcc.cpp new file mode 100644 index 0000000000..11e665bfd0 --- /dev/null +++ b/apps/essimporter/convertnpcc.cpp @@ -0,0 +1,22 @@ +#include "convertnpcc.hpp" + +namespace ESSImport +{ + + void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) + { + npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; + + for (std::vector::const_iterator it = npcc.mInventory.begin(); + it != npcc.mInventory.end(); ++it) + { + ESM::ObjectState obj; + obj.blank(); + obj.mRef.mRefID = it->mId; + obj.mRef.mCharge = it->mCondition; + + // Don't know type of object :( change save format? + // npcState.mInventory.mItems.push_back(std::make_pair(obj, std::make_pair(0,0))); + } + } +} diff --git a/apps/essimporter/convertnpcc.hpp b/apps/essimporter/convertnpcc.hpp new file mode 100644 index 0000000000..eb12d8f3bc --- /dev/null +++ b/apps/essimporter/convertnpcc.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTNPCC_H +#define OPENMW_ESSIMPORT_CONVERTNPCC_H + +#include "importnpcc.hpp" + +#include + +namespace ESSImport +{ + + void convertNPCC (const NPCC& npcc, ESM::NpcState& npcState); + +} + +#endif diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp new file mode 100644 index 0000000000..b3e399f9fc --- /dev/null +++ b/apps/essimporter/importacdt.cpp @@ -0,0 +1,28 @@ +#include "importacdt.hpp" + +#include + +namespace ESSImport +{ + + void ActorData::load(ESM::ESMReader &esm) + { + esm.getHNT(mACDT, "ACDT"); + + ACSC acsc; + esm.getHNOT(acsc, "ACSC"); + esm.getHNOT(acsc, "ACSL"); + + if (esm.isNextSub("CHRD")) // npc only + esm.getHExact(mSkills, 27*2*sizeof(int)); + + if (esm.isNextSub("CRED")) // creature only + esm.getHExact(mCombatStats, 3*2*sizeof(int)); + + mScript = esm.getHNOString("SCRI"); + + if (esm.isNextSub("ND3D")) + esm.skipHSub(); + } + +} diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index 1d79f899e3..6efae9dc57 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -1,9 +1,17 @@ #ifndef OPENMW_ESSIMPORT_ACDT_H #define OPENMW_ESSIMPORT_ACDT_H +#include + +namespace ESM +{ + struct ESMReader; +} + namespace ESSImport { + /// Actor data, shared by (at least) REFR and CellRef struct ACDT { @@ -14,6 +22,22 @@ namespace ESSImport unsigned char mUnknown3[120]; }; + struct ActorData + { + ACDT mACDT; + + int mSkills[27][2]; + + // creature combat stats, base and modified + // I think these can be ignored in the conversion, because it is not possible + // to change them ingame + int mCombatStats[3][2]; + + std::string mScript; + + void load(ESM::ESMReader& esm); + }; + /// Unknown, shared by (at least) REFR and CellRef struct ACSC { diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 51e771081c..76356769aa 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -16,22 +16,19 @@ namespace ESSImport mIndexedRefId = esm.getHNString("NAME"); - esm.getHNT(mACDT, "ACDT"); + // the following two occur in ESM::CellRef too (Charge and Gold), + // but may have entirely different meanings here + int intv; + esm.getHNOT(intv, "INTV"); + int nam9; + esm.getHNOT(nam9, "NAM9"); - ACSC acsc; - esm.getHNOT(acsc, "ACSC"); - esm.getHNOT(acsc, "ACSL"); - - if (esm.isNextSub("CRED")) - esm.skipHSub(); - - if (esm.isNextSub("ND3D")) - esm.skipHSub(); + mActorData.load(esm); mEnabled = true; esm.getHNOT(mEnabled, "ZNAM"); - esm.getHNOT(mPos, "DATA", 24); + esm.getHNT(mPos, "DATA", 24); } } diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp index 72c5af2570..77763d4343 100644 --- a/apps/essimporter/importcellref.hpp +++ b/apps/essimporter/importcellref.hpp @@ -21,10 +21,12 @@ namespace ESSImport std::string mIndexedRefId; ESM::RefNum mRefNum; - ACDT mACDT; + ActorData mActorData; ESM::Position mPos; + std::string mScript; + bool mEnabled; void load(ESM::ESMReader& esm); diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index b511dfe5e3..572489d9a7 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -185,6 +185,9 @@ namespace ESSImport Context context; + const ESM::Header& header = esm.getHeader(); + context.mPlayerCellName = header.mGameData.mCurrentCell.toString(); + const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; @@ -234,8 +237,6 @@ namespace ESSImport } } - const ESM::Header& header = esm.getHeader(); - ESM::ESMWriter writer; writer.setFormat (ESM::Header::CurrentFormat); @@ -247,6 +248,11 @@ namespace ESSImport writer.setAuthor(""); writer.setDescription(""); writer.setRecordCount (0); + + for (std::vector::const_iterator it = header.mMaster.begin(); + it != header.mMaster.end(); ++it) + writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0 + writer.save (stream); ESM::SavedGame profile; @@ -298,6 +304,15 @@ namespace ESSImport } writer.startRecord(ESM::REC_PLAY); + if (context.mPlayer.mCellId.mPaged) + { + // exterior cell -> determine cell coordinates based on position + const int cellSize = 8192; + int cellX = std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize); + int cellY = std::floor(context.mPlayer.mObject.mPosition.pos[1]/cellSize); + context.mPlayer.mCellId.mIndex.mX = cellX; + context.mPlayer.mCellId.mIndex.mY = cellY; + } context.mPlayer.save(writer); writer.endRecord(ESM::REC_PLAY); } diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 6f26e8329d..9183e61796 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -17,6 +17,9 @@ namespace ESSImport struct Context { + // set from the TES3 header + std::string mPlayerCellName; + ESM::Player mPlayer; ESM::NPC mPlayerBase; std::string mCustomPlayerClassName; @@ -38,33 +41,8 @@ namespace ESSImport //mPlayer.mLastKnownExteriorPosition mPlayer.mHasMark = 0; // TODO mPlayer.mCurrentCrimeId = 0; // TODO - mPlayer.mObject.mCount = 1; - mPlayer.mObject.mEnabled = 1; - mPlayer.mObject.mHasLocals = false; + mPlayer.mObject.blank(); mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame - mPlayer.mObject.mCreatureStats.mHasAiSettings = true; - mPlayer.mObject.mCreatureStats.mDead = false; - mPlayer.mObject.mCreatureStats.mDied = false; - mPlayer.mObject.mCreatureStats.mKnockdown = false; - mPlayer.mObject.mCreatureStats.mKnockdownOneFrame = false; - mPlayer.mObject.mCreatureStats.mKnockdownOverOneFrame = false; - mPlayer.mObject.mCreatureStats.mHitRecovery = false; - mPlayer.mObject.mCreatureStats.mBlock = false; - mPlayer.mObject.mCreatureStats.mMovementFlags = 0; - mPlayer.mObject.mCreatureStats.mAttackStrength = 0.f; - mPlayer.mObject.mCreatureStats.mFallHeight = 0.f; - mPlayer.mObject.mCreatureStats.mRecalcDynamicStats = false; - mPlayer.mObject.mCreatureStats.mDrawState = 0; - mPlayer.mObject.mCreatureStats.mDeathAnimation = 0; - mPlayer.mObject.mNpcStats.mIsWerewolf = false; - mPlayer.mObject.mNpcStats.mTimeToStartDrowning = 20; - mPlayer.mObject.mNpcStats.mLevelProgress = 0; - mPlayer.mObject.mNpcStats.mDisposition = 0; - mPlayer.mObject.mNpcStats.mTimeToStartDrowning = 20; - mPlayer.mObject.mNpcStats.mReputation = 0; - mPlayer.mObject.mNpcStats.mCrimeId = -1; - mPlayer.mObject.mNpcStats.mWerewolfKills = 0; - mPlayer.mObject.mNpcStats.mProfit = 0; } }; diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp index 192fe5da41..4cfc816c91 100644 --- a/apps/essimporter/importnpcc.cpp +++ b/apps/essimporter/importnpcc.cpp @@ -7,12 +7,29 @@ namespace ESSImport void NPCC::load(ESM::ESMReader &esm) { + mIndex = 0; + esm.getHNOT(mIndex, "INDX"); + esm.getHNT(mNPDT, "NPDT"); - // container: - // XIDX - // XHLT - condition - // WIDX - equipping? + while (esm.isNextSub("NPCO")) + { + InventoryItem item; + item.mId = esm.getHString(); + + if (esm.isNextSub("XIDX")) + esm.skipHSub(); + + item.mCondition = -1; + esm.getHNOT(item.mCondition, "XHLT"); + mInventory.push_back(item); + } + + while (esm.isNextSub("WIDX")) + { + // equipping? + esm.skipHSub(); + } } } diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index 12135850f6..d10048fe0d 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -1,7 +1,6 @@ #ifndef OPENMW_ESSIMPORT_NPCC_H #define OPENMW_ESSIMPORT_NPCC_H -#include #include namespace ESM @@ -12,7 +11,7 @@ namespace ESM namespace ESSImport { - struct NPCC : public ESM::NPC + struct NPCC { struct NPDT { @@ -21,6 +20,15 @@ namespace ESSImport unsigned char unknown2[5]; } mNPDT; + struct InventoryItem + { + std::string mId; + int mCondition; + }; + std::vector mInventory; + + int mIndex; + void load(ESM::ESMReader &esm); }; diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index 81f81e764e..f8c78dab01 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -14,16 +14,7 @@ namespace ESSImport if (esm.isNextSub("STPR")) esm.skipHSub(); // ESS TODO - esm.getHNT(mACDT, "ACDT"); - - ACSC acsc; - esm.getHNOT(acsc, "ACSC"); - esm.getHNOT(acsc, "ACSL"); - - esm.getHNExact(mSkills, 27*2*sizeof(int), "CHRD"); - - if (esm.isNextSub("ND3D")) - esm.skipHSub(); // ESS TODO (1 byte) + mActorData.load(esm); esm.getHNOT(mPos, "DATA", 24); } @@ -45,6 +36,9 @@ namespace ESSImport if (esm.isNextSub("ENAM")) esm.skipHSub(); + if (esm.isNextSub("LNAM")) + esm.skipHSub(); + while (esm.isNextSub("FNAM")) { FNAM fnam; diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index 8a0a087a53..2493fcdad0 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -21,15 +21,12 @@ namespace ESSImport /// Player-agnostic player data struct REFR { - ACDT mACDT; + ActorData mActorData; std::string mRefID; ESM::Position mPos; ESM::RefNum mRefNum; - int mSkills[27][2]; - float mAttributes[8][2]; - void load(ESM::ESMReader& esm); }; diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp index 9e9b561026..c2838f78db 100644 --- a/components/esm/creaturestate.cpp +++ b/components/esm/creaturestate.cpp @@ -17,4 +17,10 @@ void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const mInventory.save (esm); mCreatureStats.save (esm); -} \ No newline at end of file +} + +void ESM::CreatureState::blank() +{ + ObjectState::blank(); + mCreatureStats.blank(); +} diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index 604c2f3a70..9a3d41daae 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -14,6 +14,9 @@ namespace ESM InventoryState mInventory; CreatureStats mCreatureStats; + /// Initialize to default state + void blank(); + virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 1fdce703c4..a1ef7eb0e1 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -218,6 +218,38 @@ void ESM::CreatureStats::save (ESMWriter &esm) const } esm.writeHNT("AISE", mHasAiSettings); - for (int i=0; i<4; ++i) - mAiSettings[i].save(esm); + if (mHasAiSettings) + { + for (int i=0; i<4; ++i) + mAiSettings[i].save(esm); + } +} + +void ESM::CreatureStats::blank() +{ + mTradeTime.mHour = 0; + mTradeTime.mDay = 0; + mGoldPool = 0; + mActorId = 0; + mHasAiSettings = false; + mDead = false; + mDied = false; + mMurdered = false; + mFriendlyHits = 0; + mTalkedTo = false; + mAlarmed = false; + mAttacked = false; + mAttackingOrSpell = false; + mKnockdown = false; + mKnockdownOneFrame = false; + mKnockdownOverOneFrame = false; + mHitRecovery = false; + mBlock = false; + mMovementFlags = 0; + mAttackStrength = 0.f; + mFallHeight = 0.f; + mRecalcDynamicStats = false; + mDrawState = 0; + mDeathAnimation = 0; + mLevel = 1; } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 680577f932..91ee983332 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -66,6 +66,9 @@ namespace ESM SpellState mSpells; ActiveSpells mActiveSpells; + /// Initialize to default state + void blank(); + void load (ESMReader &esm); void save (ESMWriter &esm) const; }; diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp index e59ec3e268..6134193c24 100644 --- a/components/esm/npcstate.cpp +++ b/components/esm/npcstate.cpp @@ -21,4 +21,11 @@ void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const mNpcStats.save (esm); mCreatureStats.save (esm); -} \ No newline at end of file +} + +void ESM::NpcState::blank() +{ + ObjectState::blank(); + mNpcStats.blank(); + mCreatureStats.blank(); +} diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index 39858d5533..b90cd85e6a 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -16,6 +16,9 @@ namespace ESM NpcStats mNpcStats; CreatureStats mCreatureStats; + /// Initialize to default state + void blank(); + virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 4ba0ce7e3f..e305ddab09 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -150,3 +150,18 @@ void ESM::NpcStats::save (ESMWriter &esm) const if (mCrimeId != -1) esm.writeHNT ("CRID", mCrimeId); } + +void ESM::NpcStats::blank() +{ + mIsWerewolf = false; + mDisposition = 0; + mBounty = 0; + mReputation = 0; + mWerewolfKills = 0; + mProfit = 0; + mLevelProgress = 0; + for (int i=0; i<8; ++i) + mSkillIncrease[i] = 0; + mTimeToStartDrowning = 20; + mCrimeId = -1; +} diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index f6e9c1e588..a8ec4cf444 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -47,6 +47,9 @@ namespace ESM float mTimeToStartDrowning; int mCrimeId; + /// Initialize to default state + void blank(); + void load (ESMReader &esm); void save (ESMWriter &esm) const; }; diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index be00f3ef6e..f7755f8cbe 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -48,4 +48,18 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const } } -ESM::ObjectState::~ObjectState() {} \ No newline at end of file +void ESM::ObjectState::blank() +{ + mRef.blank(); + mHasLocals = 0; + mEnabled = false; + mCount = 1; + for (int i=0;i<3;++i) + { + mPosition.pos[i] = 0; + mPosition.rot[i] = 0; + mLocalRotation[i] = 0; + } +} + +ESM::ObjectState::~ObjectState() {} diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 9c9ca5f2e8..5b05e0949e 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -29,8 +29,11 @@ namespace ESM virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; + /// Initialize to default state + void blank(); + virtual ~ObjectState(); }; } -#endif \ No newline at end of file +#endif From 19ed047dec4763362556141e876b12945a4894b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 Jan 2015 22:52:11 +0100 Subject: [PATCH 344/404] ESSImport: add some subrecords to CellRef and others, most files should load now, importacdt/cellref class structure need some refactoring --- apps/essimporter/CMakeLists.txt | 1 + apps/essimporter/converter.cpp | 34 +++++++++++-- apps/essimporter/converter.hpp | 16 ++++--- apps/essimporter/convertnpcc.cpp | 12 ----- apps/essimporter/importacdt.cpp | 71 +++++++++++++++++++++++++++- apps/essimporter/importcellref.cpp | 24 ++++++---- apps/essimporter/importcrec.cpp | 7 +++ apps/essimporter/importcrec.hpp | 4 ++ apps/essimporter/importinventory.cpp | 46 ++++++++++++++++++ apps/essimporter/importinventory.hpp | 31 ++++++++++++ apps/essimporter/importnpcc.cpp | 24 +++------- apps/essimporter/importnpcc.hpp | 11 ++--- apps/essimporter/importplayer.cpp | 28 +++++++++-- apps/essimporter/importplayer.hpp | 2 + components/esm/cellref.cpp | 5 ++ components/esm/cellref.hpp | 3 ++ 16 files changed, 261 insertions(+), 58 deletions(-) create mode 100644 apps/essimporter/importinventory.cpp create mode 100644 apps/essimporter/importinventory.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 257a466ee2..3d3e0860c1 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -6,6 +6,7 @@ set(ESSIMPORTER_FILES importcrec.cpp importcellref.cpp importacdt.cpp + importinventory.cpp importercontext.cpp converter.cpp convertacdt.cpp diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 2e7a954e08..c51e91ed7c 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -60,12 +60,17 @@ namespace ESSImport // TODO: add bleeding of FOW into neighbouring cells (openmw handles this by writing to the textures, // MW handles it when rendering only) unsigned char nam8[32]; - if (esm.isNextSub("NAM8")) + // exterior has 1 NAM8, interior can have multiple ones, and have an extra 4 byte flag at the start + // (probably offset of that specific fog texture?) + while (esm.isNextSub("NAM8")) { esm.getSubHeader(); - // FIXME: different size in interior cells for some reason + if (esm.getSubSize() == 36) + { + // flag on interiors esm.skip(4); + } esm.getExact(nam8, 32); @@ -82,10 +87,24 @@ namespace ESSImport } } - std::ostringstream filename; - filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga"; + if (cell.isExterior()) + { + std::ostringstream filename; + filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga"; + + convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, Ogre::PF_BYTE_RGBA, filename.str()); + } + } - convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, Ogre::PF_BYTE_RGBA, filename.str()); + // moved reference, not handled yet + // NOTE: MVRF can also occur in within normal references (importcellref.cpp)? + // this does not match the ESM file implementation, + // verify if that can happen with ESM files too + while (esm.isNextSub("MVRF")) + { + esm.skipHSub(); // skip MVRF + esm.getSubName(); + esm.skipHSub(); // skip CNDT } std::vector cellrefs; @@ -94,7 +113,11 @@ namespace ESSImport CellRef ref; ref.load (esm); if (esm.isNextSub("DELE")) + { + // strangely this can be e.g. 52 instead of just 1, std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + esm.skipHSub(); + } cellrefs.push_back(ref); } @@ -148,6 +171,7 @@ namespace ESSImport objstate.mPosition = cellref.mPos; objstate.mRef = out; objstate.mRef.mRefNum = cellref.mRefNum; + // FIXME: change save format to not require object type, instead look up it up using the RefId esm.writeHNT ("OBJE", ESM::REC_CREA); objstate.save(esm); continue; diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 7e91669022..3f8b4cbbee 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -83,13 +83,17 @@ public: // this is always the player ESM::NPC npc; std::string id = esm.getHNString("NAME"); - assert (id == "player"); npc.load(esm); - mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; - mContext->mPlayerBase = npc; - std::map empty; - for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) - mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + if (id != "player") // seems to occur sometimes, with "chargen X" names + std::cerr << "non-player NPC record: " << id << std::endl; + else + { + mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; + mContext->mPlayerBase = npc; + std::map empty; + for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) + mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + } } }; diff --git a/apps/essimporter/convertnpcc.cpp b/apps/essimporter/convertnpcc.cpp index 11e665bfd0..fdf96c15a3 100644 --- a/apps/essimporter/convertnpcc.cpp +++ b/apps/essimporter/convertnpcc.cpp @@ -6,17 +6,5 @@ namespace ESSImport void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) { npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; - - for (std::vector::const_iterator it = npcc.mInventory.begin(); - it != npcc.mInventory.end(); ++it) - { - ESM::ObjectState obj; - obj.blank(); - obj.mRef.mRefID = it->mId; - obj.mRef.mCharge = it->mCondition; - - // Don't know type of object :( change save format? - // npcState.mInventory.mItems.push_back(std::make_pair(obj, std::make_pair(0,0))); - } } } diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index b3e399f9fc..5bec5bd823 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -2,17 +2,73 @@ #include +#include + namespace ESSImport { void ActorData::load(ESM::ESMReader &esm) { - esm.getHNT(mACDT, "ACDT"); + // unsure at which point between NAME and ESM::CellRef + if (esm.isNextSub("MNAM")) + esm.skipHSub(); + + if (esm.isNextSub("ACTN")) + esm.skipHSub(); + + if (esm.isNextSub("STPR")) + esm.skipHSub(); + + ESM::CellRef bla; + bla.ESM::CellRef::loadData(esm); + + // FIXME: actually should be required for all actors?, but ActorData is currently in base CellRef + esm.getHNOT(mACDT, "ACDT"); ACSC acsc; esm.getHNOT(acsc, "ACSC"); esm.getHNOT(acsc, "ACSL"); + if (esm.isNextSub("CSTN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + if (esm.isNextSub("LSTN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + // unsure at which point between LSTN and TGTN + if (esm.isNextSub("CSHN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + // unsure if before or after CSTN/LSTN + if (esm.isNextSub("LSHN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + while (esm.isNextSub("TGTN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + while (esm.isNextSub("FGTN")) + esm.getHString(); // fight target? + + // unsure at which point between FGTN and CHRD + if (esm.isNextSub("PWPC")) + esm.skipHSub(); + if (esm.isNextSub("PWPS")) + esm.skipHSub(); + + if (esm.isNextSub("WNAM")) + { + esm.skipHSub(); // seen values: "ancestor guardian", "bound dagger_en". Summoned creature / bound weapons? + + if (esm.isNextSub("XNAM")) + { + // "demon tanto", probably the ID of spell/item that created the bound weapon/crature? + esm.skipHSub(); + } + + if (esm.isNextSub("YNAM")) + esm.skipHSub(); // 4 byte, 0 + } + if (esm.isNextSub("CHRD")) // npc only esm.getHExact(mSkills, 27*2*sizeof(int)); @@ -21,8 +77,21 @@ namespace ESSImport mScript = esm.getHNOString("SCRI"); + // script variables? + if (!mScript.empty()) + { + if (esm.isNextSub("SLCS")) + esm.skipHSub(); + if (esm.isNextSub("SLSD")) // Short Data? + esm.skipHSub(); + if (esm.isNextSub("SLFD")) // Float Data? + esm.skipHSub(); + } + if (esm.isNextSub("ND3D")) esm.skipHSub(); + if (esm.isNextSub("ANIS")) + esm.skipHSub(); } } diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 76356769aa..454690adc0 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -7,7 +7,7 @@ namespace ESSImport void CellRef::load(ESM::ESMReader &esm) { - esm.getHNT(mRefNum.mIndex, "FRMR"); // TODO: adjust RefNum + esm.getHNT(mRefNum.mIndex, "FRMR"); // this is required since openmw supports more than 255 content files int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24; @@ -16,19 +16,27 @@ namespace ESSImport mIndexedRefId = esm.getHNString("NAME"); - // the following two occur in ESM::CellRef too (Charge and Gold), - // but may have entirely different meanings here - int intv; - esm.getHNOT(intv, "INTV"); - int nam9; - esm.getHNOT(nam9, "NAM9"); + if (esm.isNextSub("LVCR")) + esm.skipHSub(); mActorData.load(esm); mEnabled = true; esm.getHNOT(mEnabled, "ZNAM"); - esm.getHNT(mPos, "DATA", 24); + // should occur for all references but not levelled creature spawners + esm.getHNOT(mPos, "DATA", 24); + + // i've seen DATA record TWICE on a creature record - and with the exact same content too! weird + // alarmvoi0000.ess + esm.getHNOT(mPos, "DATA", 24); + + if (esm.isNextSub("MVRF")) + { + esm.skipHSub(); + esm.getSubName(); + esm.skipHSub(); + } } } diff --git a/apps/essimporter/importcrec.cpp b/apps/essimporter/importcrec.cpp index c770b2386a..9cf4555889 100644 --- a/apps/essimporter/importcrec.cpp +++ b/apps/essimporter/importcrec.cpp @@ -8,6 +8,13 @@ namespace ESSImport void CREC::load(ESM::ESMReader &esm) { esm.getHNT(mIndex, "INDX"); + + // equivalent of ESM::Creature XSCL? probably don't have to convert this, + // since the value can't be changed + float scale; + esm.getHNOT(scale, "XSCL"); + + mInventory.load(esm); } } diff --git a/apps/essimporter/importcrec.hpp b/apps/essimporter/importcrec.hpp index 62881c582a..16b7528070 100644 --- a/apps/essimporter/importcrec.hpp +++ b/apps/essimporter/importcrec.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_ESSIMPORT_CREC_H #define OPENMW_ESSIMPORT_CREC_H +#include "importinventory.hpp" + namespace ESM { class ESMReader; @@ -14,6 +16,8 @@ namespace ESSImport { int mIndex; + Inventory mInventory; + void load(ESM::ESMReader& esm); }; diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp new file mode 100644 index 0000000000..d132fd76da --- /dev/null +++ b/apps/essimporter/importinventory.cpp @@ -0,0 +1,46 @@ +#include "importinventory.hpp" + +#include + +namespace ESSImport +{ + + void Inventory::load(ESM::ESMReader &esm) + { + while (esm.isNextSub("NPCO")) + { + InventoryItem item; + item.mId = esm.getHString(); + + if (esm.isNextSub("XIDX")) + esm.skipHSub(); + + std::string script = esm.getHNOString("SCRI"); + // script variables? + // unsure if before or after ESM::CellRef + if (!script.empty()) + { + if (esm.isNextSub("SLCS")) + esm.skipHSub(); + if (esm.isNextSub("SLSD")) // Short Data? + esm.skipHSub(); + if (esm.isNextSub("SLFD")) // Float Data? + esm.skipHSub(); + } + + // for XSOL and XCHG seen so far, but probably others too + item.ESM::CellRef::loadData(esm); + + item.mCondition = -1; + esm.getHNOT(item.mCondition, "XHLT"); + mItems.push_back(item); + } + + while (esm.isNextSub("WIDX")) + { + // equipping? + esm.skipHSub(); + } + } + +} diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp new file mode 100644 index 0000000000..e31cab76ad --- /dev/null +++ b/apps/essimporter/importinventory.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTINVENTORY_H +#define OPENMW_ESSIMPORT_IMPORTINVENTORY_H + +#include +#include + +#include + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + struct Inventory + { + struct InventoryItem : public ESM::CellRef + { + std::string mId; + int mCondition; + }; + std::vector mItems; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp index 4cfc816c91..8400a9e4ac 100644 --- a/apps/essimporter/importnpcc.cpp +++ b/apps/essimporter/importnpcc.cpp @@ -12,24 +12,14 @@ namespace ESSImport esm.getHNT(mNPDT, "NPDT"); - while (esm.isNextSub("NPCO")) - { - InventoryItem item; - item.mId = esm.getHString(); - - if (esm.isNextSub("XIDX")) - esm.skipHSub(); - - item.mCondition = -1; - esm.getHNOT(item.mCondition, "XHLT"); - mInventory.push_back(item); - } - - while (esm.isNextSub("WIDX")) - { - // equipping? + if (esm.isNextSub("AI_E")) esm.skipHSub(); - } + if (esm.isNextSub("AI_T")) + esm.skipHSub(); + if (esm.isNextSub("AI_F")) + esm.skipHSub(); + + mInventory.load(esm); } } diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index d10048fe0d..f3c4e24c7c 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -3,6 +3,10 @@ #include +#include + +#include "importinventory.hpp" + namespace ESM { class ESMReader; @@ -20,12 +24,7 @@ namespace ESSImport unsigned char unknown2[5]; } mNPDT; - struct InventoryItem - { - std::string mId; - int mCondition; - }; - std::vector mInventory; + Inventory mInventory; int mIndex; diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index f8c78dab01..8a57ae5886 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -11,9 +11,6 @@ namespace ESSImport mRefID = esm.getHNString("NAME"); - if (esm.isNextSub("STPR")) - esm.skipHSub(); // ESS TODO - mActorData.load(esm); esm.getHNOT(mPos, "DATA", 24); @@ -21,6 +18,12 @@ namespace ESSImport void PCDT::load(ESM::ESMReader &esm) { + while (esm.isNextSub("DNAM")) + { + // TODO: deal with encoding? + mKnownDialogueTopics.push_back(esm.getHString()); + } + if (esm.isNextSub("PNAM")) esm.skipHSub(); if (esm.isNextSub("SNAM")) @@ -33,6 +36,17 @@ namespace ESSImport mBirthsign = esm.getHNOString("BNAM"); + // Holds the names of the last used Alchemy apparatus. Don't need to import this ATM, + // because our GUI auto-selects the best apparatus. + if (esm.isNextSub("NAM0")) + esm.skipHSub(); + if (esm.isNextSub("NAM1")) + esm.skipHSub(); + if (esm.isNextSub("NAM2")) + esm.skipHSub(); + if (esm.isNextSub("NAM3")) + esm.skipHSub(); + if (esm.isNextSub("ENAM")) esm.skipHSub(); @@ -48,6 +62,14 @@ namespace ESSImport if (esm.isNextSub("KNAM")) esm.skipHSub(); + + if (esm.isNextSub("WERE")) + { + // some werewolf data, 152 bytes + // maybe current skills and attributes for werewolf form + esm.getSubHeader(); + esm.skip(152); + } } } diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index 2493fcdad0..af8625fa64 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -36,6 +36,8 @@ struct PCDT int mBounty; std::string mBirthsign; + std::vector mKnownDialogueTopics; + struct FNAM { unsigned char mRank; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index f93fe1535c..43b1f4d293 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -20,6 +20,11 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mRefID = esm.getHNString ("NAME"); + loadData(esm); +} + +void ESM::CellRef::loadData(ESMReader &esm) +{ // Again, UNAM sometimes appears after NAME and sometimes later. // Or perhaps this UNAM means something different? mReferenceBlocked = -1; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index f3986ccf39..b7b00a6548 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -91,6 +91,9 @@ namespace ESM void load (ESMReader& esm, bool wideRefNum = false); + /// Implicitly called by load + void loadData (ESMReader& esm); + void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false) const; void blank(); From f9cf31fcd5319dca02dddf89dead998cff3e9955 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 00:35:19 +0100 Subject: [PATCH 345/404] ESSImport: convert custom map markers, not working for interiors yet --- apps/essimporter/converter.cpp | 39 +++++++++++++++++++++++++- apps/essimporter/converter.hpp | 3 ++ apps/essimporter/importacdt.cpp | 7 +++++ apps/essimporter/importcellref.cpp | 3 +- apps/essimporter/importer.cpp | 5 +++- apps/openmw/mwgui/mapwindow.cpp | 39 +++++++------------------- apps/openmw/mwgui/mapwindow.hpp | 34 ++++++---------------- apps/openmw/mwgui/windowmanagerimp.cpp | 4 +-- components/CMakeLists.txt | 2 +- components/esm/custommarkerstate.cpp | 26 +++++++++++++++++ components/esm/custommarkerstate.hpp | 30 ++++++++++++++++++++ 11 files changed, 132 insertions(+), 60 deletions(-) create mode 100644 components/esm/custommarkerstate.cpp create mode 100644 components/esm/custommarkerstate.hpp diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index c51e91ed7c..ce3cea6758 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -108,7 +108,7 @@ namespace ESSImport } std::vector cellrefs; - while (esm.hasMoreSubs()) + while (esm.hasMoreSubs() && esm.isNextSub("FRMR")) { CellRef ref; ref.load (esm); @@ -121,6 +121,36 @@ namespace ESSImport cellrefs.push_back(ref); } + while (esm.isNextSub("MPCD")) + { + float notepos[3]; + esm.getHT(notepos, 3*sizeof(float)); + + // Markers seem to be arranged in a 32*32 grid, notepos has grid-indices. + // This seems to be the reason markers can't be placed everywhere in interior cells, + // i.e. when the grid is exceeded. + // Converting the interior markers correctly could be rather tricky, but is probably similar logic + // as used for the FoW texture placement, which we need to figure out anyway + notepos[1] += 31.f; + notepos[0] += 0.5; + notepos[1] += 0.5; + notepos[0] = 8192 * notepos[0] / 32.f; + notepos[1] = 8192 * notepos[1] / 32.f; + if (cell.isExterior()) + { + notepos[0] += 8192 * cell.mData.mX; + notepos[1] += 8192 * cell.mData.mY; + } + // TODO: what encoding is this in? + std::string note = esm.getHNString("MPNT"); + ESM::CustomMarker marker; + marker.mWorldX = notepos[0]; + marker.mWorldY = notepos[1]; + marker.mNote = note; + marker.mCell = cell.getCellId(); + mMarkers.push_back(marker); + } + newcell.mRefs = cellrefs; // FIXME: map by ID for exterior cells @@ -199,6 +229,13 @@ namespace ESSImport esm.endRecord(ESM::REC_CSTA); } + + for (std::vector::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) + { + esm.startRecord(ESM::REC_MARK); + it->save(esm); + esm.endRecord(ESM::REC_MARK); + } } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 3f8b4cbbee..5f19617177 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -9,6 +9,8 @@ #include #include #include +#include + #include "importcrec.hpp" #include "importercontext.hpp" @@ -240,6 +242,7 @@ private: std::map mCells; + std::vector mMarkers; }; } diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index 5bec5bd823..d961720a73 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -49,6 +49,13 @@ namespace ESSImport while (esm.isNextSub("FGTN")) esm.getHString(); // fight target? + // unsure at which point between TGTN and CRED + if (esm.isNextSub("AADT")) + { + // occured when a creature was in the middle of its attack, 44 bytes + esm.skipHSub(); + } + // unsure at which point between FGTN and CHRD if (esm.isNextSub("PWPC")) esm.skipHSub(); diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 454690adc0..8c2b8f36d5 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -7,7 +7,8 @@ namespace ESSImport void CellRef::load(ESM::ESMReader &esm) { - esm.getHNT(mRefNum.mIndex, "FRMR"); + // (FRMR subrecord name is already read by the loop in ConvertCell) + esm.getHT(mRefNum.mIndex); // FRMR // this is required since openmw supports more than 255 content files int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24; diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 572489d9a7..12136cdea6 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -213,6 +213,7 @@ namespace ESSImport converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); + std::set unknownRecords; for (std::map >::const_iterator it = converters.begin(); it != converters.end(); ++it) @@ -232,7 +233,9 @@ namespace ESSImport } else { - std::cerr << "unknown record " << n.toString() << std::endl; + if (unknownRecords.insert(n.val).second) + std::cerr << "unknown record " << n.toString() << std::endl; + esm.skipRecord(); } } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 4de9af1086..abd1256d7f 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -86,35 +86,16 @@ namespace namespace MWGui { - void CustomMarker::save(ESM::ESMWriter &esm) const - { - esm.writeHNT("POSX", mWorldX); - esm.writeHNT("POSY", mWorldY); - mCell.save(esm); - if (!mNote.empty()) - esm.writeHNString("NOTE", mNote); - } - - void CustomMarker::load(ESM::ESMReader &esm) - { - esm.getHNT(mWorldX, "POSX"); - esm.getHNT(mWorldY, "POSY"); - mCell.load(esm); - mNote = esm.getHNOString("NOTE"); - } - - // ------------------------------------------------------ - - void CustomMarkerCollection::addMarker(const CustomMarker &marker, bool triggerEvent) + void CustomMarkerCollection::addMarker(const ESM::CustomMarker &marker, bool triggerEvent) { mMarkers.push_back(marker); if (triggerEvent) eventMarkersChanged(); } - void CustomMarkerCollection::deleteMarker(const CustomMarker &marker) + void CustomMarkerCollection::deleteMarker(const ESM::CustomMarker &marker) { - std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); + std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); if (it != mMarkers.end()) mMarkers.erase(it); else @@ -123,9 +104,9 @@ namespace MWGui eventMarkersChanged(); } - void CustomMarkerCollection::updateMarker(const CustomMarker &marker, const std::string &newNote) + void CustomMarkerCollection::updateMarker(const ESM::CustomMarker &marker, const std::string &newNote) { - std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); + std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); if (it != mMarkers.end()) it->mNote = newNote; else @@ -140,12 +121,12 @@ namespace MWGui eventMarkersChanged(); } - std::vector::const_iterator CustomMarkerCollection::begin() const + std::vector::const_iterator CustomMarkerCollection::begin() const { return mMarkers.begin(); } - std::vector::const_iterator CustomMarkerCollection::end() const + std::vector::const_iterator CustomMarkerCollection::end() const { return mMarkers.end(); } @@ -295,9 +276,9 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(*it); mCustomMarkerWidgets.clear(); - for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) + for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) { - const CustomMarker& marker = *it; + const ESM::CustomMarker& marker = *it; if (marker.mCell.mPaged != !mInterior) continue; @@ -654,7 +635,7 @@ namespace MWGui void MapWindow::onCustomMarkerDoubleClicked(MyGUI::Widget *sender) { - mEditingMarker = *sender->getUserData(); + mEditingMarker = *sender->getUserData(); mEditNoteDialog.setText(mEditingMarker.mNote); mEditNoteDialog.showDeleteButton(true); mEditNoteDialog.setVisible(true); diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 118fee0f6c..6840694673 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -7,6 +7,8 @@ #include +#include + namespace MWRender { class GlobalMap; @@ -26,43 +28,25 @@ namespace Loading namespace MWGui { - struct CustomMarker - { - float mWorldX; - float mWorldY; - - ESM::CellId mCell; - - std::string mNote; - - bool operator == (const CustomMarker& other) - { - return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY; - } - - void load (ESM::ESMReader& reader); - void save (ESM::ESMWriter& writer) const; - }; - class CustomMarkerCollection { public: - void addMarker(const CustomMarker& marker, bool triggerEvent=true); - void deleteMarker (const CustomMarker& marker); - void updateMarker(const CustomMarker& marker, const std::string& newNote); + void addMarker(const ESM::CustomMarker& marker, bool triggerEvent=true); + void deleteMarker (const ESM::CustomMarker& marker); + void updateMarker(const ESM::CustomMarker& marker, const std::string& newNote); void clear(); size_t size() const; - std::vector::const_iterator begin() const; - std::vector::const_iterator end() const; + std::vector::const_iterator begin() const; + std::vector::const_iterator end() const; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; EventHandle_Void eventMarkersChanged; private: - std::vector mMarkers; + std::vector mMarkers; }; class LocalMapBase @@ -233,7 +217,7 @@ namespace MWGui MWRender::GlobalMap* mGlobalMapRender; EditNoteDialog mEditNoteDialog; - CustomMarker mEditingMarker; + ESM::CustomMarker mEditingMarker; virtual void onPinToggled(); virtual void onTitleDoubleClicked(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index d5a71bc775..5b2bbee906 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1612,7 +1612,7 @@ namespace MWGui writer.endRecord(ESM::REC_ASPL); } - for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) + for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) { writer.startRecord(ESM::REC_MARK); (*it).save(writer); @@ -1633,7 +1633,7 @@ namespace MWGui } else if (type == ESM::REC_MARK) { - CustomMarker marker; + ESM::CustomMarker marker; marker.load(reader); mCustomMarkers.addMarker(marker, false); } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a3e79758f5..0578a44b00 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -63,7 +63,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects util + aisequence magiceffects util custommarkerstate ) add_component_dir (esmterrain diff --git a/components/esm/custommarkerstate.cpp b/components/esm/custommarkerstate.cpp new file mode 100644 index 0000000000..dc81c123d4 --- /dev/null +++ b/components/esm/custommarkerstate.cpp @@ -0,0 +1,26 @@ +#include "custommarkerstate.hpp" + +#include "esmwriter.hpp" +#include "esmreader.hpp" + +namespace ESM +{ + +void CustomMarker::save(ESM::ESMWriter &esm) const +{ + esm.writeHNT("POSX", mWorldX); + esm.writeHNT("POSY", mWorldY); + mCell.save(esm); + if (!mNote.empty()) + esm.writeHNString("NOTE", mNote); +} + +void CustomMarker::load(ESM::ESMReader &esm) +{ + esm.getHNT(mWorldX, "POSX"); + esm.getHNT(mWorldY, "POSY"); + mCell.load(esm); + mNote = esm.getHNOString("NOTE"); +} + +} diff --git a/components/esm/custommarkerstate.hpp b/components/esm/custommarkerstate.hpp new file mode 100644 index 0000000000..fc9286bfe2 --- /dev/null +++ b/components/esm/custommarkerstate.hpp @@ -0,0 +1,30 @@ +#ifndef OPENMW_ESM_CUSTOMMARKERSTATE_H +#define OPENMW_ESM_CUSTOMMARKERSTATE_H + +#include "cellid.hpp" + +namespace ESM +{ + +// format 0, saved games only +struct CustomMarker +{ + float mWorldX; + float mWorldY; + + ESM::CellId mCell; + + std::string mNote; + + bool operator == (const CustomMarker& other) + { + return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY; + } + + void load (ESM::ESMReader& reader); + void save (ESM::ESMWriter& writer) const; +}; + +} + +#endif From ad398f0c65d2df8299d22bdb7182c5deb89e71ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 01:06:45 +0100 Subject: [PATCH 346/404] ESSImport: convert kill counter --- apps/essimporter/CMakeLists.txt | 1 + apps/essimporter/converter.hpp | 29 +++++++++++++++++++++++++++++ apps/essimporter/importer.cpp | 5 ++++- apps/essimporter/importklst.cpp | 22 ++++++++++++++++++++++ apps/essimporter/importklst.hpp | 28 ++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 apps/essimporter/importklst.cpp create mode 100644 apps/essimporter/importklst.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 3d3e0860c1..cf32184545 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -7,6 +7,7 @@ set(ESSIMPORTER_FILES importcellref.cpp importacdt.cpp importinventory.cpp + importklst.cpp importercontext.cpp converter.cpp convertacdt.cpp diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 5f19617177..aee60f2e75 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -15,6 +15,7 @@ #include "importercontext.hpp" #include "importcellref.hpp" +#include "importklst.hpp" #include "convertacdt.hpp" #include "convertnpcc.hpp" @@ -93,6 +94,7 @@ public: mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayerBase = npc; std::map empty; + // FIXME: not working? for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; } @@ -245,6 +247,33 @@ private: std::vector mMarkers; }; +class ConvertKLST : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + KLST klst; + klst.load(esm); + mKillCounter = klst.mKillCounter; + + mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills; + } + + virtual void write(ESM::ESMWriter &esm) + { + esm.startRecord(ESM::REC_DCOU); + for (std::map::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it) + { + esm.writeHNString("ID__", it->first); + esm.writeHNT ("COUN", it->second); + } + esm.endRecord(ESM::REC_DCOU); + } + +private: + std::map mKillCounter; +}; + } #endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 12136cdea6..4cf41a8619 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -146,7 +146,7 @@ namespace ESSImport { std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset << " (2) 0x" << sub2.mFileOffset << std::endl; - return; // TODO: try to recover + break; // TODO: try to recover } if (sub.mData != sub2.mData) @@ -177,6 +177,7 @@ namespace ESSImport void Importer::run() { + // construct Ogre::Root to gain access to image codecs Ogre::LogManager logman; Ogre::Root root; @@ -191,6 +192,7 @@ namespace ESSImport const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; + const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value; std::map > converters; converters[ESM::REC_GLOB] = boost::shared_ptr(new ConvertGlobal()); @@ -201,6 +203,7 @@ namespace ESSImport converters[recREFR] = boost::shared_ptr(new ConvertREFR()); converters[recPCDT] = boost::shared_ptr(new ConvertPCDT()); converters[recFMAP] = boost::shared_ptr(new ConvertFMAP()); + converters[recKLST] = boost::shared_ptr(new ConvertKLST()); converters[ESM::REC_CELL] = boost::shared_ptr(new ConvertCell()); converters[ESM::REC_ALCH] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CLAS] = boost::shared_ptr(new ConvertClass()); diff --git a/apps/essimporter/importklst.cpp b/apps/essimporter/importklst.cpp new file mode 100644 index 0000000000..daa1ab0774 --- /dev/null +++ b/apps/essimporter/importklst.cpp @@ -0,0 +1,22 @@ +#include "importklst.hpp" + +#include + +namespace ESSImport +{ + + void KLST::load(ESM::ESMReader &esm) + { + while (esm.isNextSub("KNAM")) + { + std::string refId = esm.getHString(); + int count; + esm.getHNT(count, "CNAM"); + mKillCounter[refId] = count; + } + + mWerewolfKills = 0; + esm.getHNOT(mWerewolfKills, "INTV"); + } + +} diff --git a/apps/essimporter/importklst.hpp b/apps/essimporter/importklst.hpp new file mode 100644 index 0000000000..e333e71509 --- /dev/null +++ b/apps/essimporter/importklst.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_ESSIMPORT_KLST_H +#define OPENMW_ESSIMPORT_KLST_H + +#include +#include + +namespace ESM +{ + struct ESMReader; +} + +namespace ESSImport +{ + + /// Kill Stats + struct KLST + { + void load(ESM::ESMReader& esm); + + /// RefId, kill count + std::map mKillCounter; + + int mWerewolfKills; + }; + +} + +#endif From 235683e44943bc68528527f9da14953749126b21 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 10:34:49 +0100 Subject: [PATCH 347/404] Remove LightState from openmw save format to streamline inventory loading --- apps/openmw/mwclass/light.cpp | 10 ++---- apps/openmw/mwworld/cellstore.cpp | 5 ++- apps/openmw/mwworld/containerstore.cpp | 36 ++++++++------------- apps/openmw/mwworld/containerstore.hpp | 2 +- components/CMakeLists.txt | 2 +- components/esm/inventorystate.cpp | 43 +++++++++----------------- components/esm/inventorystate.hpp | 7 +---- components/esm/lightstate.cpp | 21 ------------- components/esm/lightstate.hpp | 19 ------------ components/esm/objectstate.cpp | 8 +++++ components/esm/objectstate.hpp | 2 ++ 11 files changed, 45 insertions(+), 110 deletions(-) delete mode 100644 components/esm/lightstate.cpp delete mode 100644 components/esm/lightstate.hpp diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 669b8187cf..c50ad52d81 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -2,7 +2,7 @@ #include "light.hpp" #include -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -285,21 +285,17 @@ namespace MWClass void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { - const ESM::LightState& state2 = dynamic_cast (state); - ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state2.mTime; + dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state.mTime; } void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const { - ESM::LightState& state2 = dynamic_cast (state); - ensureCustomData (ptr); - state2.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + state.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; } std::string Light::getSound(const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 1c0e7e3859..4cf99fdb75 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -624,7 +623,7 @@ namespace MWWorld writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); writeReferenceCollection (writer, mItemLists); - writeReferenceCollection (writer, mLights); + writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); writeReferenceCollection (writer, mMiscItems); writeReferenceCollection (writer, mNpcs); @@ -708,7 +707,7 @@ namespace MWWorld case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, contentFileMap); + readReferenceCollection (reader, mLights, contentFileMap); break; case ESM::REC_LOCK: diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index e9c968a07c..bd14720f7a 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -87,7 +87,7 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::Object template void MWWorld::ContainerStore::storeStates (const CellRefList& collection, - std::vector > >& states, bool equipable) const + std::vector >& states, bool equipable) const { for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) @@ -97,7 +97,7 @@ void MWWorld::ContainerStore::storeStates (const CellRefList& collection, ESM::ObjectState state; storeState (*iter, state); int slot = equipable ? getSlot (*iter) : -1; - states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, slot))); + states.push_back (std::make_pair (state, slot)); } } @@ -656,30 +656,25 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const storeStates (probes, state.mItems, true); storeStates (repairs, state.mItems); storeStates (weapons, state.mItems, true); - - state.mLights.clear(); + storeStates (lights, state.mItems, true); state.mLevelledItemMap = mLevelledItemMap; - - for (MWWorld::CellRefList::List::const_iterator iter (lights.mList.begin()); - iter!=lights.mList.end(); ++iter) - { - ESM::LightState objectState; - storeState (*iter, objectState); - state.mLights.push_back (std::make_pair (objectState, getSlot (*iter))); - } } void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) { clear(); - for (std::vector > >::const_iterator + for (std::vector >::const_iterator iter (state.mItems.begin()); iter!=state.mItems.end(); ++iter) { - int slot = iter->second.second; + int slot = iter->second; + + const ESM::ObjectState& state = iter->first; - switch (iter->second.first) + int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID); + + switch (type) { case ESM::REC_ALCH: getState (potions, iter->first); break; case ESM::REC_APPA: getState (appas, iter->first); break; @@ -692,19 +687,14 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break; case ESM::REC_REPA: getState (repairs, iter->first); break; case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; + case ESM::REC_LIGH: setSlot (getState (lights, iter->first), slot); break; default: - - std::cerr << "invalid item type in inventory state" << std::endl; + std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; + break; } } - for (std::vector >::const_iterator iter (state.mLights.begin()); - iter!=state.mLights.end(); ++iter) - { - int slot = iter->second; - setSlot (getState (lights, iter->first), slot); - } mLevelledItemMap = state.mLevelledItemMap; } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 2849463c93..68ee41a1d3 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -86,7 +86,7 @@ namespace MWWorld template void storeStates (const CellRefList& collection, - std::vector > >& states, + std::vector >& states, bool equipable = false) const; virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 0578a44b00..93228f87a7 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -61,7 +61,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile aisequence magiceffects util custommarkerstate ) diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index a2d49b144b..fdaba33228 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -14,9 +14,10 @@ namespace state.load (esm); } - void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, unsigned int type, int slot) + void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, int slot) { - esm.writeHNT ("IOBJ", type); + int unused = 0; + esm.writeHNT ("IOBJ", unused); if (slot!=-1) esm.writeHNT ("SLOT", slot); @@ -29,27 +30,15 @@ void ESM::InventoryState::load (ESMReader &esm) { while (esm.isNextSub ("IOBJ")) { - unsigned int id = 0; - esm.getHT (id); - - if (id==ESM::REC_LIGH) - { - LightState state; - int slot; - read (esm, state, slot); - if (state.mCount == 0) - continue; - mLights.push_back (std::make_pair (state, slot)); - } - else - { - ObjectState state; - int slot; - read (esm, state, slot); - if (state.mCount == 0) - continue; - mItems.push_back (std::make_pair (state, std::make_pair (id, slot))); - } + int unused; // no longer used + esm.getHT(unused); + + ObjectState state; + int slot; + read (esm, state, slot); + if (state.mCount == 0) + continue; + mItems.push_back (std::make_pair (state, slot)); } while (esm.isNextSub("LEVM")) @@ -78,12 +67,8 @@ void ESM::InventoryState::load (ESMReader &esm) void ESM::InventoryState::save (ESMWriter &esm) const { - for (std::vector > >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) - write (esm, iter->first, iter->second.first, iter->second.second); - - for (std::vector >::const_iterator iter (mLights.begin()); - iter!=mLights.end(); ++iter) - write (esm, iter->first, ESM::REC_LIGH, iter->second); + for (std::vector >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) + write (esm, iter->first, iter->second); for (std::map::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) { diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index bd0b46a224..3d4407e7b2 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -4,7 +4,6 @@ #include #include "objectstate.hpp" -#include "lightstate.hpp" namespace ESM { @@ -16,11 +15,7 @@ namespace ESM /// \brief State for inventories and containers struct InventoryState { - // anything but lights (type, slot) - std::vector > > mItems; - - // lights (slot) - std::vector > mLights; + std::vector > mItems; std::map mLevelledItemMap; diff --git a/components/esm/lightstate.cpp b/components/esm/lightstate.cpp deleted file mode 100644 index 1ef0408237..0000000000 --- a/components/esm/lightstate.cpp +++ /dev/null @@ -1,21 +0,0 @@ - -#include "lightstate.hpp" - -#include "esmreader.hpp" -#include "esmwriter.hpp" - -void ESM::LightState::load (ESMReader &esm) -{ - ObjectState::load (esm); - - mTime = 0; - esm.getHNOT (mTime, "LTIM"); -} - -void ESM::LightState::save (ESMWriter &esm, bool inInventory) const -{ - ObjectState::save (esm, inInventory); - - if (mTime) - esm.writeHNT ("LTIM", mTime); -} \ No newline at end of file diff --git a/components/esm/lightstate.hpp b/components/esm/lightstate.hpp deleted file mode 100644 index a22735e079..0000000000 --- a/components/esm/lightstate.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef OPENMW_ESM_LIGHTSTATE_H -#define OPENMW_ESM_LIGHTSTATE_H - -#include "objectstate.hpp" - -namespace ESM -{ - // format 0, saved games only - - struct LightState : public ObjectState - { - float mTime; - - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; - }; -} - -#endif diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index f7755f8cbe..98cf3eba66 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -23,6 +23,10 @@ void ESM::ObjectState::load (ESMReader &esm) esm.getHNOT (mPosition, "POS_", 24); esm.getHNOT (mLocalRotation, "LROT", 12); + + // used for lights only + mTime = 0; + esm.getHNOT (mTime, "LTIM"); } void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const @@ -46,6 +50,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const esm.writeHNT ("POS_", mPosition, 24); esm.writeHNT ("LROT", mLocalRotation, 12); } + + if (mTime) + esm.writeHNT ("LTIM", mTime); } void ESM::ObjectState::blank() @@ -60,6 +67,7 @@ void ESM::ObjectState::blank() mPosition.rot[i] = 0; mLocalRotation[i] = 0; } + mTime = 0; } ESM::ObjectState::~ObjectState() {} diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 5b05e0949e..6b536bd012 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -26,6 +26,8 @@ namespace ESM ESM::Position mPosition; float mLocalRotation[3]; + float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either + virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; From f7e32a24c9f8550c018495860a2f9e0b4dc36194 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 12:03:30 +0100 Subject: [PATCH 348/404] Fix terrain assertion --- components/esmterrain/storage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 3c76cc3b45..a66193f976 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -402,8 +402,8 @@ namespace ESMTerrain int endX = startX + 1; int endY = startY + 1; - assert(endX < ESM::Land::LAND_SIZE); - assert(endY < ESM::Land::LAND_SIZE); + endX = std::min(endX, ESM::Land::LAND_SIZE-1); + endY = std::min(endY, ESM::Land::LAND_SIZE-1); // now get points in terrain space (effectively rounding them to boundaries) float startXTS = startX * invFactor; From a7b82e5107caed0fceb407abfa364071d0338053 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 12:22:51 +0100 Subject: [PATCH 349/404] ESSImport: inventory loading works, equipment slots need more work --- apps/essimporter/CMakeLists.txt | 2 ++ apps/essimporter/convertcrec.cpp | 13 +++++++++++++ apps/essimporter/convertcrec.hpp | 15 +++++++++++++++ apps/essimporter/converter.cpp | 4 ++++ apps/essimporter/convertinventory.cpp | 23 +++++++++++++++++++++++ apps/essimporter/convertinventory.hpp | 15 +++++++++++++++ apps/essimporter/convertnpcc.cpp | 4 ++++ apps/essimporter/importinventory.cpp | 22 +++++++++++++++++++--- apps/essimporter/importinventory.hpp | 1 + components/esm/cellref.cpp | 4 ++-- 10 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 apps/essimporter/convertcrec.cpp create mode 100644 apps/essimporter/convertcrec.hpp create mode 100644 apps/essimporter/convertinventory.cpp create mode 100644 apps/essimporter/convertinventory.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index cf32184545..1af55de9b9 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -12,6 +12,8 @@ set(ESSIMPORTER_FILES converter.cpp convertacdt.cpp convertnpcc.cpp + convertinventory.cpp + convertcrec.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/convertcrec.cpp b/apps/essimporter/convertcrec.cpp new file mode 100644 index 0000000000..34e1c00708 --- /dev/null +++ b/apps/essimporter/convertcrec.cpp @@ -0,0 +1,13 @@ +#include "convertcrec.hpp" + +#include "convertinventory.hpp" + +namespace ESSImport +{ + + void convertCREC(const CREC &crec, ESM::CreatureState &state) + { + convertInventory(crec.mInventory, state.mInventory); + } + +} diff --git a/apps/essimporter/convertcrec.hpp b/apps/essimporter/convertcrec.hpp new file mode 100644 index 0000000000..7d317f03e8 --- /dev/null +++ b/apps/essimporter/convertcrec.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTCREC_H +#define OPENMW_ESSIMPORT_CONVERTCREC_H + +#include "importcrec.hpp" + +#include + +namespace ESSImport +{ + + void convertCREC(const CREC& crec, ESM::CreatureState& state); + +} + +#endif diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index ce3cea6758..22d6d82823 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -4,6 +4,8 @@ #include +#include "convertcrec.hpp" + namespace { @@ -197,6 +199,7 @@ namespace ESSImport ESM::CreatureState objstate; objstate.blank(); convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertCREC(crecIt->second, objstate); objstate.mEnabled = cellref.mEnabled; objstate.mPosition = cellref.mPos; objstate.mRef = out; @@ -215,6 +218,7 @@ namespace ESSImport objstate.blank(); convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); convertNpcData(cellref.mActorData, objstate.mNpcStats); + convertNPCC(npccIt->second, objstate); objstate.mEnabled = cellref.mEnabled; objstate.mPosition = cellref.mPos; objstate.mRef = out; diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp new file mode 100644 index 0000000000..42535d946b --- /dev/null +++ b/apps/essimporter/convertinventory.cpp @@ -0,0 +1,23 @@ +#include "convertinventory.hpp" + +#include + +namespace ESSImport +{ + + void convertInventory(const Inventory &inventory, ESM::InventoryState &state) + { + for (std::vector::const_iterator it = inventory.mItems.begin(); + it != inventory.mItems.end(); ++it) + { + ESM::ObjectState objstate; + objstate.blank(); + objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); + objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile + // openmw handles them differently, so no need to set any flags + objstate.mRef.mCharge = it->mCondition; + state.mItems.push_back(std::make_pair(objstate, -1)); + } + } + +} diff --git a/apps/essimporter/convertinventory.hpp b/apps/essimporter/convertinventory.hpp new file mode 100644 index 0000000000..8abe85a44a --- /dev/null +++ b/apps/essimporter/convertinventory.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTINVENTORY_H +#define OPENMW_ESSIMPORT_CONVERTINVENTORY_H + +#include "importinventory.hpp" + +#include + +namespace ESSImport +{ + + void convertInventory (const Inventory& inventory, ESM::InventoryState& state); + +} + +#endif diff --git a/apps/essimporter/convertnpcc.cpp b/apps/essimporter/convertnpcc.cpp index fdf96c15a3..2155f46fb9 100644 --- a/apps/essimporter/convertnpcc.cpp +++ b/apps/essimporter/convertnpcc.cpp @@ -1,10 +1,14 @@ #include "convertnpcc.hpp" +#include "convertinventory.hpp" + namespace ESSImport { void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) { npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; + + convertInventory(npcc.mInventory, npcState.mInventory); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index d132fd76da..b3bd4a86be 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -2,6 +2,8 @@ #include +#include + namespace ESSImport { @@ -9,8 +11,12 @@ namespace ESSImport { while (esm.isNextSub("NPCO")) { + ESM::ContItem contItem; + esm.getHT(contItem); + InventoryItem item; - item.mId = esm.getHString(); + item.mId = contItem.mItem.toString(); + item.mCount = contItem.mCount; if (esm.isNextSub("XIDX")) esm.skipHSub(); @@ -32,14 +38,24 @@ namespace ESSImport item.ESM::CellRef::loadData(esm); item.mCondition = -1; + // FIXME: for Lights, this is actually a float esm.getHNOT(item.mCondition, "XHLT"); mItems.push_back(item); } + // equipped items while (esm.isNextSub("WIDX")) { - // equipping? - esm.skipHSub(); + // note: same item can be equipped 2 items (e.g. 2 rings) + // and will be *stacked* in the NPCO list, unlike openmw! + esm.getSubHeader(); + int itemIndex; // index of the item in the NPCO list + esm.getT(itemIndex); + + // appears to be a relative index for only the *possible* slots this item can be equipped in, + // i.e. 0 most of the time, unlike openmw slot enum index + int slotIndex; + esm.getT(slotIndex); } } diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index e31cab76ad..359e43fa68 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -19,6 +19,7 @@ namespace ESSImport struct InventoryItem : public ESM::CellRef { std::string mId; + int mCount; int mCondition; }; std::vector mItems; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 43b1f4d293..0c2dfd5dbc 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -142,8 +142,8 @@ void ESM::CellRef::blank() mSoul.clear(); mFaction.clear(); mFactionRank = -2; - mCharge = 0; - mEnchantmentCharge = 0; + mCharge = -1; + mEnchantmentCharge = -1; mGoldValue = 0; mDestCell.clear(); mLockLevel = 0; From 8e1eeccbe190f3ec04fc60283313683e7ea7dac2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 13:16:12 +0100 Subject: [PATCH 350/404] ESSImport: container state --- apps/essimporter/CMakeLists.txt | 2 + apps/essimporter/convertcntc.cpp | 13 +++ apps/essimporter/convertcntc.hpp | 15 +++ apps/essimporter/converter.cpp | 163 ++++++++++++++++----------- apps/essimporter/converter.hpp | 20 +++- apps/essimporter/importcntc.cpp | 16 +++ apps/essimporter/importcntc.hpp | 25 ++++ apps/essimporter/importer.cpp | 1 + apps/essimporter/importercontext.hpp | 2 + components/CMakeLists.txt | 2 +- components/esm/loadcrec.hpp | 49 -------- components/esm/records.hpp | 1 - 12 files changed, 189 insertions(+), 120 deletions(-) create mode 100644 apps/essimporter/convertcntc.cpp create mode 100644 apps/essimporter/convertcntc.hpp create mode 100644 apps/essimporter/importcntc.cpp create mode 100644 apps/essimporter/importcntc.hpp delete mode 100644 components/esm/loadcrec.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 1af55de9b9..8d1af714d7 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -8,12 +8,14 @@ set(ESSIMPORTER_FILES importacdt.cpp importinventory.cpp importklst.cpp + importcntc.cpp importercontext.cpp converter.cpp convertacdt.cpp convertnpcc.cpp convertinventory.cpp convertcrec.cpp + convertcntc.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/convertcntc.cpp b/apps/essimporter/convertcntc.cpp new file mode 100644 index 0000000000..426ef44966 --- /dev/null +++ b/apps/essimporter/convertcntc.cpp @@ -0,0 +1,13 @@ +#include "convertcntc.hpp" + +#include "convertinventory.hpp" + +namespace ESSImport +{ + + void convertCNTC(const CNTC &cntc, ESM::ContainerState &state) + { + convertInventory(cntc.mInventory, state.mInventory); + } + +} diff --git a/apps/essimporter/convertcntc.hpp b/apps/essimporter/convertcntc.hpp new file mode 100644 index 0000000000..c299d87a1e --- /dev/null +++ b/apps/essimporter/convertcntc.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTCNTC_H +#define OPENMW_ESSIMPORT_CONVERTCNTC_H + +#include "importcntc.hpp" + +#include + +namespace ESSImport +{ + + void convertCNTC(const CNTC& cntc, ESM::ContainerState& state); + +} + +#endif diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 22d6d82823..293c48624f 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -3,8 +3,10 @@ #include #include +#include #include "convertcrec.hpp" +#include "convertcntc.hpp" namespace { @@ -17,6 +19,13 @@ namespace screenshot.save(out); } + + void convertCellRef(const ESSImport::CellRef& cellref, ESM::ObjectState& objstate) + { + objstate.mEnabled = cellref.mEnabled; + objstate.mPosition = cellref.mPos; + objstate.mRef.mRefNum = cellref.mRefNum; + } } namespace ESSImport @@ -116,8 +125,9 @@ namespace ESSImport ref.load (esm); if (esm.isNextSub("DELE")) { + // TODO // strangely this can be e.g. 52 instead of just 1, - std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + //std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; esm.skipHSub(); } cellrefs.push_back(ref); @@ -155,85 +165,104 @@ namespace ESSImport newcell.mRefs = cellrefs; - // FIXME: map by ID for exterior cells - mCells[id] = newcell; + + if (cell.isExterior()) + mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell; + else + mIntCells[id] = newcell; } - void ConvertCell::write(ESM::ESMWriter &esm) + void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm) { - for (std::map::const_iterator it = mCells.begin(); it != mCells.end(); ++it) + ESM::Cell esmcell = cell.mCell; + esm.startRecord(ESM::REC_CSTA); + ESM::CellState csta; + csta.mHasFogOfWar = 0; + csta.mId = esmcell.getCellId(); + csta.mId.save(esm); + // TODO csta.mLastRespawn; + // shouldn't be needed if we respawn on global schedule like in original MW + csta.mWaterLevel = esmcell.mWater; + csta.save(esm); + + for (std::vector::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt) { - const ESM::Cell& cell = it->second.mCell; - esm.startRecord(ESM::REC_CSTA); - ESM::CellState csta; - csta.mHasFogOfWar = 0; - csta.mId = cell.getCellId(); - csta.mId.save(esm); - // TODO csta.mLastRespawn; - // shouldn't be needed if we respawn on global schedule like in original MW - csta.mWaterLevel = cell.mWater; - csta.save(esm); - - for (std::vector::const_iterator refIt = it->second.mRefs.begin(); refIt != it->second.mRefs.end(); ++refIt) - { - const CellRef& cellref = *refIt; - ESM::CellRef out; - out.blank(); + const CellRef& cellref = *refIt; + ESM::CellRef out; + out.blank(); - if (cellref.mIndexedRefId.size() < 8) - { - std::cerr << "CellRef with no index?" << std::endl; - continue; - } - std::stringstream stream; - stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); - int refIndex; - stream >> refIndex; + if (cellref.mIndexedRefId.size() < 8) + { + std::cerr << "CellRef with no index?" << std::endl; + continue; + } + std::stringstream stream; + stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); + int refIndex; + stream >> refIndex; - out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); + out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); - std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (crecIt != mContext->mCreatureChanges.end()) - { - ESM::CreatureState objstate; - objstate.blank(); - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertCREC(crecIt->second, objstate); - objstate.mEnabled = cellref.mEnabled; - objstate.mPosition = cellref.mPos; - objstate.mRef = out; - objstate.mRef.mRefNum = cellref.mRefNum; - // FIXME: change save format to not require object type, instead look up it up using the RefId - esm.writeHNT ("OBJE", ESM::REC_CREA); - objstate.save(esm); - continue; - } + std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (crecIt != mContext->mCreatureChanges.end()) + { + ESM::CreatureState objstate; + objstate.blank(); + objstate.mRef = out; + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertCREC(crecIt->second, objstate); + convertCellRef(cellref, objstate); + // FIXME: change save format to not require object type, instead look up it up using the RefId + esm.writeHNT ("OBJE", ESM::REC_CREA); + objstate.save(esm); + continue; + } - std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (npccIt != mContext->mNpcChanges.end()) - { - ESM::NpcState objstate; - objstate.blank(); - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertNpcData(cellref.mActorData, objstate.mNpcStats); - convertNPCC(npccIt->second, objstate); - objstate.mEnabled = cellref.mEnabled; - objstate.mPosition = cellref.mPos; - objstate.mRef = out; - objstate.mRef.mRefNum = cellref.mRefNum; - esm.writeHNT ("OBJE", ESM::REC_NPC_); - objstate.save(esm); - continue; - } + std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (npccIt != mContext->mNpcChanges.end()) + { + ESM::NpcState objstate; + objstate.blank(); + objstate.mRef = out; + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertNpcData(cellref.mActorData, objstate.mNpcStats); + convertNPCC(npccIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_NPC_); + objstate.save(esm); + continue; + } - std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl; + std::map, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (cntcIt != mContext->mContainerChanges.end()) + { + ESM::ContainerState objstate; + objstate.blank(); + objstate.mRef = out; + convertCNTC(cntcIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_CONT); + objstate.save(esm); + continue; } - esm.endRecord(ESM::REC_CSTA); + std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl; } + esm.endRecord(ESM::REC_CSTA); + } + + void ConvertCell::write(ESM::ESMWriter &esm) + { + for (std::map::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it) + writeCell(it->second, esm); + + for (std::map, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it) + writeCell(it->second, esm); + for (std::vector::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) { esm.startRecord(ESM::REC_MARK); diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index aee60f2e75..e758db964e 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -12,6 +12,7 @@ #include #include "importcrec.hpp" +#include "importcntc.hpp" #include "importercontext.hpp" #include "importcellref.hpp" @@ -94,7 +95,8 @@ public: mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayerBase = npc; std::map empty; - // FIXME: not working? + // FIXME: player start spells, racial spells and birthsign spells aren't listed here, + // need to fix openmw to account for this for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; } @@ -209,6 +211,17 @@ public: } }; +class ConvertCNTC : public Converter +{ + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + CNTC cntc; + cntc.load(esm); + mContext->mContainerChanges.insert(std::make_pair(std::make_pair(cntc.mIndex,id), cntc)); + } +}; + class ConvertCREC : public Converter { public: @@ -242,9 +255,12 @@ private: std::vector mFogOfWar; }; - std::map mCells; + std::map mIntCells; + std::map, Cell> mExtCells; std::vector mMarkers; + + void writeCell(const Cell& cell, ESM::ESMWriter &esm); }; class ConvertKLST : public Converter diff --git a/apps/essimporter/importcntc.cpp b/apps/essimporter/importcntc.cpp new file mode 100644 index 0000000000..a492aef5aa --- /dev/null +++ b/apps/essimporter/importcntc.cpp @@ -0,0 +1,16 @@ +#include "importcntc.hpp" + +#include + +namespace ESSImport +{ + + void CNTC::load(ESM::ESMReader &esm) + { + mIndex = 0; + esm.getHNT(mIndex, "INDX"); + + mInventory.load(esm); + } + +} diff --git a/apps/essimporter/importcntc.hpp b/apps/essimporter/importcntc.hpp new file mode 100644 index 0000000000..1bc7d94bd5 --- /dev/null +++ b/apps/essimporter/importcntc.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTCNTC_H +#define OPENMW_ESSIMPORT_IMPORTCNTC_H + +#include "importinventory.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + /// Changed container contents + struct CNTC + { + int mIndex; + + Inventory mInventory; + + void load(ESM::ESMReader& esm); + }; + +} +#endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 4cf41a8619..9075db4047 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -215,6 +215,7 @@ namespace ESSImport converters[ESM::REC_WEAP] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_CNTC] = boost::shared_ptr(new ConvertCNTC()); std::set unknownRecords; diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 9183e61796..a921b81ef7 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -8,6 +8,7 @@ #include "importnpcc.hpp" #include "importcrec.hpp" +#include "importcntc.hpp" #include "importplayer.hpp" @@ -30,6 +31,7 @@ namespace ESSImport // key std::map, CREC> mCreatureChanges; std::map, NPCC> mNpcChanges; + std::map, CNTC> mContainerChanges; Context() { diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 93228f87a7..a0500127c9 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -57,7 +57,7 @@ add_component_dir (to_utf8 add_component_dir (esm attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell - loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst + loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter diff --git a/components/esm/loadcrec.hpp b/components/esm/loadcrec.hpp deleted file mode 100644 index 280739acad..0000000000 --- a/components/esm/loadcrec.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef OPENMW_ESM_CREC_H -#define OPENMW_ESM_CREC_H - -#include - -// TODO create implementation files and remove this one -#include "esmreader.hpp" - -namespace ESM { - -class ESMReader; -class ESMWriter; - -/* These two are only used in save games. They are not decoded yet. - */ - -/// Changes a creature -struct LoadCREC -{ - static unsigned int sRecordId; - - std::string mId; - - void load(ESMReader &esm) - { - esm.skipRecord(); - } - - void save(ESMWriter &esm) const - { - } -}; - -/// Changes an item list / container -struct LoadCNTC -{ - std::string mId; - - void load(ESMReader &esm) - { - esm.skipRecord(); - } - - void save(ESMWriter &esm) const - { - } -}; -} -#endif diff --git a/components/esm/records.hpp b/components/esm/records.hpp index 7a0452eb3f..c01c89d57d 100644 --- a/components/esm/records.hpp +++ b/components/esm/records.hpp @@ -14,7 +14,6 @@ #include "loadclot.hpp" #include "loadcont.hpp" #include "loadcrea.hpp" -#include "loadcrec.hpp" #include "loadinfo.hpp" #include "loaddial.hpp" #include "loaddoor.hpp" From 9014dc48ee166ff9d3e5750971787056a318356e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 23:29:06 +0100 Subject: [PATCH 351/404] Don't require the object type id for reading references from savegames This is redundant, since we can look it up from the RefID. --- apps/openmw/mwclass/creature.cpp | 10 ++++- apps/openmw/mwclass/npc.cpp | 9 +++++ apps/openmw/mwworld/cellstore.cpp | 65 +++++++++++++++++++------------ components/esm/cellref.cpp | 8 +++- components/esm/cellref.hpp | 3 ++ components/esm/creaturestate.cpp | 14 +++++-- components/esm/esmreader.cpp | 11 ++++++ components/esm/esmreader.hpp | 3 ++ components/esm/inventorystate.cpp | 1 + components/esm/npcstate.cpp | 19 ++++++--- components/esm/objectstate.cpp | 10 ++++- components/esm/objectstate.hpp | 8 ++++ components/esm/player.cpp | 1 + 13 files changed, 123 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a3614af968..2e53fe8025 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -816,6 +816,9 @@ namespace MWClass void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; + const ESM::CreatureState& state2 = dynamic_cast (state); ensureCustomData(ptr); @@ -844,7 +847,6 @@ namespace MWClass customData.mContainerStore->readState (state2.mInventory); customData.mCreatureStats.readState (state2.mCreatureStats); - } void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -852,6 +854,12 @@ namespace MWClass { ESM::CreatureState& state2 = dynamic_cast (state); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } + ensureCustomData (ptr); CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ac41827340..8f62cc1e81 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1275,6 +1275,9 @@ namespace MWClass void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; + const ESM::NpcState& state2 = dynamic_cast (state); ensureCustomData(ptr); @@ -1302,6 +1305,12 @@ namespace MWClass { ESM::NpcState& state2 = dynamic_cast (state); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } + ensureCustomData (ptr); NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 4cf99fdb75..605c8f9d2a 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -85,7 +85,9 @@ namespace RecordType state; iter->save (state); + // recordId currently unused writer.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId); + state.save (writer); } } @@ -93,12 +95,13 @@ namespace template void readReferenceCollection (ESM::ESMReader& reader, - MWWorld::CellRefList& collection, const std::map& contentFileMap) + MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); RecordType state; - state.load (reader); + state.mRef = cref; + state.load(reader); // If the reference came from a content file, make sure this content file is loaded if (state.mRef.mRefNum.hasContentFile()) @@ -640,109 +643,121 @@ namespace MWWorld while (reader.isNextSub ("OBJE")) { - unsigned int id = 0; - reader.getHT (id); + unsigned int unused; + reader.getHT (unused); + + // load the RefID first so we know what type of object it is + ESM::CellRef cref; + cref.loadId(reader, true); + + int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); + if (type == 0) + { + std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; + reader.skipHSubUntil("OBJE"); + continue; + } - switch (id) + switch (type) { case ESM::REC_ACTI: - readReferenceCollection (reader, mActivators, contentFileMap); + readReferenceCollection (reader, mActivators, cref, contentFileMap); break; case ESM::REC_ALCH: - readReferenceCollection (reader, mPotions, contentFileMap); + readReferenceCollection (reader, mPotions, cref, contentFileMap); break; case ESM::REC_APPA: - readReferenceCollection (reader, mAppas, contentFileMap); + readReferenceCollection (reader, mAppas, cref, contentFileMap); break; case ESM::REC_ARMO: - readReferenceCollection (reader, mArmors, contentFileMap); + readReferenceCollection (reader, mArmors, cref, contentFileMap); break; case ESM::REC_BOOK: - readReferenceCollection (reader, mBooks, contentFileMap); + readReferenceCollection (reader, mBooks, cref, contentFileMap); break; case ESM::REC_CLOT: - readReferenceCollection (reader, mClothes, contentFileMap); + readReferenceCollection (reader, mClothes, cref, contentFileMap); break; case ESM::REC_CONT: - readReferenceCollection (reader, mContainers, contentFileMap); + readReferenceCollection (reader, mContainers, cref, contentFileMap); break; case ESM::REC_CREA: - readReferenceCollection (reader, mCreatures, contentFileMap); + readReferenceCollection (reader, mCreatures, cref, contentFileMap); break; case ESM::REC_DOOR: - readReferenceCollection (reader, mDoors, contentFileMap); + readReferenceCollection (reader, mDoors, cref, contentFileMap); break; case ESM::REC_INGR: - readReferenceCollection (reader, mIngreds, contentFileMap); + readReferenceCollection (reader, mIngreds, cref, contentFileMap); break; case ESM::REC_LEVC: - readReferenceCollection (reader, mCreatureLists, contentFileMap); + readReferenceCollection (reader, mCreatureLists, cref, contentFileMap); break; case ESM::REC_LEVI: - readReferenceCollection (reader, mItemLists, contentFileMap); + readReferenceCollection (reader, mItemLists, cref, contentFileMap); break; case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, contentFileMap); + readReferenceCollection (reader, mLights, cref, contentFileMap); break; case ESM::REC_LOCK: - readReferenceCollection (reader, mLockpicks, contentFileMap); + readReferenceCollection (reader, mLockpicks, cref, contentFileMap); break; case ESM::REC_MISC: - readReferenceCollection (reader, mMiscItems, contentFileMap); + readReferenceCollection (reader, mMiscItems, cref, contentFileMap); break; case ESM::REC_NPC_: - readReferenceCollection (reader, mNpcs, contentFileMap); + readReferenceCollection (reader, mNpcs, cref, contentFileMap); break; case ESM::REC_PROB: - readReferenceCollection (reader, mProbes, contentFileMap); + readReferenceCollection (reader, mProbes, cref, contentFileMap); break; case ESM::REC_REPA: - readReferenceCollection (reader, mRepairs, contentFileMap); + readReferenceCollection (reader, mRepairs, cref, contentFileMap); break; case ESM::REC_STAT: - readReferenceCollection (reader, mStatics, contentFileMap); + readReferenceCollection (reader, mStatics, cref, contentFileMap); break; case ESM::REC_WEAP: - readReferenceCollection (reader, mWeapons, contentFileMap); + readReferenceCollection (reader, mWeapons, cref, contentFileMap); break; default: diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 0c2dfd5dbc..cd9fe85700 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -5,6 +5,12 @@ #include "esmwriter.hpp" void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) +{ + loadId(esm, wideRefNum); + loadData(esm); +} + +void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum) { // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. @@ -19,8 +25,6 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHNT (mRefNum.mIndex, "FRMR"); mRefID = esm.getHNString ("NAME"); - - loadData(esm); } void ESM::CellRef::loadData(ESMReader &esm) diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index b7b00a6548..7aa0e4d44c 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -89,8 +89,11 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; + /// Calls loadId and loadData void load (ESMReader& esm, bool wideRefNum = false); + void loadId (ESMReader& esm, bool wideRefNum = false); + /// Implicitly called by load void loadData (ESMReader& esm); diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp index c2838f78db..c15becd981 100644 --- a/components/esm/creaturestate.cpp +++ b/components/esm/creaturestate.cpp @@ -5,18 +5,24 @@ void ESM::CreatureState::load (ESMReader &esm) { ObjectState::load (esm); - mInventory.load (esm); + if (mHasCustomState) + { + mInventory.load (esm); - mCreatureStats.load (esm); + mCreatureStats.load (esm); + } } void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); - mInventory.save (esm); + if (mHasCustomState) + { + mInventory.save (esm); - mCreatureStats.save (esm); + mCreatureStats.save (esm); + } } void ESM::CreatureState::blank() diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 7cf0de1a99..de110dd188 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -210,6 +210,17 @@ void ESMReader::skipHSubSize(int size) fail("skipHSubSize() mismatch"); } +void ESMReader::skipHSubUntil(const char *name) +{ + while (hasMoreSubs() && !isNextSub(name)) + { + mCtx.subCached = false; + skipHSub(); + } + if (hasMoreSubs()) + mCtx.subCached = true; +} + void ESMReader::getSubHeader() { if (mCtx.leftRec < 4) diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 642ec4168e..bb615af111 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -195,6 +195,9 @@ public: // Skip sub record and check its size void skipHSubSize(int size); + // Skip all subrecords until the given subrecord or no more subrecords remaining + void skipHSubUntil(const char* name); + /* Sub-record header. This updates leftRec beyond the current sub-record as well. leftSub contains size of current sub-record. */ diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index fdaba33228..b946f9501c 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -11,6 +11,7 @@ namespace slot = -1; esm.getHNOT (slot, "SLOT"); + state.mRef.loadId(esm, true); state.load (esm); } diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp index 6134193c24..724d673265 100644 --- a/components/esm/npcstate.cpp +++ b/components/esm/npcstate.cpp @@ -5,22 +5,28 @@ void ESM::NpcState::load (ESMReader &esm) { ObjectState::load (esm); - mInventory.load (esm); + if (mHasCustomState) + { + mInventory.load (esm); - mNpcStats.load (esm); + mNpcStats.load (esm); - mCreatureStats.load (esm); + mCreatureStats.load (esm); + } } void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); - mInventory.save (esm); + if (mHasCustomState) + { + mInventory.save (esm); - mNpcStats.save (esm); + mNpcStats.save (esm); - mCreatureStats.save (esm); + mCreatureStats.save (esm); + } } void ESM::NpcState::blank() @@ -28,4 +34,5 @@ void ESM::NpcState::blank() ObjectState::blank(); mNpcStats.blank(); mCreatureStats.blank(); + mHasCustomState = true; } diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 98cf3eba66..66fd496633 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -6,7 +6,7 @@ void ESM::ObjectState::load (ESMReader &esm) { - mRef.load (esm, true); + mRef.loadData(esm); mHasLocals = 0; esm.getHNOT (mHasLocals, "HLOC"); @@ -27,6 +27,10 @@ void ESM::ObjectState::load (ESMReader &esm) // used for lights only mTime = 0; esm.getHNOT (mTime, "LTIM"); + + // FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files + mHasCustomState = true; + esm.getHNOT (mHasCustomState, "HCUS"); } void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const @@ -53,6 +57,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const if (mTime) esm.writeHNT ("LTIM", mTime); + + if (!mHasCustomState) + esm.writeHNT ("HCUS", false); } void ESM::ObjectState::blank() @@ -68,6 +75,7 @@ void ESM::ObjectState::blank() mLocalRotation[i] = 0; } mTime = 0; + mHasCustomState = true; } ESM::ObjectState::~ObjectState() {} diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 6b536bd012..b036896534 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -28,7 +28,15 @@ namespace ESM float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either + // Is there any class-specific state following the ObjectState + bool mHasCustomState; + + ObjectState() : mHasCustomState(true) + {} + + /// @note Does not load the CellRef ID, it should already be loaded before calling this method virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; /// Initialize to default state diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 52b44c945f..70c4b79e25 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -6,6 +6,7 @@ void ESM::Player::load (ESMReader &esm) { + mObject.mRef.loadId(esm, true); mObject.load (esm); mCellId.load (esm); From 40c29abe20445d8cdd61b3dc63ce8e63891757c4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 23:51:43 +0100 Subject: [PATCH 352/404] ESSImport: convert other references (non-creature/npc/container) --- apps/essimporter/converter.cpp | 129 ++++++++++++++++++----------- apps/essimporter/converter.hpp | 9 +- apps/essimporter/importcellref.cpp | 9 +- apps/essimporter/importnpcc.cpp | 3 - apps/essimporter/importnpcc.hpp | 2 - 5 files changed, 95 insertions(+), 57 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 293c48624f..83ea077eae 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -26,6 +26,17 @@ namespace objstate.mPosition = cellref.mPos; objstate.mRef.mRefNum = cellref.mRefNum; } + + bool isIndexedRefId(const std::string& indexedRefId) + { + if (indexedRefId.size() <= 8) + return false; + + std::string index = indexedRefId.substr(indexedRefId.size()-8); + if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos ) + return true; + return false; + } } namespace ESSImport @@ -191,65 +202,87 @@ namespace ESSImport ESM::CellRef out; out.blank(); - if (cellref.mIndexedRefId.size() < 8) + if (!isIndexedRefId(cellref.mIndexedRefId)) { - std::cerr << "CellRef with no index?" << std::endl; - continue; - } - std::stringstream stream; - stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); - int refIndex; - stream >> refIndex; + // non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it + // this could be any type of object really (even creatures/npcs too) + out.mRefID = cellref.mIndexedRefId; + std::string idLower = Misc::StringUtils::lowerCase(out.mRefID); - out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); - - std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (crecIt != mContext->mCreatureChanges.end()) - { - ESM::CreatureState objstate; + ESM::ObjectState objstate; objstate.blank(); objstate.mRef = out; - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertCREC(crecIt->second, objstate); + objstate.mRef.mRefID = idLower; + objstate.mHasCustomState = false; convertCellRef(cellref, objstate); - // FIXME: change save format to not require object type, instead look up it up using the RefId - esm.writeHNT ("OBJE", ESM::REC_CREA); + esm.writeHNT ("OBJE", 0); objstate.save(esm); continue; } - - std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (npccIt != mContext->mNpcChanges.end()) + else { - ESM::NpcState objstate; - objstate.blank(); - objstate.mRef = out; - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertNpcData(cellref.mActorData, objstate.mNpcStats); - convertNPCC(npccIt->second, objstate); - convertCellRef(cellref, objstate); - esm.writeHNT ("OBJE", ESM::REC_NPC_); - objstate.save(esm); - continue; - } + std::stringstream stream; + stream << std::hex << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); + int refIndex; + stream >> refIndex; - std::map, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (cntcIt != mContext->mContainerChanges.end()) - { - ESM::ContainerState objstate; - objstate.blank(); - objstate.mRef = out; - convertCNTC(cntcIt->second, objstate); - convertCellRef(cellref, objstate); - esm.writeHNT ("OBJE", ESM::REC_CONT); - objstate.save(esm); - continue; - } + out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); + std::string idLower = Misc::StringUtils::lowerCase(out.mRefID); + + std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (npccIt != mContext->mNpcChanges.end()) + { + ESM::NpcState objstate; + objstate.blank(); + objstate.mRef = out; + objstate.mRef.mRefID = idLower; + // probably need more micromanagement here so we don't overwrite values + // from the ESM with default values + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertNpcData(cellref.mActorData, objstate.mNpcStats); + convertNPCC(npccIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_NPC_); + objstate.save(esm); + continue; + } + + std::map, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (cntcIt != mContext->mContainerChanges.end()) + { + ESM::ContainerState objstate; + objstate.blank(); + objstate.mRef = out; + objstate.mRef.mRefID = idLower; + convertCNTC(cntcIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_CONT); + objstate.save(esm); + continue; + } - std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl; + std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (crecIt != mContext->mCreatureChanges.end()) + { + ESM::CreatureState objstate; + objstate.blank(); + objstate.mRef = out; + objstate.mRef.mRefID = idLower; + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + // probably need more micromanagement here so we don't overwrite values + // from the ESM with default values + convertCREC(crecIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_CREA); + objstate.save(esm); + continue; + } + + std::cerr << "Can't find type for " << cellref.mIndexedRefId << std::endl; + } } esm.endRecord(ESM::REC_CSTA); diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index e758db964e..187386b9de 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -167,8 +167,14 @@ public: convertNPCC(npcc, mContext->mPlayer.mObject); } else - mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), npcc)); + { + int index = mIndexCounter[id]++; + mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)).second; + } } + +private: + std::map mIndexCounter; }; class ConvertREFR : public Converter @@ -230,7 +236,6 @@ public: std::string id = esm.getHNString("NAME"); CREC crec; crec.load(esm); - mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec)); } }; diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 8c2b8f36d5..fd58d5bc1b 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -18,8 +18,13 @@ namespace ESSImport mIndexedRefId = esm.getHNString("NAME"); if (esm.isNextSub("LVCR")) - esm.skipHSub(); - + { + // occurs on leveled creature spawner references + // probably some identifier for the the creature that has been spawned? + unsigned char lvcr; + esm.getHT(lvcr); + //std::cout << "LVCR: " << (int)lvcr << std::endl; + } mActorData.load(esm); mEnabled = true; diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp index 8400a9e4ac..c389ede7f9 100644 --- a/apps/essimporter/importnpcc.cpp +++ b/apps/essimporter/importnpcc.cpp @@ -7,9 +7,6 @@ namespace ESSImport void NPCC::load(ESM::ESMReader &esm) { - mIndex = 0; - esm.getHNOT(mIndex, "INDX"); - esm.getHNT(mNPDT, "NPDT"); if (esm.isNextSub("AI_E")) diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index f3c4e24c7c..2f474aba07 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -26,8 +26,6 @@ namespace ESSImport Inventory mInventory; - int mIndex; - void load(ESM::ESMReader &esm); }; From 5104a5a0235061454c8aae984e88dce6b928be8c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 23:55:17 +0100 Subject: [PATCH 353/404] Add missing setFactionReaction instruction, use absolute storage instead of difference Seems to be closer to how MW is storing it (it has the complete FACT record in the savegame, actually). This (somewhat) breaks OMW savegame compatibility in that old changes are discarded, but I don't think the faction reactions are quest relevant anywhere. --- apps/openmw/mwbase/dialoguemanager.hpp | 2 ++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 36 ++++++++++++------- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 ++- apps/openmw/mwscript/dialogueextensions.cpp | 20 +++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 3 +- components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 1 + components/esm/dialoguestate.cpp | 17 ++++++--- components/esm/dialoguestate.hpp | 2 +- 9 files changed, 66 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index d0e64b23c8..d35b5950f0 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -74,6 +74,8 @@ namespace MWBase /// Changes faction1's opinion of faction2 by \a diff. virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff) = 0; + virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute) = 0; + /// @return faction1's opinion of faction2 virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index dd0fa21fae..0ee7dfe94c 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -640,7 +640,7 @@ namespace MWDialogue if (iter->second) state.mKnownTopics.push_back (iter->first); - state.mModFactionReaction = mModFactionReaction; + state.mChangedFactionReaction = mChangedFactionReaction; writer.startRecord (ESM::REC_DIAS); state.save (writer); @@ -661,7 +661,7 @@ namespace MWDialogue if (store.get().search (*iter)) mKnownTopics.insert (std::make_pair (*iter, true)); - mModFactionReaction = state.mModFactionReaction; + mChangedFactionReaction = state.mChangedFactionReaction; } } @@ -674,10 +674,23 @@ namespace MWDialogue MWBase::Environment::get().getWorld()->getStore().get().find(fact1); MWBase::Environment::get().getWorld()->getStore().get().find(fact2); - std::map& map = mModFactionReaction[fact1]; - if (map.find(fact2) == map.end()) - map[fact2] = 0; - map[fact2] += diff; + int newValue = getFactionReaction(faction1, faction2) + diff; + + std::map& map = mChangedFactionReaction[fact1]; + map[fact2] = newValue; + } + + void DialogueManager::setFactionReaction(const std::string &faction1, const std::string &faction2, int absolute) + { + std::string fact1 = Misc::StringUtils::lowerCase(faction1); + std::string fact2 = Misc::StringUtils::lowerCase(faction2); + + // Make sure the factions exist + MWBase::Environment::get().getWorld()->getStore().get().find(fact1); + MWBase::Environment::get().getWorld()->getStore().get().find(fact2); + + std::map& map = mChangedFactionReaction[fact1]; + map[fact2] = absolute; } int DialogueManager::getFactionReaction(const std::string &faction1, const std::string &faction2) const @@ -685,10 +698,9 @@ namespace MWDialogue std::string fact1 = Misc::StringUtils::lowerCase(faction1); std::string fact2 = Misc::StringUtils::lowerCase(faction2); - ModFactionReactionMap::const_iterator map = mModFactionReaction.find(fact1); - int diff = 0; - if (map != mModFactionReaction.end() && map->second.find(fact2) != map->second.end()) - diff = map->second.at(fact2); + ModFactionReactionMap::const_iterator map = mChangedFactionReaction.find(fact1); + if (map != mChangedFactionReaction.end() && map->second.find(fact2) != map->second.end()) + return map->second.at(fact2); const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().find(fact1); @@ -696,9 +708,9 @@ namespace MWDialogue for (; it != faction->mReactions.end(); ++it) { if (Misc::StringUtils::ciEqual(it->first, fact2)) - return it->second + diff; + return it->second; } - return diff; + return 0; } void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 9c3d0d54b7..c06299736f 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -27,7 +27,7 @@ namespace MWDialogue // Modified faction reactions. > typedef std::map > ModFactionReactionMap; - ModFactionReactionMap mModFactionReaction; + ModFactionReactionMap mChangedFactionReaction; std::list mActorKnownTopics; @@ -97,6 +97,8 @@ namespace MWDialogue /// Changes faction1's opinion of faction2 by \a diff. virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff); + virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute); + /// @return faction1's opinion of faction2 virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const; diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 563a9dde3a..1d82b84181 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -236,6 +236,25 @@ namespace MWScript } }; + class OpSetFactionReaction : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + int newValue = runtime[0].mInteger; + runtime.pop(); + + MWBase::Environment::get().getDialogueManager()->setFactionReaction(faction1, faction2, newValue); + } + }; + template class OpClearInfoActor : public Interpreter::Opcode0 { @@ -268,6 +287,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction); + interpreter.installSegment5 (Compiler::Dialogue::opcodeSetFactionReaction, new OpSetFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor); interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 172e1b528a..b139d61388 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -442,5 +442,6 @@ op 0x20002fb: AddToLevCreature op 0x20002fc: RemoveFromLevCreature op 0x20002fd: AddToLevItem op 0x20002fe: RemoveFromLevItem +op 0x20002ff: SetFactionReaction -opcodes 0x20002ff-0x3ffffff unused +opcodes 0x2000300-0x3ffffff unused diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 234c5b12d4..a5cc0da626 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -192,6 +192,7 @@ namespace Compiler extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction, opcodeSameFactionExplicit); extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction); + extensions.registerInstruction("setfactionreaction", "ccl", opcodeSetFactionReaction); extensions.registerFunction("getfactionreaction", 'l', "ccX", opcodeGetFactionReaction); extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit); } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index bbafd6b13b..04666e9760 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -167,6 +167,7 @@ namespace Compiler const int opcodeSameFaction = 0x20001b5; const int opcodeSameFactionExplicit = 0x20001b6; const int opcodeModFactionReaction = 0x2000242; + const int opcodeSetFactionReaction = 0x20002ff; const int opcodeGetFactionReaction = 0x2000243; const int opcodeClearInfoActor = 0x2000245; const int opcodeClearInfoActorExplicit = 0x2000246; diff --git a/components/esm/dialoguestate.cpp b/components/esm/dialoguestate.cpp index 14301ac198..f546462a3e 100644 --- a/components/esm/dialoguestate.cpp +++ b/components/esm/dialoguestate.cpp @@ -13,13 +13,20 @@ void ESM::DialogueState::load (ESMReader &esm) { std::string faction = esm.getHString(); - while (esm.isNextSub ("REAC")) + while (esm.isNextSub("REA2")) { std::string faction2 = esm.getHString(); int reaction; esm.getHNT(reaction, "INTV"); + mChangedFactionReaction[faction][faction2] = reaction; + } - mModFactionReaction[faction][faction2] = reaction; + // no longer used + while (esm.isNextSub ("REAC")) + { + esm.skipHSub(); + esm.getSubHeader(); + esm.skipHSub(); } } } @@ -32,15 +39,15 @@ void ESM::DialogueState::save (ESMWriter &esm) const esm.writeHNString ("TOPI", *iter); } - for (std::map >::const_iterator iter = mModFactionReaction.begin(); - iter != mModFactionReaction.end(); ++iter) + for (std::map >::const_iterator iter = mChangedFactionReaction.begin(); + iter != mChangedFactionReaction.end(); ++iter) { esm.writeHNString ("FACT", iter->first); for (std::map::const_iterator reactIter = iter->second.begin(); reactIter != iter->second.end(); ++reactIter) { - esm.writeHNString ("REAC", reactIter->first); + esm.writeHNString ("REA2", reactIter->first); esm.writeHNT ("INTV", reactIter->second); } } diff --git a/components/esm/dialoguestate.hpp b/components/esm/dialoguestate.hpp index 5e5f602a30..1adade5a04 100644 --- a/components/esm/dialoguestate.hpp +++ b/components/esm/dialoguestate.hpp @@ -16,7 +16,7 @@ namespace ESM { std::vector mKnownTopics; - std::map > mModFactionReaction; + std::map > mChangedFactionReaction; void load (ESMReader &esm); void save (ESMWriter &esm) const; From 6d5bb57e00ea23f573731f7ed15f31f928a76576 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 01:21:44 +0100 Subject: [PATCH 354/404] ESSImport: fix loading item stacks in containers --- apps/essimporter/importinventory.cpp | 43 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index b3bd4a86be..b4d1935409 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -18,28 +18,35 @@ namespace ESSImport item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; - if (esm.isNextSub("XIDX")) - esm.skipHSub(); - - std::string script = esm.getHNOString("SCRI"); - // script variables? - // unsure if before or after ESM::CellRef - if (!script.empty()) + // seems that a stack of items can have a set of subrecords for each item? rings0000.ess + // doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place? + // I guess we should double check the stacking logic in OpenMW + for (int i=0;i Date: Tue, 20 Jan 2015 15:24:33 +0100 Subject: [PATCH 355/404] Fix broken windows builds due to README change --- CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bbce7ad42..938162b017 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -457,8 +457,9 @@ if(WIN32) FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll") INSTALL(FILES ${dll_files} DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") + INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt") + INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt") INSTALL(FILES - "${OpenMW_SOURCE_DIR}/readme.txt" "${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt" "${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt" "${OpenMW_BINARY_DIR}/settings-default.cfg" @@ -502,13 +503,13 @@ if(WIN32) IF(BUILD_WIZARD) SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard") ENDIF(BUILD_WIZARD) - SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'") + SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\README.txt'") SET(CPACK_NSIS_DELETE_ICONS_EXTRA " !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Readme.lnk\\\" ") - SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/readme.txt") - SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt") + SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/README.md") + SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/README.md") SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}") SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") From 06736e9e0317037c0095971caff2213a9a94fa29 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 01:28:34 +0100 Subject: [PATCH 356/404] ESSImport: convert faction reactions and known dialogue topics --- apps/essimporter/converter.hpp | 26 ++++++++++++++++++++++++++ apps/essimporter/importer.cpp | 7 +++++++ apps/essimporter/importercontext.hpp | 3 +++ apps/essimporter/importplayer.cpp | 1 - components/esm/dialoguestate.hpp | 2 ++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 187386b9de..62729cd030 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include "importcrec.hpp" @@ -214,6 +216,12 @@ public: mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction; } + for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); + it != pcdt.mKnownDialogueTopics.end(); ++it) + { + mContext->mDialogueState.mKnownTopics.push_back(Misc::StringUtils::lowerCase(*it)); + } + } }; @@ -295,6 +303,24 @@ private: std::map mKillCounter; }; +class ConvertFACT : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + std::string id = esm.getHNString("NAME"); + ESM::Faction faction; + faction.load(esm); + + Misc::StringUtils::toLower(id); + for (std::map::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it) + { + std::string faction2 = Misc::StringUtils::lowerCase(it->first); + mContext->mDialogueState.mChangedFactionReaction[id].insert(std::make_pair(faction2, it->second)); + } + } +}; + } #endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 9075db4047..e73aaafae3 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -181,6 +181,8 @@ namespace ESSImport Ogre::LogManager logman; Ogre::Root root; + // TODO: set up encoding on ESMReader based on openmw.cfg / --encoding switch + ESM::ESMReader esm; esm.open(mEssFile); @@ -216,6 +218,7 @@ namespace ESSImport converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CNTC] = boost::shared_ptr(new ConvertCNTC()); + converters[ESM::REC_FACT] = boost::shared_ptr(new ConvertFACT()); std::set unknownRecords; @@ -322,6 +325,10 @@ namespace ESSImport } context.mPlayer.save(writer); writer.endRecord(ESM::REC_PLAY); + + writer.startRecord (ESM::REC_DIAS); + context.mDialogueState.save(writer); + writer.endRecord(ESM::REC_DIAS); } diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index a921b81ef7..02585799a6 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -5,6 +5,7 @@ #include #include +#include #include "importnpcc.hpp" #include "importcrec.hpp" @@ -25,6 +26,8 @@ namespace ESSImport ESM::NPC mPlayerBase; std::string mCustomPlayerClassName; + ESM::DialogueState mDialogueState; + int mDay, mMonth, mYear; float mHour; diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index 8a57ae5886..f94d3aacc3 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -20,7 +20,6 @@ namespace ESSImport { while (esm.isNextSub("DNAM")) { - // TODO: deal with encoding? mKnownDialogueTopics.push_back(esm.getHString()); } diff --git a/components/esm/dialoguestate.hpp b/components/esm/dialoguestate.hpp index 1adade5a04..d7cdb941c2 100644 --- a/components/esm/dialoguestate.hpp +++ b/components/esm/dialoguestate.hpp @@ -14,8 +14,10 @@ namespace ESM struct DialogueState { + // must be lower case topic IDs std::vector mKnownTopics; + // must be lower case faction IDs std::map > mChangedFactionReaction; void load (ESMReader &esm); From 25ff45d819290feaa25a344ffdd491509a25083c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 15:44:47 +0100 Subject: [PATCH 357/404] Update command line options in Readme --- README.md | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index bb59b7e02c..7c24a4c815 100644 --- a/README.md +++ b/README.md @@ -41,51 +41,58 @@ Command line options --version print version information and quit --data arg (=data) set data directories (later directories have higher priority) - --data-local arg set local data directory (highest + --data-local arg set local data directory (highest priority) --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later + set fallback BSA archives (later archives have higher priority) --resources arg (=resources) set resources directory - --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or + --start arg set initial cell + --content arg content file(s): esm/esp, or omwgame/omwaddon - --anim-verbose [=arg(=1)] (=0) output animation indices files --no-sound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script + --script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup + --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file containing a list of - console commands that is executed on + --script-run arg select a file containing a list of + console commands that is executed on startup - --script-warn [=arg(=1)] (=1) handling of warnings when compiling + --script-warn [=arg(=1)] (=1) handling of warnings when compiling scripts 0 - ignore warning 1 - show warning but consider script as correctly compiled anyway 2 - treat warnings as errors + --script-blacklist arg ignore the specified script (if the use + of the blacklist is enabled) + --script-blacklist-use [=arg(=1)] (=1) + enable script blacklisting + --load-savegame arg load a save game file on game startup --skip-menu [=arg(=1)] (=0) skip main menu on game startup - --new-game [=arg(=1)] (=0) run new game sequence (ignored if + --new-game [=arg(=1)] (=0) run new game sequence (ignored if skip-menu=0) - --fs-strict [=arg(=1)] (=0) strict file system handling (no case + --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) - --encoding arg (=win1252) Character encoding used in OpenMW game + --encoding arg (=win1252) Character encoding used in OpenMW game messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic and other languages - - win1252 - Western European (Latin) + + win1252 - Western European (Latin) alphabet, used by default --fallback arg fallback values --no-grab Don't grab mouse cursor + --export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG + image and XML file in current directory --activate-dist arg (=-1) activation distance override From 3a88f4ebd5fbaadae05b74a604399d78157392da Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Tue, 20 Jan 2015 17:07:26 +0100 Subject: [PATCH 358/404] Fix windows build, MessageBox is a defined symbol Windows and its stupid defines... --- apps/openmw/mwgui/messagebox.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 9cb778ea2d..cdbcf784df 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -12,6 +12,8 @@ #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" +#undef MessageBox + namespace MWGui { From 1375a4e4bb36ac144ba9511451feb1669783b1a3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 16:06:15 +0100 Subject: [PATCH 359/404] ESSImport: read stolen items (not converted yet) --- apps/essimporter/converter.hpp | 24 ++++++++++++++++++++++++ apps/essimporter/importer.cpp | 10 ++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 62729cd030..716ddc9c76 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -321,6 +321,30 @@ public: } }; +/// Stolen items +class ConvertSTLN : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string itemid = esm.getHNString("NAME"); + + while (esm.isNextSub("ONAM")) + { + std::string ownerid = esm.getHString(); + mStolenItems.insert(std::make_pair(itemid, ownerid)); + } + while (esm.isNextSub("FNAM")) + { + std::string factionid = esm.getHString(); + mFactionStolenItems.insert(std::make_pair(itemid, factionid)); + } + } +private: + std::multimap mStolenItems; + std::multimap mFactionStolenItems; +}; + } #endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index e73aaafae3..699725d676 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -195,6 +195,7 @@ namespace ESSImport const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value; + const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value; std::map > converters; converters[ESM::REC_GLOB] = boost::shared_ptr(new ConvertGlobal()); @@ -202,10 +203,11 @@ namespace ESSImport converters[ESM::REC_NPC_] = boost::shared_ptr(new ConvertNPC()); converters[ESM::REC_NPCC] = boost::shared_ptr(new ConvertNPCC()); converters[ESM::REC_CREC] = boost::shared_ptr(new ConvertCREC()); - converters[recREFR] = boost::shared_ptr(new ConvertREFR()); - converters[recPCDT] = boost::shared_ptr(new ConvertPCDT()); - converters[recFMAP] = boost::shared_ptr(new ConvertFMAP()); - converters[recKLST] = boost::shared_ptr(new ConvertKLST()); + converters[recREFR ] = boost::shared_ptr(new ConvertREFR()); + converters[recPCDT ] = boost::shared_ptr(new ConvertPCDT()); + converters[recFMAP ] = boost::shared_ptr(new ConvertFMAP()); + converters[recKLST ] = boost::shared_ptr(new ConvertKLST()); + converters[recSTLN ] = boost::shared_ptr(new ConvertSTLN()); converters[ESM::REC_CELL] = boost::shared_ptr(new ConvertCell()); converters[ESM::REC_ALCH] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CLAS] = boost::shared_ptr(new ConvertClass()); From 0fc9221eb36058ccb31e4fb57f09cc1a1a3bc2fb Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 18:22:02 +0100 Subject: [PATCH 360/404] ESSImport: convert NPC disposition --- apps/essimporter/convertnpcc.cpp | 1 + apps/essimporter/importnpcc.hpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/essimporter/convertnpcc.cpp b/apps/essimporter/convertnpcc.cpp index 2155f46fb9..48d3d9232e 100644 --- a/apps/essimporter/convertnpcc.cpp +++ b/apps/essimporter/convertnpcc.cpp @@ -7,6 +7,7 @@ namespace ESSImport void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) { + npcState.mNpcStats.mDisposition = npcc.mNPDT.mDisposition; npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; convertInventory(npcc.mInventory, npcState.mInventory); diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index 2f474aba07..ee8eccda2a 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -19,7 +19,8 @@ namespace ESSImport { struct NPDT { - unsigned char unknown[2]; + unsigned char mDisposition; + unsigned char unknown; unsigned char mReputation; unsigned char unknown2[5]; } mNPDT; From d473629dcd6aa6e04d53accc2e7a69314b8fa79c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 19:12:07 +0100 Subject: [PATCH 361/404] Improve ESMReader error messages --- components/esm/esmreader.cpp | 6 +++++- components/esm/esmreader.hpp | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index de110dd188..3c43d067f9 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -134,7 +134,11 @@ void ESMReader::getHExact(void*p, int size) { getSubHeader(); if (size != static_cast (mCtx.leftSub)) - fail("getHExact() size mismatch"); + { + std::stringstream error; + error << "getHExact(): size mismatch (requested " << size << ", got " << mCtx.leftSub << ")"; + fail(error.str()); + } getExact(p, size); } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index bb615af111..e7721bb8f5 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -137,7 +137,11 @@ public: { getSubHeader(); if (mCtx.leftSub != sizeof(X)) - fail("getHT(): subrecord size mismatch"); + { + std::stringstream error; + error << "getHT(): subrecord size mismatch (requested " << sizeof(X) << ", got " << mCtx.leftSub << ")"; + fail(error.str()); + } getT(x); } From eede2c8e55fe9579dd8e746385be2a794ce969bb Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 19:30:39 +0100 Subject: [PATCH 362/404] ESSImport: convert breath meter and skill increases --- apps/essimporter/convertacdt.cpp | 2 ++ apps/essimporter/converter.hpp | 2 ++ apps/essimporter/importacdt.hpp | 16 +++++++++++++--- apps/essimporter/importer.cpp | 5 +++++ apps/essimporter/importplayer.cpp | 4 ++-- apps/essimporter/importplayer.hpp | 11 +++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 22 +++++++++++----------- 7 files changed, 46 insertions(+), 16 deletions(-) diff --git a/apps/essimporter/convertacdt.cpp b/apps/essimporter/convertacdt.cpp index 81ab61084f..af66ec272f 100644 --- a/apps/essimporter/convertacdt.cpp +++ b/apps/essimporter/convertacdt.cpp @@ -37,6 +37,8 @@ namespace ESSImport npcStats.mSkills[i].mRegular.mCurrent = actorData.mSkills[i][1]; npcStats.mSkills[i].mRegular.mBase = actorData.mSkills[i][0]; } + + npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter; } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 716ddc9c76..23b0434b1b 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -215,6 +215,8 @@ public: faction.mReputation = it->mReputation; mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction; } + for (int i=0; i<8; ++i) + mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); it != pcdt.mKnownDialogueTopics.end(); ++it) diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index 6efae9dc57..cf72b5cf79 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -13,14 +13,24 @@ namespace ESSImport /// Actor data, shared by (at least) REFR and CellRef +#pragma pack(push) +#pragma pack(1) struct ACDT { - unsigned char mUnknown1[40]; + // Note, not stored at *all*: + // - Level changes are lost on reload, except for the player (there it's in the NPC record). + unsigned char mUnknown1[16]; + float mBreathMeter; // Seconds left before drowning + unsigned char mUnknown2[20]; float mDynamic[3][2]; - unsigned char mUnknown2[16]; + unsigned char mUnknown3[16]; float mAttributes[8][2]; - unsigned char mUnknown3[120]; + unsigned char mUnknown4[109]; + // This seems to increase when purchasing training, though I don't see it anywhere ingame. + int mGold; + unsigned char mUnknown5[7]; }; +#pragma pack(pop) struct ActorData { diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 699725d676..c2092ea4df 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -104,6 +104,11 @@ namespace ESSImport blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour blacklist.insert(std::make_pair("REFR", "DATA")); // player position blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war + blacklist.insert(std::make_pair("GAME", "GMDT")); // weather data, current time always changes + + // this changes way too often, name suggests some renderer internal data? + blacklist.insert(std::make_pair("CELL", "ND3D")); + blacklist.insert(std::make_pair("REFR", "ND3D")); File file1; read(mEssFile, file1); diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index f94d3aacc3..2856050682 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -23,8 +23,8 @@ namespace ESSImport mKnownDialogueTopics.push_back(esm.getHString()); } - if (esm.isNextSub("PNAM")) - esm.skipHSub(); + esm.getHNT(mPNAM, "PNAM"); + if (esm.isNextSub("SNAM")) esm.skipHSub(); if (esm.isNextSub("NAM9")) diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index af8625fa64..2bd55ef73e 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -38,6 +38,8 @@ struct PCDT std::vector mKnownDialogueTopics; +#pragma pack(push) +#pragma pack(1) struct FNAM { unsigned char mRank; @@ -47,7 +49,16 @@ struct PCDT unsigned char mUnknown2[3]; ESM::NAME32 mFactionName; }; + struct PNAM + { + unsigned char mUnknown1[116]; + unsigned char mSkillIncreases[8]; // number of skill increases for each attribute + unsigned char mUnknown2[88]; + }; +#pragma pack(pop) + std::vector mFactions; + PNAM mPNAM; void load(ESM::ESMReader& esm); }; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 4594492e10..9e543bb798 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -22,27 +22,27 @@ namespace MWMechanics class NpcStats : public CreatureStats { - /// NPCs other than the player can only have one faction. But for the sake of consistency - /// we use the same data structure for the PC and the NPCs. - /// \note the faction key must be in lowercase - std::map mFactionRank; - int mDisposition; SkillValue mSkill[ESM::Skill::Length]; SkillValue mWerewolfSkill[ESM::Skill::Length]; - int mBounty; - std::set mExpelled; - std::map mFactionReputation; int mReputation; int mCrimeId; - int mWerewolfKills; + int mProfit; + // ----- used by the player only, maybe should be moved at some point ------- + int mBounty; + int mWerewolfKills; + /// NPCs other than the player can only have one faction. But for the sake of consistency + /// we use the same data structure for the PC and the NPCs. + /// \note the faction key must be in lowercase + std::map mFactionRank; + std::set mExpelled; + std::map mFactionReputation; int mLevelProgress; // 0-10 - std::vector mSkillIncreases; // number of skill increases for each attribute - std::set mUsedIds; + // --------------------------------------------------------------------------- /// Countdown to getting damage while underwater float mTimeToStartDrowning; From 5b705196bc11acb9e471ad96c6eb928bbe5bad3e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 19:47:54 +0100 Subject: [PATCH 363/404] ESSImport: convert level progress --- apps/essimporter/converter.hpp | 1 + apps/essimporter/importplayer.hpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 23b0434b1b..920e6eead4 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -217,6 +217,7 @@ public: } for (int i=0; i<8; ++i) mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; + mContext->mPlayer.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress; for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); it != pcdt.mKnownDialogueTopics.end(); ++it) diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index 2bd55ef73e..64ceddfd79 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -51,9 +51,11 @@ struct PCDT }; struct PNAM { - unsigned char mUnknown1[116]; + unsigned char mUnknown1[4]; + unsigned char mLevelProgress; + unsigned char mUnknown2[111]; unsigned char mSkillIncreases[8]; // number of skill increases for each attribute - unsigned char mUnknown2[88]; + unsigned char mUnknown3[88]; }; #pragma pack(pop) From e38d75634505ea77173d492ee90fff5e0a347128 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 20:18:30 +0100 Subject: [PATCH 364/404] ESSImport: fix NPCC indices --- apps/essimporter/converter.cpp | 6 +++++- apps/essimporter/converter.hpp | 5 +---- apps/essimporter/importnpcc.hpp | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 83ea077eae..0999877627 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -1,5 +1,7 @@ #include "converter.hpp" +#include + #include #include @@ -281,7 +283,9 @@ namespace ESSImport continue; } - std::cerr << "Can't find type for " << cellref.mIndexedRefId << std::endl; + std::stringstream error; + error << "Can't find type for " << cellref.mIndexedRefId << std::endl; + throw std::runtime_error(error.str()); } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 920e6eead4..b68f9a2187 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -170,13 +170,10 @@ public: } else { - int index = mIndexCounter[id]++; + int index = npcc.mNPDT.mIndex; mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)).second; } } - -private: - std::map mIndexCounter; }; class ConvertREFR : public Converter diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index ee8eccda2a..c69fa3e035 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -22,7 +22,8 @@ namespace ESSImport unsigned char mDisposition; unsigned char unknown; unsigned char mReputation; - unsigned char unknown2[5]; + unsigned char unknown2; + int mIndex; } mNPDT; Inventory mInventory; From 142a138b75e3391963ce109b216c7eb625a441b7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 20:19:08 +0100 Subject: [PATCH 365/404] ESSImport: convert TalkedTo flag and gold pool --- apps/essimporter/convertacdt.cpp | 2 ++ apps/essimporter/importacdt.cpp | 3 ++- apps/essimporter/importacdt.hpp | 15 ++++++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/essimporter/convertacdt.cpp b/apps/essimporter/convertacdt.cpp index af66ec272f..496eab9e99 100644 --- a/apps/essimporter/convertacdt.cpp +++ b/apps/essimporter/convertacdt.cpp @@ -27,6 +27,8 @@ namespace ESSImport cStats.mAttributes[i].mMod = acdt.mAttributes[i][0]; cStats.mAttributes[i].mCurrent = acdt.mAttributes[i][0]; } + cStats.mGoldPool = acdt.mGoldPool; + cStats.mTalkedTo = acdt.mFlags & TalkedToPlayer; } void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats) diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index d961720a73..84c8008978 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -22,7 +22,7 @@ namespace ESSImport ESM::CellRef bla; bla.ESM::CellRef::loadData(esm); - // FIXME: actually should be required for all actors?, but ActorData is currently in base CellRef + // FIXME: not all actors have this, add flag esm.getHNOT(mACDT, "ACDT"); ACSC acsc; @@ -76,6 +76,7 @@ namespace ESSImport esm.skipHSub(); // 4 byte, 0 } + // FIXME: not all actors have this, add flag if (esm.isNextSub("CHRD")) // npc only esm.getHExact(mSkills, 27*2*sizeof(int)); diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index cf72b5cf79..79f67d2c24 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -11,6 +11,10 @@ namespace ESM namespace ESSImport { + enum ACDTFlags + { + TalkedToPlayer = 0x4 + }; /// Actor data, shared by (at least) REFR and CellRef #pragma pack(push) @@ -19,16 +23,17 @@ namespace ESSImport { // Note, not stored at *all*: // - Level changes are lost on reload, except for the player (there it's in the NPC record). - unsigned char mUnknown1[16]; + unsigned char mUnknown[12]; + unsigned char mFlags; // ACDTFlags + unsigned char mUnknown1[3]; float mBreathMeter; // Seconds left before drowning unsigned char mUnknown2[20]; float mDynamic[3][2]; unsigned char mUnknown3[16]; float mAttributes[8][2]; - unsigned char mUnknown4[109]; - // This seems to increase when purchasing training, though I don't see it anywhere ingame. - int mGold; - unsigned char mUnknown5[7]; + unsigned char mUnknown4[112]; + unsigned int mGoldPool; + unsigned char mUnknown5[4]; }; #pragma pack(pop) From 89d9649b50b2b6e102b90517a5b136cc74415ba0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 23:55:26 +0100 Subject: [PATCH 366/404] Change save format to store relative equipment index Store the index for the allowedSlots vector instead of the absolute slot index. This will more gracefully handle edge cases like the available slots for an item having changed when loading the game, or the "allows stacking" property having changed. However the main reason this was done is to ease work on the essimporter. --- apps/openmw/mwworld/cellstore.cpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 27 ++++++++++---------- apps/openmw/mwworld/containerstore.hpp | 12 +++++---- apps/openmw/mwworld/inventorystore.cpp | 35 ++++++++++++++++++++------ apps/openmw/mwworld/inventorystore.hpp | 6 ++--- apps/openmw/mwworld/worldimp.cpp | 4 +-- components/esm/inventorystate.hpp | 1 + 7 files changed, 56 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 605c8f9d2a..545bbd4b31 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -466,7 +466,7 @@ namespace MWWorld // List moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { - ESM::CellRef &ref = const_cast(*it); + const ESM::CellRef &ref = *it; mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index bd14720f7a..5911b45f74 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -85,28 +85,29 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::Object ref.save (state); } +/// \todo make this method const once const-correct ContainerStoreIterators are available template -void MWWorld::ContainerStore::storeStates (const CellRefList& collection, - std::vector >& states, bool equipable) const +void MWWorld::ContainerStore::storeStates (CellRefList& collection, + std::vector >& states, bool equipable) { - for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); + for (typename CellRefList::List::iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { if (iter->mData.getCount() == 0) continue; ESM::ObjectState state; storeState (*iter, state); - int slot = equipable ? getSlot (*iter) : -1; + int slot = equipable ? getRelativeSlot (MWWorld::ContainerStoreIterator(this, iter)) : -1; states.push_back (std::make_pair (state, slot)); } } -int MWWorld::ContainerStore::getSlot (const MWWorld::LiveCellRefBase& ref) const +int MWWorld::ContainerStore::getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) const { return -1; } -void MWWorld::ContainerStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {} +void MWWorld::ContainerStore::setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {} const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; @@ -641,7 +642,7 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id) return Ptr(); } -void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const +void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) { state.mItems.clear(); @@ -678,16 +679,16 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) { case ESM::REC_ALCH: getState (potions, iter->first); break; case ESM::REC_APPA: getState (appas, iter->first); break; - case ESM::REC_ARMO: setSlot (getState (armors, iter->first), slot); break; + case ESM::REC_ARMO: setRelativeSlot (getState (armors, iter->first), slot); break; case ESM::REC_BOOK: getState (books, iter->first); break; - case ESM::REC_CLOT: setSlot (getState (clothes, iter->first), slot); break; + case ESM::REC_CLOT: setRelativeSlot (getState (clothes, iter->first), slot); break; case ESM::REC_INGR: getState (ingreds, iter->first); break; - case ESM::REC_LOCK: setSlot (getState (lockpicks, iter->first), slot); break; + case ESM::REC_LOCK: setRelativeSlot (getState (lockpicks, iter->first), slot); break; case ESM::REC_MISC: getState (miscItems, iter->first); break; - case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break; + case ESM::REC_PROB: setRelativeSlot (getState (probes, iter->first), slot); break; case ESM::REC_REPA: getState (repairs, iter->first); break; - case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; - case ESM::REC_LIGH: setSlot (getState (lights, iter->first), slot); break; + case ESM::REC_WEAP: setRelativeSlot (getState (weapons, iter->first), slot); break; + case ESM::REC_LIGH: setRelativeSlot (getState (lights, iter->first), slot); break; default: std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 68ee41a1d3..4a95606f7b 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -84,15 +84,16 @@ namespace MWWorld template void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; + /// \todo make this method const once const-correct ContainerStoreIterators are available template - void storeStates (const CellRefList& collection, + void storeStates (CellRefList& collection, std::vector >& states, - bool equipable = false) const; + bool equipable = false); - virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; + virtual int getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) const; ///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot). - virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot); + virtual void setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int slot); ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. public: @@ -171,7 +172,8 @@ namespace MWWorld Ptr search (const std::string& id); - virtual void writeState (ESM::InventoryState& state) const; + /// \todo make this method const once const-correct ContainerStoreIterators are available + virtual void writeState (ESM::InventoryState& state); virtual void readState (const ESM::InventoryState& state); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 445b42d8db..8f272ca499 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -49,19 +49,40 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_) slots_.push_back (end()); } -int MWWorld::InventoryStore::getSlot (const MWWorld::LiveCellRefBase& ref) const +int MWWorld::InventoryStore::getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) { for (int i = 0; i (mSlots.size()); ++i) - if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref) - return i; + if (mSlots[i].getType()!=-1 && mSlots[i] == iter) + { + // linear complexity, but allowedSlots is most of the time just 1 anyway + std::vector allowedSlots = iter->getClass().getEquipmentSlots(*iter).first; + std::vector::iterator found = std::find(allowedSlots.begin(),allowedSlots.end(),i); + if (found == allowedSlots.end()) + return -1; + else + return std::distance(allowedSlots.begin(), found); + } return -1; } -void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) +void MWWorld::InventoryStore::setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot) { - if (iter!=end() && slot>=0 && slot, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter); + relativeSlot = std::min(int(allowedSlots.first.size()-1), relativeSlot); + + // unstack if required + if (!allowedSlots.second && iter->getRefData().getCount() > 1) + { + MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1); + iter->getRefData().setCount(iter->getRefData().getCount()-1); + mSlots[allowedSlots.first[relativeSlot]] = newIter; + } + else + mSlots[allowedSlots.first[relativeSlot]] = iter; } MWWorld::InventoryStore::InventoryStore() @@ -703,7 +724,7 @@ bool MWWorld::InventoryStore::isEquipped(const MWWorld::Ptr &item) return false; } -void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const +void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) { MWWorld::ContainerStore::writeState(state); diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 9fd18c54b0..ae2ced2b80 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,10 +113,10 @@ namespace MWWorld void fireEquipmentChangedEvent(); - virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; + virtual int getRelativeSlot (const MWWorld::ContainerStoreIterator& iter); ///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot). - virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot); + virtual void setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot); ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. public: @@ -209,7 +209,7 @@ namespace MWWorld virtual void clear(); ///< Empty container. - virtual void writeState (ESM::InventoryState& state) const; + virtual void writeState (ESM::InventoryState& state); virtual void readState (const ESM::InventoryState& state); }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 104605e653..f7d5d96165 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2031,7 +2031,7 @@ namespace MWWorld bool World::isOnGround(const MWWorld::Ptr &ptr) const { RefData &refdata = ptr.getRefData(); - const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); if(!physactor) return false; @@ -2049,7 +2049,7 @@ namespace MWWorld mPhysEngine); if(tracer.mFraction < 1.0f) // collision, must be close to something below { - const_cast (physactor)->setOnGround(true); + physactor->setOnGround(true); return true; } else diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index 3d4407e7b2..13af28e30e 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -15,6 +15,7 @@ namespace ESM /// \brief State for inventories and containers struct InventoryState { + /// std::vector > mItems; std::map mLevelledItemMap; From 03df659e7d60c1047dd51b8d293cdd26f1cb9e2d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 23:58:24 +0100 Subject: [PATCH 367/404] ESSImport: convert inventory equipment slots --- apps/essimporter/convertinventory.cpp | 2 +- apps/essimporter/importinventory.cpp | 12 +++++++++++- apps/essimporter/importinventory.hpp | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 42535d946b..6a31be6a3e 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -16,7 +16,7 @@ namespace ESSImport objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags objstate.mRef.mCharge = it->mCondition; - state.mItems.push_back(std::make_pair(objstate, -1)); + state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index b4d1935409..2f5c3c054f 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -1,5 +1,7 @@ #include "importinventory.hpp" +#include + #include #include @@ -17,6 +19,7 @@ namespace ESSImport InventoryItem item; item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; + item.mRelativeEquipmentSlot = -1; // seems that a stack of items can have a set of subrecords for each item? rings0000.ess // doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place? @@ -55,14 +58,21 @@ namespace ESSImport { // note: same item can be equipped 2 items (e.g. 2 rings) // and will be *stacked* in the NPCO list, unlike openmw! + // this is currently not handled properly. + esm.getSubHeader(); int itemIndex; // index of the item in the NPCO list esm.getT(itemIndex); + if (itemIndex < 0 || itemIndex >= int(mItems.size())) + esm.fail("equipment item index out of range"); + // appears to be a relative index for only the *possible* slots this item can be equipped in, - // i.e. 0 most of the time, unlike openmw slot enum index + // i.e. 0 most of the time int slotIndex; esm.getT(slotIndex); + + mItems[itemIndex].mRelativeEquipmentSlot = slotIndex; } } diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 359e43fa68..8a1594465b 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -21,6 +21,7 @@ namespace ESSImport std::string mId; int mCount; int mCondition; + int mRelativeEquipmentSlot; }; std::vector mItems; From 8c32b7e304ae5ce07209849a0f772ecbdb097aab Mon Sep 17 00:00:00 2001 From: Scott Howard Date: Tue, 20 Jan 2015 17:59:11 -0500 Subject: [PATCH 368/404] missing include in essimporter/main.cpp error: 'cout' is not a member of 'std' error: 'cerr' is not a member of 'std' https://launchpadlibrarian.net/195402684/buildlog_ubuntu-trusty-amd64.openmw_0.34.0%2Bgit20150120.442-0~ubuntu14.04.1_FAILEDTOBUILD.txt.gz --- apps/essimporter/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/essimporter/main.cpp b/apps/essimporter/main.cpp index 0e5c65e951..1c7d511160 100644 --- a/apps/essimporter/main.cpp +++ b/apps/essimporter/main.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include From db64ff66453245ecf0b273354bee3ec1af7a0810 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Jan 2015 01:33:48 +0100 Subject: [PATCH 369/404] Fix actorId initialization --- components/esm/creaturestats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index a1ef7eb0e1..cac5cc0a6a 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -230,7 +230,7 @@ void ESM::CreatureStats::blank() mTradeTime.mHour = 0; mTradeTime.mDay = 0; mGoldPool = 0; - mActorId = 0; + mActorId = -1; mHasAiSettings = false; mDead = false; mDied = false; From 5e0428243b38d18c55b80e7675ea74a0563f5e44 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Jan 2015 01:59:45 +0100 Subject: [PATCH 370/404] Light charge handling fix --- apps/esmtool/esmtool.cpp | 2 +- apps/essimporter/convertinventory.cpp | 2 +- apps/essimporter/importinventory.cpp | 6 +-- apps/essimporter/importinventory.hpp | 1 - apps/opencs/model/world/columnimp.hpp | 4 +- apps/openmw/mwclass/light.cpp | 56 +++------------------------ apps/openmw/mwclass/light.hpp | 10 ----- apps/openmw/mwworld/cellref.cpp | 20 ++++++++-- apps/openmw/mwworld/cellref.hpp | 3 ++ apps/openmw/mwworld/manualref.hpp | 2 +- components/esm/cellref.cpp | 10 ++--- components/esm/cellref.hpp | 6 ++- components/esm/objectstate.cpp | 10 ++--- components/esm/objectstate.hpp | 2 - 14 files changed, 47 insertions(+), 87 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a18736bf26..b23ba72f9c 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -261,7 +261,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Faction: '" << ref.mFaction << "'" << std::endl; std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; - std::cout << " Uses/health: '" << ref.mCharge << "'\n"; + std::cout << " Uses/health: '" << ref.mChargeInt << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Deleted: " << deleted << std::endl; diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 6a31be6a3e..31272bf5aa 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -12,10 +12,10 @@ namespace ESSImport { ESM::ObjectState objstate; objstate.blank(); + objstate.mRef = *it; objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags - objstate.mRef.mCharge = it->mCondition; state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index 2f5c3c054f..a20584ff5a 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -45,9 +45,9 @@ namespace ESSImport // for XSOL and XCHG seen so far, but probably others too item.ESM::CellRef::loadData(esm); - item.mCondition = -1; - // FIXME: for Lights, this is actually a float - esm.getHNOT(item.mCondition, "XHLT"); + int charge=-1; + esm.getHNOT(charge, "XHLT"); + item.mChargeInt = charge; } mItems.push_back(item); diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 8a1594465b..99fed9b20e 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -20,7 +20,6 @@ namespace ESSImport { std::string mId; int mCount; - int mCondition; int mRelativeEquipmentSlot; }; std::vector mItems; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index eba73cf0b0..da14bb4955 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1053,13 +1053,13 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return record.get().mCharge; + return record.get().mChargeInt; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); - record2.mCharge = data.toInt(); + record2.mChargeInt = data.toInt(); record.setModified (record2); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index c50ad52d81..35a21b63e4 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -26,27 +26,6 @@ #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" -namespace -{ - struct LightCustomData : public MWWorld::CustomData - { - float mTime; - ///< Time remaining - - LightCustomData(MWWorld::Ptr ptr) - { - MWWorld::LiveCellRef *ref = ptr.get(); - mTime = ref->mBase->mData.mTime; - } - ///< Constructs this CustomData from the base values for Ptr. - - virtual MWWorld::CustomData *clone() const - { - return new LightCustomData (*this); - } - }; -} - namespace MWClass { std::string Light::getId (const MWWorld::Ptr& ptr) const @@ -219,17 +198,16 @@ namespace MWClass void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const { - ensureCustomData(ptr); - - float &timeRemaining = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; - timeRemaining = duration; + ptr.getCellRef().setChargeFloat(duration); } float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const { - ensureCustomData(ptr); - - return dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + MWWorld::LiveCellRef *ref = ptr.get(); + if (ptr.getCellRef().getCharge() == -1) + return ref->mBase->mData.mTime; + else + return ptr.getCellRef().getChargeFloat(); } MWWorld::Ptr @@ -241,12 +219,6 @@ namespace MWClass return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } - void Light::ensureCustomData (const MWWorld::Ptr& ptr) const - { - if (!ptr.getRefData().getCustomData()) - ptr.getRefData().setCustomData(new LightCustomData(ptr)); - } - bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const { return npcServices & ESM::NPC::Lights; @@ -282,22 +254,6 @@ namespace MWClass return std::make_pair(1,""); } - void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) - const - { - ensureCustomData (ptr); - - dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state.mTime; - } - - void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) - const - { - ensureCustomData (ptr); - - state.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; - } - std::string Light::getSound(const MWWorld::Ptr& ptr) const { return ptr.get()->mBase->mSound; diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index bbca30113c..8658375b77 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -10,8 +10,6 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; - void ensureCustomData (const MWWorld::Ptr& ptr) const; - public: /// Return ID of \a ptr @@ -75,14 +73,6 @@ namespace MWClass std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; - virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) - const; - ///< Read additional state from \a state into \a ptr. - - virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) - const; - ///< Write additional state from \a ptr into \a state. - virtual std::string getSound(const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index aa6627de53..744e0e27a4 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -81,15 +81,29 @@ namespace MWWorld int CellRef::getCharge() const { - return mCellRef.mCharge; + return mCellRef.mChargeInt; } void CellRef::setCharge(int charge) { - if (charge != mCellRef.mCharge) + if (charge != mCellRef.mChargeInt) { mChanged = true; - mCellRef.mCharge = charge; + mCellRef.mChargeInt = charge; + } + } + + float CellRef::getChargeFloat() const + { + return mCellRef.mChargeFloat; + } + + void CellRef::setChargeFloat(float charge) + { + if (charge != mCellRef.mChargeFloat) + { + mChanged = true; + mCellRef.mChargeFloat = charge; } } diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index a7ffbe08b5..054fd7fc68 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -60,8 +60,11 @@ namespace MWWorld // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. + // If this returns int(-1) it means full health. int getCharge() const; + float getChargeFloat() const; // Implemented as union with int charge void setCharge(int charge); + void setChargeFloat(float charge); // The NPC that owns this object (and will get angry if you steal it) std::string getOwner() const; diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 4eb93543ba..f32b074715 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -28,7 +28,7 @@ namespace MWWorld cellRef.mRefID = name; cellRef.mScale = 1; cellRef.mFactionRank = 0; - cellRef.mCharge = -1; + cellRef.mChargeInt = -1; cellRef.mGoldValue = 1; cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index cd9fe85700..dfc3052ee5 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -46,12 +46,12 @@ void ESM::CellRef::loadData(ESMReader &esm) esm.getHNOT (mFactionRank, "INDX"); mGoldValue = 1; - mCharge = -1; + mChargeInt = -1; mEnchantmentCharge = -1; esm.getHNOT (mEnchantmentCharge, "XCHG"); - esm.getHNOT (mCharge, "INTV"); + esm.getHNOT (mChargeInt, "INTV"); esm.getHNOT (mGoldValue, "NAM9"); @@ -106,8 +106,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons if (mEnchantmentCharge != -1) esm.writeHNT("XCHG", mEnchantmentCharge); - if (mCharge != -1) - esm.writeHNT("INTV", mCharge); + if (mChargeInt != -1) + esm.writeHNT("INTV", mChargeInt); if (mGoldValue != 1) { esm.writeHNT("NAM9", mGoldValue); @@ -146,7 +146,7 @@ void ESM::CellRef::blank() mSoul.clear(); mFaction.clear(); mFactionRank = -2; - mCharge = -1; + mChargeInt = -1; mEnchantmentCharge = -1; mGoldValue = 0; mDestCell.clear(); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 7aa0e4d44c..56932aa4db 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -59,7 +59,11 @@ namespace ESM // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. - int mCharge; + union + { + int mChargeInt; + float mChargeFloat; + }; // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). float mEnchantmentCharge; diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 66fd496633..9ef1ccf80d 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -24,9 +24,9 @@ void ESM::ObjectState::load (ESMReader &esm) esm.getHNOT (mLocalRotation, "LROT", 12); - // used for lights only - mTime = 0; - esm.getHNOT (mTime, "LTIM"); + // obsolete + int unused; + esm.getHNOT(unused, "LTIM"); // FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files mHasCustomState = true; @@ -55,9 +55,6 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const esm.writeHNT ("LROT", mLocalRotation, 12); } - if (mTime) - esm.writeHNT ("LTIM", mTime); - if (!mHasCustomState) esm.writeHNT ("HCUS", false); } @@ -74,7 +71,6 @@ void ESM::ObjectState::blank() mPosition.rot[i] = 0; mLocalRotation[i] = 0; } - mTime = 0; mHasCustomState = true; } diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index b036896534..d1077733a5 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -26,8 +26,6 @@ namespace ESM ESM::Position mPosition; float mLocalRotation[3]; - float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either - // Is there any class-specific state following the ObjectState bool mHasCustomState; From b95748d044a421c2e02fa54cdc7e8aa902039e6c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 02:32:38 +0100 Subject: [PATCH 371/404] Revert "Light charge handling fix" This reverts commit 5e0428243b38d18c55b80e7675ea74a0563f5e44. --- apps/esmtool/esmtool.cpp | 2 +- apps/essimporter/convertinventory.cpp | 2 +- apps/essimporter/importinventory.cpp | 6 +-- apps/essimporter/importinventory.hpp | 1 + apps/opencs/model/world/columnimp.hpp | 4 +- apps/openmw/mwclass/light.cpp | 56 ++++++++++++++++++++++++--- apps/openmw/mwclass/light.hpp | 10 +++++ apps/openmw/mwworld/cellref.cpp | 20 ++-------- apps/openmw/mwworld/cellref.hpp | 3 -- apps/openmw/mwworld/manualref.hpp | 2 +- components/esm/cellref.cpp | 10 ++--- components/esm/cellref.hpp | 6 +-- components/esm/objectstate.cpp | 10 +++-- components/esm/objectstate.hpp | 2 + 14 files changed, 87 insertions(+), 47 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index b23ba72f9c..a18736bf26 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -261,7 +261,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Faction: '" << ref.mFaction << "'" << std::endl; std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; - std::cout << " Uses/health: '" << ref.mChargeInt << "'\n"; + std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Deleted: " << deleted << std::endl; diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 31272bf5aa..6a31be6a3e 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -12,10 +12,10 @@ namespace ESSImport { ESM::ObjectState objstate; objstate.blank(); - objstate.mRef = *it; objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags + objstate.mRef.mCharge = it->mCondition; state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index a20584ff5a..2f5c3c054f 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -45,9 +45,9 @@ namespace ESSImport // for XSOL and XCHG seen so far, but probably others too item.ESM::CellRef::loadData(esm); - int charge=-1; - esm.getHNOT(charge, "XHLT"); - item.mChargeInt = charge; + item.mCondition = -1; + // FIXME: for Lights, this is actually a float + esm.getHNOT(item.mCondition, "XHLT"); } mItems.push_back(item); diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 99fed9b20e..8a1594465b 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -20,6 +20,7 @@ namespace ESSImport { std::string mId; int mCount; + int mCondition; int mRelativeEquipmentSlot; }; std::vector mItems; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index da14bb4955..eba73cf0b0 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1053,13 +1053,13 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return record.get().mChargeInt; + return record.get().mCharge; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); - record2.mChargeInt = data.toInt(); + record2.mCharge = data.toInt(); record.setModified (record2); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 35a21b63e4..c50ad52d81 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -26,6 +26,27 @@ #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" +namespace +{ + struct LightCustomData : public MWWorld::CustomData + { + float mTime; + ///< Time remaining + + LightCustomData(MWWorld::Ptr ptr) + { + MWWorld::LiveCellRef *ref = ptr.get(); + mTime = ref->mBase->mData.mTime; + } + ///< Constructs this CustomData from the base values for Ptr. + + virtual MWWorld::CustomData *clone() const + { + return new LightCustomData (*this); + } + }; +} + namespace MWClass { std::string Light::getId (const MWWorld::Ptr& ptr) const @@ -198,16 +219,17 @@ namespace MWClass void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const { - ptr.getCellRef().setChargeFloat(duration); + ensureCustomData(ptr); + + float &timeRemaining = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + timeRemaining = duration; } float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); - if (ptr.getCellRef().getCharge() == -1) - return ref->mBase->mData.mTime; - else - return ptr.getCellRef().getChargeFloat(); + ensureCustomData(ptr); + + return dynamic_cast (*ptr.getRefData().getCustomData()).mTime; } MWWorld::Ptr @@ -219,6 +241,12 @@ namespace MWClass return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } + void Light::ensureCustomData (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getCustomData()) + ptr.getRefData().setCustomData(new LightCustomData(ptr)); + } + bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const { return npcServices & ESM::NPC::Lights; @@ -254,6 +282,22 @@ namespace MWClass return std::make_pair(1,""); } + void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const + { + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state.mTime; + } + + void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ensureCustomData (ptr); + + state.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + } + std::string Light::getSound(const MWWorld::Ptr& ptr) const { return ptr.get()->mBase->mSound; diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 8658375b77..bbca30113c 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -10,6 +10,8 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: /// Return ID of \a ptr @@ -73,6 +75,14 @@ namespace MWClass std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const; + ///< Read additional state from \a state into \a ptr. + + virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const; + ///< Write additional state from \a ptr into \a state. + virtual std::string getSound(const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 744e0e27a4..aa6627de53 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -81,29 +81,15 @@ namespace MWWorld int CellRef::getCharge() const { - return mCellRef.mChargeInt; + return mCellRef.mCharge; } void CellRef::setCharge(int charge) { - if (charge != mCellRef.mChargeInt) + if (charge != mCellRef.mCharge) { mChanged = true; - mCellRef.mChargeInt = charge; - } - } - - float CellRef::getChargeFloat() const - { - return mCellRef.mChargeFloat; - } - - void CellRef::setChargeFloat(float charge) - { - if (charge != mCellRef.mChargeFloat) - { - mChanged = true; - mCellRef.mChargeFloat = charge; + mCellRef.mCharge = charge; } } diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 054fd7fc68..a7ffbe08b5 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -60,11 +60,8 @@ namespace MWWorld // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. - // If this returns int(-1) it means full health. int getCharge() const; - float getChargeFloat() const; // Implemented as union with int charge void setCharge(int charge); - void setChargeFloat(float charge); // The NPC that owns this object (and will get angry if you steal it) std::string getOwner() const; diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index f32b074715..4eb93543ba 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -28,7 +28,7 @@ namespace MWWorld cellRef.mRefID = name; cellRef.mScale = 1; cellRef.mFactionRank = 0; - cellRef.mChargeInt = -1; + cellRef.mCharge = -1; cellRef.mGoldValue = 1; cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index dfc3052ee5..cd9fe85700 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -46,12 +46,12 @@ void ESM::CellRef::loadData(ESMReader &esm) esm.getHNOT (mFactionRank, "INDX"); mGoldValue = 1; - mChargeInt = -1; + mCharge = -1; mEnchantmentCharge = -1; esm.getHNOT (mEnchantmentCharge, "XCHG"); - esm.getHNOT (mChargeInt, "INTV"); + esm.getHNOT (mCharge, "INTV"); esm.getHNOT (mGoldValue, "NAM9"); @@ -106,8 +106,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons if (mEnchantmentCharge != -1) esm.writeHNT("XCHG", mEnchantmentCharge); - if (mChargeInt != -1) - esm.writeHNT("INTV", mChargeInt); + if (mCharge != -1) + esm.writeHNT("INTV", mCharge); if (mGoldValue != 1) { esm.writeHNT("NAM9", mGoldValue); @@ -146,7 +146,7 @@ void ESM::CellRef::blank() mSoul.clear(); mFaction.clear(); mFactionRank = -2; - mChargeInt = -1; + mCharge = -1; mEnchantmentCharge = -1; mGoldValue = 0; mDestCell.clear(); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 56932aa4db..7aa0e4d44c 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -59,11 +59,7 @@ namespace ESM // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. - union - { - int mChargeInt; - float mChargeFloat; - }; + int mCharge; // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). float mEnchantmentCharge; diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 9ef1ccf80d..66fd496633 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -24,9 +24,9 @@ void ESM::ObjectState::load (ESMReader &esm) esm.getHNOT (mLocalRotation, "LROT", 12); - // obsolete - int unused; - esm.getHNOT(unused, "LTIM"); + // used for lights only + mTime = 0; + esm.getHNOT (mTime, "LTIM"); // FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files mHasCustomState = true; @@ -55,6 +55,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const esm.writeHNT ("LROT", mLocalRotation, 12); } + if (mTime) + esm.writeHNT ("LTIM", mTime); + if (!mHasCustomState) esm.writeHNT ("HCUS", false); } @@ -71,6 +74,7 @@ void ESM::ObjectState::blank() mPosition.rot[i] = 0; mLocalRotation[i] = 0; } + mTime = 0; mHasCustomState = true; } diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index d1077733a5..b036896534 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -26,6 +26,8 @@ namespace ESM ESM::Position mPosition; float mLocalRotation[3]; + float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either + // Is there any class-specific state following the ObjectState bool mHasCustomState; From fe31ca0ac0649a0e34617a5821454e04863531ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 02:32:42 +0100 Subject: [PATCH 372/404] Revert "ESSImport: convert inventory equipment slots" This reverts commit 03df659e7d60c1047dd51b8d293cdd26f1cb9e2d. --- apps/essimporter/convertinventory.cpp | 2 +- apps/essimporter/importinventory.cpp | 12 +----------- apps/essimporter/importinventory.hpp | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 6a31be6a3e..42535d946b 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -16,7 +16,7 @@ namespace ESSImport objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags objstate.mRef.mCharge = it->mCondition; - state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); + state.mItems.push_back(std::make_pair(objstate, -1)); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index 2f5c3c054f..b4d1935409 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -1,7 +1,5 @@ #include "importinventory.hpp" -#include - #include #include @@ -19,7 +17,6 @@ namespace ESSImport InventoryItem item; item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; - item.mRelativeEquipmentSlot = -1; // seems that a stack of items can have a set of subrecords for each item? rings0000.ess // doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place? @@ -58,21 +55,14 @@ namespace ESSImport { // note: same item can be equipped 2 items (e.g. 2 rings) // and will be *stacked* in the NPCO list, unlike openmw! - // this is currently not handled properly. - esm.getSubHeader(); int itemIndex; // index of the item in the NPCO list esm.getT(itemIndex); - if (itemIndex < 0 || itemIndex >= int(mItems.size())) - esm.fail("equipment item index out of range"); - // appears to be a relative index for only the *possible* slots this item can be equipped in, - // i.e. 0 most of the time + // i.e. 0 most of the time, unlike openmw slot enum index int slotIndex; esm.getT(slotIndex); - - mItems[itemIndex].mRelativeEquipmentSlot = slotIndex; } } diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 8a1594465b..359e43fa68 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -21,7 +21,6 @@ namespace ESSImport std::string mId; int mCount; int mCondition; - int mRelativeEquipmentSlot; }; std::vector mItems; From c65f9cb3c0fa39f6f5664f68852ee5109663bf30 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 02:32:43 +0100 Subject: [PATCH 373/404] Revert "Change save format to store relative equipment index" This reverts commit 89d9649b50b2b6e102b90517a5b136cc74415ba0. --- apps/openmw/mwworld/cellstore.cpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 27 ++++++++++---------- apps/openmw/mwworld/containerstore.hpp | 12 ++++----- apps/openmw/mwworld/inventorystore.cpp | 35 ++++++-------------------- apps/openmw/mwworld/inventorystore.hpp | 6 ++--- apps/openmw/mwworld/worldimp.cpp | 4 +-- components/esm/inventorystate.hpp | 1 - 7 files changed, 31 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 545bbd4b31..605c8f9d2a 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -466,7 +466,7 @@ namespace MWWorld // List moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { - const ESM::CellRef &ref = *it; + ESM::CellRef &ref = const_cast(*it); mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 5911b45f74..bd14720f7a 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -85,29 +85,28 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::Object ref.save (state); } -/// \todo make this method const once const-correct ContainerStoreIterators are available template -void MWWorld::ContainerStore::storeStates (CellRefList& collection, - std::vector >& states, bool equipable) +void MWWorld::ContainerStore::storeStates (const CellRefList& collection, + std::vector >& states, bool equipable) const { - for (typename CellRefList::List::iterator iter (collection.mList.begin()); + for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { if (iter->mData.getCount() == 0) continue; ESM::ObjectState state; storeState (*iter, state); - int slot = equipable ? getRelativeSlot (MWWorld::ContainerStoreIterator(this, iter)) : -1; + int slot = equipable ? getSlot (*iter) : -1; states.push_back (std::make_pair (state, slot)); } } -int MWWorld::ContainerStore::getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) const +int MWWorld::ContainerStore::getSlot (const MWWorld::LiveCellRefBase& ref) const { return -1; } -void MWWorld::ContainerStore::setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {} +void MWWorld::ContainerStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {} const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; @@ -642,7 +641,7 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id) return Ptr(); } -void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) +void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const { state.mItems.clear(); @@ -679,16 +678,16 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) { case ESM::REC_ALCH: getState (potions, iter->first); break; case ESM::REC_APPA: getState (appas, iter->first); break; - case ESM::REC_ARMO: setRelativeSlot (getState (armors, iter->first), slot); break; + case ESM::REC_ARMO: setSlot (getState (armors, iter->first), slot); break; case ESM::REC_BOOK: getState (books, iter->first); break; - case ESM::REC_CLOT: setRelativeSlot (getState (clothes, iter->first), slot); break; + case ESM::REC_CLOT: setSlot (getState (clothes, iter->first), slot); break; case ESM::REC_INGR: getState (ingreds, iter->first); break; - case ESM::REC_LOCK: setRelativeSlot (getState (lockpicks, iter->first), slot); break; + case ESM::REC_LOCK: setSlot (getState (lockpicks, iter->first), slot); break; case ESM::REC_MISC: getState (miscItems, iter->first); break; - case ESM::REC_PROB: setRelativeSlot (getState (probes, iter->first), slot); break; + case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break; case ESM::REC_REPA: getState (repairs, iter->first); break; - case ESM::REC_WEAP: setRelativeSlot (getState (weapons, iter->first), slot); break; - case ESM::REC_LIGH: setRelativeSlot (getState (lights, iter->first), slot); break; + case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; + case ESM::REC_LIGH: setSlot (getState (lights, iter->first), slot); break; default: std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 4a95606f7b..68ee41a1d3 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -84,16 +84,15 @@ namespace MWWorld template void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; - /// \todo make this method const once const-correct ContainerStoreIterators are available template - void storeStates (CellRefList& collection, + void storeStates (const CellRefList& collection, std::vector >& states, - bool equipable = false); + bool equipable = false) const; - virtual int getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) const; + virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; ///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot). - virtual void setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int slot); + virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot); ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. public: @@ -172,8 +171,7 @@ namespace MWWorld Ptr search (const std::string& id); - /// \todo make this method const once const-correct ContainerStoreIterators are available - virtual void writeState (ESM::InventoryState& state); + virtual void writeState (ESM::InventoryState& state) const; virtual void readState (const ESM::InventoryState& state); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 8f272ca499..445b42d8db 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -49,40 +49,19 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_) slots_.push_back (end()); } -int MWWorld::InventoryStore::getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) +int MWWorld::InventoryStore::getSlot (const MWWorld::LiveCellRefBase& ref) const { for (int i = 0; i (mSlots.size()); ++i) - if (mSlots[i].getType()!=-1 && mSlots[i] == iter) - { - // linear complexity, but allowedSlots is most of the time just 1 anyway - std::vector allowedSlots = iter->getClass().getEquipmentSlots(*iter).first; - std::vector::iterator found = std::find(allowedSlots.begin(),allowedSlots.end(),i); - if (found == allowedSlots.end()) - return -1; - else - return std::distance(allowedSlots.begin(), found); - } + if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref) + return i; return -1; } -void MWWorld::InventoryStore::setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot) +void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) { - if (relativeSlot < 0 || iter == end()) - return; - - std::pair, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter); - relativeSlot = std::min(int(allowedSlots.first.size()-1), relativeSlot); - - // unstack if required - if (!allowedSlots.second && iter->getRefData().getCount() > 1) - { - MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1); - iter->getRefData().setCount(iter->getRefData().getCount()-1); - mSlots[allowedSlots.first[relativeSlot]] = newIter; - } - else - mSlots[allowedSlots.first[relativeSlot]] = iter; + if (iter!=end() && slot>=0 && slotgetCharacter(refdata.getHandle()); + const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); if(!physactor) return false; @@ -2049,7 +2049,7 @@ namespace MWWorld mPhysEngine); if(tracer.mFraction < 1.0f) // collision, must be close to something below { - physactor->setOnGround(true); + const_cast (physactor)->setOnGround(true); return true; } else diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index 13af28e30e..3d4407e7b2 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -15,7 +15,6 @@ namespace ESM /// \brief State for inventories and containers struct InventoryState { - /// std::vector > mItems; std::map mLevelledItemMap; From d13335ba40f10203cd9e9f86a198eab98c93c168 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 15:31:44 +0100 Subject: [PATCH 374/404] Ensure the item can be equipped in the given slot when loading inventory --- apps/openmw/mwworld/cellstore.cpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 5 ++--- apps/openmw/mwworld/containerstore.hpp | 5 +++-- apps/openmw/mwworld/inventorystore.cpp | 22 ++++++++++++++++++---- apps/openmw/mwworld/inventorystore.hpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 4 ++-- components/esm/inventorystate.hpp | 1 + 7 files changed, 29 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 605c8f9d2a..545bbd4b31 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -466,7 +466,7 @@ namespace MWWorld // List moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { - ESM::CellRef &ref = const_cast(*it); + const ESM::CellRef &ref = *it; mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index bd14720f7a..ba70e2f8f2 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -86,7 +86,7 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::Object } template -void MWWorld::ContainerStore::storeStates (const CellRefList& collection, +void MWWorld::ContainerStore::storeStates (CellRefList& collection, std::vector >& states, bool equipable) const { for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); @@ -641,7 +641,7 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id) return Ptr(); } -void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const +void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) { state.mItems.clear(); @@ -688,7 +688,6 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) case ESM::REC_REPA: getState (repairs, iter->first); break; case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; case ESM::REC_LIGH: setSlot (getState (lights, iter->first), slot); break; - default: std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; break; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 68ee41a1d3..f7c8a369c8 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -85,7 +85,7 @@ namespace MWWorld void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; template - void storeStates (const CellRefList& collection, + void storeStates (CellRefList& collection, std::vector >& states, bool equipable = false) const; @@ -171,7 +171,8 @@ namespace MWWorld Ptr search (const std::string& id); - virtual void writeState (ESM::InventoryState& state) const; + /// \todo make this method const once const-correct ContainerStoreIterators are available + virtual void writeState (ESM::InventoryState& state); virtual void readState (const ESM::InventoryState& state); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 445b42d8db..be93824e5a 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -58,10 +58,24 @@ int MWWorld::InventoryStore::getSlot (const MWWorld::LiveCellRefBase& ref) const return -1; } -void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) +void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot) { - if (iter!=end() && slot>=0 && slot, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter); + relativeSlot = std::min(int(allowedSlots.first.size()-1), relativeSlot); + + // unstack if required + if (!allowedSlots.second && iter->getRefData().getCount() > 1) + { + MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1); + iter->getRefData().setCount(iter->getRefData().getCount()-1); + mSlots[allowedSlots.first[relativeSlot]] = newIter; + } + else + mSlots[allowedSlots.first[relativeSlot]] = iter; } MWWorld::InventoryStore::InventoryStore() @@ -703,7 +717,7 @@ bool MWWorld::InventoryStore::isEquipped(const MWWorld::Ptr &item) return false; } -void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const +void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) { MWWorld::ContainerStore::writeState(state); diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 9fd18c54b0..7bd977e39b 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -116,7 +116,7 @@ namespace MWWorld virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; ///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot). - virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot); + virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot); ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. public: @@ -209,7 +209,7 @@ namespace MWWorld virtual void clear(); ///< Empty container. - virtual void writeState (ESM::InventoryState& state) const; + virtual void writeState (ESM::InventoryState& state); virtual void readState (const ESM::InventoryState& state); }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 104605e653..f7d5d96165 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2031,7 +2031,7 @@ namespace MWWorld bool World::isOnGround(const MWWorld::Ptr &ptr) const { RefData &refdata = ptr.getRefData(); - const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); if(!physactor) return false; @@ -2049,7 +2049,7 @@ namespace MWWorld mPhysEngine); if(tracer.mFraction < 1.0f) // collision, must be close to something below { - const_cast (physactor)->setOnGround(true); + physactor->setOnGround(true); return true; } else diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index 3d4407e7b2..13af28e30e 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -15,6 +15,7 @@ namespace ESM /// \brief State for inventories and containers struct InventoryState { + /// std::vector > mItems; std::map mLevelledItemMap; From 7ead963075c0ef0ff036dec10531367548ae4a56 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 15:33:30 +0100 Subject: [PATCH 375/404] Revert "Revert "ESSImport: convert inventory equipment slots"" This reverts commit fe31ca0ac0649a0e34617a5821454e04863531ca. --- apps/essimporter/convertinventory.cpp | 2 +- apps/essimporter/importinventory.cpp | 12 +++++++++++- apps/essimporter/importinventory.hpp | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 42535d946b..6a31be6a3e 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -16,7 +16,7 @@ namespace ESSImport objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags objstate.mRef.mCharge = it->mCondition; - state.mItems.push_back(std::make_pair(objstate, -1)); + state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index b4d1935409..2f5c3c054f 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -1,5 +1,7 @@ #include "importinventory.hpp" +#include + #include #include @@ -17,6 +19,7 @@ namespace ESSImport InventoryItem item; item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; + item.mRelativeEquipmentSlot = -1; // seems that a stack of items can have a set of subrecords for each item? rings0000.ess // doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place? @@ -55,14 +58,21 @@ namespace ESSImport { // note: same item can be equipped 2 items (e.g. 2 rings) // and will be *stacked* in the NPCO list, unlike openmw! + // this is currently not handled properly. + esm.getSubHeader(); int itemIndex; // index of the item in the NPCO list esm.getT(itemIndex); + if (itemIndex < 0 || itemIndex >= int(mItems.size())) + esm.fail("equipment item index out of range"); + // appears to be a relative index for only the *possible* slots this item can be equipped in, - // i.e. 0 most of the time, unlike openmw slot enum index + // i.e. 0 most of the time int slotIndex; esm.getT(slotIndex); + + mItems[itemIndex].mRelativeEquipmentSlot = slotIndex; } } diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 359e43fa68..8a1594465b 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -21,6 +21,7 @@ namespace ESSImport std::string mId; int mCount; int mCondition; + int mRelativeEquipmentSlot; }; std::vector mItems; From c7d15e6f74a81e007b48ddeb5f3a077ac4812312 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 15:33:39 +0100 Subject: [PATCH 376/404] Revert "Revert "Light charge handling fix"" This reverts commit b95748d044a421c2e02fa54cdc7e8aa902039e6c. --- apps/esmtool/esmtool.cpp | 2 +- apps/essimporter/convertinventory.cpp | 2 +- apps/essimporter/importinventory.cpp | 6 +-- apps/essimporter/importinventory.hpp | 1 - apps/opencs/model/world/columnimp.hpp | 4 +- apps/openmw/mwclass/light.cpp | 56 +++------------------------ apps/openmw/mwclass/light.hpp | 10 ----- apps/openmw/mwworld/cellref.cpp | 20 ++++++++-- apps/openmw/mwworld/cellref.hpp | 3 ++ apps/openmw/mwworld/manualref.hpp | 2 +- components/esm/cellref.cpp | 10 ++--- components/esm/cellref.hpp | 6 ++- components/esm/objectstate.cpp | 10 ++--- components/esm/objectstate.hpp | 2 - 14 files changed, 47 insertions(+), 87 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a18736bf26..b23ba72f9c 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -261,7 +261,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Faction: '" << ref.mFaction << "'" << std::endl; std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; - std::cout << " Uses/health: '" << ref.mCharge << "'\n"; + std::cout << " Uses/health: '" << ref.mChargeInt << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Deleted: " << deleted << std::endl; diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 6a31be6a3e..31272bf5aa 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -12,10 +12,10 @@ namespace ESSImport { ESM::ObjectState objstate; objstate.blank(); + objstate.mRef = *it; objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags - objstate.mRef.mCharge = it->mCondition; state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index 2f5c3c054f..a20584ff5a 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -45,9 +45,9 @@ namespace ESSImport // for XSOL and XCHG seen so far, but probably others too item.ESM::CellRef::loadData(esm); - item.mCondition = -1; - // FIXME: for Lights, this is actually a float - esm.getHNOT(item.mCondition, "XHLT"); + int charge=-1; + esm.getHNOT(charge, "XHLT"); + item.mChargeInt = charge; } mItems.push_back(item); diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 8a1594465b..99fed9b20e 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -20,7 +20,6 @@ namespace ESSImport { std::string mId; int mCount; - int mCondition; int mRelativeEquipmentSlot; }; std::vector mItems; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index eba73cf0b0..da14bb4955 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1053,13 +1053,13 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return record.get().mCharge; + return record.get().mChargeInt; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); - record2.mCharge = data.toInt(); + record2.mChargeInt = data.toInt(); record.setModified (record2); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index c50ad52d81..35a21b63e4 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -26,27 +26,6 @@ #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" -namespace -{ - struct LightCustomData : public MWWorld::CustomData - { - float mTime; - ///< Time remaining - - LightCustomData(MWWorld::Ptr ptr) - { - MWWorld::LiveCellRef *ref = ptr.get(); - mTime = ref->mBase->mData.mTime; - } - ///< Constructs this CustomData from the base values for Ptr. - - virtual MWWorld::CustomData *clone() const - { - return new LightCustomData (*this); - } - }; -} - namespace MWClass { std::string Light::getId (const MWWorld::Ptr& ptr) const @@ -219,17 +198,16 @@ namespace MWClass void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const { - ensureCustomData(ptr); - - float &timeRemaining = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; - timeRemaining = duration; + ptr.getCellRef().setChargeFloat(duration); } float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const { - ensureCustomData(ptr); - - return dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + MWWorld::LiveCellRef *ref = ptr.get(); + if (ptr.getCellRef().getCharge() == -1) + return ref->mBase->mData.mTime; + else + return ptr.getCellRef().getChargeFloat(); } MWWorld::Ptr @@ -241,12 +219,6 @@ namespace MWClass return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } - void Light::ensureCustomData (const MWWorld::Ptr& ptr) const - { - if (!ptr.getRefData().getCustomData()) - ptr.getRefData().setCustomData(new LightCustomData(ptr)); - } - bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const { return npcServices & ESM::NPC::Lights; @@ -282,22 +254,6 @@ namespace MWClass return std::make_pair(1,""); } - void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) - const - { - ensureCustomData (ptr); - - dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state.mTime; - } - - void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) - const - { - ensureCustomData (ptr); - - state.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; - } - std::string Light::getSound(const MWWorld::Ptr& ptr) const { return ptr.get()->mBase->mSound; diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index bbca30113c..8658375b77 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -10,8 +10,6 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; - void ensureCustomData (const MWWorld::Ptr& ptr) const; - public: /// Return ID of \a ptr @@ -75,14 +73,6 @@ namespace MWClass std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; - virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) - const; - ///< Read additional state from \a state into \a ptr. - - virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) - const; - ///< Write additional state from \a ptr into \a state. - virtual std::string getSound(const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index aa6627de53..744e0e27a4 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -81,15 +81,29 @@ namespace MWWorld int CellRef::getCharge() const { - return mCellRef.mCharge; + return mCellRef.mChargeInt; } void CellRef::setCharge(int charge) { - if (charge != mCellRef.mCharge) + if (charge != mCellRef.mChargeInt) { mChanged = true; - mCellRef.mCharge = charge; + mCellRef.mChargeInt = charge; + } + } + + float CellRef::getChargeFloat() const + { + return mCellRef.mChargeFloat; + } + + void CellRef::setChargeFloat(float charge) + { + if (charge != mCellRef.mChargeFloat) + { + mChanged = true; + mCellRef.mChargeFloat = charge; } } diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index a7ffbe08b5..054fd7fc68 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -60,8 +60,11 @@ namespace MWWorld // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. + // If this returns int(-1) it means full health. int getCharge() const; + float getChargeFloat() const; // Implemented as union with int charge void setCharge(int charge); + void setChargeFloat(float charge); // The NPC that owns this object (and will get angry if you steal it) std::string getOwner() const; diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 4eb93543ba..f32b074715 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -28,7 +28,7 @@ namespace MWWorld cellRef.mRefID = name; cellRef.mScale = 1; cellRef.mFactionRank = 0; - cellRef.mCharge = -1; + cellRef.mChargeInt = -1; cellRef.mGoldValue = 1; cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index cd9fe85700..dfc3052ee5 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -46,12 +46,12 @@ void ESM::CellRef::loadData(ESMReader &esm) esm.getHNOT (mFactionRank, "INDX"); mGoldValue = 1; - mCharge = -1; + mChargeInt = -1; mEnchantmentCharge = -1; esm.getHNOT (mEnchantmentCharge, "XCHG"); - esm.getHNOT (mCharge, "INTV"); + esm.getHNOT (mChargeInt, "INTV"); esm.getHNOT (mGoldValue, "NAM9"); @@ -106,8 +106,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons if (mEnchantmentCharge != -1) esm.writeHNT("XCHG", mEnchantmentCharge); - if (mCharge != -1) - esm.writeHNT("INTV", mCharge); + if (mChargeInt != -1) + esm.writeHNT("INTV", mChargeInt); if (mGoldValue != 1) { esm.writeHNT("NAM9", mGoldValue); @@ -146,7 +146,7 @@ void ESM::CellRef::blank() mSoul.clear(); mFaction.clear(); mFactionRank = -2; - mCharge = -1; + mChargeInt = -1; mEnchantmentCharge = -1; mGoldValue = 0; mDestCell.clear(); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 7aa0e4d44c..56932aa4db 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -59,7 +59,11 @@ namespace ESM // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. - int mCharge; + union + { + int mChargeInt; + float mChargeFloat; + }; // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). float mEnchantmentCharge; diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 66fd496633..9ef1ccf80d 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -24,9 +24,9 @@ void ESM::ObjectState::load (ESMReader &esm) esm.getHNOT (mLocalRotation, "LROT", 12); - // used for lights only - mTime = 0; - esm.getHNOT (mTime, "LTIM"); + // obsolete + int unused; + esm.getHNOT(unused, "LTIM"); // FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files mHasCustomState = true; @@ -55,9 +55,6 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const esm.writeHNT ("LROT", mLocalRotation, 12); } - if (mTime) - esm.writeHNT ("LTIM", mTime); - if (!mHasCustomState) esm.writeHNT ("HCUS", false); } @@ -74,7 +71,6 @@ void ESM::ObjectState::blank() mPosition.rot[i] = 0; mLocalRotation[i] = 0; } - mTime = 0; mHasCustomState = true; } diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index b036896534..d1077733a5 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -26,8 +26,6 @@ namespace ESM ESM::Position mPosition; float mLocalRotation[3]; - float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either - // Is there any class-specific state following the ObjectState bool mHasCustomState; From 69676906aef59e4179d8a194425e77a09cb5d812 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Jan 2015 21:24:25 +0100 Subject: [PATCH 377/404] Don't clear known spells when reading from the savegame This is needed because the .ess format doesn't include the racial spells in the player's spell list. --- apps/essimporter/importplayer.cpp | 4 ++++ apps/openmw/mwmechanics/spells.cpp | 20 ++++++++------------ components/esm/spellstate.hpp | 1 + 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index 2856050682..ea9f1932c5 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -23,6 +23,10 @@ namespace ESSImport mKnownDialogueTopics.push_back(esm.getHString()); } + if (esm.isNextSub("MNAM")) + esm.skipHSub(); // If this field is here it seems to specify the interior cell the player is in, + // but it's not always here, so it's kinda useless + esm.getHNT(mPNAM, "PNAM"); if (esm.isNextSub("SNAM")) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 1f8ada06d5..dcbd3f09f3 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -312,21 +312,17 @@ namespace MWMechanics void Spells::readState(const ESM::SpellState &state) { - mSpells = state.mSpells; - mSelectedSpell = state.mSelectedSpell; - - // Discard spells that are no longer available due to changed content files - for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) + for (TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(iter->first); - if (!spell) + // Discard spells that are no longer available due to changed content files + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); + if (spell) { - if (iter->first == mSelectedSpell) - mSelectedSpell = ""; - mSpells.erase(iter++); + mSpells[it->first] = it->second; + + if (it->first == state.mSelectedSpell) + mSelectedSpell = it->first; } - else - ++iter; } // No need to discard spells here (doesn't really matter if non existent ids are kept) diff --git a/components/esm/spellstate.hpp b/components/esm/spellstate.hpp index 2ab27e908b..028e6a3878 100644 --- a/components/esm/spellstate.hpp +++ b/components/esm/spellstate.hpp @@ -12,6 +12,7 @@ namespace ESM class ESMReader; class ESMWriter; + // NOTE: spell ids must be lower case struct SpellState { struct CorprusStats From acf8461841546f5691f3c2e817af7a0d59116433 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Jan 2015 22:25:37 +0100 Subject: [PATCH 378/404] ESSImport: some fixes --- apps/essimporter/converter.cpp | 22 ++++++++++------------ apps/essimporter/converter.hpp | 9 +++++++-- apps/essimporter/importacdt.cpp | 10 ++++------ apps/essimporter/importacdt.hpp | 4 +++- apps/essimporter/importcellref.cpp | 21 ++++++++++++++++----- apps/essimporter/importcellref.hpp | 10 +++------- apps/essimporter/importcrec.cpp | 10 ++++++++++ apps/essimporter/importnpcc.cpp | 3 +++ 8 files changed, 56 insertions(+), 33 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 0999877627..81521a0283 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -27,6 +27,8 @@ namespace objstate.mEnabled = cellref.mEnabled; objstate.mPosition = cellref.mPos; objstate.mRef.mRefNum = cellref.mRefNum; + if (cellref.mDeleted) + objstate.mCount = 0; } bool isIndexedRefId(const std::string& indexedRefId) @@ -34,6 +36,10 @@ namespace if (indexedRefId.size() <= 8) return false; + if (indexedRefId.find_first_not_of("0123456789") == std::string::npos) + return false; // entirely numeric refid, this is a reference to + // a dynamically created record e.g. player-enchanted weapon + std::string index = indexedRefId.substr(indexedRefId.size()-8); if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos ) return true; @@ -136,13 +142,6 @@ namespace ESSImport { CellRef ref; ref.load (esm); - if (esm.isNextSub("DELE")) - { - // TODO - // strangely this can be e.g. 52 instead of just 1, - //std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; - esm.skipHSub(); - } cellrefs.push_back(ref); } @@ -201,8 +200,7 @@ namespace ESSImport for (std::vector::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt) { const CellRef& cellref = *refIt; - ESM::CellRef out; - out.blank(); + ESM::CellRef out (cellref); if (!isIndexedRefId(cellref.mIndexedRefId)) { @@ -241,8 +239,8 @@ namespace ESSImport objstate.mRef.mRefID = idLower; // probably need more micromanagement here so we don't overwrite values // from the ESM with default values - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertNpcData(cellref.mActorData, objstate.mNpcStats); + convertACDT(cellref.mACDT, objstate.mCreatureStats); + convertNpcData(cellref, objstate.mNpcStats); convertNPCC(npccIt->second, objstate); convertCellRef(cellref, objstate); esm.writeHNT ("OBJE", ESM::REC_NPC_); @@ -273,7 +271,7 @@ namespace ESSImport objstate.blank(); objstate.mRef = out; objstate.mRef.mRefID = idLower; - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertACDT(cellref.mACDT, objstate.mCreatureStats); // probably need more micromanagement here so we don't overwrite values // from the ESM with default values convertCREC(crecIt->second, objstate); diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index b68f9a2187..9f6ad49dfe 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -90,14 +90,19 @@ public: ESM::NPC npc; std::string id = esm.getHNString("NAME"); npc.load(esm); - if (id != "player") // seems to occur sometimes, with "chargen X" names + if (id != "player") + { + // this handles changes to the NPC struct, but since there is no index here + // it will apply to ALL instances of the class. seems to be the reason for the + // "feature" in MW where changing AI settings of one guard will change it for all guards of that refID. std::cerr << "non-player NPC record: " << id << std::endl; + } else { mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayerBase = npc; std::map empty; - // FIXME: player start spells, racial spells and birthsign spells aren't listed here, + // FIXME: player start spells and birthsign spells aren't listed here, // need to fix openmw to account for this for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index 84c8008978..4d4a87a35f 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -9,18 +9,16 @@ namespace ESSImport void ActorData::load(ESM::ESMReader &esm) { - // unsure at which point between NAME and ESM::CellRef - if (esm.isNextSub("MNAM")) - esm.skipHSub(); - if (esm.isNextSub("ACTN")) esm.skipHSub(); if (esm.isNextSub("STPR")) esm.skipHSub(); - ESM::CellRef bla; - bla.ESM::CellRef::loadData(esm); + if (esm.isNextSub("MNAM")) + esm.skipHSub(); + + ESM::CellRef::loadData(esm); // FIXME: not all actors have this, add flag esm.getHNOT(mACDT, "ACDT"); diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index 79f67d2c24..271f6df745 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -3,6 +3,8 @@ #include +#include + namespace ESM { struct ESMReader; @@ -37,7 +39,7 @@ namespace ESSImport }; #pragma pack(pop) - struct ActorData + struct ActorData : public ESM::CellRef { ACDT mACDT; diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index fd58d5bc1b..7c4af1e022 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -7,6 +7,8 @@ namespace ESSImport void CellRef::load(ESM::ESMReader &esm) { + blank(); + // (FRMR subrecord name is already read by the loop in ConvertCell) esm.getHT(mRefNum.mIndex); // FRMR @@ -25,17 +27,26 @@ namespace ESSImport esm.getHT(lvcr); //std::cout << "LVCR: " << (int)lvcr << std::endl; } - mActorData.load(esm); + ActorData::load(esm); mEnabled = true; esm.getHNOT(mEnabled, "ZNAM"); - // should occur for all references but not levelled creature spawners - esm.getHNOT(mPos, "DATA", 24); - - // i've seen DATA record TWICE on a creature record - and with the exact same content too! weird + // DATA should occur for all references, except leveled creature spawners + // I've seen DATA *twice* on a creature record, and with the exact same content too! weird // alarmvoi0000.ess esm.getHNOT(mPos, "DATA", 24); + esm.getHNOT(mPos, "DATA", 24); + + mDeleted = 0; + if (esm.isNextSub("DELE")) + { + int deleted; + esm.getHT(deleted); + // Neither of this seems to work right... + //mDeleted = (deleted != 0); + //mDeleted = (deleted&0x1); + } if (esm.isNextSub("MVRF")) { diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp index 77763d4343..556ed19bfa 100644 --- a/apps/essimporter/importcellref.hpp +++ b/apps/essimporter/importcellref.hpp @@ -15,20 +15,16 @@ namespace ESM namespace ESSImport { - // Not sure if we can share any code with ESM::CellRef here - struct CellRef + struct CellRef : public ActorData { std::string mIndexedRefId; - ESM::RefNum mRefNum; - - ActorData mActorData; - - ESM::Position mPos; std::string mScript; bool mEnabled; + bool mDeleted; + void load(ESM::ESMReader& esm); }; diff --git a/apps/essimporter/importcrec.cpp b/apps/essimporter/importcrec.cpp index 9cf4555889..28e5ac56f3 100644 --- a/apps/essimporter/importcrec.cpp +++ b/apps/essimporter/importcrec.cpp @@ -14,6 +14,16 @@ namespace ESSImport float scale; esm.getHNOT(scale, "XSCL"); + // FIXME: use AiPackageList, need to fix getSubName() + if (esm.isNextSub("AI_W")) + esm.skipHSub(); + if (esm.isNextSub("AI_E")) + esm.skipHSub(); + if (esm.isNextSub("AI_T")) + esm.skipHSub(); + if (esm.isNextSub("AI_F")) + esm.skipHSub(); + mInventory.load(esm); } diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp index c389ede7f9..7b8dda3719 100644 --- a/apps/essimporter/importnpcc.cpp +++ b/apps/essimporter/importnpcc.cpp @@ -9,6 +9,9 @@ namespace ESSImport { esm.getHNT(mNPDT, "NPDT"); + // FIXME: use AiPackageList, need to fix getSubName() + if (esm.isNextSub("AI_W")) + esm.skipHSub(); if (esm.isNextSub("AI_E")) esm.skipHSub(); if (esm.isNextSub("AI_T")) From 1e92cab3e76b9b258ae813eb43f020ecba2da754 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Jan 2015 23:35:09 +0100 Subject: [PATCH 379/404] ESSImport: read dialogue/journal records, not converted yet --- apps/essimporter/CMakeLists.txt | 5 ++ apps/essimporter/converter.hpp | 81 +++++++++++++++++++++++++++++++-- apps/essimporter/importdial.cpp | 13 ++++++ apps/essimporter/importdial.hpp | 20 ++++++++ apps/essimporter/importer.cpp | 10 +++- apps/essimporter/importgame.cpp | 13 ++++++ apps/essimporter/importgame.hpp | 33 ++++++++++++++ apps/essimporter/importinfo.cpp | 14 ++++++ apps/essimporter/importinfo.hpp | 24 ++++++++++ apps/essimporter/importjour.cpp | 13 ++++++ apps/essimporter/importjour.hpp | 25 ++++++++++ apps/essimporter/importques.cpp | 14 ++++++ apps/essimporter/importques.hpp | 28 ++++++++++++ components/esm/defs.hpp | 2 +- components/esm/loadnpcc.hpp | 17 ------- 15 files changed, 290 insertions(+), 22 deletions(-) create mode 100644 apps/essimporter/importdial.cpp create mode 100644 apps/essimporter/importdial.hpp create mode 100644 apps/essimporter/importgame.cpp create mode 100644 apps/essimporter/importgame.hpp create mode 100644 apps/essimporter/importinfo.cpp create mode 100644 apps/essimporter/importinfo.hpp create mode 100644 apps/essimporter/importjour.cpp create mode 100644 apps/essimporter/importjour.hpp create mode 100644 apps/essimporter/importques.cpp create mode 100644 apps/essimporter/importques.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 8d1af714d7..70eaefe2c6 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -9,6 +9,11 @@ set(ESSIMPORTER_FILES importinventory.cpp importklst.cpp importcntc.cpp + importgame.cpp + importinfo.cpp + importdial.cpp + importques.cpp + importjour.cpp importercontext.cpp converter.cpp convertacdt.cpp diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 9f6ad49dfe..4ca0e83864 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "importcrec.hpp" #include "importcntc.hpp" @@ -19,6 +20,11 @@ #include "importercontext.hpp" #include "importcellref.hpp" #include "importklst.hpp" +#include "importgame.hpp" +#include "importinfo.hpp" +#include "importdial.hpp" +#include "importques.hpp" +#include "importjour.hpp" #include "convertacdt.hpp" #include "convertnpcc.hpp" @@ -86,16 +92,15 @@ class ConvertNPC : public Converter public: virtual void read(ESM::ESMReader &esm) { - // this is always the player ESM::NPC npc; std::string id = esm.getHNString("NAME"); npc.load(esm); if (id != "player") { - // this handles changes to the NPC struct, but since there is no index here + // TODO: + // this should handle changes to the NPC struct, but since there is no index here // it will apply to ALL instances of the class. seems to be the reason for the // "feature" in MW where changing AI settings of one guard will change it for all guards of that refID. - std::cerr << "non-player NPC record: " << id << std::endl; } else { @@ -110,6 +115,18 @@ public: } }; +class ConvertCREA : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + // See comment in ConvertNPC + ESM::Creature creature; + std::string id = esm.getHNString("NAME"); + creature.load(esm); + } +}; + class ConvertGlobal : public DefaultConverter { public: @@ -350,6 +367,64 @@ private: std::multimap mFactionStolenItems; }; +/// Seen responses for a dialogue topic? +/// Each DIAL record is followed by a number of INFO records, I believe, just like in ESMs +/// Dialogue conversion problems (probably have to adjust OpenMW format) - +/// - Journal is stored in one continuous HTML markup rather than each entry separately with associated info ID. +/// - Seen dialogue responses only store the INFO id, rather than the fulltext. +/// - Quest stages only store the INFO id, rather than the journal entry fulltext. +class ConvertINFO : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + INFO info; + info.load(esm); + } +}; + +class ConvertDIAL : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + std::string id = esm.getHNString("NAME"); + DIAL dial; + dial.load(esm); + } +}; + +class ConvertQUES : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + std::string id = esm.getHNString("NAME"); + QUES quest; + quest.load(esm); + } +}; + +class ConvertJOUR : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + JOUR journal; + journal.load(esm); + } +}; + +class ConvertGAME : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + GAME game; + game.load(esm); + } +}; + } #endif diff --git a/apps/essimporter/importdial.cpp b/apps/essimporter/importdial.cpp new file mode 100644 index 0000000000..228d00a5ae --- /dev/null +++ b/apps/essimporter/importdial.cpp @@ -0,0 +1,13 @@ +#include "importdial.hpp" + +#include + +namespace ESSImport +{ + + void DIAL::load(ESM::ESMReader &esm) + { + esm.getHNT(mIndex, "XIDX"); + } + +} diff --git a/apps/essimporter/importdial.hpp b/apps/essimporter/importdial.hpp new file mode 100644 index 0000000000..fe4b0c5b4c --- /dev/null +++ b/apps/essimporter/importdial.hpp @@ -0,0 +1,20 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTDIAL_H +#define OPENMW_ESSIMPORT_IMPORTDIAL_H +namespace ESM +{ + struct ESMReader; +} + +namespace ESSImport +{ + + struct DIAL + { + int mIndex; // Journal index + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index c2092ea4df..8603200da8 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -201,11 +201,14 @@ namespace ESSImport const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value; const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value; + const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value; + const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value; std::map > converters; converters[ESM::REC_GLOB] = boost::shared_ptr(new ConvertGlobal()); converters[ESM::REC_BOOK] = boost::shared_ptr(new ConvertBook()); converters[ESM::REC_NPC_] = boost::shared_ptr(new ConvertNPC()); + converters[ESM::REC_CREA] = boost::shared_ptr(new ConvertCREA()); converters[ESM::REC_NPCC] = boost::shared_ptr(new ConvertNPCC()); converters[ESM::REC_CREC] = boost::shared_ptr(new ConvertCREC()); converters[recREFR ] = boost::shared_ptr(new ConvertREFR()); @@ -213,6 +216,7 @@ namespace ESSImport converters[recFMAP ] = boost::shared_ptr(new ConvertFMAP()); converters[recKLST ] = boost::shared_ptr(new ConvertKLST()); converters[recSTLN ] = boost::shared_ptr(new ConvertSTLN()); + converters[recGAME ] = boost::shared_ptr(new ConvertGAME()); converters[ESM::REC_CELL] = boost::shared_ptr(new ConvertCell()); converters[ESM::REC_ALCH] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CLAS] = boost::shared_ptr(new ConvertClass()); @@ -226,6 +230,10 @@ namespace ESSImport converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CNTC] = boost::shared_ptr(new ConvertCNTC()); converters[ESM::REC_FACT] = boost::shared_ptr(new ConvertFACT()); + converters[ESM::REC_INFO] = boost::shared_ptr(new ConvertINFO()); + converters[ESM::REC_DIAL] = boost::shared_ptr(new ConvertDIAL()); + converters[ESM::REC_QUES] = boost::shared_ptr(new ConvertQUES()); + converters[recJOUR ] = boost::shared_ptr(new ConvertJOUR()); std::set unknownRecords; @@ -248,7 +256,7 @@ namespace ESSImport else { if (unknownRecords.insert(n.val).second) - std::cerr << "unknown record " << n.toString() << std::endl; + std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl; esm.skipRecord(); } diff --git a/apps/essimporter/importgame.cpp b/apps/essimporter/importgame.cpp new file mode 100644 index 0000000000..0b3a4f1a7f --- /dev/null +++ b/apps/essimporter/importgame.cpp @@ -0,0 +1,13 @@ +#include "importgame.hpp" + +#include + +namespace ESSImport +{ + +void GAME::load(ESM::ESMReader &esm) +{ + esm.getHNT(mGMDT, "GMDT"); +} + +} diff --git a/apps/essimporter/importgame.hpp b/apps/essimporter/importgame.hpp new file mode 100644 index 0000000000..7bb8143207 --- /dev/null +++ b/apps/essimporter/importgame.hpp @@ -0,0 +1,33 @@ +#ifndef OPENMW_ESSIMPORT_GAME_H +#define OPENMW_ESSIMPORT_GAME_H + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + /// Weather data + struct GAME + { + struct GMDT + { + char mCellName[64]; + int mFogColour; + float mFogDensity; + int mCurrentWeather, mNextWeather; + int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage + float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition + int masserPhase, secundaPhase; // top 3 bytes may be garbage + }; + + GMDT mGMDT; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importinfo.cpp b/apps/essimporter/importinfo.cpp new file mode 100644 index 0000000000..1131553709 --- /dev/null +++ b/apps/essimporter/importinfo.cpp @@ -0,0 +1,14 @@ +#include "importinfo.hpp" + +#include + +namespace ESSImport +{ + + void INFO::load(ESM::ESMReader &esm) + { + mInfo = esm.getHNString("INAM"); + mActorRefId = esm.getHNString("ACDT"); + } + +} diff --git a/apps/essimporter/importinfo.hpp b/apps/essimporter/importinfo.hpp new file mode 100644 index 0000000000..08f588f8d8 --- /dev/null +++ b/apps/essimporter/importinfo.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTINFO_H +#define OPENMW_ESSIMPORT_IMPORTINFO_H + +#include + +namespace ESM +{ + struct ESMReader; +} + +namespace ESSImport +{ + + struct INFO + { + std::string mInfo; + std::string mActorRefId; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importjour.cpp b/apps/essimporter/importjour.cpp new file mode 100644 index 0000000000..e5d24e113c --- /dev/null +++ b/apps/essimporter/importjour.cpp @@ -0,0 +1,13 @@ +#include "importjour.hpp" + +#include + +namespace ESSImport +{ + + void JOUR::load(ESM::ESMReader &esm) + { + mText = esm.getHNString("NAME"); + } + +} diff --git a/apps/essimporter/importjour.hpp b/apps/essimporter/importjour.hpp new file mode 100644 index 0000000000..fe8775a93b --- /dev/null +++ b/apps/essimporter/importjour.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTJOUR_H +#define OPENMW_ESSIMPORT_IMPORTJOUR_H + +#include + +namespace ESM +{ + struct ESMReader; +} + +namespace ESSImport +{ + + /// Journal + struct JOUR + { + // The entire journal, in HTML + std::string mText; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importques.cpp b/apps/essimporter/importques.cpp new file mode 100644 index 0000000000..78b779e439 --- /dev/null +++ b/apps/essimporter/importques.cpp @@ -0,0 +1,14 @@ +#include "importques.hpp" + +#include + +namespace ESSImport +{ + + void QUES::load(ESM::ESMReader &esm) + { + while (esm.isNextSub("DATA")) + mInfo.push_back(esm.getHString()); + } + +} diff --git a/apps/essimporter/importques.hpp b/apps/essimporter/importques.hpp new file mode 100644 index 0000000000..d7e718b928 --- /dev/null +++ b/apps/essimporter/importques.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTQUES_H +#define OPENMW_ESSIMPORT_IMPORTQUES_H + +#include +#include + +namespace ESM +{ + struct ESMReader; +} + +namespace ESSImport +{ + + /// State for a quest + /// Presumably this record only exists when Tribunal is installed, + /// since pre-Tribunal there weren't any quest names in the data files. + struct QUES + { + std::string mName; // NAME, should be assigned from outside as usual + std::vector mInfo; // list of journal entries for the quest + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index fe45d69149..effe9a3f18 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -97,7 +97,7 @@ enum RecNameInts // format 0 - saved games REC_SAVE = 0x45564153, - REC_JOUR = 0x524f55a4, + REC_JOUR = 0x524f55a4, // TODO: this is actually "\nOUR" rather than "JOUR", fix REC_QUES = 0x53455551, REC_GSCR = 0x52435347, REC_PLAY = 0x59414c50, diff --git a/components/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp index 6c087fd017..6b3cd533f4 100644 --- a/components/esm/loadnpcc.hpp +++ b/components/esm/loadnpcc.hpp @@ -20,24 +20,7 @@ class ESMWriter; * SCPT records do not define new scripts, but assign values to the * variables of existing ones. * - * STLN - stolen items, ONAM is the owner - * - * GAME - weather data - * struct GMDT - { - char mCellName[64]; - int mFogColour; - float mFogDensity; - int mCurrentWeather, mNextWeather; - int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage - float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition - int masserPhase, secundaPhase; // top 3 bytes may be garbage - }; - * * VFXM, SPLM - no clue - * KLST - kill counter - * - * PCDT - seems to contain a lot of DNAMs, strings? * * FMAP - MAPH and MAPD, global map image. * From 5ce8a931a55fb74041d3690eb1ac4cffe4c73970 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 03:34:44 +0100 Subject: [PATCH 380/404] ESSImport: fix a subrecord ordering issue --- apps/essimporter/importcellref.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 7c4af1e022..845c574e4b 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -19,6 +19,7 @@ namespace ESSImport mIndexedRefId = esm.getHNString("NAME"); + ActorData::load(esm); if (esm.isNextSub("LVCR")) { // occurs on leveled creature spawner references @@ -27,7 +28,6 @@ namespace ESSImport esm.getHT(lvcr); //std::cout << "LVCR: " << (int)lvcr << std::endl; } - ActorData::load(esm); mEnabled = true; esm.getHNOT(mEnabled, "ZNAM"); From cc7be1600d7491f8d23081ab6e7a413c943454fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 03:36:12 +0100 Subject: [PATCH 381/404] Make missing hair/head in NpcAnimation non-fatal for consistency with addOrReplaceIndividualPart --- apps/openmw/mwrender/npcanimation.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 5e73a95f23..a8ba1abf5d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -263,17 +263,27 @@ void NpcAnimation::updateNpcBase() } else { - if (isVampire) + mHeadModel = ""; + if (isVampire) // FIXME: fall back to regular head when getVampireHead fails? mHeadModel = getVampireHead(mNpc->mRace, mNpc->mFlags & ESM::NPC::Female); else if (!mNpc->mHead.empty()) - mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; - else - mHeadModel = ""; + { + const ESM::BodyPart* bp = store.get().search(mNpc->mHead); + if (bp) + mHeadModel = "meshes\\" + bp->mModel; + else + std::cerr << "Failed to load body part '" << mNpc->mHead << "'" << std::endl; + } + mHairModel = ""; if (!mNpc->mHair.empty()) - mHairModel = "meshes\\" + store.get().find(mNpc->mHair)->mModel; - else - mHairModel = ""; + { + const ESM::BodyPart* bp = store.get().search(mNpc->mHair); + if (bp) + mHairModel = "meshes\\" + bp->mModel; + else + std::cerr << "Failed to load body part '" << mNpc->mHair << "'" << std::endl; + } } bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; From 0fd5575efee6495d0fa2eb4322457e543b2d69de Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 03:39:00 +0100 Subject: [PATCH 382/404] Improve warning message in ContainerStore::readState --- apps/openmw/mwworld/containerstore.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index ba70e2f8f2..57621b7a47 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -688,8 +688,11 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) case ESM::REC_REPA: getState (repairs, iter->first); break; case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; case ESM::REC_LIGH: setSlot (getState (lights, iter->first), slot); break; + case 0: + std::cerr << "Dropping reference to '" << state.mRef.mRefID << "' (object no longer exists)" << std::endl; + break; default: - std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; + std::cerr << "Invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; break; } } From 98402e579de14b6e203e4073481ef0fb689e2553 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 03:39:37 +0100 Subject: [PATCH 383/404] ESSImport: fix non-existing items in player record causing load failure in OpenMW --- apps/essimporter/converter.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 4ca0e83864..a88a364d25 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -111,6 +111,14 @@ public: // need to fix openmw to account for this for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + + // Clear the list now that we've written it, this prevents issues cropping up with + // ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal. + mContext->mPlayerBase.mSpells.mList.clear(); + + // Same with inventory. Actually it's strange this would contain something, since there's already an + // inventory list in NPCC. There seems to be a fair amount of redundancy in this format. + mContext->mPlayerBase.mInventory.mList.clear(); } } }; @@ -127,6 +135,10 @@ public: } }; +// Do we need ConvertCONT? +// I've seen a CONT record in a certain save file, but the container contents in it +// were identical to a corresponding CNTC record. See previous comment about redundancy... + class ConvertGlobal : public DefaultConverter { public: From e2031279520aefbe00f053bd9dd75e2f842e2198 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 03:54:33 +0100 Subject: [PATCH 384/404] ESSImport: read script variables (not converted yet) --- apps/essimporter/CMakeLists.txt | 2 + apps/essimporter/converter.hpp | 12 ++++++ apps/essimporter/importacdt.cpp | 13 +------ apps/essimporter/importacdt.hpp | 4 +- apps/essimporter/importer.cpp | 1 + apps/essimporter/importinventory.cpp | 13 +------ apps/essimporter/importinventory.hpp | 2 + apps/essimporter/importscpt.cpp | 20 ++++++++++ apps/essimporter/importscpt.hpp | 32 ++++++++++++++++ apps/essimporter/importscri.cpp | 55 ++++++++++++++++++++++++++++ apps/essimporter/importscri.hpp | 30 +++++++++++++++ components/esm/loadscpt.cpp | 32 ---------------- components/esm/loadscpt.hpp | 5 +++ 13 files changed, 164 insertions(+), 57 deletions(-) create mode 100644 apps/essimporter/importscpt.cpp create mode 100644 apps/essimporter/importscpt.hpp create mode 100644 apps/essimporter/importscri.cpp create mode 100644 apps/essimporter/importscri.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 70eaefe2c6..5db3a86dc4 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -14,6 +14,8 @@ set(ESSIMPORTER_FILES importdial.cpp importques.cpp importjour.cpp + importscri.cpp + importscpt.cpp importercontext.cpp converter.cpp convertacdt.cpp diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index a88a364d25..9894de2996 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -25,6 +25,7 @@ #include "importdial.hpp" #include "importques.hpp" #include "importjour.hpp" +#include "importscpt.hpp" #include "convertacdt.hpp" #include "convertnpcc.hpp" @@ -437,6 +438,17 @@ public: } }; +/// Running global script +class ConvertSCPT : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + SCPT script; + script.load(esm); + } +}; + } #endif diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index 4d4a87a35f..e590e2826d 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -81,18 +81,7 @@ namespace ESSImport if (esm.isNextSub("CRED")) // creature only esm.getHExact(mCombatStats, 3*2*sizeof(int)); - mScript = esm.getHNOString("SCRI"); - - // script variables? - if (!mScript.empty()) - { - if (esm.isNextSub("SLCS")) - esm.skipHSub(); - if (esm.isNextSub("SLSD")) // Short Data? - esm.skipHSub(); - if (esm.isNextSub("SLFD")) // Float Data? - esm.skipHSub(); - } + mSCRI.load(esm); if (esm.isNextSub("ND3D")) esm.skipHSub(); diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index 271f6df745..3e3003f58a 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -5,6 +5,8 @@ #include +#include "importscri.hpp" + namespace ESM { struct ESMReader; @@ -50,7 +52,7 @@ namespace ESSImport // to change them ingame int mCombatStats[3][2]; - std::string mScript; + SCRI mSCRI; void load(ESM::ESMReader& esm); }; diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 8603200da8..a29d26e2a8 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -234,6 +234,7 @@ namespace ESSImport converters[ESM::REC_DIAL] = boost::shared_ptr(new ConvertDIAL()); converters[ESM::REC_QUES] = boost::shared_ptr(new ConvertQUES()); converters[recJOUR ] = boost::shared_ptr(new ConvertJOUR()); + converters[ESM::REC_SCPT] = boost::shared_ptr(new ConvertSCPT()); std::set unknownRecords; diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index a20584ff5a..d27cd5c8ca 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -29,18 +29,7 @@ namespace ESSImport if (esm.isNextSub("XIDX")) // index in the stack? esm.skipHSub(); - std::string script = esm.getHNOString("SCRI"); - // script variables? - // unsure if before or after ESM::CellRef - if (!script.empty()) - { - if (esm.isNextSub("SLCS")) - esm.skipHSub(); - if (esm.isNextSub("SLSD")) // Short Data? - esm.skipHSub(); - if (esm.isNextSub("SLFD")) // Float Data? - esm.skipHSub(); - } + item.mSCRI.load(esm); // for XSOL and XCHG seen so far, but probably others too item.ESM::CellRef::loadData(esm); diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 99fed9b20e..0b5405d961 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -5,6 +5,7 @@ #include #include +#include "importscri.hpp" namespace ESM { @@ -21,6 +22,7 @@ namespace ESSImport std::string mId; int mCount; int mRelativeEquipmentSlot; + SCRI mSCRI; }; std::vector mItems; diff --git a/apps/essimporter/importscpt.cpp b/apps/essimporter/importscpt.cpp new file mode 100644 index 0000000000..374fd05fad --- /dev/null +++ b/apps/essimporter/importscpt.cpp @@ -0,0 +1,20 @@ +#include "importscpt.hpp" + +#include + + + +namespace ESSImport +{ + + void SCPT::load(ESM::ESMReader &esm) + { + esm.getHNT(mSCHD, "SCHD"); + + mSCRI.load(esm); + + mRNAM = -1; + esm.getHNOT(mRNAM, "RNAM"); + } + +} diff --git a/apps/essimporter/importscpt.hpp b/apps/essimporter/importscpt.hpp new file mode 100644 index 0000000000..ca2439dda2 --- /dev/null +++ b/apps/essimporter/importscpt.hpp @@ -0,0 +1,32 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTSCPT_H +#define OPENMW_ESSIMPORT_IMPORTSCPT_H + +#include "importscri.hpp" + +#include + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + // A running global script + // TODO: test how targeted scripts are saved + struct SCPT + { + ESM::Script::SCHD mSCHD; + + // values of local variables + SCRI mSCRI; + + int mRNAM; // unknown, seems to be -1 for some scripts, some huge integer for others + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importscri.cpp b/apps/essimporter/importscri.cpp new file mode 100644 index 0000000000..de0b35c86c --- /dev/null +++ b/apps/essimporter/importscri.cpp @@ -0,0 +1,55 @@ +#include "importscri.hpp" + +#include + +namespace ESSImport +{ + + void SCRI::load(ESM::ESMReader &esm) + { + mScript = esm.getHNOString("SCRI"); + + int numShorts = 0, numLongs = 0, numFloats = 0; + if (esm.isNextSub("SLCS")) + { + esm.getSubHeader(); + esm.getT(numShorts); + esm.getT(numLongs); + esm.getT(numFloats); + } + + if (esm.isNextSub("SLSD")) + { + esm.getSubHeader(); + for (int i=0; i + +#include + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + /// Local variable assigments for a running script + struct SCRI + { + std::string mScript; + + std::vector mShorts; + std::vector mLongs; + std::vector mFloats; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index f2f13d0864..2df8e66cee 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -7,12 +7,6 @@ namespace ESM { -struct SCHD -{ - NAME32 mName; - Script::SCHDstruct mData; -}; - unsigned int Script::sRecordId = REC_SCPT; void Script::load(ESMReader &esm) @@ -92,32 +86,6 @@ void Script::load(ESMReader &esm) if (esm.isNextSub("SCVR")) { esm.skipHSub(); } - - // ESS only, TODO: move - if (esm.isNextSub("SLCS")) - { - esm.getSubHeader(); - char unknown[12]; - esm.getExact(unknown, 12); - } - if (esm.isNextSub("SLSD")) - { - float read; - esm.getSubHeader(); - // values of variables? - for (int i=0; i Date: Thu, 22 Jan 2015 04:12:08 +0100 Subject: [PATCH 385/404] Fix Tribunal/BM summon effects not working --- apps/essimporter/importcellref.cpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 13 ++++++++++--- apps/openmw/mwmechanics/spellcasting.hpp | 2 ++ apps/openmw/mwmechanics/summoning.cpp | 5 +++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 845c574e4b..1894b0023b 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -23,7 +23,7 @@ namespace ESSImport if (esm.isNextSub("LVCR")) { // occurs on leveled creature spawner references - // probably some identifier for the the creature that has been spawned? + // probably some identifier for the creature that has been spawned? unsigned char lvcr; esm.getHT(lvcr); //std::cout << "LVCR: " << (int)lvcr << std::endl; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index ff27ac5f62..49887e560f 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -472,9 +472,7 @@ namespace MWMechanics applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude); // Re-casting a summon effect will remove the creature from previous castings of that effect. - if (effectIt->mEffectID >= ESM::MagicEffect::SummonScamp - && effectIt->mEffectID <= ESM::MagicEffect::SummonStormAtronach - && !target.isEmpty() && target.getClass().isActor()) + if (isSummoningEffect(effectIt->mEffectID) && !target.isEmpty() && target.getClass().isActor()) { CreatureStats& targetStats = target.getClass().getCreatureStats(target); std::map::iterator found = targetStats.getSummonedCreatureMap().find(std::make_pair(effectIt->mEffectID, mId)); @@ -956,4 +954,13 @@ namespace MWMechanics return static_cast((result < 1) ? 1 : result); } + + bool isSummoningEffect(int effectId) + { + return ((effectId >= ESM::MagicEffect::SummonScamp + && effectId <= ESM::MagicEffect::SummonStormAtronach) + || effectId == ESM::MagicEffect::SummonCenturionSphere + || (effectId >= ESM::MagicEffect::SummonFabricant + && effectId <= ESM::MagicEffect::SummonCreature05)); + } } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 00fae847fc..2d550e0856 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -22,6 +22,8 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); + bool isSummoningEffect(int effectId); + /** * @param spell spell to cast * @param actor calculate spell success chance for this actor (depends on actor's skills) diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 356cb422f1..ec9bd0ea01 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -5,6 +5,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwmechanics/spellcasting.hpp" + #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/manualref.hpp" @@ -51,8 +53,7 @@ namespace MWMechanics void UpdateSummonedCreatures::visit(EffectKey key, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) { - if (key.mId >= ESM::MagicEffect::SummonScamp - && key.mId <= ESM::MagicEffect::SummonStormAtronach && magnitude > 0) + if (isSummoningEffect(key.mId) && magnitude > 0) { mActiveEffects.insert(std::make_pair(key.mId, sourceId)); } From af0e91c2d348b93d65a8db36582db27dac6de971 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 15:21:03 +0100 Subject: [PATCH 386/404] ESSImport: stolen items reading fix --- apps/essimporter/converter.hpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 9894de2996..7565b78d42 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -364,15 +364,18 @@ public: { std::string itemid = esm.getHNString("NAME"); - while (esm.isNextSub("ONAM")) + while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM")) { - std::string ownerid = esm.getHString(); - mStolenItems.insert(std::make_pair(itemid, ownerid)); - } - while (esm.isNextSub("FNAM")) - { - std::string factionid = esm.getHString(); - mFactionStolenItems.insert(std::make_pair(itemid, factionid)); + if (esm.retSubName().toString() == "FNAM") + { + std::string factionid = esm.getHString(); + mFactionStolenItems.insert(std::make_pair(itemid, factionid)); + } + else + { + std::string ownerid = esm.getHString(); + mStolenItems.insert(std::make_pair(itemid, ownerid)); + } } } private: From 8b5effe3e06eb628ac3c12f6fec39052ef26a79b Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 17:47:00 +0100 Subject: [PATCH 387/404] Fix wrong magic number for JOUR in openmw savegames --- apps/openmw/mwdialogue/journalimp.cpp | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 1 + components/esm/defs.hpp | 22 ++++++++++++---------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 1d17134dff..597a908e8e 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -229,7 +229,7 @@ namespace MWDialogue void Journal::readRecord (ESM::ESMReader& reader, int32_t type) { - if (type==ESM::REC_JOUR) + if (type==ESM::REC_JOUR || type==ESM::REC_JOUR_LEGACY) { ESM::JournalEntry record; record.load (reader); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 87b303b6fb..a282f152ad 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -368,6 +368,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str break; case ESM::REC_JOUR: + case ESM::REC_JOUR_LEGACY: case ESM::REC_QUES: MWBase::Environment::get().getJournal()->readRecord (reader, n.val); diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index effe9a3f18..60926d562d 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -96,15 +96,17 @@ enum RecNameInts REC_WEAP = 0x50414557, // format 0 - saved games - REC_SAVE = 0x45564153, - REC_JOUR = 0x524f55a4, // TODO: this is actually "\nOUR" rather than "JOUR", fix - REC_QUES = 0x53455551, - REC_GSCR = 0x52435347, - REC_PLAY = 0x59414c50, - REC_CSTA = 0x41545343, - REC_GMAP = 0x50414d47, - REC_DIAS = 0x53414944, - REC_WTHR = 0x52485457, + REC_SAVE = FourCC<'S','A','V','E'>::value, + REC_JOUR_LEGACY = FourCC<0xa4,'U','O','R'>::value, // "\xa4UOR", rather than "JOUR", little oversight when magic numbers were + // calculated by hand, needs to be supported for older files now + REC_JOUR = FourCC<'J','O','U','R'>::value, + REC_QUES = FourCC<'Q','U','E','S'>::value, + REC_GSCR = FourCC<'G','S','C','R'>::value, + REC_PLAY = FourCC<'P','L','A','Y'>::value, + REC_CSTA = FourCC<'C','S','T','A'>::value, + REC_GMAP = FourCC<'G','M','A','P'>::value, + REC_DIAS = FourCC<'D','I','A','S'>::value, + REC_WTHR = FourCC<'W','T','H','R'>::value, REC_KEYS = FourCC<'K','E','Y','S'>::value, REC_DYNA = FourCC<'D','Y','N','A'>::value, REC_ASPL = FourCC<'A','S','P','L'>::value, @@ -117,7 +119,7 @@ enum RecNameInts REC_CAM_ = FourCC<'C','A','M','_'>::value, // format 1 - REC_FILT = 0x544C4946, + REC_FILT = FourCC<'F','I','L','T'>::value, REC_DBGP = FourCC<'D','B','G','P'>::value ///< only used in project files }; From c883a73d30adc988738c6c577f5c4ae67d8da8a0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 19:04:59 +0100 Subject: [PATCH 388/404] Several warning fixes --- apps/bsatool/bsatool.cpp | 8 +++---- apps/esmtool/esmtool.cpp | 24 +++++++++---------- apps/esmtool/labels.cpp | 21 ++++++++-------- apps/esmtool/record.cpp | 22 +++++++++-------- apps/esmtool/record.hpp | 10 +++----- apps/essimporter/main.cpp | 2 +- apps/openmw/mwbase/dialoguemanager.hpp | 2 +- apps/openmw/mwbase/journal.hpp | 2 +- apps/openmw/mwbase/mechanicsmanager.hpp | 2 +- apps/openmw/mwbase/windowmanager.hpp | 2 +- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 2 +- apps/openmw/mwdialogue/journalimp.cpp | 2 +- apps/openmw/mwdialogue/journalimp.hpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 2 +- apps/openmw/mwgui/mapwindow.hpp | 2 +- apps/openmw/mwgui/quickkeysmenu.cpp | 2 +- apps/openmw/mwgui/quickkeysmenu.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/actors.hpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.hpp | 2 +- apps/openmw/mwscript/globalscripts.cpp | 2 +- apps/openmw/mwscript/globalscripts.hpp | 2 +- apps/openmw/mwworld/cells.cpp | 2 +- apps/openmw/mwworld/cells.hpp | 2 +- apps/openmw/mwworld/esmstore.cpp | 2 +- apps/openmw/mwworld/esmstore.hpp | 2 +- apps/openmw/mwworld/globals.cpp | 2 +- apps/openmw/mwworld/globals.hpp | 2 +- apps/openmw/mwworld/player.cpp | 2 +- apps/openmw/mwworld/player.hpp | 2 +- apps/openmw/mwworld/projectilemanager.cpp | 2 +- apps/openmw/mwworld/projectilemanager.hpp | 2 +- apps/openmw/mwworld/weather.cpp | 2 +- apps/openmw/mwworld/weather.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- apps/openmw/mwworld/worldimp.hpp | 2 +- components/esm/esmcommon.hpp | 2 +- 42 files changed, 78 insertions(+), 81 deletions(-) diff --git a/apps/bsatool/bsatool.cpp b/apps/bsatool/bsatool.cpp index 5b1f7d6bba..c0a6dcc81f 100644 --- a/apps/bsatool/bsatool.cpp +++ b/apps/bsatool/bsatool.cpp @@ -27,8 +27,8 @@ struct Arguments void replaceAll(std::string& str, const std::string& needle, const std::string& substitute) { - int pos = str.find(needle); - while(pos != -1) + size_t pos = str.find(needle); + while(pos != std::string::npos) { str.replace(pos, needle.size(), substitute); pos = str.find(needle); @@ -138,8 +138,8 @@ bool parseOptions (int argc, char** argv, Arguments &info) else if (variables["input-file"].as< std::vector >().size() > 1) info.outdir = variables["input-file"].as< std::vector >()[1]; - info.longformat = variables.count("long"); - info.fullpath = variables.count("full-path"); + info.longformat = variables.count("long") != 0; + info.fullpath = variables.count("full-path") != 0; return true; } diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index b23ba72f9c..9bbf202665 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -22,7 +22,7 @@ struct ESMData { std::string author; std::string description; - int version; + unsigned int version; std::vector masters; std::deque mRecords; @@ -48,9 +48,9 @@ const std::set ESMData::sLabeledRec = // Based on the legacy struct struct Arguments { - unsigned int raw_given; - unsigned int quiet_given; - unsigned int loadcells_given; + bool raw_given; + bool quiet_given; + bool loadcells_given; bool plain_given; std::string mode; @@ -177,10 +177,10 @@ bool parseOptions (int argc, char** argv, Arguments &info) if (variables["input-file"].as< std::vector >().size() > 1) info.outname = variables["input-file"].as< std::vector >()[1]; - info.raw_given = variables.count ("raw"); - info.quiet_given = variables.count ("quiet"); - info.loadcells_given = variables.count ("loadcells"); - info.plain_given = (variables.count("plain") > 0); + info.raw_given = variables.count ("raw") != 0; + info.quiet_given = variables.count ("quiet") != 0; + info.loadcells_given = variables.count ("loadcells") != 0; + info.plain_given = variables.count("plain") != 0; // Font encoding settings info.encoding = variables["encoding"].as(); @@ -430,7 +430,7 @@ int clone(Arguments& info) return 1; } - int recordCount = info.data.mRecords.size(); + size_t recordCount = info.data.mRecords.size(); int digitCount = 1; // For a nicer output if (recordCount > 9) ++digitCount; @@ -501,9 +501,9 @@ int clone(Arguments& info) if (!info.data.mCellRefs[ptr].empty()) { typedef std::deque RefList; RefList &refs = info.data.mCellRefs[ptr]; - for (RefList::iterator it = refs.begin(); it != refs.end(); ++it) + for (RefList::iterator refIt = refs.begin(); refIt != refs.end(); ++refIt) { - it->save(esm); + refIt->save(esm); } } } @@ -511,7 +511,7 @@ int clone(Arguments& info) esm.endRecord(name.toString()); saved++; - int perc = (saved / (float)recordCount)*100; + int perc = (int)((saved / (float)recordCount)*100); if (perc % 10 == 0) { std::cerr << "\r" << perc << "%"; diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 9543628f51..88e188df01 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -13,14 +13,13 @@ #include #include -#include #include std::string bodyPartLabel(int idx) { if (idx >= 0 && idx <= 26) { - const char *bodyPartLabels[] = { + static const char *bodyPartLabels[] = { "Head", "Hair", "Neck", @@ -59,7 +58,7 @@ std::string meshPartLabel(int idx) { if (idx >= 0 && idx <= ESM::BodyPart::MP_Tail) { - const char *meshPartLabels[] = { + static const char *meshPartLabels[] = { "Head", "Hair", "Neck", @@ -86,7 +85,7 @@ std::string meshTypeLabel(int idx) { if (idx >= 0 && idx <= ESM::BodyPart::MT_Armor) { - const char *meshTypeLabels[] = { + static const char *meshTypeLabels[] = { "Skin", "Clothing", "Armor" @@ -101,7 +100,7 @@ std::string clothingTypeLabel(int idx) { if (idx >= 0 && idx <= 9) { - const char *clothingTypeLabels[] = { + static const char *clothingTypeLabels[] = { "Pants", "Shoes", "Shirt", @@ -123,7 +122,7 @@ std::string armorTypeLabel(int idx) { if (idx >= 0 && idx <= 10) { - const char *armorTypeLabels[] = { + static const char *armorTypeLabels[] = { "Helmet", "Cuirass", "Left Pauldron", @@ -146,7 +145,7 @@ std::string dialogTypeLabel(int idx) { if (idx >= 0 && idx <= 4) { - const char *dialogTypeLabels[] = { + static const char *dialogTypeLabels[] = { "Topic", "Voice", "Greeting", @@ -165,7 +164,7 @@ std::string questStatusLabel(int idx) { if (idx >= 0 && idx <= 4) { - const char *questStatusLabels[] = { + static const char *questStatusLabels[] = { "None", "Name", "Finished", @@ -182,7 +181,7 @@ std::string creatureTypeLabel(int idx) { if (idx >= 0 && idx <= 3) { - const char *creatureTypeLabels[] = { + static const char *creatureTypeLabels[] = { "Creature", "Daedra", "Undead", @@ -198,7 +197,7 @@ std::string soundTypeLabel(int idx) { if (idx >= 0 && idx <= 7) { - const char *soundTypeLabels[] = { + static const char *soundTypeLabels[] = { "Left Foot", "Right Foot", "Swim Left", @@ -218,7 +217,7 @@ std::string weaponTypeLabel(int idx) { if (idx >= 0 && idx <= 13) { - const char *weaponTypeLabels[] = { + static const char *weaponTypeLabels[] = { "Short Blade One Hand", "Long Blade One Hand", "Long Blade Two Hand", diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 3f2ebc2ba5..2f07a5f088 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -25,7 +25,7 @@ void printAIPackage(ESM::AIPackage p) { std::cout << " Travel Coordinates: (" << p.mTravel.mX << "," << p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl; - std::cout << " Travel Unknown: " << (int)p.mTravel.mUnk << std::endl; + std::cout << " Travel Unknown: " << p.mTravel.mUnk << std::endl; } else if (p.mType == ESM::AI_Follow || p.mType == ESM::AI_Escort) { @@ -33,12 +33,12 @@ void printAIPackage(ESM::AIPackage p) << p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl; std::cout << " Duration: " << p.mTarget.mDuration << std::endl; std::cout << " Target ID: " << p.mTarget.mId.toString() << std::endl; - std::cout << " Unknown: " << (int)p.mTarget.mUnk << std::endl; + std::cout << " Unknown: " << p.mTarget.mUnk << std::endl; } else if (p.mType == ESM::AI_Activate) { std::cout << " Name: " << p.mActivate.mName.toString() << std::endl; - std::cout << " Activate Unknown: " << (int)p.mActivate.mUnk << std::endl; + std::cout << " Activate Unknown: " << p.mActivate.mUnk << std::endl; } else { std::cout << " BadPackage: " << boost::format("0x%08x") % p.mType << std::endl; @@ -89,6 +89,7 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss) case 'A': if (indicator == 'R') type_str = "Not Race"; break; case 'B': if (indicator == 'L') type_str = "Not Cell"; break; case 'C': if (indicator == 's') type_str = "Not Local"; break; + default: break; } // Append the variable name to the function string if any. @@ -110,6 +111,7 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss) case '3': oper_str = ">="; break; case '4': oper_str = "< "; break; case '5': oper_str = "<="; break; + default: break; } std::ostringstream stream; @@ -430,7 +432,7 @@ void Record::print() std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Type: " << apparatusTypeLabel(mData.mData.mType) - << " (" << (int)mData.mData.mType << ")" << std::endl; + << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; @@ -816,7 +818,7 @@ void Record::print() // Seems like this should done with reference counting in the // loader to me. But I'm not really knowledgable about this // record type yet. --Cory - bool wasLoaded = mData.mDataLoaded; + bool wasLoaded = (mData.mDataLoaded != 0); if (mData.mDataTypes) mData.loadData(mData.mDataTypes); if (mData.mDataLoaded) { @@ -999,7 +1001,7 @@ void Record::print() << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown2) << std::endl; std::cout << " Unknown3: " << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown3) << std::endl; - std::cout << " Gold: " << (int)mData.mNpdt12.mGold << std::endl; + std::cout << " Gold: " << mData.mNpdt12.mGold << std::endl; } else { std::cout << " Level: " << mData.mNpdt52.mLevel << std::endl; @@ -1021,7 +1023,7 @@ void Record::print() std::cout << " Skills:" << std::endl; for (int i = 0; i != ESM::Skill::Length; i++) std::cout << " " << skillLabel(i) << ": " - << (int)((unsigned char)mData.mNpdt52.mSkills[i]) << std::endl; + << (int)(mData.mNpdt52.mSkills[i]) << std::endl; std::cout << " Health: " << mData.mNpdt52.mHealth << std::endl; std::cout << " Magicka: " << mData.mNpdt52.mMana << std::endl; @@ -1123,9 +1125,9 @@ void Record::print() std::cout << (male ? " Male:" : " Female:") << std::endl; - for (int i=0; i<8; ++i) - std::cout << " " << sAttributeNames[i] << ": " - << mData.mData.mAttributeValues[i].getValue (male) << std::endl; + for (int j=0; j<8; ++j) + std::cout << " " << sAttributeNames[j] << ": " + << mData.mData.mAttributeValues[j].getValue (male) << std::endl; std::cout << " Height: " << mData.mData.mHeight.getValue (male) << std::endl; std::cout << " Weight: " << mData.mData.mWeight.getValue (male) << std::endl; diff --git a/apps/esmtool/record.hpp b/apps/esmtool/record.hpp index 45b6d04266..c1b90ac2bc 100644 --- a/apps/esmtool/record.hpp +++ b/apps/esmtool/record.hpp @@ -19,7 +19,7 @@ namespace EsmTool { protected: std::string mId; - int mFlags; + uint32_t mFlags; ESM::NAME mType; bool mPrintPlain; @@ -40,11 +40,11 @@ namespace EsmTool mId = id; } - int getFlags() const { + uint32_t getFlags() const { return mFlags; } - void setFlags(int flags) { + void setFlags(uint32_t flags) { mFlags = flags; } @@ -52,10 +52,6 @@ namespace EsmTool return mType; } - bool getPrintPlain() const { - return mPrintPlain; - } - void setPrintPlain(bool plain) { mPrintPlain = plain; } diff --git a/apps/essimporter/main.cpp b/apps/essimporter/main.cpp index 1c7d511160..5a9e0c77d3 100644 --- a/apps/essimporter/main.cpp +++ b/apps/essimporter/main.cpp @@ -12,7 +12,7 @@ namespace bfs = boost::filesystem; -int main(int argc, const char** argv) +int main(int argc, char** argv) { try { diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index d35b5950f0..1fe63e6337 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -69,7 +69,7 @@ namespace MWBase virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0; - virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; + virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0; /// Changes faction1's opinion of faction2 by \a diff. virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff) = 0; diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 938cec74b2..738014ba6e 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -92,7 +92,7 @@ namespace MWBase virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0; - virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; + virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0; }; } diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index a51e3a301c..33a2ef1eaa 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -193,7 +193,7 @@ namespace MWBase virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0; - virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; + virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0; virtual void clear() = 0; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 3e957ef148..02cbc69e91 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -310,7 +310,7 @@ namespace MWBase virtual void clear() = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) = 0; - virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; + virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0; virtual int countSavedGameRecords() const = 0; /// Does the current stack of GUI-windows permit saving? diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index db0f0a7634..f58ef08095 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -108,7 +108,7 @@ namespace MWBase virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0; - virtual void readRecord (ESM::ESMReader& reader, int32_t type, + virtual void readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) = 0; virtual MWWorld::CellStore *getExterior (int x, int y) = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 0ee7dfe94c..d2534cd829 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -647,7 +647,7 @@ namespace MWDialogue writer.endRecord (ESM::REC_DIAS); } - void DialogueManager::readRecord (ESM::ESMReader& reader, int32_t type) + void DialogueManager::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_DIAS) { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index c06299736f..77035bc60a 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -92,7 +92,7 @@ namespace MWDialogue virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - virtual void readRecord (ESM::ESMReader& reader, int32_t type); + virtual void readRecord (ESM::ESMReader& reader, uint32_t type); /// Changes faction1's opinion of faction2 by \a diff. virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff); diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 597a908e8e..3b57912dae 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -227,7 +227,7 @@ namespace MWDialogue } } - void Journal::readRecord (ESM::ESMReader& reader, int32_t type) + void Journal::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_JOUR || type==ESM::REC_JOUR_LEGACY) { diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index d15b909fa0..7f26a5bb90 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -71,7 +71,7 @@ namespace MWDialogue virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - virtual void readRecord (ESM::ESMReader& reader, int32_t type); + virtual void readRecord (ESM::ESMReader& reader, uint32_t type); }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index abd1256d7f..00d60f23bf 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -886,7 +886,7 @@ namespace MWGui writer.endRecord(ESM::REC_GMAP); } - void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type) + void MapWindow::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type == ESM::REC_GMAP) { diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 6840694673..7b6c1f2050 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -177,7 +177,7 @@ namespace MWGui void clear(); void write (ESM::ESMWriter& writer, Loading::Listener& progress); - void readRecord (ESM::ESMReader& reader, int32_t type); + void readRecord (ESM::ESMReader& reader, uint32_t type); private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index deba644345..a102de1e56 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -431,7 +431,7 @@ namespace MWGui writer.endRecord(ESM::REC_KEYS); } - void QuickKeysMenu::readRecord(ESM::ESMReader &reader, int32_t type) + void QuickKeysMenu::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type != ESM::REC_KEYS) return; diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 30e6728911..00afa45610 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -47,7 +47,7 @@ namespace MWGui void write (ESM::ESMWriter& writer); - void readRecord (ESM::ESMReader& reader, int32_t type); + void readRecord (ESM::ESMReader& reader, uint32_t type); void clear(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5b2bbee906..e976b984ff 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1620,7 +1620,7 @@ namespace MWGui } } - void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type) + void WindowManager::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type == ESM::REC_GMAP) mMap->readRecord(reader, type); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 44d5819c24..a08c59a2fb 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -306,7 +306,7 @@ namespace MWGui virtual void clear(); virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress); - virtual void readRecord (ESM::ESMReader& reader, int32_t type); + virtual void readRecord (ESM::ESMReader& reader, uint32_t type); virtual int countSavedGameRecords() const; /// Does the current stack of GUI-windows permit saving? diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 64434a3045..f8068e8000 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1468,7 +1468,7 @@ namespace MWMechanics writer.endRecord(ESM::REC_DCOU); } - void Actors::readRecord (ESM::ESMReader& reader, int32_t type) + void Actors::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type == ESM::REC_DCOU) { diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 39fe38208c..39598b2a25 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -128,7 +128,7 @@ namespace MWMechanics void write (ESM::ESMWriter& writer, Loading::Listener& listener) const; - void readRecord (ESM::ESMReader& reader, int32_t type); + void readRecord (ESM::ESMReader& reader, uint32_t type); void clear(); // Clear death counter diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 3d757bde68..0a2c3cfff6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1360,7 +1360,7 @@ namespace MWMechanics mActors.write(writer, listener); } - void MechanicsManager::readRecord(ESM::ESMReader &reader, int32_t type) + void MechanicsManager::readRecord(ESM::ESMReader &reader, uint32_t type) { mActors.readRecord(reader, type); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index fc9c589746..2f443ad592 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -158,7 +158,7 @@ namespace MWMechanics virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const; - virtual void readRecord (ESM::ESMReader& reader, int32_t type); + virtual void readRecord (ESM::ESMReader& reader, uint32_t type); virtual void clear(); diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index c7371100f7..6c4bb3be52 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -145,7 +145,7 @@ namespace MWScript } } - bool GlobalScripts::readRecord (ESM::ESMReader& reader, int32_t type) + bool GlobalScripts::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_GSCR) { diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 55c2e93217..9f009db98f 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -62,7 +62,7 @@ namespace MWScript void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - bool readRecord (ESM::ESMReader& reader, int32_t type); + bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< Records for variables that do not exist are dropped silently. /// /// \return Known type? diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 3a8b7d5c44..fd5ec20dce 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -299,7 +299,7 @@ void MWWorld::Cells::write (ESM::ESMWriter& writer, Loading::Listener& progress) } } -bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type, +bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) { if (type==ESM::REC_CSTA) diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index a9c17fa930..0b7d9444fd 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -76,7 +76,7 @@ namespace MWWorld void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - bool readRecord (ESM::ESMReader& reader, int32_t type, + bool readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap); }; } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index b096f4b90d..caa88d751c 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -175,7 +175,7 @@ void ESMStore::setUp() mCreatureLists.write (writer, progress); } - bool ESMStore::readRecord (ESM::ESMReader& reader, int32_t type) + bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type) { switch (type) { diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 01770e6b38..05b6339566 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -233,7 +233,7 @@ namespace MWWorld void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - bool readRecord (ESM::ESMReader& reader, int32_t type); + bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< \return Known type? }; diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 302a05824a..e7cb04590b 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -88,7 +88,7 @@ namespace MWWorld } } - bool Globals::readRecord (ESM::ESMReader& reader, int32_t type) + bool Globals::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_GLOB) { diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index 3ff4a5d6e0..bb4ab13d92 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -53,7 +53,7 @@ namespace MWWorld void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - bool readRecord (ESM::ESMReader& reader, int32_t type); + bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< Records for variables that do not exist are dropped silently. /// /// \return Known type? diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index a57934bd32..c1e176b910 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -227,7 +227,7 @@ namespace MWWorld writer.endRecord (ESM::REC_PLAY); } - bool Player::readRecord (ESM::ESMReader& reader, int32_t type) + bool Player::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_PLAY) { diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d8cde5952b..25d8981cde 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -102,7 +102,7 @@ namespace MWWorld void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - bool readRecord (ESM::ESMReader& reader, int32_t type); + bool readRecord (ESM::ESMReader& reader, uint32_t type); int getNewCrimeId(); // get new id for witnesses void recordCrimeId(); // record the paid crime id when bounty is 0 diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index e4b2d5a1e9..41fbfea9f8 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -343,7 +343,7 @@ namespace MWWorld } } - bool ProjectileManager::readRecord(ESM::ESMReader &reader, int32_t type) + bool ProjectileManager::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type == ESM::REC_PROJ) { diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index 6e84b9efb2..93f54c0086 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -53,7 +53,7 @@ namespace MWWorld void clear(); void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - bool readRecord (ESM::ESMReader& reader, int32_t type); + bool readRecord (ESM::ESMReader& reader, uint32_t type); int countSavedGameRecords() const; private: diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index cd05cb798f..6532c59692 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -762,7 +762,7 @@ void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) writer.endRecord(ESM::REC_WTHR); } -bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type) +bool WeatherManager::readRecord(ESM::ESMReader& reader, uint32_t type) { if(ESM::REC_WTHR == type) { diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index dee9fc52ae..830b5c376c 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -199,7 +199,7 @@ namespace MWWorld void write(ESM::ESMWriter& writer, Loading::Listener& progress); - bool readRecord(ESM::ESMReader& reader, int32_t type); + bool readRecord(ESM::ESMReader& reader, uint32_t type); private: float mHour; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f7d5d96165..ceac85bcab 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -345,7 +345,7 @@ namespace MWWorld writer.endRecord(ESM::REC_CAM_); } - void World::readRecord (ESM::ESMReader& reader, int32_t type, + void World::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) { switch (type) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f050c498d0..9834015ac6 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -170,7 +170,7 @@ namespace MWWorld virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - virtual void readRecord (ESM::ESMReader& reader, int32_t type, + virtual void readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap); virtual CellStore *getExterior (int x, int y); diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index d3e6e7feae..1bdadead2f 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -23,7 +23,7 @@ template union NAME_T { char name[LEN]; - int32_t val; + uint32_t val; bool operator==(const char *str) const { From a619cff615f011beb2ed89fc2d1900671f3107d3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 21:39:07 +0100 Subject: [PATCH 389/404] Implement EnableLevelupMenu to trigger level-up --- apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/guiextensions.cpp | 2 ++ components/compiler/extensions0.cpp | 2 +- components/compiler/opcodes.hpp | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b139d61388..41cc6b88a9 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -443,5 +443,6 @@ op 0x20002fc: RemoveFromLevCreature op 0x20002fd: AddToLevItem op 0x20002fe: RemoveFromLevItem op 0x20002ff: SetFactionReaction +op 0x2000300: EnableLevelupMenu -opcodes 0x2000300-0x3ffffff unused +opcodes 0x2000301-0x3ffffff unused diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 1d34adbca0..f5549a1721 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -231,6 +231,8 @@ namespace MWScript new OpShowDialogue (MWGui::GM_Race)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableStatsReviewMenu, new OpShowDialogue (MWGui::GM_Review)); + interpreter.installSegment5 (Compiler::Gui::opcodeEnableLevelupMenu, + new OpShowDialogue (MWGui::GM_Levelup)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableInventoryMenu, new OpEnableWindow (MWGui::GW_Inventory)); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index a5cc0da626..70338669e3 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -215,7 +215,7 @@ namespace Compiler extensions.registerInstruction ("enablestatsmenu", "", opcodeEnableStatsMenu); extensions.registerInstruction ("enablerest", "", opcodeEnableRest); - extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest); + extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableLevelupMenu); extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu, opcodeShowRestMenuExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 04666e9760..e5cf2e965e 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -185,6 +185,7 @@ namespace Compiler const int opcodeEnableMapMenu = 0x2000015; const int opcodeEnableStatsMenu = 0x2000016; const int opcodeEnableRest = 0x2000017; + const int opcodeEnableLevelupMenu = 0x2000300; const int opcodeShowRestMenu = 0x2000018; const int opcodeShowRestMenuExplicit = 0x2000234; const int opcodeGetButtonPressed = 0x2000137; From ba7cd04ff7ac7546ef71ebd999e97dd4ef36028f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 23:28:31 +0100 Subject: [PATCH 390/404] ESSImport: prevent accidental overwriting of file --- apps/essimporter/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/essimporter/main.cpp b/apps/essimporter/main.cpp index 5a9e0c77d3..d467e053ec 100644 --- a/apps/essimporter/main.cpp +++ b/apps/essimporter/main.cpp @@ -50,7 +50,15 @@ int main(int argc, char** argv) if (vm.count("compare")) importer.compare(); else + { + const std::string& ext = ".omwsave"; + if (boost::filesystem::exists(boost::filesystem::path(outputFile)) + && (outputFile.size() < ext.size() || outputFile.substr(outputFile.size()-ext.size()) != ext)) + { + throw std::runtime_error("Output file already exists and does not end in .omwsave. Did you mean to use --compare?"); + } importer.run(); + } } catch (std::exception& e) { From 1d29180e003f00406b5dafef6364534ef84dac07 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 23:28:43 +0100 Subject: [PATCH 391/404] ESSImport: handle deleted cell references --- apps/essimporter/importcellref.cpp | 6 ++---- apps/essimporter/importer.cpp | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 1894b0023b..906b2ed242 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -41,11 +41,9 @@ namespace ESSImport mDeleted = 0; if (esm.isNextSub("DELE")) { - int deleted; + unsigned int deleted; esm.getHT(deleted); - // Neither of this seems to work right... - //mDeleted = (deleted != 0); - //mDeleted = (deleted&0x1); + mDeleted = (deleted >> 24) & 0x2; // the other 3 bytes seem to be uninitialized garbage } if (esm.isNextSub("MVRF")) diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index a29d26e2a8..9fd2903d89 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -105,6 +105,7 @@ namespace ESSImport blacklist.insert(std::make_pair("REFR", "DATA")); // player position blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war blacklist.insert(std::make_pair("GAME", "GMDT")); // weather data, current time always changes + blacklist.insert(std::make_pair("CELL", "DELE")); // first 3 bytes are uninitialized // this changes way too often, name suggests some renderer internal data? blacklist.insert(std::make_pair("CELL", "ND3D")); From c354f48a07080a08e713d4114ede4c13ca869996 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Jan 2015 23:42:43 +0100 Subject: [PATCH 392/404] ESSImport: some fixes --- apps/essimporter/importcrec.cpp | 9 ++------- apps/essimporter/importdial.cpp | 12 +++++++++++- apps/essimporter/importnpcc.cpp | 9 ++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/essimporter/importcrec.cpp b/apps/essimporter/importcrec.cpp index 28e5ac56f3..7a8a3eb00d 100644 --- a/apps/essimporter/importcrec.cpp +++ b/apps/essimporter/importcrec.cpp @@ -15,13 +15,8 @@ namespace ESSImport esm.getHNOT(scale, "XSCL"); // FIXME: use AiPackageList, need to fix getSubName() - if (esm.isNextSub("AI_W")) - esm.skipHSub(); - if (esm.isNextSub("AI_E")) - esm.skipHSub(); - if (esm.isNextSub("AI_T")) - esm.skipHSub(); - if (esm.isNextSub("AI_F")) + while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") + || esm.isNextSub("AI_A")) esm.skipHSub(); mInventory.load(esm); diff --git a/apps/essimporter/importdial.cpp b/apps/essimporter/importdial.cpp index 228d00a5ae..5797a708a1 100644 --- a/apps/essimporter/importdial.cpp +++ b/apps/essimporter/importdial.cpp @@ -7,7 +7,17 @@ namespace ESSImport void DIAL::load(ESM::ESMReader &esm) { - esm.getHNT(mIndex, "XIDX"); + // See ESM::Dialogue::Type enum, not sure why we would need this here though + int type = 0; + esm.getHNOT(type, "DATA"); + + // Deleted dialogue in a savefile. No clue what this means... + int deleted = 0; + esm.getHNOT(deleted, "DELE"); + + mIndex = 0; + // *should* always occur except when the dialogue is deleted, but leaving it optional just in case... + esm.getHNOT(mIndex, "XIDX"); } } diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp index 7b8dda3719..547b014413 100644 --- a/apps/essimporter/importnpcc.cpp +++ b/apps/essimporter/importnpcc.cpp @@ -10,13 +10,8 @@ namespace ESSImport esm.getHNT(mNPDT, "NPDT"); // FIXME: use AiPackageList, need to fix getSubName() - if (esm.isNextSub("AI_W")) - esm.skipHSub(); - if (esm.isNextSub("AI_E")) - esm.skipHSub(); - if (esm.isNextSub("AI_T")) - esm.skipHSub(); - if (esm.isNextSub("AI_F")) + while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") + || esm.isNextSub("AI_A")) esm.skipHSub(); mInventory.load(esm); From e484a91708683c5f6f0d26ea4c46ad73fce2bdd3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 00:15:57 +0100 Subject: [PATCH 393/404] ESSImport: convert global map markers --- apps/essimporter/converter.cpp | 10 ++++++++++ apps/essimporter/importer.cpp | 4 ++-- apps/essimporter/importercontext.hpp | 8 ++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 81521a0283..75c2911fd1 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -76,6 +76,12 @@ namespace ESSImport cell.mName = id; cell.load(esm, false); + // I wonder what 0x40 does? + if (cell.isExterior() && cell.mData.mFlags & 0x20) + { + mContext->mGlobalMapState.mMarkers.insert(std::make_pair(cell.mData.mX, cell.mData.mY)); + } + // note if the player is in a nameless exterior cell, we will assign the cellId later based on player position if (id == mContext->mPlayerCellName) { @@ -304,6 +310,10 @@ namespace ESSImport it->save(esm); esm.endRecord(ESM::REC_MARK); } + + esm.startRecord(ESM::REC_GMAP); + mContext->mGlobalMapState.save(esm); + esm.endRecord(ESM::REC_GMAP); } } diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 9fd2903d89..ed149b0232 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -123,7 +123,7 @@ namespace ESSImport if (i >= file2.mRecords.size()) { - std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset; + std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl; return; } @@ -142,7 +142,7 @@ namespace ESSImport if (j >= rec2.mSubrecords.size()) { - std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset; + std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl; return; } diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 02585799a6..211195d3f5 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "importnpcc.hpp" #include "importcrec.hpp" @@ -28,6 +29,8 @@ namespace ESSImport ESM::DialogueState mDialogueState; + ESM::GlobalMap mGlobalMapState; + int mDay, mMonth, mYear; float mHour; @@ -48,6 +51,11 @@ namespace ESSImport mPlayer.mCurrentCrimeId = 0; // TODO mPlayer.mObject.blank(); mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame + + mGlobalMapState.mBounds.mMinX = 0; + mGlobalMapState.mBounds.mMaxX = 0; + mGlobalMapState.mBounds.mMinY = 0; + mGlobalMapState.mBounds.mMaxY = 0; } }; From 0c1d76279049155fa9f6d3f1a44fa28585091e2f Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Fri, 23 Jan 2015 15:37:32 +0100 Subject: [PATCH 394/404] Fix build when using new boost with Ogre. This fixes a [Parse error at "BOOST_JOIN"] if you're using - for instance - Boost 1.57.0 --- apps/launcher/graphicspage.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index da4cb9fb33..a96d9b7329 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -3,8 +3,10 @@ #include +#ifndef Q_MOC_RUN #include #include +#endif #include From 7a903b7100d85838033177a5b15520f9afd146d2 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Fri, 23 Jan 2015 16:05:36 +0100 Subject: [PATCH 395/404] Doing forward declaration instead --- apps/launcher/graphicspage.hpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index a96d9b7329..213b6bccb4 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -3,16 +3,11 @@ #include -#ifndef Q_MOC_RUN -#include -#include -#endif - #include - #include "ui_graphicspage.h" +namespace Ogre { class Root; class RenderSystem; } namespace Files { struct ConfigurationManager; } From 942cf26eeed3b92cb7ee5c7dccee35fc6fe4c8e0 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Fri, 23 Jan 2015 16:06:19 +0100 Subject: [PATCH 396/404] And the missing includes --- apps/launcher/graphicspage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index da707b0056..1f85bb5e7b 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -12,6 +12,9 @@ #include +#include +#include + #include #include From 79d2eebe5435b9e1e92acbec2c191dc416b12a7b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 16:45:47 +0100 Subject: [PATCH 397/404] Store selected enchant item in savegame (Fixes #1702) --- apps/essimporter/convertinventory.cpp | 9 ++- apps/openmw/mwworld/containerstore.cpp | 87 ++++++++++++++------------ apps/openmw/mwworld/containerstore.hpp | 9 +-- apps/openmw/mwworld/inventorystore.cpp | 48 +++++++++----- apps/openmw/mwworld/inventorystore.hpp | 7 +-- components/esm/inventorystate.cpp | 76 +++++++++++++--------- components/esm/inventorystate.hpp | 9 ++- 7 files changed, 145 insertions(+), 100 deletions(-) diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 31272bf5aa..f476fe1ee2 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -7,6 +7,7 @@ namespace ESSImport void convertInventory(const Inventory &inventory, ESM::InventoryState &state) { + int index = 0; for (std::vector::const_iterator it = inventory.mItems.begin(); it != inventory.mItems.end(); ++it) { @@ -16,7 +17,13 @@ namespace ESSImport objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags - state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); + state.mItems.push_back(objstate); + if (it->mRelativeEquipmentSlot != -1) + // Note we should really write the absolute slot here, which we do not know about + // Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when + // an item could be equipped in two different slots (e.g. equipped two rings) + state.mEquipmentSlots[index] = it->mRelativeEquipmentSlot; + ++index; } } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 57621b7a47..b21486c2cc 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -79,6 +79,14 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) const { @@ -87,7 +95,7 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::Object template void MWWorld::ContainerStore::storeStates (CellRefList& collection, - std::vector >& states, bool equipable) const + ESM::InventoryState& inventory, int& index, bool equipable) const { for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) @@ -96,18 +104,13 @@ void MWWorld::ContainerStore::storeStates (CellRefList& collection, continue; ESM::ObjectState state; storeState (*iter, state); - int slot = equipable ? getSlot (*iter) : -1; - states.push_back (std::make_pair (state, slot)); + if (equipable) + storeEquipmentState(*iter, index, inventory); + inventory.mItems.push_back (state); + ++index; } } -int MWWorld::ContainerStore::getSlot (const MWWorld::LiveCellRefBase& ref) const -{ - return -1; -} - -void MWWorld::ContainerStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {} - const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; MWWorld::ContainerStore::ContainerStore() : mCachedWeight (0), mWeightUpToDate (false) {} @@ -645,49 +648,51 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) { state.mItems.clear(); - storeStates (potions, state.mItems); - storeStates (appas, state.mItems); - storeStates (armors, state.mItems, true); - storeStates (books, state.mItems); - storeStates (clothes, state.mItems, true); - storeStates (ingreds, state.mItems); - storeStates (lockpicks, state.mItems, true); - storeStates (miscItems, state.mItems); - storeStates (probes, state.mItems, true); - storeStates (repairs, state.mItems); - storeStates (weapons, state.mItems, true); - storeStates (lights, state.mItems, true); + int index = 0; + storeStates (potions, state, index); + storeStates (appas, state, index); + storeStates (armors, state, index, true); + storeStates (books, state, index, true); // not equipable as such, but for selectedEnchantItem + storeStates (clothes, state, index, true); + storeStates (ingreds, state, index); + storeStates (lockpicks, state, index, true); + storeStates (miscItems, state, index); + storeStates (probes, state, index, true); + storeStates (repairs, state, index); + storeStates (weapons, state, index, true); + storeStates (lights, state, index, true); state.mLevelledItemMap = mLevelledItemMap; } -void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) +void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory) { clear(); - for (std::vector >::const_iterator - iter (state.mItems.begin()); iter!=state.mItems.end(); ++iter) + int index = 0; + for (std::vector::const_iterator + iter (inventory.mItems.begin()); iter!=inventory.mItems.end(); ++iter) { - int slot = iter->second; - - const ESM::ObjectState& state = iter->first; + const ESM::ObjectState& state = *iter; int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID); + int thisIndex = index++; + switch (type) { - case ESM::REC_ALCH: getState (potions, iter->first); break; - case ESM::REC_APPA: getState (appas, iter->first); break; - case ESM::REC_ARMO: setSlot (getState (armors, iter->first), slot); break; - case ESM::REC_BOOK: getState (books, iter->first); break; - case ESM::REC_CLOT: setSlot (getState (clothes, iter->first), slot); break; - case ESM::REC_INGR: getState (ingreds, iter->first); break; - case ESM::REC_LOCK: setSlot (getState (lockpicks, iter->first), slot); break; - case ESM::REC_MISC: getState (miscItems, iter->first); break; - case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break; - case ESM::REC_REPA: getState (repairs, iter->first); break; - case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; - case ESM::REC_LIGH: setSlot (getState (lights, iter->first), slot); break; + case ESM::REC_ALCH: getState (potions, state); break; + case ESM::REC_APPA: getState (appas, state); break; + case ESM::REC_ARMO: readEquipmentState (getState (armors, state), thisIndex, inventory); break; + case ESM::REC_BOOK: readEquipmentState (getState (books, state), thisIndex, inventory); break; // not equipable as such, but for selectedEnchantItem + case ESM::REC_CLOT: readEquipmentState (getState (clothes, state), thisIndex, inventory); break; + case ESM::REC_INGR: getState (ingreds, state); break; + case ESM::REC_LOCK: readEquipmentState (getState (lockpicks, state), thisIndex, inventory); break; + case ESM::REC_MISC: getState (miscItems, state); break; + case ESM::REC_PROB: readEquipmentState (getState (probes, state), thisIndex, inventory); break; + case ESM::REC_REPA: getState (repairs, state); break; + case ESM::REC_WEAP: readEquipmentState (getState (weapons, state), thisIndex, inventory); break; + case ESM::REC_LIGH: readEquipmentState (getState (lights, state), thisIndex, inventory); break; case 0: std::cerr << "Dropping reference to '" << state.mRef.mRefID << "' (object no longer exists)" << std::endl; break; @@ -698,7 +703,7 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) } - mLevelledItemMap = state.mLevelledItemMap; + mLevelledItemMap = inventory.mLevelledItemMap; } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index f7c8a369c8..d909a98ccf 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -86,15 +86,12 @@ namespace MWWorld template void storeStates (CellRefList& collection, - std::vector >& states, + ESM::InventoryState& inventory, int& index, bool equipable = false) const; - virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; - ///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot). - - virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot); - ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. + virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; + virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory); public: ContainerStore(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index be93824e5a..4950be9126 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -49,33 +49,47 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_) slots_.push_back (end()); } -int MWWorld::InventoryStore::getSlot (const MWWorld::LiveCellRefBase& ref) const +void MWWorld::InventoryStore::storeEquipmentState(const MWWorld::LiveCellRefBase &ref, int index, ESM::InventoryState &inventory) const { for (int i = 0; i (mSlots.size()); ++i) if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref) - return i; + { + inventory.mEquipmentSlots[index] = i; + } - return -1; + if (mSelectedEnchantItem.getType()!=-1 && mSelectedEnchantItem->getBase() == &ref) + inventory.mSelectedEnchantItem = index; } -void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot) +void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIterator &iter, int index, const ESM::InventoryState &inventory) { - if (relativeSlot < 0 || iter == end()) - return; - - // make sure the item can actually be equipped in this slot - std::pair, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter); - relativeSlot = std::min(int(allowedSlots.first.size()-1), relativeSlot); + if (index == inventory.mSelectedEnchantItem) + mSelectedEnchantItem = iter; - // unstack if required - if (!allowedSlots.second && iter->getRefData().getCount() > 1) + std::map::const_iterator found = inventory.mEquipmentSlots.find(index); + if (found != inventory.mEquipmentSlots.end()) { - MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1); - iter->getRefData().setCount(iter->getRefData().getCount()-1); - mSlots[allowedSlots.first[relativeSlot]] = newIter; + if (found->second < 0 || found->second >= MWWorld::InventoryStore::Slots) + throw std::runtime_error("Invalid slot index in inventory state"); + + // make sure the item can actually be equipped in this slot + int slot = found->second; + std::pair, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter); + if (!allowedSlots.first.size()) + return; + if (std::find(allowedSlots.first.begin(), allowedSlots.first.end(), slot) == allowedSlots.first.end()) + slot = allowedSlots.first.front(); + + // unstack if required + if (!allowedSlots.second && iter->getRefData().getCount() > 1) + { + MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1); + iter->getRefData().setCount(iter->getRefData().getCount()-1); + mSlots[slot] = newIter; + } + else + mSlots[slot] = iter; } - else - mSlots[allowedSlots.first[relativeSlot]] = iter; } MWWorld::InventoryStore::InventoryStore() diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 7bd977e39b..9a154373a1 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,11 +113,8 @@ namespace MWWorld void fireEquipmentChangedEvent(); - virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; - ///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot). - - virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot); - ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. + virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; + virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory); public: diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index b946f9501c..4eaaa9f9f1 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -4,42 +4,33 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -namespace -{ - void read (ESM::ESMReader &esm, ESM::ObjectState& state, int& slot) - { - slot = -1; - esm.getHNOT (slot, "SLOT"); - - state.mRef.loadId(esm, true); - state.load (esm); - } - - void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, int slot) - { - int unused = 0; - esm.writeHNT ("IOBJ", unused); - - if (slot!=-1) - esm.writeHNT ("SLOT", slot); - - state.save (esm, true); - } -} - void ESM::InventoryState::load (ESMReader &esm) { + int index = 0; while (esm.isNextSub ("IOBJ")) { int unused; // no longer used esm.getHT(unused); ObjectState state; - int slot; - read (esm, state, slot); + + // obsolete + if (esm.isNextSub("SLOT")) + { + int slot; + esm.getHT(slot); + mEquipmentSlots[index] = slot; + } + + state.mRef.loadId(esm, true); + state.load (esm); + if (state.mCount == 0) continue; - mItems.push_back (std::make_pair (state, slot)); + + mItems.push_back (state); + + ++index; } while (esm.isNextSub("LEVM")) @@ -64,12 +55,30 @@ void ESM::InventoryState::load (ESMReader &esm) } mPermanentMagicEffectMagnitudes[id] = params; } + + while (esm.isNextSub("EQUI")) + { + esm.getSubHeader(); + int index; + esm.getT(index); + int slot; + esm.getT(slot); + mEquipmentSlots[index] = slot; + } + + mSelectedEnchantItem = -1; + esm.getHNOT(mSelectedEnchantItem, "SELE"); } void ESM::InventoryState::save (ESMWriter &esm) const { - for (std::vector >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) - write (esm, iter->first, iter->second); + for (std::vector::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) + { + int unused = 0; + esm.writeHNT ("IOBJ", unused); + + iter->save (esm, true); + } for (std::map::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) { @@ -88,4 +97,15 @@ void ESM::InventoryState::save (ESMWriter &esm) const esm.writeHNT ("MULT", pIt->second); } } + + for (std::map::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it) + { + esm.startSubRecord("EQUI"); + esm.writeT(it->first); + esm.writeT(it->second); + esm.endRecord("EQUI"); + } + + if (mSelectedEnchantItem != -1) + esm.writeHNT ("SELE", mSelectedEnchantItem); } diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index 13af28e30e..d5c317beb9 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -15,14 +15,19 @@ namespace ESM /// \brief State for inventories and containers struct InventoryState { - /// - std::vector > mItems; + std::vector mItems; + + // + std::map mEquipmentSlots; std::map mLevelledItemMap; typedef std::map > > TEffectMagnitudes; TEffectMagnitudes mPermanentMagicEffectMagnitudes; + int mSelectedEnchantItem; // For inventories only + + InventoryState() : mSelectedEnchantItem(-1) {} virtual ~InventoryState() {} virtual void load (ESMReader &esm); From b0c2aec374e7faddbcc9927c537457f63de6a6be Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 17:29:03 +0100 Subject: [PATCH 398/404] Add warning for missing body parts --- apps/openmw/mwrender/npcanimation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index a8ba1abf5d..9b05ce0a23 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -811,6 +811,8 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormData.mPart == ESM::BodyPart::MP_Upperarm)) bodypart = NULL; } + else if (!bodypart) + std::cerr << "Failed to find body part '" << part->mFemale << "'" << std::endl; } if(!bodypart && !part->mMale.empty()) { @@ -824,6 +826,8 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormData.mPart == ESM::BodyPart::MP_Upperarm)) bodypart = NULL; } + else if (!bodypart) + std::cerr << "Failed to find body part '" << part->mMale << "'" << std::endl; } if(bodypart) From bf90b86f73d47a95442865b9254b46d8e350c0ae Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 20:09:30 +0100 Subject: [PATCH 399/404] ESSImport: add some missing subrecords --- apps/essimporter/importacdt.cpp | 4 ++++ apps/essimporter/importplayer.cpp | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index e590e2826d..00bd4ad806 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -60,6 +60,10 @@ namespace ESSImport if (esm.isNextSub("PWPS")) esm.skipHSub(); + // unsure at which point between LSTN and CHRD + if (esm.isNextSub("APUD")) + esm.skipHSub(); // 40 bytes, starts with string "ancestor guardian"... + if (esm.isNextSub("WNAM")) { esm.skipHSub(); // seen values: "ancestor guardian", "bound dagger_en". Summoned creature / bound weapons? diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index ea9f1932c5..9845ab072a 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -63,8 +63,11 @@ namespace ESSImport mFactions.push_back(fnam); } + if (esm.isNextSub("AADT")) + esm.skipHSub(); // 44 bytes, no clue + if (esm.isNextSub("KNAM")) - esm.skipHSub(); + esm.skipHSub(); // assigned Quick Keys, I think if (esm.isNextSub("WERE")) { @@ -73,6 +76,10 @@ namespace ESSImport esm.getSubHeader(); esm.skip(152); } + + // unsure if before or after WERE + if (esm.isNextSub("ANIS")) + esm.skipHSub(); } } From 8e5c8aa562ba4d03c112bf78ee3a840d0c05c12d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Jan 2015 23:57:00 +0100 Subject: [PATCH 400/404] Add support for passing in Ogre plugin directory via CMake --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ceee29e65..c3def1381c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,8 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binarie option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE) option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE) +set(CUSTOM_OGRE_PLUGIN_DIR "" CACHE PATH "Specify a custom directory for Ogre plugins (autodetected by default)") + option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE) # Apps and tools @@ -323,8 +325,10 @@ if (APPLE AND OPENMW_OSX_DEPLOYMENT) # make it empty so plugin loading code can check this and try to find plugins inside app bundle add_definitions(-DOGRE_PLUGIN_DIR="") else() - if (NOT DEFINED ${OGRE_PLUGIN_DIR}) + if (CUSTOM_OGRE_PLUGIN_DIR STREQUAL "") set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) + else() + set(OGRE_PLUGIN_DIR ${CUSTOM_OGRE_PLUGIN_DIR}) endif() add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") From 09f11fbff224b0c5c364629d56bd2929d0956875 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Jan 2015 00:18:17 +0100 Subject: [PATCH 401/404] ESSImport: convert selected spell / selected enchant item --- apps/essimporter/converter.hpp | 21 +++++++++++++++++++++ apps/essimporter/importacdt.cpp | 11 +++++------ apps/essimporter/importacdt.hpp | 3 +++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 7565b78d42..6f85b01cff 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -226,7 +226,28 @@ public: ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats; convertNpcData(refr.mActorData, npcStats); + + mSelectedSpell = refr.mActorData.mSelectedSpell; + if (!refr.mActorData.mSelectedEnchantItem.empty()) + { + ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory; + + for (unsigned int i=0; i Date: Sat, 24 Jan 2015 14:32:02 +0100 Subject: [PATCH 402/404] Fix loading faction reactions for older savegames (Fixes #2301) --- components/esm/dialoguestate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/dialoguestate.cpp b/components/esm/dialoguestate.cpp index f546462a3e..f302e36dc8 100644 --- a/components/esm/dialoguestate.cpp +++ b/components/esm/dialoguestate.cpp @@ -25,7 +25,7 @@ void ESM::DialogueState::load (ESMReader &esm) while (esm.isNextSub ("REAC")) { esm.skipHSub(); - esm.getSubHeader(); + esm.getSubName(); esm.skipHSub(); } } From b1bd236345fbe4fbd84f9855c958c02d323a9863 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Jan 2015 16:15:52 +0100 Subject: [PATCH 403/404] ESSImport: convert script local variables Had to add special reading code to openmw, because the variable names are not stored. --- apps/essimporter/CMakeLists.txt | 1 + apps/essimporter/converter.cpp | 3 ++ apps/essimporter/convertscri.cpp | 32 ++++++++++++++++ apps/essimporter/convertscri.hpp | 16 ++++++++ apps/essimporter/importer.cpp | 6 +++ apps/openmw/mwscript/locals.cpp | 63 +++++++++++++++++++++++++------- components/esm/locals.cpp | 6 +-- components/esm/variant.cpp | 26 ++++++++++++- components/esm/variant.hpp | 3 +- components/esm/variantimp.cpp | 33 +++++++++++++++-- 10 files changed, 167 insertions(+), 22 deletions(-) create mode 100644 apps/essimporter/convertscri.cpp create mode 100644 apps/essimporter/convertscri.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 5db3a86dc4..409b8bcfff 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -23,6 +23,7 @@ set(ESSIMPORTER_FILES convertinventory.cpp convertcrec.cpp convertcntc.cpp + convertscri.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 75c2911fd1..3b82988b8f 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -9,6 +9,7 @@ #include "convertcrec.hpp" #include "convertcntc.hpp" +#include "convertscri.hpp" namespace { @@ -29,6 +30,8 @@ namespace objstate.mRef.mRefNum = cellref.mRefNum; if (cellref.mDeleted) objstate.mCount = 0; + convertSCRI(cellref.mSCRI, objstate.mLocals); + objstate.mHasLocals = !objstate.mLocals.mVariables.empty(); } bool isIndexedRefId(const std::string& indexedRefId) diff --git a/apps/essimporter/convertscri.cpp b/apps/essimporter/convertscri.cpp new file mode 100644 index 0000000000..dbe35a0108 --- /dev/null +++ b/apps/essimporter/convertscri.cpp @@ -0,0 +1,32 @@ +#include "convertscri.hpp" + +#include + +namespace +{ + + template + void storeVariables(const std::vector& variables, ESM::Locals& locals, const std::string& scriptname) + { + for (typename std::vector::const_iterator it = variables.begin(); it != variables.end(); ++it) + { + ESM::Variant val(*it); + val.setType(VariantType); + locals.mVariables.push_back(std::make_pair(std::string(), val)); + } + } + +} + +namespace ESSImport +{ + + void convertSCRI(const SCRI &scri, ESM::Locals &locals) + { + // order *is* important, as we do not have variable names available in this format + storeVariables (scri.mShorts, locals, scri.mScript); + storeVariables (scri.mLongs, locals, scri.mScript); + storeVariables (scri.mFloats, locals, scri.mScript); + } + +} diff --git a/apps/essimporter/convertscri.hpp b/apps/essimporter/convertscri.hpp new file mode 100644 index 0000000000..2d89456662 --- /dev/null +++ b/apps/essimporter/convertscri.hpp @@ -0,0 +1,16 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTSCRI_H +#define OPENMW_ESSIMPORT_CONVERTSCRI_H + +#include "importscri.hpp" + +#include + +namespace ESSImport +{ + + /// Convert script variable assignments + void convertSCRI (const SCRI& scri, ESM::Locals& locals); + +} + +#endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index ed149b0232..73a12769ab 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -237,6 +237,12 @@ namespace ESSImport converters[recJOUR ] = boost::shared_ptr(new ConvertJOUR()); converters[ESM::REC_SCPT] = boost::shared_ptr(new ConvertSCPT()); + // TODO: + // - REGN (weather in certain regions?) + // - VFXM + // - SPLM (active spell effects) + // - PROJ (magic projectiles in air) + std::set unknownRecords; for (std::map >::const_iterator it = converters.begin(); diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index a1ee48ae68..fe3e498271 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -10,6 +10,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/scriptmanager.hpp" +#include + namespace MWScript { void Locals::configure (const ESM::Script& script) @@ -124,27 +126,60 @@ namespace MWScript const Compiler::Locals& declarations = MWBase::Environment::get().getScriptManager()->getLocals(script); - for (std::vector >::const_iterator iter - = locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter) + int index = 0, numshorts = 0, numlongs = 0; + for (unsigned int v=0; vfirst); - char index = declarations.getIndex (iter->first); + ESM::VarType type = locals.mVariables[v].second.getType(); + if (type == ESM::VT_Short) + ++numshorts; + else if (type == ESM::VT_Int) + ++numlongs; + } - try + for (std::vector >::const_iterator iter + = locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter,++index) + { + if (iter->first.empty()) { - switch (type) + // no variable names available (this will happen for legacy, i.e. ESS-imported savegames only) + try { - case 's': mShorts.at (index) = iter->second.getInteger(); break; - case 'l': mLongs.at (index) = iter->second.getInteger(); break; - case 'f': mFloats.at (index) = iter->second.getFloat(); break; - - // silently ignore locals that don't exist anymore + if (index >= numshorts+numlongs) + mFloats.at(index - (numshorts+numlongs)) = iter->second.getFloat(); + else if (index >= numshorts) + mLongs.at(index - numshorts) = iter->second.getInteger(); + else + mShorts.at(index) = iter->second.getInteger(); + } + catch (std::exception& e) + { + std::cerr << "Failed to read local variable state for script '" + << script << "' (legacy format): " << e.what() + << "\nNum shorts: " << numshorts << " / " << mShorts.size() + << " Num longs: " << numlongs << " / " << mLongs.size() << std::endl; } } - catch (...) + else { - // ignore type changes - /// \todo write to log + char type = declarations.getType (iter->first); + char index = declarations.getIndex (iter->first); + + try + { + switch (type) + { + case 's': mShorts.at (index) = iter->second.getInteger(); break; + case 'l': mLongs.at (index) = iter->second.getInteger(); break; + case 'f': mFloats.at (index) = iter->second.getFloat(); break; + + // silently ignore locals that don't exist anymore + } + } + catch (...) + { + // ignore type changes + /// \todo write to log + } } } } diff --git a/components/esm/locals.cpp b/components/esm/locals.cpp index 9c470a0253..f0cfd49f03 100644 --- a/components/esm/locals.cpp +++ b/components/esm/locals.cpp @@ -11,7 +11,7 @@ void ESM::Locals::load (ESMReader &esm) std::string id = esm.getHString(); Variant value; - value.read (esm, Variant::Format_Info); + value.read (esm, Variant::Format_Local); mVariables.push_back (std::make_pair (id, value)); } @@ -23,6 +23,6 @@ void ESM::Locals::save (ESMWriter &esm) const iter!=mVariables.end(); ++iter) { esm.writeHNString ("LOCA", iter->first); - iter->second.write (esm, Variant::Format_Info); + iter->second.write (esm, Variant::Format_Local); } -} \ No newline at end of file +} diff --git a/components/esm/variant.cpp b/components/esm/variant.cpp index 4127a9d627..c65eed5e09 100644 --- a/components/esm/variant.cpp +++ b/components/esm/variant.cpp @@ -13,6 +13,7 @@ namespace const uint32_t STRV = ESM::FourCC<'S','T','R','V'>::value; const uint32_t INTV = ESM::FourCC<'I','N','T','V'>::value; const uint32_t FLTV = ESM::FourCC<'F','L','T','V'>::value; + const uint32_t STTV = ESM::FourCC<'S','T','T','V'>::value; } ESM::Variant::Variant() : mType (VT_None), mData (0) {} @@ -141,7 +142,7 @@ void ESM::Variant::read (ESMReader& esm, Format format) esm.fail ("invalid subrecord: " + name.toString()); } } - else // info + else if (format == Format_Info) { esm.getSubName(); NAME name = esm.retSubName(); @@ -157,6 +158,26 @@ void ESM::Variant::read (ESMReader& esm, Format format) else esm.fail ("invalid subrecord: " + name.toString()); } + else if (format == Format_Local) + { + esm.getSubName(); + NAME name = esm.retSubName(); + + if (name==INTV) + { + type = VT_Int; + } + else if (name==FLTV) + { + type = VT_Float; + } + else if (name==STTV) + { + type = VT_Short; + } + else + esm.fail ("invalid subrecord: " + name.toString()); + } setType (type); @@ -179,6 +200,9 @@ void ESM::Variant::write (ESMWriter& esm, Format format) const if (format==Format_Info) throw std::runtime_error ("can not serialise variant of type none to info format"); + if (format==Format_Local) + throw std::runtime_error ("can not serialise variant of type none to local format"); + // nothing to do here for GMST format } else diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp index d6c1a5489f..5f179a7bdc 100644 --- a/components/esm/variant.hpp +++ b/components/esm/variant.hpp @@ -33,7 +33,8 @@ namespace ESM { Format_Global, Format_Gmst, - Format_Info // also used for local variables in saved game files + Format_Info, + Format_Local // local script variables in save game files }; Variant(); diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp index 1bacdc0770..fd068fc196 100644 --- a/components/esm/variantimp.cpp +++ b/components/esm/variantimp.cpp @@ -81,6 +81,9 @@ void ESM::VariantStringData::read (ESMReader& esm, Variant::Format format, VarTy if (format==Variant::Format_Info) esm.fail ("info variables of type string not supported"); + if (format==Variant::Format_Local) + esm.fail ("local variables of type string not supported"); + // GMST mValue = esm.getHString(); } @@ -173,6 +176,21 @@ void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarT esm.getHT (mValue); } + else if (format==Variant::Format_Local) + { + if (type==VT_Short) + { + short value; + esm.getHT(value); + mValue = value; + } + else if (type==VT_Int) + { + esm.getHT(mValue); + } + else + esm.fail("unsupported local variable integer type"); + } } void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, VarType type) const @@ -204,6 +222,15 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var esm.writeHNT ("INTV", mValue); } + else if (format==Variant::Format_Local) + { + if (type==VT_Short) + esm.writeHNT ("STTV", (short)mValue); + else if (type == VT_Int) + esm.writeHNT ("INTV", mValue); + else + throw std::runtime_error("unsupported local variable integer type"); + } } bool ESM::VariantIntegerData::isEqual (const VariantDataBase& value) const @@ -252,7 +279,7 @@ void ESM::VariantFloatData::read (ESMReader& esm, Variant::Format format, VarTyp { esm.getHNT (mValue, "FLTV"); } - else if (format==Variant::Format_Gmst || format==Variant::Format_Info) + else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local) { esm.getHT (mValue); } @@ -268,7 +295,7 @@ void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarTy esm.writeHNString ("FNAM", "f"); esm.writeHNT ("FLTV", mValue); } - else if (format==Variant::Format_Gmst || format==Variant::Format_Info) + else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local) { esm.writeHNT ("FLTV", mValue); } @@ -277,4 +304,4 @@ void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarTy bool ESM::VariantFloatData::isEqual (const VariantDataBase& value) const { return dynamic_cast (value).mValue==mValue; -} \ No newline at end of file +} From 72f7c2e555222120b870765758ab41e0ff29245c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Jan 2015 16:26:43 +0100 Subject: [PATCH 404/404] Move weather reset to clear() method --- apps/openmw/mwworld/weather.cpp | 18 ++++++++++-------- apps/openmw/mwworld/weather.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 1 + 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 6532c59692..a74c247681 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -770,14 +770,6 @@ bool WeatherManager::readRecord(ESM::ESMReader& reader, uint32_t type) ESM::WeatherState state; state.load(reader); - // reset other temporary state, now that we loaded successfully - stopSounds(); // let's hope this never throws - mRegionOverrides.clear(); - mRegionMods.clear(); - mThunderFlash = 0.0; - mThunderChance = 0.0; - mThunderChanceNeeded = 50.0; - // swap in the loaded values now that we can't fail mHour = state.mHour; mWindSpeed = state.mWindSpeed; @@ -794,6 +786,16 @@ bool WeatherManager::readRecord(ESM::ESMReader& reader, uint32_t type) return false; } +void WeatherManager::clear() +{ + stopSounds(); + mRegionOverrides.clear(); + mRegionMods.clear(); + mThunderFlash = 0.0; + mThunderChance = 0.0; + mThunderChanceNeeded = 50.0; +} + void WeatherManager::switchToNextWeather(bool instantly) { MWBase::World* world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 830b5c376c..a2e6681595 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -201,6 +201,8 @@ namespace MWWorld bool readRecord(ESM::ESMReader& reader, uint32_t type); + void clear(); + private: float mHour; float mWindSpeed; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ceac85bcab..8bcfcb4211 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -265,6 +265,7 @@ namespace MWWorld void World::clear() { + mWeatherManager->clear(); mRendering->clear(); mProjectileManager->clear();