diff --git a/AUTHORS.md b/AUTHORS.md index 3422f28a9..074081876 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -148,11 +148,13 @@ Programmers Scott Howard scrawl Sebastian Wick (swick) + Sergey Fukanchik Sergey Shambir ShadowRadiance Siimacore sir_herrbatka smbas + Sophie Kirschner (pineapplemachine) spycrab Stefan Galowicz (bogglez) Stanislav Bobrov (Jiub) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d8df78e..3ad7e9aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ 0.45.0 ------ + Bug #1875: Actors in inactive cells don't heal from resting Bug #1990: Sunrise/sunset not set correct Bug #2131: Lustidrike's spell misses the player every time Bug #2222: Fatigue's effect on selling price is backwards + Bug #2256: Landing sound not playing when jumping immediately after landing + Bug #2274: Thin platform clips through player character instead of lifting Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped Bug #2455: Creatures attacks degrade armor Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash @@ -15,17 +18,24 @@ Bug #2872: Tab completion in console doesn't work with explicit reference Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue + Bug #3059: Unable to hit with marksman weapons when too close to an enemy Bug #3072: Fatal error on AddItem that has a script containing Equip + Bug #3219: NPC and creature initial position tracing down limit is too small Bug #3249: Fixed revert function not updating views properly + Bug #3288: TrueType fonts are handled incorrectly Bug #3374: Touch spells not hitting kwama foragers Bug #3486: [Mod] NPC Commands does not work Bug #3533: GetSpellEffects should detect effects with zero duration Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning + Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers + Bug #3762: AddSoulGem and RemoveSpell redundant count arguments break script execution Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla + Bug #3836: Script fails to compile when command argument contains "\n" Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters + Bug #3920: RemoveSpellEffects doesn't remove constant effects Bug #3948: AiCombat moving target aiming uses incorrect speed for magic projectiles Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes Bug #3993: Terrain texture blending map is not upscaled @@ -39,6 +49,7 @@ Bug #4230: AiTravel package issues break some Tribunal quests Bug #4231: Infected rats from the "Crimson Plague" quest rendered unconscious by change in Drain Fatigue functionality Bug #4251: Stationary NPCs do not return to their position after combat + Bug #4260: Keyboard navigation makes persuasion exploitable Bug #4271: Scamp flickers when attacking Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4286: Scripted animations can be interrupted @@ -48,6 +59,7 @@ Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching + Bug #4333: Keyboard navigation in containers is not intuitive Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4368: Settings window ok button doesn't have key focus by default Bug #4378: On-self absorb spells restore stats @@ -69,6 +81,7 @@ Bug #4460: Script function "Equip" doesn't bypass beast restrictions Bug #4461: "Open" spell from non-player caster isn't a crime Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages + Bug #4467: Content selector: cyrillic characters are decoded incorrectly in plugin descriptions Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour Bug #4474: No fallback when getVampireHead fails @@ -84,13 +97,17 @@ Bug #4503: Cast and ExplodeSpell commands increase alteration skill Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode + Bug #4527: Sun renders on water shader in some situations where it shouldn't Bug #4531: Movement does not reset idle animations + Bug #4532: Underwater sfx isn't tied to 3rd person camera Bug #4539: Paper Doll is affected by GUI scaling + Bug #4543: Picking cursed items through inventory (menumode) makes it disappear Bug #4545: Creatures flee from werewolves Bug #4551: Replace 0 sound range with default range separately Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #4557: Topics with reserved names are handled differently from vanilla Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive + Bug #4560: OpenMW does not update pinned windows properly Bug #4563: Fast travel price logic checks destination cell instead of service actor cell Bug #4565: Underwater view distance should be limited Bug #4573: Player uses headtracking in the 1st-person mode @@ -98,8 +115,36 @@ Bug #4575: Weird result of attack animation blending with movement animations Bug #4576: Reset of idle animations when attack can not be started Bug #4591: Attack strength should be 0 if player did not hold the attack button + Bug #4593: Editor: Instance dragging is broken + Bug #4597: <> operator causes a compile error + Bug #4604: Picking up gold from the ground only makes 1 grabbed + Bug #4607: Scaling for animated collision shapes is applied twice + Bug #4608: Falling damage is applied twice + Bug #4611: Instant magic effects have 0 duration in custom spell cost calculations unlike vanilla + Bug #4614: Crash due to division by zero when FlipController has no textures + Bug #4615: Flicker effects for light sources are handled incorrectly + Bug #4617: First person sneaking offset is not applied while the character is in air + Bug #4618: Sneaking is possible while the character is flying + Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails + Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type + Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch + Bug #4641: GetPCJumping is handled incorrectly + Bug #4644: %Name should be available for all actors, not just for NPCs + Bug #4648: Hud thinks that throwing weapons have condition + Bug #4649: Levelup fully restores health + Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader + Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects + Bug #4668: Editor: Light source color is displayed as an integer + Bug #4669: ToggleCollision should trace the player down after collision being enabled + Bug #4671: knownEffect functions should use modified Alchemy skill + Bug #4672: Pitch factor is handled incorrectly for crossbow animations + Bug #4674: Journal can be opened when settings window is open + Feature #912: Editor: Add missing icons to UniversalId tables + Feature #1221: Editor: Creature/NPC rendering + Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search + Feature #2847: Content selector: allow to copy the path to a file by using the context menu Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results @@ -121,8 +166,21 @@ Feature #4550: Weapon priority: make ranged weapon bonus more sensible Feature #4579: Add option for applying Strength into hand to hand damage Feature #4581: Use proper logging system + Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment + Feature #4625: Weapon priority: use weighted mean for melee damage rating + Feature #4626: Weapon priority: account for weapon speed + Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating + Feature #4636: Use sTo GMST in spellmaking menu + Feature #4642: Batching potion creation Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test + Task #4605: Optimize skinning + Task #4606: Support Rapture3D's OpenAL driver + Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 + Task #4621: Optimize combat AI + Task #4643: Revise editor record verifying functionality + Task #4645: Use constants instead of widely used magic numbers + Task #4652: Move call to enemiesNearby() from InputManager::rest() to World::canRest() 0.44.0 ------ diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 30cbaec15..bb577503c 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -346,7 +346,7 @@ if [ -z $SKIP_DOWNLOAD ]; then # OpenAL download "OpenAL-Soft 1.17.2" \ - "http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \ + "http://openal-soft.org/openal-binaries/openal-soft-1.17.2-bin.zip" \ "OpenAL-Soft-1.17.2.zip" # OSG diff --git a/CI/build_googletest.sh b/CI/build_googletest.sh index cd61379b5..ee89ebda6 100755 --- a/CI/build_googletest.sh +++ b/CI/build_googletest.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -git clone https://github.com/google/googletest.git +git clone -b release-1.8.1 https://github.com/google/googletest.git cd googletest mkdir build cd build diff --git a/CMakeLists.txt b/CMakeLists.txt index f786a5439..3efcaeb3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,7 +624,7 @@ if (WIN32) # Warnings that aren't enabled normally and don't need to be enabled # They're unneeded and sometimes completely retarded warnings that /Wall enables # Not going to bother commenting them as they tend to warn on every standard library file - 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 + 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045 # Warnings that are thrown on standard libraries and not OpenMW @@ -643,6 +643,7 @@ if (WIN32) # caused by boost 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) + 4643 # Forward declaring 'X' in namespace std is not permitted by the C++ Standard. (in *_std_fwd.h files) # caused by MyGUI 4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception' diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 2473daf95..900fbb05c 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "convertcrec.hpp" #include "convertcntc.hpp" #include "convertscri.hpp" @@ -288,12 +290,12 @@ namespace ESSImport 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; + notepos[0] = Constants::CellSizeInUnits * notepos[0] / 32.f; + notepos[1] = Constants::CellSizeInUnits * notepos[1] / 32.f; if (cell.isExterior()) { - notepos[0] += 8192 * cell.mData.mX; - notepos[1] += 8192 * cell.mData.mY; + notepos[0] += Constants::CellSizeInUnits * cell.mData.mX; + notepos[1] += Constants::CellSizeInUnits * cell.mData.mY; } // TODO: what encoding is this in? std::string note = esm.getHNString("MPNT"); diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 1772e0e2d..2694ea570 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -526,7 +526,10 @@ public: class ConvertGAME : public Converter { public: - ConvertGAME() : mHasGame(false) {} + ConvertGAME() + : mHasGame(false) + { + } virtual void read(ESM::ESMReader &esm) { diff --git a/apps/essimporter/convertplayer.cpp b/apps/essimporter/convertplayer.cpp index 4a4a9a573..5e2da2b03 100644 --- a/apps/essimporter/convertplayer.cpp +++ b/apps/essimporter/convertplayer.cpp @@ -1,5 +1,6 @@ #include "convertplayer.hpp" +#include #include namespace ESSImport @@ -78,9 +79,8 @@ namespace ESSImport if (pcdt.mHasENAM) { - const int cellSize = 8192; - out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize; - out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize; + out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * Constants::CellSizeInUnits; + out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * Constants::CellSizeInUnits; out.mLastKnownExteriorPosition[2] = 0.0f; } } diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 4538d4e63..a54c13334 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include #include "importercontext.hpp" @@ -413,9 +415,8 @@ namespace ESSImport if (context.mPlayer.mCellId.mPaged) { // exterior cell -> determine cell coordinates based on position - const int cellSize = 8192; - int cellX = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize)); - int cellY = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize)); + int cellX = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[0] / Constants::CellSizeInUnits)); + int cellY = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[1] / Constants::CellSizeInUnits)); context.mPlayer.mCellId.mIndex.mX = cellX; context.mPlayer.mCellId.mIndex.mY = cellY; } diff --git a/apps/essimporter/importgame.hpp b/apps/essimporter/importgame.hpp index fca7d72a0..d8051a527 100644 --- a/apps/essimporter/importgame.hpp +++ b/apps/essimporter/importgame.hpp @@ -14,13 +14,13 @@ namespace ESSImport { 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 mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage + char mCellName[64] {}; + int mFogColour {0}; + float mFogDensity {0.f}; + int mCurrentWeather {0}, mNextWeather {0}; + int mWeatherTransition {0}; // 0-100 transition between weathers, top 3 bytes may be garbage + float mTimeOfNextTransition {0.f}; // weather changes when gamehour == timeOfNextTransition + int mMasserPhase {0}, mSecundaPhase {0}; // top 3 bytes may be garbage }; GMDT mGMDT; diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 102463085..41f546af4 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -72,6 +72,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); + loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); @@ -131,6 +132,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 7b703a924..6f2389de3 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -36,6 +36,8 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config: ui.setupUi (this); setObjectName ("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); + const QString encoding = mGameSettings.value("encoding", "win1252"); + mSelector->setEncoding(encoding); mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); @@ -357,4 +359,4 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles) QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles)); std::sort(cellNamesList.begin(), cellNamesList.end()); emit signalLoadedCellsChanged(cellNamesList); -} \ No newline at end of file +} diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d1ebcde42..b0bd95eb9 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -19,6 +19,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel + actoradapter ) @@ -42,7 +43,7 @@ opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck - mergestages gmstcheck topicinfocheck journalcheck + mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck ) opencs_hdrs_noqt (model/tools @@ -88,7 +89,7 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode + cellwater terraintexturemode actor ) opencs_units_noqt (view/render diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index e733d9924..450b434e6 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -22,7 +22,7 @@ CS::Editor::Editor (int argc, char **argv) : mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), mViewManager (mDocumentManager), mPid(""), mLock(), mMerge (mDocumentManager), - mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) + mIpcServerName ("org.openmw.OpenCS"), mServer(nullptr), mClientSocket(nullptr) { std::pair > config = readConfig(); @@ -108,8 +108,9 @@ std::pair > CS::Editor::readConfi mCfgMgr.readConfiguration(variables, desc, quiet); - mDocumentManager.setEncoding ( - ToUTF8::calculateEncoding (variables["encoding"].as().toStdString())); + const std::string encoding = variables["encoding"].as().toStdString(); + mDocumentManager.setEncoding (ToUTF8::calculateEncoding (encoding)); + mFileDialog.setEncoding (QString::fromUtf8(encoding.c_str())); mDocumentManager.setResourceDir (mResources = variables["resources"].as().toStdString()); @@ -338,7 +339,7 @@ bool CS::Editor::makeIPCServer() } mServer->close(); - mServer = NULL; + mServer = nullptr; return false; } diff --git a/apps/opencs/model/doc/messages.cpp b/apps/opencs/model/doc/messages.cpp index 76bbb6f22..b70d44eda 100644 --- a/apps/opencs/model/doc/messages.cpp +++ b/apps/opencs/model/doc/messages.cpp @@ -35,11 +35,6 @@ void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& mMessages.push_back (Message (id, message, hint, severity)); } -void CSMDoc::Messages::push_back (const std::pair& data) -{ - add (data.first, data.second); -} - CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const { return mMessages.begin(); diff --git a/apps/opencs/model/doc/messages.hpp b/apps/opencs/model/doc/messages.hpp index 4041e1a67..671ded82a 100644 --- a/apps/opencs/model/doc/messages.hpp +++ b/apps/opencs/model/doc/messages.hpp @@ -56,9 +56,6 @@ namespace CSMDoc const std::string& hint = "", Message::Severity severity = Message::Severity_Default); - /// \deprecated Use add instead. - void push_back (const std::pair& data); - Iterator begin() const; Iterator end() const; diff --git a/apps/opencs/model/doc/operationholder.cpp b/apps/opencs/model/doc/operationholder.cpp index ccbed6c8b..0fd2bef95 100644 --- a/apps/opencs/model/doc/operationholder.cpp +++ b/apps/opencs/model/doc/operationholder.cpp @@ -3,7 +3,7 @@ #include "operation.hpp" CSMDoc::OperationHolder::OperationHolder (Operation *operation) - : mOperation(NULL) + : mOperation(nullptr) , mRunning (false) { if (operation) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index a704fb825..b6a55ea86 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -305,6 +305,7 @@ void CSMPrefs::State::declare() declareShortcut ("document-assets-videos", "Open Video Asset List", QKeySequence()); declareShortcut ("document-debug-run", "Run Debug", QKeySequence()); declareShortcut ("document-debug-shutdown", "Stop Debug", QKeySequence()); + declareShortcut ("document-debug-profiles", "Debug Profiles", QKeySequence()); declareShortcut ("document-debug-runlog", "Open Run Log", QKeySequence()); declareSubcategory ("Table"); diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index fc2989307..f91fc22f6 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -1,16 +1,15 @@ #include "birthsigncheck.hpp" -#include -#include - -#include +#include #include "../prefs/state.hpp" #include "../world/universalid.hpp" -CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns) -: mBirthsigns (birthsigns) +CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns, + const CSMWorld::Resources &textures) +: mBirthsigns(birthsigns), + mTextures(textures) { mIgnoreBaseRecords = false; } @@ -34,17 +33,20 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); - // test for empty name, description and texture if (birthsign.mName.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " has an empty name")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); if (birthsign.mDescription.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " has an empty description")); + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); if (birthsign.mTexture.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture")); - - /// \todo test if the texture exists + messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error); + else if (mTextures.searchId(birthsign.mTexture) == -1) + { + std::string ddsTexture = birthsign.mTexture; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1)) + messages.add(id, "Image '" + birthsign.mTexture + "' does not exist", "", CSMDoc::Message::Severity_Error); + } /// \todo check data members that can't be edited in the table view } diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index a8a7a2c14..9001c524c 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -4,6 +4,7 @@ #include #include "../world/idcollection.hpp" +#include "../world/resources.hpp" #include "../doc/stage.hpp" @@ -12,12 +13,14 @@ namespace CSMTools /// \brief VerifyStage: make sure that birthsign records are internally consistent class BirthsignCheckStage : public CSMDoc::Stage { - const CSMWorld::IdCollection& mBirthsigns; + const CSMWorld::IdCollection &mBirthsigns; + const CSMWorld::Resources &mTextures; bool mIgnoreBaseRecords; public: - BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns); + BirthsignCheckStage (const CSMWorld::IdCollection &birthsigns, + const CSMWorld::Resources &textures); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/tools/bodypartcheck.cpp b/apps/opencs/model/tools/bodypartcheck.cpp index b5bd78f6c..16dd9891e 100644 --- a/apps/opencs/model/tools/bodypartcheck.cpp +++ b/apps/opencs/model/tools/bodypartcheck.cpp @@ -34,25 +34,23 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message // Check BYDT if (bodyPart.mData.mPart > 14 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." )); + messages.add(id, "Invalid part", "", CSMDoc::Message::Severity_Error); if (bodyPart.mData.mFlags > 3 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." )); + messages.add(id, "Invalid flags", "", CSMDoc::Message::Severity_Error); if (bodyPart.mData.mType > 2 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." )); + messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error); // Check MODL - if ( bodyPart.mModel.empty() ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." )); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." )); + messages.add(id, "Model '" + bodyPart.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check FNAM - if ( bodyPart.mRace.empty() ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." )); + messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error); else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." )); + messages.add(id, "Race '" + bodyPart.mRace + " does not exist", "", CSMDoc::Message::Severity_Error); } diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 89923a398..a82121597 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -1,6 +1,5 @@ #include "classcheck.hpp" -#include #include #include @@ -37,26 +36,22 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - messages.push_back (std::make_pair (id, class_.mId + " doesn't have a name")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) - messages.push_back (std::make_pair (id, class_.mId + " doesn't have a description and it's playable")); + messages.add(id, "Description of a playable class is missing", "", CSMDoc::Message::Severity_Warning); // test for invalid attributes for (int i=0; i<2; ++i) if (class_.mData.mAttribute[i]==-1) { - std::ostringstream stream; - - stream << "Attribute #" << i << " of " << class_.mId << " is not set"; - - messages.push_back (std::make_pair (id, stream.str())); + messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error); } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id, "Class lists same attribute twice")); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error); } // test for non-unique skill @@ -66,10 +61,9 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) for (int i2=0; i2<2; ++i2) ++skills[class_.mData.mSkills[i][i2]]; - for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) - if (iter->second>1) + for (auto &skill : skills) + if (skill.second>1) { - messages.push_back (std::make_pair (id, - ESM::Skill::indexToId (iter->first) + " is listed more than once")); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/enchantmentcheck.cpp b/apps/opencs/model/tools/enchantmentcheck.cpp new file mode 100644 index 000000000..28f2b32cb --- /dev/null +++ b/apps/opencs/model/tools/enchantmentcheck.cpp @@ -0,0 +1,82 @@ +#include "enchantmentcheck.hpp" + +#include "../prefs/state.hpp" + +#include "../world/universalid.hpp" + +CSMTools::EnchantmentCheckStage::EnchantmentCheckStage (const CSMWorld::IdCollection& enchantments) + : mEnchantments (enchantments) +{ + mIgnoreBaseRecords = false; +} + +int CSMTools::EnchantmentCheckStage::setup() +{ + mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue(); + + return mEnchantments.getSize(); +} + +void CSMTools::EnchantmentCheckStage::perform (int stage, CSMDoc::Messages& messages) +{ + const CSMWorld::Record& record = mEnchantments.getRecord (stage); + + // Skip "Base" records (setting!) and "Deleted" records + if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted()) + return; + + const ESM::Enchantment& enchantment = record.get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Enchantment, enchantment.mId); + + if (enchantment.mData.mType < 0 || enchantment.mData.mType > 3) + messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mData.mCost < 0) + messages.add(id, "Cost is negative", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mData.mCharge < 0) + messages.add(id, "Charge is negative", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mData.mCost > enchantment.mData.mCharge) + messages.add(id, "Cost is higher than charge", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mEffects.mList.empty()) + { + messages.add(id, "Enchantment doesn't have any magic effects", "", CSMDoc::Message::Severity_Warning); + } + else + { + std::vector::const_iterator effect = enchantment.mEffects.mList.begin(); + + for (size_t i = 1; i <= enchantment.mEffects.mList.size(); i++) + { + const std::string number = std::to_string(i); + // At the time of writing this effects, attributes and skills are hardcoded + if (effect->mEffectID < 0 || effect->mEffectID > 142) + { + messages.add(id, "Effect #" + number + " is invalid", "", CSMDoc::Message::Severity_Error); + ++effect; + continue; + } + + if (effect->mSkill < -1 || effect->mSkill > 26) + messages.add(id, "Effect #" + number + " affected skill is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mAttribute < -1 || effect->mAttribute > 7) + messages.add(id, "Effect #" + number + " affected attribute is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mRange < 0 || effect->mRange > 2) + messages.add(id, "Effect #" + number + " range is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mArea < 0) + messages.add(id, "Effect #" + number + " area is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mDuration < 0) + messages.add(id, "Effect #" + number + " duration is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMin < 0) + messages.add(id, "Effect #" + number + " minimum magnitude is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMax < 0) + messages.add(id, "Effect #" + number + " maximum magnitude is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMin > effect->mMagnMax) + messages.add(id, "Effect #" + number + " minimum magnitude is higher than maximum magnitude", "", CSMDoc::Message::Severity_Error); + ++effect; + } + } +} diff --git a/apps/opencs/model/tools/enchantmentcheck.hpp b/apps/opencs/model/tools/enchantmentcheck.hpp new file mode 100644 index 000000000..3bd85326f --- /dev/null +++ b/apps/opencs/model/tools/enchantmentcheck.hpp @@ -0,0 +1,31 @@ +#ifndef CSM_TOOLS_ENCHANTMENTCHECK_H +#define CSM_TOOLS_ENCHANTMENTCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "../doc/stage.hpp" + +namespace CSMTools +{ + /// \brief Make sure that enchantment records are correct + class EnchantmentCheckStage : public CSMDoc::Stage + { + const CSMWorld::IdCollection& mEnchantments; + bool mIgnoreBaseRecords; + + public: + + EnchantmentCheckStage (const CSMWorld::IdCollection& enchantments); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, CSMDoc::Messages& messages); + ///< Messages resulting from this tage will be appended to \a messages. + + }; +} + +#endif diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 39073db5f..8a198e953 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -1,9 +1,7 @@ #include "factioncheck.hpp" -#include #include -#include #include #include "../prefs/state.hpp" @@ -37,12 +35,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages // test for empty name if (faction.mName.empty()) - messages.push_back (std::make_pair (id, faction.mId + " has an empty name")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id , "Faction lists same attribute twice")); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error); } // test for non-unique skill @@ -52,11 +50,10 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages if (faction.mData.mSkills[i]!=-1) ++skills[faction.mData.mSkills[i]]; - for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) - if (iter->second>1) + for (auto &skill : skills) + if (skill.second>1) { - messages.push_back (std::make_pair (id, - ESM::Skill::indexToId (iter->first) + " is listed more than once")); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error); } /// \todo check data members that can't be edited in the table view diff --git a/apps/opencs/model/tools/journalcheck.cpp b/apps/opencs/model/tools/journalcheck.cpp index 4a7ab7d66..ae83abfa0 100644 --- a/apps/opencs/model/tools/journalcheck.cpp +++ b/apps/opencs/model/tools/journalcheck.cpp @@ -1,7 +1,6 @@ #include "journalcheck.hpp" #include -#include #include "../prefs/state.hpp" @@ -57,34 +56,27 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) if (journalInfo.mResponse.empty()) { CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); - - messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning); } std::pair::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex); // Duplicate index - if (result.second == false) + if (!result.second) { CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); - - std::ostringstream stream; - stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Duplicated quest index " + std::to_string(journalInfo.mData.mJournalIndex), "", CSMDoc::Message::Severity_Error); } } if (totalInfoCount == 0) { CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); - - messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "No related journal entry", "", CSMDoc::Message::Severity_Warning); } else if (statusNamedCount > 1) { CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); - - messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Multiple entries with quest status 'Named'", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 531bd9e1d..f55fb14ee 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -4,79 +4,26 @@ #include "../prefs/state.hpp" -#include "../world/resources.hpp" -#include "../world/data.hpp" - -namespace -{ - void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) - { - if (!text.empty()) - { - messages.push_back(std::make_pair(id, text)); - } - } -} - -bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const -{ - const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; - bool exists = false; - - if (textures.searchId(texture) != -1) - { - exists = true; - } - else - { - std::string ddsTexture = texture; - if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1) - { - exists = true; - } - } - - return exists; -} - -std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id, +std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const { - std::string error; - if (!id.empty()) - { - CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id); - if (index.first == -1) - { - error = "No such " + column + " '" + id + "'"; - } - else if (index.second != type.getType()) - { - error = column + " is not of type " + type.getTypeName(); - } - } - return error; -} - -std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const -{ - std::string error; - if (!id.empty() && mSounds.searchId(id) == -1) - { - error = "No such " + column + " '" + id + "'"; - } - return error; + CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); + if (index.first == -1) + return (column + " '" + id + "' does not exist"); + else if (index.second != type.getType()) + return (column + " '" + id + "' does not have " + type.getTypeName() + " type"); + return std::string(); } CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables, + const CSMWorld::RefIdCollection &objects, const CSMWorld::Resources &icons, const CSMWorld::Resources &textures) : mMagicEffects(effects), mSounds(sounds), - mReferenceables(referenceables), + mObjects(objects), mIcons(icons), mTextures(textures) { @@ -100,46 +47,75 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa ESM::MagicEffect effect = record.get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId); - + + if (effect.mDescription.empty()) + { + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); + } + if (effect.mData.mBaseCost < 0.0f) { - messages.push_back(std::make_pair(id, "Base Cost is negative")); + messages.add(id, "Base cost is negative", "", CSMDoc::Message::Severity_Error); } if (effect.mIcon.empty()) { - messages.push_back(std::make_pair(id, "Icon is not specified")); + messages.add(id, "Icon is missing", "", CSMDoc::Message::Severity_Error); } - else if (!isTextureExists(effect.mIcon, true)) + else { - messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'")); + if (mIcons.searchId(effect.mIcon) == -1) + { + std::string ddsIcon = effect.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.add(id, "Icon '" + effect.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } - if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false)) + if (!effect.mParticle.empty()) { - messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'")); + if (mTextures.searchId(effect.mParticle) == -1) + { + std::string ddsParticle = effect.mParticle; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsParticle) && mTextures.searchId(ddsParticle) != -1)) + messages.add(id, "Particle texture '" + effect.mParticle + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object")); - - addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound")); - - if (effect.mDescription.empty()) + if (!effect.mCasting.empty()) { - messages.push_back(std::make_pair(id, "Description is empty")); + const std::string error = checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); } + + if (!effect.mHit.empty()) + { + const std::string error = checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mArea.empty()) + { + const std::string error = checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mBolt.empty()) + { + const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mCastSound.empty() && mSounds.searchId(effect.mCastSound) == -1) + messages.add(id, "Casting sound '" + effect.mCastSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mHitSound.empty() && mSounds.searchId(effect.mHitSound) == -1) + messages.add(id, "Hit sound '" + effect.mHitSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mAreaSound.empty() && mSounds.searchId(effect.mAreaSound) == -1) + messages.add(id, "Area sound '" + effect.mAreaSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mBoltSound.empty() && mSounds.searchId(effect.mBoltSound) == -1) + messages.add(id, "Bolt sound '" + effect.mBoltSound + "' does not exist", "", CSMDoc::Message::Severity_Error); } diff --git a/apps/opencs/model/tools/magiceffectcheck.hpp b/apps/opencs/model/tools/magiceffectcheck.hpp index 28a406283..a52723b0f 100644 --- a/apps/opencs/model/tools/magiceffectcheck.hpp +++ b/apps/opencs/model/tools/magiceffectcheck.hpp @@ -6,14 +6,10 @@ #include "../world/idcollection.hpp" #include "../world/refidcollection.hpp" +#include "../world/resources.hpp" #include "../doc/stage.hpp" -namespace CSMWorld -{ - class Resources; -} - namespace CSMTools { /// \brief VerifyStage: make sure that magic effect records are internally consistent @@ -21,23 +17,18 @@ namespace CSMTools { const CSMWorld::IdCollection &mMagicEffects; const CSMWorld::IdCollection &mSounds; - const CSMWorld::RefIdCollection &mReferenceables; + const CSMWorld::RefIdCollection &mObjects; const CSMWorld::Resources &mIcons; const CSMWorld::Resources &mTextures; bool mIgnoreBaseRecords; private: - bool isTextureExists(const std::string &texture, bool isIcon) const; - - std::string checkReferenceable(const std::string &id, - const CSMWorld::UniversalId &type, - const std::string &column) const; - std::string checkSound(const std::string &id, const std::string &column) const; + std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const; public: MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables, + const CSMWorld::RefIdCollection &objects, const CSMWorld::Resources &icons, const CSMWorld::Resources &textures); diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 6427bb119..febb79c64 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -37,9 +37,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message // check the number of pathgrid points if (pathgrid.mData.mS2 < static_cast(pathgrid.mPoints.size())) - messages.add (id, pathgrid.mId + " has less points than expected", "", CSMDoc::Message::Severity_Error); + messages.add (id, "Less points than expected", "", CSMDoc::Message::Severity_Error); else if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) - messages.add (id, pathgrid.mId + " has more points than expected", "", CSMDoc::Message::Severity_Error); + messages.add (id, "More points than expected", "", CSMDoc::Message::Severity_Error); std::vector pointList(pathgrid.mPoints.size()); std::vector duplList; @@ -56,9 +56,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) { std::ostringstream ss; - ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0 - << " and " << pathgrid.mEdges[i].mV1; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "Duplicate edge between points #" << pathgrid.mEdges[i].mV0 << " and #" << pathgrid.mEdges[i].mV1; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); break; } } @@ -70,8 +69,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message else { std::ostringstream ss; - ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "An edge is connected to a non-existent point #" << pathgrid.mEdges[i].mV0; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -93,31 +92,26 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (!foundReverse) { std::ostringstream ss; - ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "Missing edge between points #" << i << " and #" << pointList[i].mOtherIndex[j]; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } // check duplicate points // FIXME: how to do this efficiently? - for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) + for (unsigned int j = 0; j != i; ++j) { - if (j == i) - continue; - if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX && pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) { - std::vector::const_iterator it = find(duplList.begin(), duplList.end(), i); + std::vector::const_iterator it = find(duplList.begin(), duplList.end(), static_cast(i)); if (it == duplList.end()) { std::ostringstream ss; - ss << " has a duplicated point (" << i - << ") x=" << pathgrid.mPoints[i].mX - << ", y=" << pathgrid.mPoints[i].mY - << ", z=" << pathgrid.mPoints[i].mZ; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); + ss << "Point #" << i << " duplicates point #" << j + << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")"; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); duplList.push_back(i); break; @@ -132,11 +126,11 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[i].mConnectionNum == 0) { std::ostringstream ss; - ss << " has an orphaned point (" << i - << ") x=" << pathgrid.mPoints[i].mX - << ", y=" << pathgrid.mPoints[i].mY - << ", z=" << pathgrid.mPoints[i].mZ; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); + ss << "Point #" << i << " (" + << pathgrid.mPoints[i].mX << ", " + << pathgrid.mPoints[i].mY << ", " + << pathgrid.mPoints[i].mZ << ") is disconnected from other points"; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); } } diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp index 38abfef18..6585a31cc 100644 --- a/apps/opencs/model/tools/racecheck.cpp +++ b/apps/opencs/model/tools/racecheck.cpp @@ -1,9 +1,5 @@ #include "racecheck.hpp" -#include - -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -29,24 +25,24 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me // test for empty name and description if (race.mName.empty()) - messages.push_back (std::make_pair (id, race.mId + " has an empty name")); + messages.add(id, "Name is missing", "", (race.mData.mFlags & 0x1) ? CSMDoc::Message::Severity_Error : CSMDoc::Message::Severity_Warning); if (race.mDescription.empty()) - messages.push_back (std::make_pair (id, race.mId + " has an empty description")); + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); // test for positive height if (race.mData.mHeight.mMale<=0) - messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height")); + messages.add(id, "Male height is non-positive", "", CSMDoc::Message::Severity_Error); if (race.mData.mHeight.mFemale<=0) - messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height")); + messages.add(id, "Female height is non-positive", "", CSMDoc::Message::Severity_Error); // test for non-negative weight if (race.mData.mWeight.mMale<0) - messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight")); + messages.add(id, "Male weight is negative", "", CSMDoc::Message::Severity_Error); if (race.mData.mWeight.mFemale<0) - messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight")); + messages.add(id, "Female weight is negative", "", CSMDoc::Message::Severity_Error); /// \todo check data members that can't be edited in the table view } @@ -56,7 +52,7 @@ void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races); if (!mPlayable) - messages.push_back (std::make_pair (id, "No playable race")); + messages.add(id, "No playable race", "", CSMDoc::Message::Severity_SeriousError); } CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection& races) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 3e8dc1188..fdbab7fd0 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1,6 +1,7 @@ #include "referenceablecheck.hpp" #include +#include #include "../prefs/state.hpp" @@ -11,13 +12,18 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction, - const CSMWorld::IdCollection& scripts) - : - mReferencables(referenceable), + const CSMWorld::IdCollection& scripts, + const CSMWorld::Resources& models, + const CSMWorld::Resources& icons, + const CSMWorld::IdCollection& bodyparts) + :mReferencables(referenceable), mRaces(races), mClasses(classes), mFactions(faction), mScripts(scripts), + mModels(models), + mIcons(icons), + mBodyParts(bodyparts), mPlayerPresent(false) { mIgnoreBaseRecords = false; @@ -270,9 +276,10 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( const ESM::Activator& activator = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId); - //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) - messages.push_back (std::make_pair (id, activator.mId + " has no model")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(activator.mModel) == -1) + messages.add(id, "Model '" + activator.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(activator, messages, id.toString()); @@ -293,7 +300,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); inventoryItemCheck(potion, messages, id.toString()); - //IIRC potion can have empty effects list just fine. + /// \todo Check magic effects for validity // Check that mentioned scripts exist scriptCheck(potion, messages, id.toString()); @@ -338,13 +345,13 @@ void CSMTools::ReferenceableCheckStage::armorCheck( inventoryItemCheck(armor, messages, id.toString(), true); - //checking for armor class, armor should have poistive armor class, but 0 is considered legal + // Armor should have positive armor class, but 0 class is not an error if (armor.mData.mArmor < 0) - messages.push_back (std::make_pair (id, armor.mId + " has negative armor class")); + messages.add(id, "Armor class is negative", "", CSMDoc::Message::Severity_Error); - //checking for health. Only positive numbers are allowed, or 0 is illegal + // Armor durability must be a positive number if (armor.mData.mHealth <= 0) - messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); + messages.add(id, "Durability is non-positive", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(armor, messages, id.toString()); @@ -383,18 +390,19 @@ void CSMTools::ReferenceableCheckStage::containerCheck( const ESM::Container& container = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); - //Checking for model, IIRC all containers should have a model + //checking for name + if (container.mName.empty()) + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + + //Checking for model if (container.mModel.empty()) - messages.push_back (std::make_pair (id, container.mId + " has no model")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(container.mModel) == -1) + messages.add(id, "Model '" + container.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //Checking for capacity (weight) if (container.mWeight < 0) //0 is allowed - messages.push_back (std::make_pair (id, - container.mId + " has negative weight (capacity)")); - - //checking for name - if (container.mName.empty()) - messages.push_back (std::make_pair (id, container.mId + " has an empty name")); + messages.add(id, "Capacity is negative", "", CSMDoc::Message::Severity_Error); //checking contained items inventoryListCheck(container.mInventory.mList, messages, id.toString()); @@ -416,68 +424,81 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( const ESM::Creature& creature = (dynamic_cast&>(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); - if (creature.mModel.empty()) - messages.push_back (std::make_pair (id, creature.mId + " has no model")); - if (creature.mName.empty()) - messages.push_back (std::make_pair (id, creature.mId + " has an empty name")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + + if (creature.mModel.empty()) + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(creature.mModel) == -1) + messages.add(id, "Model '" + creature.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //stats checks - if (creature.mData.mLevel < 1) - messages.push_back (std::make_pair (id, creature.mId + " has non-positive level")); + if (creature.mData.mLevel <= 0) + messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mStrength < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative strength")); - + messages.add(id, "Strength is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mIntelligence < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative intelligence")); - + messages.add(id, "Intelligence is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mWillpower < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative willpower")); - + messages.add(id, "Willpower is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mAgility < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative agility")); - + messages.add(id, "Agility is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mSpeed < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative speed")); - + messages.add(id, "Speed is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mEndurance < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative endurance")); - + messages.add(id, "Endurance is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mPersonality < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative personality")); - + messages.add(id, "Personality is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mLuck < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative luck")); + messages.add(id, "Luck is negative", "", CSMDoc::Message::Severity_Warning); + + if (creature.mData.mCombat < 0) + messages.add(id, "Combat is negative", "", CSMDoc::Message::Severity_Warning); + if (creature.mData.mMagic < 0) + messages.add(id, "Magic is negative", "", CSMDoc::Message::Severity_Warning); + if (creature.mData.mStealth < 0) + messages.add(id, "Stealth is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mHealth < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative health")); + messages.add(id, "Health is negative", "", CSMDoc::Message::Severity_Error); + if (creature.mData.mMana < 0) + messages.add(id, "Magicka is negative", "", CSMDoc::Message::Severity_Error); + if (creature.mData.mFatigue < 0) + messages.add(id, "Fatigue is negative", "", CSMDoc::Message::Severity_Error); if (creature.mData.mSoul < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative soul value")); + messages.add(id, "Soul value is negative", "", CSMDoc::Message::Severity_Error); for (int i = 0; i < 6; ++i) { if (creature.mData.mAttack[i] < 0) - { - messages.push_back (std::make_pair (id, - creature.mId + " has negative attack strength")); - break; - } + messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error); + if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1]) + messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Error); } - //TODO, find meaning of other values - if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures - messages.push_back (std::make_pair (id, creature.mId + " has negative gold ")); + if (creature.mData.mGold < 0) + messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error); if (creature.mScale == 0) - messages.push_back (std::make_pair (id, creature.mId + " has zero scale value")); + messages.add(id, "Scale is equal to zero", "", CSMDoc::Message::Severity_Error); + + if (!creature.mOriginal.empty()) + { + CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(creature.mOriginal); + if (index.first == -1) + messages.add(id, "Parent creature '" + creature.mOriginal + "' does not exist", "", CSMDoc::Message::Severity_Error); + else if (index.second != CSMWorld::UniversalId::Type_Creature) + messages.add(id, "'" + creature.mOriginal + "' is not a creature", "", CSMDoc::Message::Severity_Error); + } // Check inventory inventoryListCheck(creature.mInventory.mList, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(creature, messages, id.toString()); + /// \todo Check spells, teleport table, AI data and AI packages for validity } void CSMTools::ReferenceableCheckStage::doorCheck( @@ -495,10 +516,12 @@ void CSMTools::ReferenceableCheckStage::doorCheck( //usual, name or model if (door.mName.empty()) - messages.push_back (std::make_pair (id, door.mId + " has an empty name")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); if (door.mModel.empty()) - messages.push_back (std::make_pair (id, door.mId + " has no model")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(door.mModel) == -1) + messages.add(id, "Model '" + door.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(door, messages, id.toString()); @@ -572,7 +595,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); if (light.mData.mRadius < 0) - messages.push_back (std::make_pair (id, light.mId + " has negative light radius")); + messages.add(id, "Light radius is negative", "", CSMDoc::Message::Severity_Error); if (light.mData.mFlags & ESM::Light::Carry) inventoryItemCheck(light, messages, id.toString()); @@ -644,96 +667,75 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( return; short level(npc.mNpdt.mLevel); - char disposition(npc.mNpdt.mDisposition); - char reputation(npc.mNpdt.mReputation); - char rank(npc.mNpdt.mRank); - //Don't know what unknown is for int gold(npc.mNpdt.mGold); if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag { - messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happen? + messages.add(id, "NPC with autocalculated stats doesn't have autocalc flag turned on", "", CSMDoc::Message::Severity_Error); //should not happen? return; } - - level = npc.mNpdt.mLevel; - disposition = npc.mNpdt.mDisposition; - reputation = npc.mNpdt.mReputation; - rank = npc.mNpdt.mRank; - gold = npc.mNpdt.mGold; } else { - if (npc.mNpdt.mAgility == 0) - messages.push_back (std::make_pair (id, npc.mId + " agility has zero value")); - - if (npc.mNpdt.mEndurance == 0) - messages.push_back (std::make_pair (id, npc.mId + " endurance has zero value")); - - if (npc.mNpdt.mIntelligence == 0) - messages.push_back (std::make_pair (id, npc.mId + " intelligence has zero value")); - - if (npc.mNpdt.mLuck == 0) - messages.push_back (std::make_pair (id, npc.mId + " luck has zero value")); - - if (npc.mNpdt.mPersonality == 0) - messages.push_back (std::make_pair (id, npc.mId + " personality has zero value")); - if (npc.mNpdt.mStrength == 0) - messages.push_back (std::make_pair (id, npc.mId + " strength has zero value")); - - if (npc.mNpdt.mSpeed == 0) - messages.push_back (std::make_pair (id, npc.mId + " speed has zero value")); - + messages.add(id, "Strength is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mIntelligence == 0) + messages.add(id, "Intelligence is equal to zero", "", CSMDoc::Message::Severity_Warning); if (npc.mNpdt.mWillpower == 0) - messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value")); + messages.add(id, "Willpower is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mAgility == 0) + messages.add(id, "Agility is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mSpeed == 0) + messages.add(id, "Speed is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mEndurance == 0) + messages.add(id, "Endurance is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mPersonality == 0) + messages.add(id, "Personality is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mLuck == 0) + messages.add(id, "Luck is equal to zero", "", CSMDoc::Message::Severity_Warning); } - if (level < 1) - messages.push_back (std::make_pair (id, npc.mId + " level is non positive")); + if (level <= 0) + messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning); if (gold < 0) - messages.push_back (std::make_pair (id, npc.mId + " gold has negative value")); + messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error); if (npc.mName.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has any empty name")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); if (npc.mClass.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has an empty class")); + messages.add(id, "Class is missing", "", CSMDoc::Message::Severity_Error); else if (mClasses.searchId (npc.mClass) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid class")); + messages.add(id, "Class '" + npc.mClass + "' does not exist", "", CSMDoc::Message::Severity_Error); if (npc.mRace.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has an empty race")); + messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error); else if (mRaces.searchId (npc.mRace) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid race")); + messages.add(id, "Race '" + npc.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error); - if (disposition < 0) - messages.push_back (std::make_pair (id, npc.mId + " has negative disposition")); - - if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid - { - messages.push_back (std::make_pair (id, npc.mId + " has negative reputation")); - } - - if (!npc.mFaction.empty()) - { - if (rank < 0) - messages.push_back (std::make_pair (id, npc.mId + " has negative rank")); - - if (mFactions.searchId(npc.mFaction) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid faction")); - } + if (!npc.mFaction.empty() && mFactions.searchId(npc.mFaction) == -1) + messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); if (npc.mHead.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has no head")); + messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error); + else + { + if (mBodyParts.searchId(npc.mHead) == -1) + messages.add(id, "Head body part '" + npc.mHead + "' does not exist", "", CSMDoc::Message::Severity_Error); + /// \todo Check gender, race and other body parts stuff validity for the specific NPC + } if (npc.mHair.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has no hair")); - - //TODO: reputation, Disposition, rank, everything else + messages.add(id, "Hair is missing", "", CSMDoc::Message::Severity_Error); + else + { + if (mBodyParts.searchId(npc.mHair) == -1) + messages.add(id, "Hair body part '" + npc.mHair + "' does not exist", "", CSMDoc::Message::Severity_Error); + /// \todo Check gender, race and other body part stuff validity for the specific NPC + } // Check inventory inventoryListCheck(npc.mInventory.mList, messages, id.toString()); @@ -793,28 +795,25 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( weapon.mData.mType == ESM::Weapon::Bolt)) { if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum slash damage higher than maximum")); + messages.add(id, "Minimum slash damage higher than maximum", "", CSMDoc::Message::Severity_Warning); if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum thrust damage higher than maximum")); + messages.add(id, "Minimum thrust damage higher than maximum", "", CSMDoc::Message::Severity_Warning); } if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum chop damage higher than maximum")); + messages.add(id, "Minimum chop damage higher than maximum", "", CSMDoc::Message::Severity_Warning); if (!(weapon.mData.mType == ESM::Weapon::Arrow || weapon.mData.mType == ESM::Weapon::Bolt || weapon.mData.mType == ESM::Weapon::MarksmanThrown)) { //checking of health - if (weapon.mData.mHealth <= 0) - messages.push_back (std::make_pair (id, weapon.mId + " has non-positive health")); + if (weapon.mData.mHealth == 0) + messages.add(id, "Durability is equal to zero", "", CSMDoc::Message::Severity_Warning); if (weapon.mData.mReach < 0) - messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); + messages.add(id, "Reach is negative", "", CSMDoc::Message::Severity_Error); } } @@ -877,7 +876,9 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId); if (staticElement.mModel.empty()) - messages.push_back (std::make_pair (id, staticElement.mId + " has no model")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(staticElement.mModel) == -1) + messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); } //final check @@ -885,8 +886,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages) { if (!mPlayerPresent) - messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables, - "There is no player record")); + messages.add(CSMWorld::UniversalId::Type_Referenceables, "Player record is missing", "", CSMDoc::Message::Severity_SeriousError); } void CSMTools::ReferenceableCheckStage::inventoryListCheck( @@ -900,8 +900,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); if (localIndex.first == -1) - messages.push_back (std::make_pair (id, - id + " contains non-existing item (" + itemName + ")")); + messages.add(id, "Item '" + itemName + "' does not exist", "", CSMDoc::Message::Severity_Error); else { // Needs to accommodate containers, creatures, and NPCs @@ -922,8 +921,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( case CSMWorld::UniversalId::Type_ItemLevelledList: break; default: - messages.push_back (std::make_pair(id, - id + " contains item of invalid type (" + itemName + ")")); + messages.add(id, "'" + itemName + "' is not an item", "", CSMDoc::Message::Severity_Error); } } } @@ -935,67 +933,82 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); + messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); + messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); + messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); + messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(someItem.mModel) == -1) + messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); + messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error); + else if (mIcons.searchId(someItem.mIcon) == -1) + { + std::string ddsIcon = someItem.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); + } if (enchantable && someItem.mData.mEnchant < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative enchantment")); + messages.add(someID, "Enchantment points number is negative", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::inventoryItemCheck ( const Item& someItem, CSMDoc::Messages& messages, const std::string& someID) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); + messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); + messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); + messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); + messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(someItem.mModel) == -1) + messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); + messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error); + else if (mIcons.searchId(someItem.mIcon) == -1) + { + std::string ddsIcon = someItem.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken) { if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); + messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error); if (canBeBroken && someTool.mData.mUses<=0) - messages.push_back (std::make_pair (someID, - someTool.mId + " has non-positive uses count")); + messages.add(someID, "Number of uses is non-positive", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) { if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); + messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::listCheck ( @@ -1004,12 +1017,10 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( for (unsigned i = 0; i < someList.mList.size(); ++i) { if (mReferencables.searchId(someList.mList[i].mId).first == -1) - messages.push_back (std::make_pair (someID, - someList.mId + " contains item without referencable")); + messages.add(someID, "Object '" + someList.mList[i].mId + "' does not exist", "", CSMDoc::Message::Severity_Error); if (someList.mList[i].mLevel < 1) - messages.push_back (std::make_pair (someID, - someList.mId + " contains item with non-positive level")); + messages.add(someID, "Level of item '" + someList.mList[i].mId + "' is non-positive", "", CSMDoc::Message::Severity_Error); } } @@ -1019,6 +1030,6 @@ template void CSMTools::ReferenceableCheckStage::scriptCheck ( if (!someTool.mScript.empty()) { if (mScripts.searchId(someTool.mScript) == -1) - messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\"")); + messages.add(someID, "Script '" + someTool.mScript + "' does not exist", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index f9341bd9c..e55e5fad9 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -5,6 +5,7 @@ #include "../doc/stage.hpp" #include "../world/data.hpp" #include "../world/refiddata.hpp" +#include "../world/resources.hpp" namespace CSMTools { @@ -16,7 +17,10 @@ namespace CSMTools const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& factions, - const CSMWorld::IdCollection& scripts); + const CSMWorld::IdCollection& scripts, + const CSMWorld::Resources& models, + const CSMWorld::Resources& icons, + const CSMWorld::IdCollection& bodyparts); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -81,6 +85,9 @@ namespace CSMTools const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; const CSMWorld::IdCollection& mScripts; + const CSMWorld::Resources& mModels; + const CSMWorld::Resources& mIcons; + const CSMWorld::IdCollection& mBodyParts; bool mPlayerPresent; bool mIgnoreBaseRecords; }; diff --git a/apps/opencs/model/tools/referencecheck.cpp b/apps/opencs/model/tools/referencecheck.cpp index 447238be4..76bfeb3ba 100644 --- a/apps/opencs/model/tools/referencecheck.cpp +++ b/apps/opencs/model/tools/referencecheck.cpp @@ -9,7 +9,7 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage( const CSMWorld::IdCollection& factions) : mReferences(references), - mReferencables(referencables), + mObjects(referencables), mDataSet(referencables.getDataSet()), mCells(cells), mFactions(factions) @@ -28,78 +28,59 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message const CSMWorld::CellRef& cellRef = record.get(); const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId); - // Check for empty reference id - if (cellRef.mRefID.empty()) { - messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)")); - } else { + // Check reference id + if (cellRef.mRefID.empty()) + messages.add(id, "Instance is not based on an object", "", CSMDoc::Message::Severity_Error); + else + { // Check for non existing referenced object - if (mReferencables.searchId(cellRef.mRefID) == -1) { - messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID)); - } else { + if (mObjects.searchId(cellRef.mRefID) == -1) + messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID + "'", "", CSMDoc::Message::Severity_Error); + else + { // Check if reference charge is valid for it's proper referenced type CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID); bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light; - if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) { - std::string str = " has invalid charge "; - if (localIndex.second == CSMWorld::UniversalId::Type_Light) - str += std::to_string(cellRef.mChargeFloat); - else - str += std::to_string(cellRef.mChargeInt); - messages.push_back(std::make_pair(id, id.getId() + str)); - } + if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) + messages.add(id, "Invalid charge", "", CSMDoc::Message::Severity_Error); } } // If object have owner, check if that owner reference is valid - if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1) - messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner)); + if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1) + messages.add(id, "Owner object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error); // If object have creature soul trapped, check if that creature reference is valid if (!cellRef.mSoul.empty()) - if (mReferencables.searchId(cellRef.mSoul) == -1) - messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul)); + if (mObjects.searchId(cellRef.mSoul) == -1) + messages.add(id, "Trapped soul object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error); - bool hasFaction = !cellRef.mFaction.empty(); - - // If object have faction, check if that faction reference is valid - if (hasFaction) - if (mFactions.searchId(cellRef.mFaction) == -1) - messages.push_back(std::make_pair(id, " has non existing faction " + cellRef.mFaction)); - - // Check item's faction rank - if (hasFaction && cellRef.mFactionRank < -1) - messages.push_back(std::make_pair(id, " has faction set but has invalid faction rank " + std::to_string(cellRef.mFactionRank))); - else if (!hasFaction && cellRef.mFactionRank != -2) - messages.push_back(std::make_pair(id, " has invalid faction rank " + std::to_string(cellRef.mFactionRank))); - - // If door have destination cell, check if that reference is valid - if (!cellRef.mDestCell.empty()) - if (mCells.searchId(cellRef.mDestCell) == -1) - messages.push_back(std::make_pair(id, " has non existing destination cell " + cellRef.mDestCell)); - - // Check if scale isn't negative - if (cellRef.mScale < 0) + if (cellRef.mFaction.empty()) { - std::string str = " has negative scale "; - str += std::to_string(cellRef.mScale); - messages.push_back(std::make_pair(id, id.getId() + str)); + if (cellRef.mFactionRank != -2) + messages.add(id, "Reference without a faction has a faction rank", "", CSMDoc::Message::Severity_Error); } + else + { + if (mFactions.searchId(cellRef.mFaction) == -1) + messages.add(id, "Faction '" + cellRef.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); + else if (cellRef.mFactionRank < -1) + messages.add(id, "Invalid faction rank", "", CSMDoc::Message::Severity_Error); + } + + if (!cellRef.mDestCell.empty() && mCells.searchId(cellRef.mDestCell) == -1) + messages.add(id, "Destination cell '" + cellRef.mDestCell + "' does not exist", "", CSMDoc::Message::Severity_Error); + + if (cellRef.mScale < 0) + messages.add(id, "Negative scale", "", CSMDoc::Message::Severity_Error); // Check if enchantement points aren't negative or are at full (-1) - if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1) - { - std::string str = " has negative enchantment points "; - str += std::to_string(cellRef.mEnchantmentCharge); - messages.push_back(std::make_pair(id, id.getId() + str)); - } + if (cellRef.mEnchantmentCharge < -1) + messages.add(id, "Negative number of enchantment points", "", CSMDoc::Message::Severity_Error); // Check if gold value isn't negative if (cellRef.mGoldValue < 0) - { - std::string str = " has negative gold value "; - str += cellRef.mGoldValue; - messages.push_back(std::make_pair(id, id.getId() + str)); - } + messages.add(id, "Negative gold value", "", CSMDoc::Message::Severity_Error); } int CSMTools::ReferenceCheckStage::setup() diff --git a/apps/opencs/model/tools/referencecheck.hpp b/apps/opencs/model/tools/referencecheck.hpp index 5e25924f3..7373903e6 100644 --- a/apps/opencs/model/tools/referencecheck.hpp +++ b/apps/opencs/model/tools/referencecheck.hpp @@ -19,7 +19,7 @@ namespace CSMTools private: const CSMWorld::RefCollection& mReferences; - const CSMWorld::RefIdCollection& mReferencables; + const CSMWorld::RefIdCollection& mObjects; const CSMWorld::RefIdData& mDataSet; const CSMWorld::IdCollection& mCells; const CSMWorld::IdCollection& mFactions; diff --git a/apps/opencs/model/tools/regioncheck.cpp b/apps/opencs/model/tools/regioncheck.cpp index f21253090..0b6537d7f 100644 --- a/apps/opencs/model/tools/regioncheck.cpp +++ b/apps/opencs/model/tools/regioncheck.cpp @@ -1,10 +1,5 @@ #include "regioncheck.hpp" -#include -#include - -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -36,7 +31,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages) // test for empty name if (region.mName.empty()) - messages.add(id, region.mId + " has an empty name", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); /// \todo test that the ID in mSleeplist exists diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index d3c6221cd..d3d9d1503 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -30,10 +30,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); - stream - << "script " << mFile - << ", line " << loc.mLine << ", column " << loc.mColumn - << " (" << loc.mLiteral << "): " << message; + stream << "line " << loc.mLine << ", column " << loc.mColumn << ": " << message << " (" << loc.mLiteral << ")"; std::ostringstream hintStream; @@ -47,7 +44,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); std::ostringstream stream; - stream << "script " << mFile << ": " << message; + stream << message; mMessages->add (id, stream.str(), "", getSeverity (type)); } @@ -128,7 +125,7 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); std::ostringstream stream; - stream << "script " << mFile << ": " << error.what(); + stream << error.what(); messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError); } diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index b34d18e2a..c5b38dc1e 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -1,9 +1,5 @@ #include "skillcheck.hpp" -#include - -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -33,16 +29,12 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId); + if (skill.mDescription.empty()) + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); + for (int i=0; i<4; ++i) if (skill.mData.mUseValue[i]<0) { - std::ostringstream stream; - - stream << "Use value #" << i << " of " << skill.mId << " is negative"; - - messages.push_back (std::make_pair (id, stream.str())); + messages.add(id, "Use value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); } - - if (skill.mDescription.empty()) - messages.push_back (std::make_pair (id, skill.mId + " has an empty description")); } diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index b84453b5c..c0d893f1a 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -1,15 +1,13 @@ #include "soundcheck.hpp" -#include - -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" -CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection& sounds) -: mSounds (sounds) +CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection &sounds, + const CSMWorld::Resources &soundfiles) + : mSounds (sounds), + mSoundFiles (soundfiles) { mIgnoreBaseRecords = false; } @@ -34,7 +32,16 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); if (sound.mData.mMinRange>sound.mData.mMaxRange) - messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); + { + messages.add(id, "Minimum range is larger than maximum range", "", CSMDoc::Message::Severity_Warning); + } - /// \todo check, if the sound file exists + if (sound.mSound.empty()) + { + messages.add(id, "Sound file is missing", "", CSMDoc::Message::Severity_Error); + } + else if (mSoundFiles.searchId(sound.mSound) == -1) + { + messages.add(id, "Sound file '" + sound.mSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index d6fff5263..fc5925717 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -3,6 +3,7 @@ #include +#include "../world/resources.hpp" #include "../world/idcollection.hpp" #include "../doc/stage.hpp" @@ -13,11 +14,13 @@ namespace CSMTools class SoundCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSounds; + const CSMWorld::Resources &mSoundFiles; bool mIgnoreBaseRecords; public: - SoundCheckStage (const CSMWorld::IdCollection& sounds); + SoundCheckStage (const CSMWorld::IdCollection& sounds, + const CSMWorld::Resources &soundfiles); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/tools/soundgencheck.cpp b/apps/opencs/model/tools/soundgencheck.cpp index 3692259ce..ec29e23fe 100644 --- a/apps/opencs/model/tools/soundgencheck.cpp +++ b/apps/opencs/model/tools/soundgencheck.cpp @@ -1,7 +1,5 @@ #include "soundgencheck.hpp" -#include - #include "../prefs/state.hpp" #include "../world/refiddata.hpp" @@ -9,10 +7,10 @@ CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables) + const CSMWorld::RefIdCollection &objects) : mSoundGens(soundGens), mSounds(sounds), - mReferenceables(referenceables) + mObjects(objects) { mIgnoreBaseRecords = false; } @@ -37,23 +35,23 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages if (!soundGen.mCreature.empty()) { - CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature); + CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature); if (creatureIndex.first == -1) { - messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'")); + messages.add(id, "Creature '" + soundGen.mCreature + "' doesn't exist", "", CSMDoc::Message::Severity_Error); } else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature) { - messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature")); + messages.add(id, "'" + soundGen.mCreature + "' is not a creature", "", CSMDoc::Message::Severity_Error); } } if (soundGen.mSound.empty()) { - messages.push_back(std::make_pair(id, "Sound is not specified")); + messages.add(id, "Sound is missing", "", CSMDoc::Message::Severity_Error); } else if (mSounds.searchId(soundGen.mSound) == -1) { - messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'")); + messages.add(id, "Sound '" + soundGen.mSound + "' doesn't exist", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/soundgencheck.hpp b/apps/opencs/model/tools/soundgencheck.hpp index 19388cb91..3c2a7f071 100644 --- a/apps/opencs/model/tools/soundgencheck.hpp +++ b/apps/opencs/model/tools/soundgencheck.hpp @@ -12,13 +12,13 @@ namespace CSMTools { const CSMWorld::IdCollection &mSoundGens; const CSMWorld::IdCollection &mSounds; - const CSMWorld::RefIdCollection &mReferenceables; + const CSMWorld::RefIdCollection &mObjects; bool mIgnoreBaseRecords; public: SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables); + const CSMWorld::RefIdCollection &objects); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/tools/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp index 3e59f0d9a..dc9ce65c0 100644 --- a/apps/opencs/model/tools/spellcheck.cpp +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -34,13 +34,13 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); - // test for empty name and description + // test for empty name if (spell.mName.empty()) - messages.push_back (std::make_pair (id, spell.mId + " has an empty name")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // test for invalid cost values if (spell.mData.mCost<0) - messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs")); + messages.add(id, "Spell cost is negative", "", CSMDoc::Message::Severity_Error); /// \todo check data members that can't be edited in the table view } diff --git a/apps/opencs/model/tools/startscriptcheck.cpp b/apps/opencs/model/tools/startscriptcheck.cpp index b1d92380b..deb7d384f 100644 --- a/apps/opencs/model/tools/startscriptcheck.cpp +++ b/apps/opencs/model/tools/startscriptcheck.cpp @@ -25,8 +25,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId); if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1) - messages.push_back ( - std::make_pair (id, "Start script " + scriptId + " does not exist")); + messages.add(id, "Start script " + scriptId + " does not exist", "", CSMDoc::Message::Severity_Error); } int CSMTools::StartScriptCheckStage::setup() diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 445db53af..07a721e8e 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -32,6 +32,7 @@ #include "gmstcheck.hpp" #include "topicinfocheck.hpp" #include "journalcheck.hpp" +#include "enchantmentcheck.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { @@ -74,15 +75,17 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces())); - mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds())); + mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds(), mData.getResources (CSMWorld::UniversalId::Type_SoundsRes))); mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions())); - mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); + mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns(), mData.getResources (CSMWorld::UniversalId::Type_Textures))); mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts())); + mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(), + mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons), + mData.getBodyParts())); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); @@ -126,6 +129,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos())); + mVerifierOperation->appendStage (new EnchantmentCheckStage(mData.getEnchantments())); + mVerifier.setOperation (mVerifierOperation); } diff --git a/apps/opencs/model/tools/topicinfocheck.cpp b/apps/opencs/model/tools/topicinfocheck.cpp index ac1f596ae..fe9bc991d 100644 --- a/apps/opencs/model/tools/topicinfocheck.cpp +++ b/apps/opencs/model/tools/topicinfocheck.cpp @@ -133,8 +133,7 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1) { - std::ostringstream stream; - messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error); } if (!topicInfo.mRace.empty()) @@ -166,23 +165,24 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Actor"; - CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor); if (index.first == -1) { - writeMissingIdError(specifier, actor, id, messages); + messages.add(id, "Actor '" + actor + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - writeDeletedRecordError(specifier, actor, id, messages); + messages.add(id, "Deleted actor '" + actor + "' is being referenced", "", CSMDoc::Message::Severity_Error); return false; } else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature) { - writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages); + CSMWorld::UniversalId tempId(index.second, actor); + std::ostringstream stream; + stream << "Object '" << actor << "' has invalid type " << tempId.getTypeName() << " (an actor must be an NPC or a creature)"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -192,11 +192,9 @@ bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Cell"; - if (mCellNames.find(cell) == mCellNames.end()) { - writeMissingIdError(specifier, cell, id, messages); + messages.add(id, "Cell '" + cell + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } @@ -209,9 +207,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction if (rank < -1) { std::ostringstream stream; - stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + stream << "Faction rank is set to " << rank << ", but it should be set to -1 if there are no rank requirements"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; } @@ -229,8 +226,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction if (rank >= limit) { std::ostringstream stream; - stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1 - << " for the " << factionName << " faction"; + stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1 + << " for the '" << factionName << "' faction"; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; @@ -242,18 +239,16 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Item"; - CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item); if (index.first == -1) { - writeMissingIdError(specifier, item, id, messages); + messages.add(id, ("Item '" + item + "' does not exist"), "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - writeDeletedRecordError(specifier, item, id, messages); + messages.add(id, ("Deleted item '" + item + "' is being referenced"), "", CSMDoc::Message::Severity_Error); return false; } else @@ -276,8 +271,13 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS break; default: - writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages); + { + CSMWorld::UniversalId tempId(index.second, item); + std::ostringstream stream; + stream << "Object '" << item << "' has invalid type " << tempId.getTypeName() << " (an item can be a potion, an armor piece, a book and so on)"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; + } } } @@ -291,13 +291,13 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None) { - messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error); return false; } else if (!infoCondition.variantTypeIsValid()) { std::ostringstream stream; - stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of "; + stream << "Value of condition '" << infoCondition.toString() << "' has invalid "; switch (select.mValue.getType()) { @@ -307,26 +307,21 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele case ESM::VT_Long: stream << "Long"; break; case ESM::VT_Float: stream << "Float"; break; case ESM::VT_String: stream << "String"; break; - default: stream << "Unknown"; break; + default: stream << "unknown"; break; } + stream << " type"; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (infoCondition.conditionIsAlwaysTrue()) { - std::ostringstream stream; - stream << "Info Condition: " << infoCondition.toString() << " is always true"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Condition '" + infoCondition.toString() + "' is always true", "", CSMDoc::Message::Severity_Warning); return false; } else if (infoCondition.conditionIsNeverTrue()) { - std::ostringstream stream; - stream << "Info Condition: " << infoCondition.toString() << " is never true"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Condition '" + infoCondition.toString() + "' is never true", "", CSMDoc::Message::Severity_Warning); return false; } @@ -383,11 +378,9 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Sound File"; - if (mSoundFiles.searchId(sound) == -1) { - writeMissingIdError(specifier, sound, id, messages); + messages.add(id, "Sound file '" + sound + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } @@ -402,47 +395,14 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW if (index == -1) { - writeMissingIdError(T::getRecordType(), name, id, messages); + messages.add(id, T::getRecordType() + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } else if (collection.getRecord(index).isDeleted()) { - writeDeletedRecordError(T::getRecordType(), name, id, messages); + messages.add(id, "Deleted " + T::getRecordType() + " record '" + name + "' is being referenced", "", CSMDoc::Message::Severity_Error); return false; } return true; } - -// Error functions - -void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) -{ - std::ostringstream stream; - stream << specifier << ": ID or name \"" << missingId << "\" could not be found"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} - -void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) -{ - std::ostringstream stream; - stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} - -void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId, - CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id, - CSMDoc::Messages& messages) -{ - CSMWorld::UniversalId tempId(invalidType, invalidId); - - std::ostringstream stream; - stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \"" - << invalidId << "\" (can be of type " << expectedType << ")"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} diff --git a/apps/opencs/model/tools/topicinfocheck.hpp b/apps/opencs/model/tools/topicinfocheck.hpp index dbd5fe1c5..9575181b0 100644 --- a/apps/opencs/model/tools/topicinfocheck.hpp +++ b/apps/opencs/model/tools/topicinfocheck.hpp @@ -80,17 +80,6 @@ namespace CSMTools template bool verifyId(const std::string& name, const CSMWorld::IdCollection& collection, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - // Common error messages - void writeMissingIdError(const std::string& specifier, const std::string& missingId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - void writeDeletedRecordError(const std::string& specifier, const std::string& recordId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId, - CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); }; } diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp new file mode 100644 index 000000000..ea40a1f8d --- /dev/null +++ b/apps/opencs/model/world/actoradapter.cpp @@ -0,0 +1,686 @@ +#include "actoradapter.hpp" + +#include +#include +#include +#include +#include +#include + +#include "data.hpp" + +namespace CSMWorld +{ + const std::string& ActorAdapter::RaceData::getId() const + { + return mId; + } + + bool ActorAdapter::RaceData::isBeast() const + { + return mIsBeast; + } + + bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const + { + switch (type) + { + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + return false; + default: + return true; + } + } + + const std::string& ActorAdapter::RaceData::getFemalePart(ESM::PartReferenceType index) const + { + return mFemaleParts[ESM::getMeshPart(index)]; + } + + const std::string& ActorAdapter::RaceData::getMalePart(ESM::PartReferenceType index) const + { + return mMaleParts[ESM::getMeshPart(index)]; + } + + bool ActorAdapter::RaceData::hasDependency(const std::string& id) const + { + return mDependencies.find(id) != mDependencies.end(); + } + + void ActorAdapter::RaceData::setFemalePart(ESM::BodyPart::MeshPart index, const std::string& partId) + { + mFemaleParts[index] = partId; + addOtherDependency(partId); + } + + void ActorAdapter::RaceData::setMalePart(ESM::BodyPart::MeshPart index, const std::string& partId) + { + mMaleParts[index] = partId; + addOtherDependency(partId); + } + + void ActorAdapter::RaceData::addOtherDependency(const std::string& id) + { + if (!id.empty()) mDependencies.emplace(id); + } + + void ActorAdapter::RaceData::reset_data(const std::string& id, bool isBeast) + { + mId = id; + mIsBeast = isBeast; + for (auto& str : mFemaleParts) + str.clear(); + for (auto& str : mMaleParts) + str.clear(); + mDependencies.clear(); + + // Mark self as a dependency + addOtherDependency(id); + } + + + const std::string& ActorAdapter::ActorData::getId() const + { + return mId; + } + + bool ActorAdapter::ActorData::isCreature() const + { + return mCreature; + } + + bool ActorAdapter::ActorData::isFemale() const + { + return mFemale; + } + + std::string ActorAdapter::ActorData::getSkeleton() const + { + if (mCreature || !mSkeletonOverride.empty()) + return "meshes\\" + mSkeletonOverride; + + bool firstPerson = false; + bool beast = mRaceData ? mRaceData->isBeast() : false; + bool werewolf = false; + + return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf); + } + + const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const + { + auto it = mParts.find(index); + if (it == mParts.end() && mRaceData && mRaceData->handlesPart(index)) + { + if (mFemale) + { + // Note: we should use male parts for females as fallback + const std::string femalePart = mRaceData->getFemalePart(index); + if (!femalePart.empty()) + return femalePart; + } + + return mRaceData->getMalePart(index); + } + + const std::string& partName = it->second.first; + return partName; + } + + bool ActorAdapter::ActorData::hasDependency(const std::string& id) const + { + return mDependencies.find(id) != mDependencies.end(); + } + + void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId, int priority) + { + auto it = mParts.find(index); + if (it != mParts.end()) + { + if (it->second.second >= priority) + return; + } + + mParts[index] = std::make_pair(partId, priority); + addOtherDependency(partId); + } + + void ActorAdapter::ActorData::addOtherDependency(const std::string& id) + { + if (!id.empty()) mDependencies.emplace(id); + } + + void ActorAdapter::ActorData::reset_data(const std::string& id, const std::string& skeleton, bool isCreature, bool isFemale, RaceDataPtr raceData) + { + mId = id; + mCreature = isCreature; + mFemale = isFemale; + mSkeletonOverride = skeleton; + mRaceData = raceData; + mParts.clear(); + mDependencies.clear(); + + // Mark self and race as a dependency + addOtherDependency(id); + if (raceData) addOtherDependency(raceData->getId()); + } + + + ActorAdapter::ActorAdapter(Data& data) + : mReferenceables(data.getReferenceables()) + , mRaces(data.getRaces()) + , mBodyParts(data.getBodyParts()) + { + // Setup qt slots and signals + QAbstractItemModel* refModel = data.getTableModel(UniversalId::Type_Referenceable); + connect(refModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleReferenceablesInserted(const QModelIndex&, int, int))); + connect(refModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleReferenceableChanged(const QModelIndex&, const QModelIndex&))); + connect(refModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int))); + + QAbstractItemModel* raceModel = data.getTableModel(UniversalId::Type_Race); + connect(raceModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int))); + connect(raceModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleRaceChanged(const QModelIndex&, const QModelIndex&))); + connect(raceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int))); + + QAbstractItemModel* partModel = data.getTableModel(UniversalId::Type_BodyPart); + connect(partModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleBodyPartsInserted(const QModelIndex&, int, int))); + connect(partModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&))); + connect(partModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int))); + } + + ActorAdapter::ActorDataPtr ActorAdapter::getActorData(const std::string& id) + { + // Return cached actor data if it exists + ActorDataPtr data = mCachedActors.get(id); + if (data) + { + return data; + } + + // Create the actor data + data.reset(new ActorData()); + setupActor(id, data); + mCachedActors.insert(id, data); + return data; + } + + void ActorAdapter::handleReferenceablesInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top level are pertinent. Others are caught by dataChanged handler. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string refId = mReferenceables.getId(row); + markDirtyDependency(refId); + } + } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); + + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + + // Handle each record + for (int row = start; row <= end; ++row) + { + std::string refId = mReferenceables.getId(row); + markDirtyDependency(refId); + } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleReferenceablesAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only rows at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string refId = mReferenceables.getId(row); + markDirtyDependency(refId); + } + } + } + + void ActorAdapter::handleReferenceablesRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleReferenceablesAboutToBeRemoved + updateDirty(); + } + + void ActorAdapter::handleRacesInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string raceId = mReferenceables.getId(row); + markDirtyDependency(raceId); + } + } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); + + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + + for (int row = start; row <= end; ++row) + { + std::string raceId = mRaces.getId(row); + markDirtyDependency(raceId); + } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleRacesAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only changes at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string raceId = mRaces.getId(row); + markDirtyDependency(raceId); + } + } + } + + void ActorAdapter::handleRacesRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleRacesAboutToBeRemoved + updateDirty(); + } + + void ActorAdapter::handleBodyPartsInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + // Race specified by part may need update + auto& record = mBodyParts.getRecord(row); + if (!record.isDeleted()) + { + markDirtyDependency(record.get().mRace); + } + + std::string partId = mBodyParts.getId(row); + markDirtyDependency(partId); + } + } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); + + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + + for (int row = start; row <= end; ++row) + { + // Race specified by part may need update + auto& record = mBodyParts.getRecord(row); + if (!record.isDeleted()) + { + markDirtyDependency(record.get().mRace); + } + + // Update entries with a tracked dependency + std::string partId = mBodyParts.getId(row); + markDirtyDependency(partId); + } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleBodyPartsAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only changes at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string partId = mBodyParts.getId(row); + markDirtyDependency(partId); + } + } + } + + void ActorAdapter::handleBodyPartsRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleBodyPartsAboutToBeRemoved + updateDirty(); + } + + QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const + { + while (index.parent().isValid()) + index = index.parent(); + return index; + } + + bool ActorAdapter::is1stPersonPart(const std::string& name) const + { + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + } + + ActorAdapter::RaceDataPtr ActorAdapter::getRaceData(const std::string& id) + { + // Return cached race data if it exists + RaceDataPtr data = mCachedRaces.get(id); + if (data) return data; + + // Create the race data + data.reset(new RaceData()); + setupRace(id, data); + mCachedRaces.insert(id, data); + return data; + } + + void ActorAdapter::setupActor(const std::string& id, ActorDataPtr data) + { + int index = mReferenceables.searchId(id); + if (index == -1) + { + // Record does not exist + data->reset_data(id); + emit actorChanged(id); + return; + } + + auto& record = mReferenceables.getRecord(index); + if (record.isDeleted()) + { + // Record is deleted and therefore not accessible + data->reset_data(id); + emit actorChanged(id); + return; + } + + const int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType); + int type = mReferenceables.getData(index, TypeColumn).toInt(); + if (type == UniversalId::Type_Creature) + { + // Valid creature record + setupCreature(id, data); + emit actorChanged(id); + } + else if (type == UniversalId::Type_Npc) + { + // Valid npc record + setupNpc(id, data); + emit actorChanged(id); + } + else + { + // Wrong record type + data->reset_data(id); + emit actorChanged(id); + } + } + + void ActorAdapter::setupRace(const std::string& id, RaceDataPtr data) + { + int index = mRaces.searchId(id); + if (index == -1) + { + // Record does not exist + data->reset_data(id); + return; + } + + auto& raceRecord = mRaces.getRecord(index); + if (raceRecord.isDeleted()) + { + // Record is deleted, so not accessible + data->reset_data(id); + return; + } + + auto& race = raceRecord.get(); + data->reset_data(id, race.mData.mFlags & ESM::Race::Beast); + + // Setup body parts + for (int i = 0; i < mBodyParts.getSize(); ++i) + { + std::string partId = mBodyParts.getId(i); + auto& partRecord = mBodyParts.getRecord(i); + + if (partRecord.isDeleted()) + { + // Record is deleted, so not accessible. + continue; + } + + auto& part = partRecord.get(); + if (part.mRace == id && part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId)) + { + auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; + bool female = part.mData.mFlags & ESM::BodyPart::BPF_Female; + if (female) data->setFemalePart(type, part.mId); + else data->setMalePart(type, part.mId); + } + } + } + + void ActorAdapter::setupNpc(const std::string& id, ActorDataPtr data) + { + // Common setup, record is known to exist and is not deleted + int index = mReferenceables.searchId(id); + auto& npc = dynamic_cast&>(mReferenceables.getRecord(index)).get(); + + RaceDataPtr raceData = getRaceData(npc.mRace); + data->reset_data(id, "", false, !npc.isMale(), raceData); + + // Add inventory items + for (auto& item : npc.mInventory.mList) + { + if (item.mCount <= 0) continue; + std::string itemId = item.mItem.toString(); + addNpcItem(itemId, data); + } + + // Add head and hair + data->setPart(ESM::PRT_Head, npc.mHead, 0); + data->setPart(ESM::PRT_Hair, npc.mHair, 0); + } + + void ActorAdapter::addNpcItem(const std::string& itemId, ActorDataPtr data) + { + int index = mReferenceables.searchId(itemId); + if (index == -1) + { + // Item does not exist yet + data->addOtherDependency(itemId); + return; + } + + auto& record = mReferenceables.getRecord(index); + if (record.isDeleted()) + { + // Item cannot be accessed yet + data->addOtherDependency(itemId); + return; + } + + // Convenience function to add a parts list to actor data + auto addParts = [&](const std::vector& list, int priority) { + for (auto& part : list) + { + std::string partId; + auto partType = (ESM::PartReferenceType) part.mPart; + + if (data->isFemale()) + partId = part.mFemale; + if (partId.empty()) + partId = part.mMale; + + data->setPart(partType, partId, priority); + } + }; + + int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType); + int type = mReferenceables.getData(index, TypeColumn).toInt(); + if (type == UniversalId::Type_Armor) + { + auto& armor = dynamic_cast&>(record).get(); + addParts(armor.mParts.mParts, 1); + + // Changing parts could affect what is picked for rendering + data->addOtherDependency(itemId); + } + else if (type == UniversalId::Type_Clothing) + { + int priority = 0; + // TODO: reserve bodyparts for robes and skirts + auto& clothing = dynamic_cast&>(record).get(); + + if (clothing.mData.mType == ESM::Clothing::Robe) + { + auto reservedList = std::vector(); + + ESM::PartReference pr; + pr.mMale = ""; + pr.mFemale = ""; + + ESM::PartReferenceType parts[] = { + ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, + ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, + ESM::PRT_RForearm, ESM::PRT_LForearm + }; + size_t parts_size = sizeof(parts)/sizeof(parts[0]); + for(size_t p = 0;p < parts_size;++p) + { + pr.mPart = parts[p]; + reservedList.push_back(pr); + } + + priority = parts_size; + addParts(reservedList, priority); + } + else if (clothing.mData.mType == ESM::Clothing::Skirt) + { + auto reservedList = std::vector(); + + ESM::PartReference pr; + pr.mMale = ""; + pr.mFemale = ""; + + ESM::PartReferenceType parts[] = { + ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg + }; + size_t parts_size = sizeof(parts)/sizeof(parts[0]); + for(size_t p = 0;p < parts_size;++p) + { + pr.mPart = parts[p]; + reservedList.push_back(pr); + } + + priority = parts_size; + addParts(reservedList, priority); + } + + addParts(clothing.mParts.mParts, priority); + + // Changing parts could affect what is picked for rendering + data->addOtherDependency(itemId); + } + } + + void ActorAdapter::setupCreature(const std::string& id, ActorDataPtr data) + { + // Record is known to exist and is not deleted + int index = mReferenceables.searchId(id); + auto& creature = dynamic_cast&>(mReferenceables.getRecord(index)).get(); + + data->reset_data(id, creature.mModel, true); + } + + void ActorAdapter::markDirtyDependency(const std::string& dep) + { + for (auto raceIt : mCachedRaces) + { + if (raceIt->hasDependency(dep)) + mDirtyRaces.emplace(raceIt->getId()); + } + for (auto actorIt : mCachedActors) + { + if (actorIt->hasDependency(dep)) + mDirtyActors.emplace(actorIt->getId()); + } + } + + void ActorAdapter::updateDirty() + { + // Handle races before actors, since actors are dependent on race + for (auto& race : mDirtyRaces) + { + RaceDataPtr data = mCachedRaces.get(race); + if (data) + { + setupRace(race, data); + // Race was changed. Need to mark actor dependencies as dirty. + // Cannot use markDirtyDependency because that would invalidate + // the current iterator. + for (auto actorIt : mCachedActors) + { + if (actorIt->hasDependency(race)) + mDirtyActors.emplace(actorIt->getId()); + } + } + } + mDirtyRaces.clear(); + + for (auto& actor : mDirtyActors) + { + ActorDataPtr data = mCachedActors.get(actor); + if (data) + { + setupActor(actor, data); + } + } + mDirtyActors.clear(); + } +} diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp new file mode 100644 index 000000000..1c9265be4 --- /dev/null +++ b/apps/opencs/model/world/actoradapter.hpp @@ -0,0 +1,174 @@ +#ifndef CSM_WOLRD_ACTORADAPTER_H +#define CSM_WOLRD_ACTORADAPTER_H + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "refidcollection.hpp" +#include "idcollection.hpp" + +namespace ESM +{ + struct Race; +} + +namespace CSMWorld +{ + class Data; + + /// Adapts multiple collections to provide the data needed to render + /// an npc or creature. + class ActorAdapter : public QObject + { + Q_OBJECT + public: + + /// A list indexed by ESM::PartReferenceType + using ActorPartList = std::map>; + /// A list indexed by ESM::BodyPart::MeshPart + using RacePartList = std::array; + /// Tracks unique strings + using StringSet = std::unordered_set; + + + /// Contains base race data shared between actors + class RaceData + { + public: + /// Retrieves the id of the race represented + const std::string& getId() const; + /// Checks if it's a beast race + bool isBeast() const; + /// Checks if a part could exist for the given type + bool handlesPart(ESM::PartReferenceType type) const; + /// Retrieves the associated body part + const std::string& getFemalePart(ESM::PartReferenceType index) const; + /// Retrieves the associated body part + const std::string& getMalePart(ESM::PartReferenceType index) const; + /// Checks if the race has a data dependency + bool hasDependency(const std::string& id) const; + + /// Sets the associated part if it's empty and marks a dependency + void setFemalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId); + /// Sets the associated part if it's empty and marks a dependency + void setMalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId); + /// Marks an additional dependency + void addOtherDependency(const std::string& id); + /// Clears parts and dependencies + void reset_data(const std::string& raceId, bool isBeast=false); + + private: + bool handles(ESM::PartReferenceType type) const; + std::string mId; + bool mIsBeast; + RacePartList mFemaleParts; + RacePartList mMaleParts; + StringSet mDependencies; + }; + using RaceDataPtr = std::shared_ptr; + + /// Contains all the data needed to render an actor. Tracks dependencies + /// so that pertinent data changes can be checked. + class ActorData + { + public: + /// Retrieves the id of the actor represented + const std::string& getId() const; + /// Checks if the actor is a creature + bool isCreature() const; + /// Checks if the actor is female + bool isFemale() const; + /// Returns the skeleton the actor should use for attaching parts to + std::string getSkeleton() const; + /// Retrieves the associated actor part + const std::string getPart(ESM::PartReferenceType index) const; + /// Checks if the actor has a data dependency + bool hasDependency(const std::string& id) const; + + /// Sets the actor part used and marks a dependency + void setPart(ESM::PartReferenceType partIndex, const std::string& partId, int priority); + /// Marks an additional dependency for the actor + void addOtherDependency(const std::string& id); + /// Clears race, parts, and dependencies + void reset_data(const std::string& actorId, const std::string& skeleton="", bool isCreature=false, bool female=true, RaceDataPtr raceData=nullptr); + + private: + std::string mId; + bool mCreature; + bool mFemale; + std::string mSkeletonOverride; + RaceDataPtr mRaceData; + ActorPartList mParts; + StringSet mDependencies; + }; + using ActorDataPtr = std::shared_ptr; + + + ActorAdapter(Data& data); + + /// Obtains the shared data for a given actor + ActorDataPtr getActorData(const std::string& refId); + + signals: + + void actorChanged(const std::string& refId); + + public slots: + + void handleReferenceablesInserted(const QModelIndex&, int, int); + void handleReferenceableChanged(const QModelIndex&, const QModelIndex&); + void handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int); + void handleReferenceablesRemoved(const QModelIndex&, int, int); + + void handleRacesInserted(const QModelIndex&, int, int); + void handleRaceChanged(const QModelIndex&, const QModelIndex&); + void handleRacesAboutToBeRemoved(const QModelIndex&, int, int); + void handleRacesRemoved(const QModelIndex&, int, int); + + void handleBodyPartsInserted(const QModelIndex&, int, int); + void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); + void handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int); + void handleBodyPartsRemoved(const QModelIndex&, int, int); + + private: + + ActorAdapter(const ActorAdapter&) = delete; + ActorAdapter& operator=(const ActorAdapter&) = delete; + + QModelIndex getHighestIndex(QModelIndex) const; + bool is1stPersonPart(const std::string& id) const; + + RaceDataPtr getRaceData(const std::string& raceId); + + void setupActor(const std::string& id, ActorDataPtr data); + void setupRace(const std::string& id, RaceDataPtr data); + + void setupNpc(const std::string& id, ActorDataPtr data); + void addNpcItem(const std::string& itemId, ActorDataPtr data); + + void setupCreature(const std::string& id, ActorDataPtr data); + + void markDirtyDependency(const std::string& dependency); + void updateDirty(); + + RefIdCollection& mReferenceables; + IdCollection& mRaces; + IdCollection& mBodyParts; + + Misc::WeakCache mCachedActors; // Key: referenceable id + Misc::WeakCache mCachedRaces; // Key: race id + + StringSet mDirtyActors; // Actors that need updating + StringSet mDirtyRaces; // Races that need updating + }; +} + +#endif diff --git a/apps/opencs/model/world/cellcoordinates.cpp b/apps/opencs/model/world/cellcoordinates.cpp index abb3bc82e..dbe90b906 100644 --- a/apps/opencs/model/world/cellcoordinates.cpp +++ b/apps/opencs/model/world/cellcoordinates.cpp @@ -5,6 +5,8 @@ #include #include +#include + CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} @@ -61,9 +63,7 @@ std::pair CSMWorld::CellCoordinates::fromId ( std::pair CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y) { - const int cellSize = 8192; - - return std::make_pair (std::floor (x/cellSize), std::floor (y/cellSize)); + return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits)); } bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 49e4bebe6..b202a97d9 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -318,7 +318,7 @@ namespace CSMWorld QVariant BodyPartRaceColumn::get(const Record &record) const { - if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) + if (mMeshType != nullptr && mMeshType->get(record) == ESM::BodyPart::MT_Skin) { return QString::fromUtf8(record.get().mRace.c_str()); } diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 79900c6c4..4c7096479 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -244,7 +244,7 @@ void CSMWorld::CreateCommand::applyModifications() if (!mNestedValues.empty()) { CSMWorld::IdTree *tree = dynamic_cast(&mModel); - if (tree == NULL) + if (tree == nullptr) { throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model"); } @@ -445,16 +445,14 @@ void CSMWorld::UpdateCellCommand::redo() int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell); mIndex = mModel.index (mRow, cellColumn); - const int cellSize = 8192; - QModelIndex xIndex = mModel.index ( mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos)); QModelIndex yIndex = mModel.index ( mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos)); - int x = std::floor (mModel.data (xIndex).toFloat() / cellSize); - int y = std::floor (mModel.data (yIndex).toFloat() / cellSize); + int x = std::floor (mModel.data (xIndex).toFloat() / Constants::CellSizeInUnits); + int y = std::floor (mModel.data (yIndex).toFloat() / Constants::CellSizeInUnits); std::ostringstream stream; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 053754943..f3f897a29 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -572,6 +572,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat UniversalId::Type_Video); addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData); + mActorAdapter.reset(new ActorAdapter(*this)); + mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files } @@ -912,6 +914,16 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& return iter->second; } +const CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() const +{ + return mActorAdapter.get(); +} + +CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() +{ + return mActorAdapter.get(); +} + void CSMWorld::Data::merge() { mGlobals.merge(); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 1b975f430..e50780f50 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -36,6 +36,7 @@ #include "../doc/stage.hpp" +#include "actoradapter.hpp" #include "idcollection.hpp" #include "nestedidcollection.hpp" #include "universalid.hpp" @@ -110,6 +111,7 @@ namespace CSMWorld RefCollection mRefs; IdCollection mFilters; Collection mMetaData; + std::unique_ptr mActorAdapter; const Fallback::Map* mFallbackMap; std::vector mModels; std::map mModelIndex; @@ -287,6 +289,10 @@ namespace CSMWorld /// \note The returned table may either be the model for the ID itself or the model that /// contains the record specified by the ID. + const ActorAdapter* getActorAdapter() const; + + ActorAdapter* getActorAdapter(); + void merge(); ///< Merge modified into base. diff --git a/apps/opencs/model/world/idcompletionmanager.cpp b/apps/opencs/model/world/idcompletionmanager.cpp index 9fa6e3add..649a96038 100644 --- a/apps/opencs/model/world/idcompletionmanager.cpp +++ b/apps/opencs/model/world/idcompletionmanager.cpp @@ -92,7 +92,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data) { QAbstractItemModel *model = data.getTableModel(current->second); CSMWorld::IdTableBase *table = dynamic_cast(model); - if (table != NULL) + if (table != nullptr) { int idColumn = table->searchColumnIndex(CSMWorld::Columns::ColumnId_Id); if (idColumn != -1) diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 6d50e9edb..534e5dd63 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -18,7 +18,7 @@ namespace void CSMWorld::IdTableProxyModel::updateColumnMap() { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); mColumnMap.clear(); if (mFilter) @@ -33,7 +33,7 @@ void CSMWorld::IdTableProxyModel::updateColumnMap() bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); // It is not possible to use filterAcceptsColumn() and check for // sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags) @@ -51,14 +51,14 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent), - mSourceModel(NULL) + mSourceModel(nullptr) { setSortCaseSensitivity (Qt::CaseInsensitive); } QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); return mapFromSource(mSourceModel->getModelIndex (id, column)); } @@ -113,7 +113,7 @@ bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModel QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id); return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString(); diff --git a/apps/opencs/model/world/infotableproxymodel.cpp b/apps/opencs/model/world/infotableproxymodel.cpp index 4f0b2e752..7c6a9c323 100644 --- a/apps/opencs/model/world/infotableproxymodel.cpp +++ b/apps/opencs/model/world/infotableproxymodel.cpp @@ -28,7 +28,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod { IdTableProxyModel::setSourceModel(sourceModel); - if (mSourceModel != NULL) + if (mSourceModel != nullptr) { mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId); mFirstRowCache.clear(); @@ -37,7 +37,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column()); QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column()); @@ -52,7 +52,7 @@ bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QMod int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); int row = currentRow; int column = mInfoColumnIndex; diff --git a/apps/opencs/model/world/metadata.cpp b/apps/opencs/model/world/metadata.cpp index 960fdc9e4..b2fa3487c 100644 --- a/apps/opencs/model/world/metadata.cpp +++ b/apps/opencs/model/world/metadata.cpp @@ -14,8 +14,8 @@ void CSMWorld::MetaData::blank() void CSMWorld::MetaData::load (ESM::ESMReader& esm) { mFormat = esm.getHeader().mFormat; - mAuthor = esm.getHeader().mData.author.toString(); - mDescription = esm.getHeader().mData.desc.toString(); + mAuthor = esm.getHeader().mData.author; + mDescription = esm.getHeader().mData.desc; } void CSMWorld::MetaData::save (ESM::ESMWriter& esm) const diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 5ac9ecb18..1948a95cb 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -453,13 +453,13 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), - mType(NULL), - mScale(NULL), - mOriginal(NULL), - mAttributes(NULL), - mAttacks(NULL), - mMisc(NULL), - mBloodType(NULL) + mType(nullptr), + mScale(nullptr), + mOriginal(nullptr), + mAttributes(nullptr), + mAttacks(nullptr), + mMisc(nullptr), + mBloodType(nullptr) {} CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) @@ -748,16 +748,16 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), - mRace(NULL), - mClass(NULL), - mFaction(NULL), - mHair(NULL), - mHead(NULL), - mAttributes(NULL), - mSkills(NULL), - mMisc(NULL), - mBloodType(NULL), - mGender(NULL) + mRace(nullptr), + mClass(nullptr), + mFaction(nullptr), + mHair(nullptr), + mHead(nullptr), + mAttributes(nullptr), + mSkills(nullptr), + mMisc(nullptr), + mBloodType(nullptr), + mGender(nullptr) {} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 6716c7240..272722f64 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -437,7 +437,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer)); lightColumns.mRadius = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Colour)); lightColumns.mColor = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound)); diff --git a/apps/opencs/model/world/resourcesmanager.cpp b/apps/opencs/model/world/resourcesmanager.cpp index 62dfe53a9..1af9c5e9b 100644 --- a/apps/opencs/model/world/resourcesmanager.cpp +++ b/apps/opencs/model/world/resourcesmanager.cpp @@ -3,7 +3,7 @@ #include CSMWorld::ResourcesManager::ResourcesManager() - : mVFS(NULL) + : mVFS(nullptr) { } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 8d7a7761e..486f3770a 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -18,47 +18,43 @@ namespace static const TypeData sNoArg[] = { { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, - "Objects", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, - "Instances", 0 }, - { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, - "Region Map", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, - + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", ":./global-variable.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", ":./skill.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", ":./class.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", ":./faction.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", ":./race.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", ":./sound.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", ":./script.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", ":./region.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", ":./birthsign.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", ":./spell.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", ":./dialogue-topics.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", ":./journal-topics.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", ":./dialogue-topic-infos.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", ":./journal-topic-infos.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", ":./cell.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", ":./enchantment.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", ":./body-part.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects", ":./object.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances", ":./instance.png" }, + { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", ":./region-map.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", ":./filter.png" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", ":./resources-mesh" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", ":./resources-icon" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", ":./resources-music" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", ":resources-sound" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", ":./resources-texture" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":./resources-video" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", ":./debug-profile.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", ":./sound-generator.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", ":./magic-effect.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":./land-heightmap.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "Land Textures", ":./land-texture.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":./pathgrid.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", ":./start-script.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":./metadata.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; @@ -81,7 +77,7 @@ namespace { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", ":./journal-topic-infos.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", ":./object.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, @@ -93,9 +89,9 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, - "Creature Levelled List", ":./leveled-creature.png" }, + "Creature Levelled List", ":./levelled-creature.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, - "Item Levelled List", ":./leveled-item.png" }, + "Item Levelled List", ":./levelled-item.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, @@ -105,35 +101,35 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, - { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", 0 }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", ":./instance.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, - { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, - { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", ":./scene.png" }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", ":./record-preview.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", ":./enchantment.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":./body-part.png" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":resources-mesh"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":resources-icon"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":resources-music" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":resources-sound" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":resources-texture"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":resources-video"}, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":./resources-mesh"}, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":./resources-icon"}, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":./resources-music" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":./resources-sound" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":./resources-texture" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":./resources-video" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", ":./debug-profile.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", ":./sound-generator.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", ":./magic-effect.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", ":./land-heightmap.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", ":./land-texture.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "Land Texture", ":./land-texture.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", ":./pathgrid.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", ":./start-script.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", 0 }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", ":./menu-verify.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", ":./error-log.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", ":./menu-search.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; } diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index b6f4aaec3..7693276c6 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -33,6 +33,11 @@ void CSVDoc::FileDialog::addFiles(const QString &path) mSelector->addFiles(path); } +void CSVDoc::FileDialog::setEncoding(const QString &encoding) +{ + mSelector->setEncoding(encoding); +} + void CSVDoc::FileDialog::clearFiles() { mSelector->clearFiles(); @@ -184,7 +189,7 @@ void CSVDoc::FileDialog::slotRejected() if(mFileWidget) { delete mFileWidget; - mFileWidget = NULL; + mFileWidget = nullptr; } close(); } @@ -195,7 +200,7 @@ void CSVDoc::FileDialog::slotNewFile() if(mFileWidget) { delete mFileWidget; - mFileWidget = NULL; + mFileWidget = nullptr; } disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); close(); diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index ca6145b9c..69acfac3d 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -42,6 +42,7 @@ namespace CSVDoc void showDialog (ContentAction action); void addFiles (const QString &path); + void setEncoding (const QString &encoding); void clearFiles (); QString filename() const; diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 8402bf79a..44b81743f 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -64,7 +64,7 @@ namespace CSVDoc void updateTitle(); - void updateSubViewIndices (SubView *view = NULL); + void updateSubViewIndices (SubView *view = nullptr); void universalIdChanged (const CSMWorld::UniversalId& universalId); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 3ebefd69a..ed4dcff76 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -47,58 +47,40 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("File")); - QAction *newGame = new QAction (tr ("New Game"), this); + QAction* newGame = createMenuEntry("New Game", ":./menu-new-game.png", file, "document-file-newgame"); connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest())); - setupShortcut("document-file-newgame", newGame); - file->addAction (newGame); - - QAction *newAddon = new QAction (tr ("New Addon"), this); + QAction* newAddon = createMenuEntry("New Addon", ":./menu-new-addon.png", file, "document-file-newaddon"); connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); - setupShortcut("document-file-newaddon", newAddon); - file->addAction (newAddon); - QAction *open = new QAction (tr ("Open"), this); + QAction* open = createMenuEntry("Open", ":./menu-open.png", file, "document-file-open"); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); - setupShortcut("document-file-open", open); - file->addAction (open); - mSave = new QAction (tr ("Save"), this); - connect (mSave, SIGNAL (triggered()), this, SLOT (save())); - setupShortcut("document-file-save", mSave); - file->addAction (mSave); + QAction* save = createMenuEntry("Save", ":./menu-save.png", file, "document-file-save"); + connect (save, SIGNAL (triggered()), this, SLOT (save())); + mSave = save; - mVerify = new QAction (tr ("Verify"), this); - connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); - setupShortcut("document-file-verify", mVerify); - file->addAction (mVerify); + QAction* verify = createMenuEntry("Verify", ":./menu-verify.png", file, "document-file-verify"); + connect (verify, SIGNAL (triggered()), this, SLOT (verify())); + mVerify = verify; - mMerge = new QAction (tr ("Merge"), this); - connect (mMerge, SIGNAL (triggered()), this, SLOT (merge())); - setupShortcut("document-file-merge", mMerge); - file->addAction (mMerge); + QAction* merge = createMenuEntry("Merge", ":./menu-merge.png", file, "document-file-merge"); + connect (merge, SIGNAL (triggered()), this, SLOT (merge())); + mMerge = merge; - QAction *loadErrors = new QAction (tr ("Open Load Error Log"), this); + QAction* loadErrors = createMenuEntry("Error Log", ":./error-log.png", file, "document-file-errorlog"); connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog())); - setupShortcut("document-file-errorlog", loadErrors); - file->addAction (loadErrors); - QAction *meta = new QAction (tr ("Meta Data"), this); + QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata"); connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView())); - setupShortcut("document-file-metadata", meta); - file->addAction (meta); - QAction *close = new QAction (tr ("Close Document"), this); + QAction* close = createMenuEntry("Close", ":./menu-close.png", file, "document-file-close"); connect (close, SIGNAL (triggered()), this, SLOT (close())); - setupShortcut("document-file-close", close); - file->addAction(close); - QAction *exit = new QAction (tr ("Exit Application"), this); + QAction* exit = createMenuEntry("Exit", ":./menu-exit.png", file, "document-file-exit"); connect (exit, SIGNAL (triggered()), this, SLOT (exit())); - connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); - setupShortcut("document-file-exit", exit); - file->addAction(exit); + connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); } namespace @@ -130,251 +112,174 @@ void CSVDoc::View::setupEditMenu() mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo")); setupShortcut("document-edit-undo", mUndo); connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ())); + mUndo->setIcon(QIcon(QString::fromStdString(":./menu-undo.png"))); edit->addAction (mUndo); mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo")); connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ())); setupShortcut("document-edit-redo", mRedo); + mRedo->setIcon(QIcon(QString::fromStdString(":./menu-redo.png"))); edit->addAction (mRedo); - QAction *userSettings = new QAction (tr ("Preferences"), this); + QAction* userSettings = createMenuEntry("Preferences", ":./menu-preferences.png", edit, "document-edit-preferences"); connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest())); - setupShortcut("document-edit-preferences", userSettings); - edit->addAction (userSettings); - QAction *search = new QAction (tr ("Search"), this); + QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search"); connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView())); - setupShortcut("document-edit-search", search); - edit->addAction (search); } void CSVDoc::View::setupViewMenu() { QMenu *view = menuBar()->addMenu (tr ("View")); - QAction *newWindow = new QAction (tr ("New View"), this); + QAction *newWindow = createMenuEntry("New View", ":./menu-new-window.png", view, "document-view-newview"); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); - setupShortcut("document-view-newview", newWindow); - view->addAction (newWindow); - mShowStatusBar = new QAction (tr ("Toggle Status Bar"), this); - mShowStatusBar->setCheckable (true); + mShowStatusBar = createMenuEntry("Toggle Status Bar", ":./menu-status-bar.png", view, "document-view-statusbar"); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); - setupShortcut("document-view-statusbar", mShowStatusBar); - + mShowStatusBar->setCheckable (true); mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); view->addAction (mShowStatusBar); - QAction *filters = new QAction (tr ("Filters"), this); + QAction *filters = createMenuEntry(CSMWorld::UniversalId::Type_Filters, view, "document-mechanics-filters"); connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView())); - setupShortcut("document-view-filters", filters); - view->addAction (filters); } void CSVDoc::View::setupWorldMenu() { QMenu *world = menuBar()->addMenu (tr ("World")); - QAction *regions = new QAction (tr ("Regions"), this); + QAction* regions = createMenuEntry(CSMWorld::UniversalId::Type_Regions, world, "document-world-regions"); connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView())); - setupShortcut("document-world-regions", regions); - world->addAction (regions); - QAction *cells = new QAction (tr ("Cells"), this); + QAction* cells = createMenuEntry(CSMWorld::UniversalId::Type_Cells, world, "document-world-cells"); connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); - setupShortcut("document-world-cells", cells); - world->addAction (cells); - QAction *referenceables = new QAction (tr ("Objects"), this); + QAction* referenceables = createMenuEntry(CSMWorld::UniversalId::Type_Referenceables, world, "document-world-referencables"); connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); - setupShortcut("document-world-referencables", referenceables); - world->addAction (referenceables); - QAction *references = new QAction (tr ("Instances"), this); + QAction* references = createMenuEntry(CSMWorld::UniversalId::Type_References, world, "document-world-references"); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); - setupShortcut("document-world-references", references); - world->addAction (references); - QAction *lands = new QAction (tr ("Lands"), this); + QAction *lands = createMenuEntry(CSMWorld::UniversalId::Type_Lands, world, "document-world-lands"); connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView())); - setupShortcut("document-world-lands", lands); - world->addAction (lands); - QAction *landTextures = new QAction (tr ("Land Textures"), this); + QAction *landTextures = createMenuEntry(CSMWorld::UniversalId::Type_LandTextures, world, "document-world-landtextures"); connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView())); - setupShortcut("document-world-landtextures", landTextures); - world->addAction (landTextures); - QAction *grid = new QAction (tr ("Pathgrid"), this); + QAction *grid = createMenuEntry(CSMWorld::UniversalId::Type_Pathgrids, world, "document-world-pathgrid"); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); - setupShortcut("document-world-pathgrid", grid); - world->addAction (grid); world->addSeparator(); // items that don't represent single record lists follow here - QAction *regionMap = new QAction (tr ("Region Map"), this); + QAction *regionMap = createMenuEntry(CSMWorld::UniversalId::Type_RegionMap, world, "document-world-regionmap"); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); - setupShortcut("document-world-regionmap", regionMap); - world->addAction (regionMap); } void CSVDoc::View::setupMechanicsMenu() { QMenu *mechanics = menuBar()->addMenu (tr ("Mechanics")); - QAction *globals = new QAction (tr ("Globals"), this); + QAction* globals = createMenuEntry(CSMWorld::UniversalId::Type_Globals, mechanics, "document-mechanics-globals"); connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView())); - setupShortcut("document-mechanics-globals", globals); - mechanics->addAction (globals); - QAction *gmsts = new QAction (tr ("Game Settings"), this); + QAction* gmsts = createMenuEntry(CSMWorld::UniversalId::Type_Gmsts, mechanics, "document-mechanics-gamesettings"); connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); - setupShortcut("document-mechanics-gamesettings", gmsts); - mechanics->addAction (gmsts); - QAction *scripts = new QAction (tr ("Scripts"), this); + QAction* scripts = createMenuEntry(CSMWorld::UniversalId::Type_Scripts, mechanics, "document-mechanics-scripts"); connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); - setupShortcut("document-mechanics-scripts", scripts); - mechanics->addAction (scripts); - QAction *spells = new QAction (tr ("Spells"), this); + QAction* spells = createMenuEntry(CSMWorld::UniversalId::Type_Spells, mechanics, "document-mechanics-spells"); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); - setupShortcut("document-mechanics-spells", spells); - mechanics->addAction (spells); - QAction *enchantments = new QAction (tr ("Enchantments"), this); + QAction* enchantments = createMenuEntry(CSMWorld::UniversalId::Type_Enchantments, mechanics, "document-mechanics-enchantments"); connect (enchantments, SIGNAL (triggered()), this, SLOT (addEnchantmentsSubView())); - setupShortcut("document-mechanics-enchantments", enchantments); - mechanics->addAction (enchantments); - QAction *effects = new QAction (tr ("Magic Effects"), this); - connect (effects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView())); - setupShortcut("document-mechanics-magiceffects", effects); - mechanics->addAction (effects); + QAction* magicEffects = createMenuEntry(CSMWorld::UniversalId::Type_MagicEffects, mechanics, "document-mechanics-magiceffects"); + connect (magicEffects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView())); - QAction *startScripts = new QAction (tr ("Start Scripts"), this); + QAction* startScripts = createMenuEntry(CSMWorld::UniversalId::Type_StartScripts, mechanics, "document-mechanics-startscripts"); connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView())); - setupShortcut("document-mechanics-startscripts", startScripts); - mechanics->addAction (startScripts); } void CSVDoc::View::setupCharacterMenu() { QMenu *characters = menuBar()->addMenu (tr ("Characters")); - QAction *skills = new QAction (tr ("Skills"), this); + QAction* skills = createMenuEntry(CSMWorld::UniversalId::Type_Skills, characters, "document-character-skills"); connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); - setupShortcut("document-character-skills", skills); - characters->addAction (skills); - QAction *classes = new QAction (tr ("Classes"), this); + QAction* classes = createMenuEntry(CSMWorld::UniversalId::Type_Classes, characters, "document-character-classes"); connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); - setupShortcut("document-character-classes", classes); - characters->addAction (classes); - QAction *factions = new QAction (tr ("Factions"), this); + QAction* factions = createMenuEntry(CSMWorld::UniversalId::Type_Faction, characters, "document-character-factions"); connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); - setupShortcut("document-character-factions", factions); - characters->addAction (factions); - QAction *races = new QAction (tr ("Races"), this); + QAction* races = createMenuEntry(CSMWorld::UniversalId::Type_Races, characters, "document-character-races"); connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); - setupShortcut("document-character-races", races); - characters->addAction (races); - QAction *birthsigns = new QAction (tr ("Birthsigns"), this); + QAction* birthsigns = createMenuEntry(CSMWorld::UniversalId::Type_Birthsigns, characters, "document-character-birthsigns"); connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); - setupShortcut("document-character-birthsigns", birthsigns); - characters->addAction (birthsigns); - QAction *topics = new QAction (tr ("Topics"), this); + QAction* topics = createMenuEntry(CSMWorld::UniversalId::Type_Topics, characters, "document-character-topics"); connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); - setupShortcut("document-character-topics", topics); - characters->addAction (topics); - QAction *journals = new QAction (tr ("Journals"), this); + QAction* journals = createMenuEntry(CSMWorld::UniversalId::Type_Journals, characters, "document-character-journals"); connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); - setupShortcut("document-character-journals", journals); - characters->addAction (journals); - QAction *topicInfos = new QAction (tr ("Topic Infos"), this); + QAction* topicInfos = createMenuEntry(CSMWorld::UniversalId::Type_TopicInfos, characters, "document-character-topicinfos"); connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView())); - setupShortcut("document-character-topicinfos", topicInfos); - characters->addAction (topicInfos); - QAction *journalInfos = new QAction (tr ("Journal Infos"), this); + QAction* journalInfos = createMenuEntry(CSMWorld::UniversalId::Type_JournalInfos, characters, "document-character-journalinfos"); connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView())); - setupShortcut("document-character-journalinfos", journalInfos); - characters->addAction (journalInfos); - QAction *bodyParts = new QAction (tr ("Body Parts"), this); + QAction* bodyParts = createMenuEntry(CSMWorld::UniversalId::Type_BodyParts, characters, "document-character-bodyparts"); connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView())); - setupShortcut("document-character-bodyparts", bodyParts); - characters->addAction (bodyParts); } void CSVDoc::View::setupAssetsMenu() { QMenu *assets = menuBar()->addMenu (tr ("Assets")); - QAction *reload = new QAction (tr ("Reload"), this); + QAction* reload = createMenuEntry("Reload", ":./menu-reload.png", assets, "document-assets-reload"); connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged())); - setupShortcut("document-assets-reload", reload); - assets->addAction (reload); assets->addSeparator(); - QAction *sounds = new QAction (tr ("Sounds"), this); + QAction* sounds = createMenuEntry(CSMWorld::UniversalId::Type_Sounds, assets, "document-assets-sounds"); connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); - setupShortcut("document-assets-sounds", sounds); - assets->addAction (sounds); - QAction *soundGens = new QAction (tr ("Sound Generators"), this); + QAction* soundGens = createMenuEntry(CSMWorld::UniversalId::Type_SoundGens, assets, "document-assets-soundgens"); connect (soundGens, SIGNAL (triggered()), this, SLOT (addSoundGensSubView())); - setupShortcut("document-assets-soundgens", soundGens); - assets->addAction (soundGens); assets->addSeparator(); // resources follow here - QAction *meshes = new QAction (tr ("Meshes"), this); + QAction* meshes = createMenuEntry(CSMWorld::UniversalId::Type_Meshes, assets, "document-assets-meshes"); connect (meshes, SIGNAL (triggered()), this, SLOT (addMeshesSubView())); - setupShortcut("document-assets-meshes", meshes); - assets->addAction (meshes); - QAction *icons = new QAction (tr ("Icons"), this); + QAction* icons = createMenuEntry(CSMWorld::UniversalId::Type_Icons, assets, "document-assets-icons"); connect (icons, SIGNAL (triggered()), this, SLOT (addIconsSubView())); - setupShortcut("document-assets-icons", icons); - assets->addAction (icons); - QAction *musics = new QAction (tr ("Music"), this); + QAction* musics = createMenuEntry(CSMWorld::UniversalId::Type_Musics, assets, "document-assets-musics"); connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView())); - setupShortcut("document-assets-music", musics); - assets->addAction (musics); - QAction *soundsRes = new QAction (tr ("Sound Files"), this); - connect (soundsRes, SIGNAL (triggered()), this, SLOT (addSoundsResSubView())); - setupShortcut("document-assets-soundres", soundsRes); - assets->addAction (soundsRes); + QAction* soundFiles = createMenuEntry(CSMWorld::UniversalId::Type_SoundsRes, assets, "document-assets-soundres"); + connect (soundFiles, SIGNAL (triggered()), this, SLOT (addSoundsResSubView())); - QAction *textures = new QAction (tr ("Textures"), this); + QAction* textures = createMenuEntry(CSMWorld::UniversalId::Type_Textures, assets, "document-assets-textures"); connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView())); - setupShortcut("document-assets-textures", textures); - assets->addAction (textures); - QAction *videos = new QAction (tr ("Videos"), this); + QAction* videos = createMenuEntry(CSMWorld::UniversalId::Type_Videos, assets, "document-assets-videos"); connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView())); - setupShortcut("document-assets-videos", videos); - assets->addAction (videos); } void CSVDoc::View::setupDebugMenu() { QMenu *debug = menuBar()->addMenu (tr ("Debug")); - QAction *profiles = new QAction (tr ("Debug Profiles"), this); + QAction* profiles = createMenuEntry(CSMWorld::UniversalId::Type_DebugProfiles, debug, "document-debug-profiles"); connect (profiles, SIGNAL (triggered()), this, SLOT (addDebugProfilesSubView())); - debug->addAction (profiles); debug->addSeparator(); @@ -387,18 +292,41 @@ void CSVDoc::View::setupDebugMenu() QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu); runDebug->setText (tr ("Run OpenMW")); - setupShortcut("document-debug-run", runDebug); + runDebug->setIcon(QIcon(QString::fromStdString(":./run-openmw.png"))); - mStopDebug = new QAction (tr ("Shutdown OpenMW"), this); - connect (mStopDebug, SIGNAL (triggered()), this, SLOT (stop())); - setupShortcut("document-debug-shutdown", mStopDebug); - debug->addAction (mStopDebug); + QAction* stopDebug = createMenuEntry("Stop OpenMW", ":./stop-openmw.png", debug, "document-debug-shutdown"); + connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop())); + mStopDebug = stopDebug; - QAction *runLog = new QAction (tr ("Open Run Log"), this); + QAction* runLog = createMenuEntry(CSMWorld::UniversalId::Type_RunLog, debug, "document-debug-runlog"); connect (runLog, SIGNAL (triggered()), this, SLOT (addRunLogSubView())); - setupShortcut("document-debug-runlog", runLog); - debug->addAction (runLog); +} + +QAction* CSVDoc::View::createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName) +{ + const std::string title = CSMWorld::UniversalId (type).getTypeName(); + QAction *entry = new QAction(QString::fromStdString(title), this); + setupShortcut(shortcutName, entry); + const std::string iconName = CSMWorld::UniversalId (type).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + entry->setIcon(QIcon(QString::fromStdString(iconName))); + + menu->addAction (entry); + + return entry; +} + +QAction* CSVDoc::View::createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName) +{ + QAction *entry = new QAction(QString::fromStdString(title), this); + setupShortcut(shortcutName, entry); + if (!iconName.empty() && iconName != ":placeholder") + entry->setIcon(QIcon(QString::fromStdString(iconName))); + + menu->addAction (entry); + + return entry; } void CSVDoc::View::setupUi() @@ -472,7 +400,7 @@ void CSVDoc::View::updateSubViewIndices(SubView *view) else { delete subView->titleBarWidget(); - subView->setTitleBarWidget (NULL); + subView->setTitleBarWidget (nullptr); } } } @@ -501,7 +429,7 @@ void CSVDoc::View::updateActions() CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), - mViewTotal (totalViews), mScroll(NULL), mScrollbarOnly(false) + mViewTotal (totalViews), mScroll(nullptr), mScrollbarOnly(false) { CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; @@ -635,7 +563,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin return; } - SubView *view = NULL; + SubView *view = nullptr; if(isReferenceable) { view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument); @@ -703,7 +631,7 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max) void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="Windows/hide-subview") - updateSubViewIndices (NULL); + updateSubViewIndices (nullptr); else if (*setting=="Windows/mainwindow-scrollbar") { if (setting->toString()!="Grow Only") @@ -731,7 +659,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) mScroll->takeWidget(); setCentralWidget (&mSubViewWindow); mScroll->deleteLater(); - mScroll = NULL; + mScroll = nullptr; } } } diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 5418b7720..7a9a48b0f 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -66,6 +66,9 @@ namespace CSVDoc void closeEvent (QCloseEvent *event); + QAction* createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName); + QAction* createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName); + void setupFileMenu(); void setupEditMenu(); @@ -146,7 +149,7 @@ namespace CSVDoc void updateTitle(); // called when subviews are added or removed - void updateSubViewIndices (SubView *view = NULL); + void updateSubViewIndices (SubView *view = nullptr); private slots: diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp new file mode 100644 index 000000000..d6077a65a --- /dev/null +++ b/apps/opencs/view/render/actor.cpp @@ -0,0 +1,128 @@ +#include "actor.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../../model/world/data.hpp" + +namespace CSVRender +{ + const std::string Actor::MeshPrefix = "meshes\\"; + + Actor::Actor(const std::string& id, CSMWorld::Data& data) + : mId(id) + , mData(data) + , mBaseNode(new osg::Group()) + , mSkeleton(nullptr) + { + mActorData = mData.getActorAdapter()->getActorData(mId); + connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), + this, SLOT(handleActorChanged(const std::string&))); + } + + osg::Group* Actor::getBaseNode() + { + return mBaseNode; + } + + void Actor::update() + { + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + + // Load skeleton + std::string skeletonModel = mActorData->getSkeleton(); + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + loadSkeleton(skeletonModel); + + if (!mActorData->isCreature()) + { + // Get rid of the extra attachments + SceneUtil::CleanObjectRootVisitor cleanVisitor; + mSkeleton->accept(cleanVisitor); + cleanVisitor.remove(); + + // Attach parts to skeleton + loadBodyParts(); + } + else + { + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; + mSkeleton->accept(removeTriBipVisitor); + removeTriBipVisitor.remove(); + } + + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + + void Actor::handleActorChanged(const std::string& refId) + { + if (mId == refId) + { + update(); + } + } + + void Actor::loadSkeleton(const std::string& model) + { + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + osg::ref_ptr temp = sceneMgr->getInstance(model); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) + { + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); + } + mBaseNode->addChild(mSkeleton); + + // Map bone names to bones + mNodeMap.clear(); + SceneUtil::NodeMapVisitor nmVisitor(mNodeMap); + mSkeleton->accept(nmVisitor); + + } + + void Actor::loadBodyParts() + { + for (int i = 0; i < ESM::PRT_Count; ++i) + { + auto type = (ESM::PartReferenceType) i; + std::string partId = mActorData->getPart(type); + attachBodyPart(type, getBodyPartMesh(partId)); + } + } + + void Actor::attachBodyPart(ESM::PartReferenceType type, const std::string& mesh) + { + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = mNodeMap.find(boneName); + if (!mesh.empty() && node != mNodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } + } + + std::string Actor::getBodyPartMesh(const std::string& bodyPartId) + { + const auto& bodyParts = mData.getBodyParts(); + + int index = bodyParts.searchId(bodyPartId); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; + } +} diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp new file mode 100644 index 000000000..2f19454f7 --- /dev/null +++ b/apps/opencs/view/render/actor.hpp @@ -0,0 +1,71 @@ +#ifndef OPENCS_VIEW_RENDER_ACTOR_H +#define OPENCS_VIEW_RENDER_ACTOR_H + +#include + +#include + +#include + +#include +#include + +#include "../../model/world/actoradapter.hpp" + +namespace osg +{ + class Group; +} + +namespace CSMWorld +{ + class Data; +} + +namespace SceneUtil +{ + class Skeleton; +} + +namespace CSVRender +{ + /// Handles loading an npc or creature + class Actor : public QObject + { + Q_OBJECT + public: + /// Creates an actor. + /// \param id The referenceable id + /// \param type The record type + /// \param data The data store + Actor(const std::string& id, CSMWorld::Data& data); + + /// Retrieves the base node that meshes are attached to + osg::Group* getBaseNode(); + + /// (Re)creates the npc or creature renderable + void update(); + + private slots: + void handleActorChanged(const std::string& refId); + + private: + void loadSkeleton(const std::string& model); + void loadBodyParts(); + void attachBodyPart(ESM::PartReferenceType, const std::string& mesh); + + std::string getBodyPartMesh(const std::string& bodyPartId); + + static const std::string MeshPrefix; + + std::string mId; + CSMWorld::Data& mData; + CSMWorld::ActorAdapter::ActorDataPtr mActorData; + + osg::ref_ptr mBaseNode; + SceneUtil::Skeleton* mSkeleton; + SceneUtil::NodeMapVisitor::NodeMap mNodeMap; + }; +} + +#endif diff --git a/apps/opencs/view/render/cameracontroller.cpp b/apps/opencs/view/render/cameracontroller.cpp index 37f439fd3..524a79821 100644 --- a/apps/opencs/view/render/cameracontroller.cpp +++ b/apps/opencs/view/render/cameracontroller.cpp @@ -38,7 +38,7 @@ namespace CSVRender , mCameraSensitivity(1/650.f) , mSecondaryMoveMult(50) , mWheelMoveMult(8) - , mCamera(NULL) + , mCamera(nullptr) { } @@ -81,7 +81,7 @@ namespace CSVRender bool wasActive = mActive; mCamera = camera; - mActive = (mCamera != NULL); + mActive = (mCamera != nullptr); if (mActive != wasActive) { diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 552a54ac2..a0c408df0 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -47,6 +47,7 @@ namespace CSVRender virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { + traverse(node, nv); CellNodeContainer* container = static_cast(node->getUserData()); container->getCell()->updateLand(); } diff --git a/apps/opencs/view/render/cellarrow.cpp b/apps/opencs/view/render/cellarrow.cpp index b8c89c83d..b6fee1545 100644 --- a/apps/opencs/view/render/cellarrow.cpp +++ b/apps/opencs/view/render/cellarrow.cpp @@ -10,6 +10,8 @@ #include "../../model/prefs/state.hpp" #include "../../model/prefs/shortcutmanager.hpp" +#include + #include "mask.hpp" CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow) @@ -57,7 +59,7 @@ QString CSVRender::CellArrowTag::getToolTip (bool hideBasics) const void CSVRender::CellArrow::adjustTransform() { // position - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; const int offset = cellSize / 2 + 800; int x = mCoordinates.getX()*cellSize + cellSize/2; diff --git a/apps/opencs/view/render/cellmarker.cpp b/apps/opencs/view/render/cellmarker.cpp index d0521a7b7..3de96ab02 100644 --- a/apps/opencs/view/render/cellmarker.cpp +++ b/apps/opencs/view/render/cellmarker.cpp @@ -5,6 +5,8 @@ #include #include +#include + CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker) : TagBase(Mask_CellMarker), mMarker(marker) {} @@ -49,7 +51,7 @@ void CSVRender::CellMarker::buildMarker() void CSVRender::CellMarker::positionMarker() { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; const int markerHeight = 0; // Move marker to center of cell. diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 4b14e29bf..1cf8a5698 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -29,15 +29,13 @@ int CSVRender::InstanceMode::getSubModeFromId (const std::string& id) const osg::Vec3f CSVRender::InstanceMode::quatToEuler(const osg::Quat& rot) const { - const float Pi = 3.14159265f; - float x, y, z; float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z()); if (std::abs(test) >= 1.f) { x = atan2(rot.x(), rot.w()); - y = (test > 0) ? (Pi / 2) : (-Pi / 2); + y = (test > 0) ? (osg::PI / 2) : (-osg::PI / 2); z = 0; } else diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 8301f4e9e..026625795 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -1,6 +1,8 @@ #include "object.hpp" #include +#include +#include #include #include @@ -29,6 +31,7 @@ #include #include +#include "actor.hpp" #include "mask.hpp" @@ -78,64 +81,62 @@ void CSVRender::Object::update() { clear(); - std::string model; - int error = 0; // 1 referenceable does not exist, 2 referenceable does not specify a mesh - const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); + const int TypeIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model); int index = referenceables.searchId (mReferenceableId); - const ESM::Light* light = NULL; - - if (index==-1) - error = 1; - else - { - /// \todo check for Deleted state (error 1) - - model = referenceables.getData (index, - referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). - toString().toUtf8().constData(); - - int recordType = - referenceables.getData (index, - referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt(); - if (recordType == CSMWorld::UniversalId::Type_Light) - { - light = &dynamic_cast& >(referenceables.getRecord(index)).get(); - if (model.empty()) - model = "marker_light.nif"; - } - - if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList) - { - if (model.empty()) - model = "marker_creature.nif"; - } - - if (model.empty()) - error = 2; - } + const ESM::Light* light = nullptr; mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - if (error) + if (index == -1) { mBaseNode->addChild(createErrorCube()); + return; } - else + + /// \todo check for Deleted state (error 1) + + int recordType = referenceables.getData(index, TypeIndex).toInt(); + std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData(); + + if (recordType == CSMWorld::UniversalId::Type_Light) { - try + light = &dynamic_cast& >(referenceables.getRecord(index)).get(); + if (model.empty()) + model = "marker_light.nif"; + } + + if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList) + { + if (model.empty()) + model = "marker_creature.nif"; + } + + try + { + if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) + { + if (!mActor) mActor.reset(new Actor(mReferenceableId, mData)); + mActor->update(); + mBaseNode->addChild(mActor->getBaseNode()); + } + else if (!model.empty()) { std::string path = "meshes\\" + model; - mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); } - catch (std::exception& e) + else { - // TODO: use error marker mesh - Log(Debug::Error) << e.what(); + throw std::runtime_error(mReferenceableId + " has no model"); } } + catch (std::exception& e) + { + // TODO: use error marker mesh + Log(Debug::Error) << e.what(); + } if (light) { @@ -313,20 +314,18 @@ osg::ref_ptr CSVRender::Object::makeMoveOrScaleMarker (int axis) osg::ref_ptr CSVRender::Object::makeRotateMarker (int axis) { - const float Pi = 3.14159265f; - const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius()); const float OuterRadius = InnerRadius + MarkerShaftWidth; const float SegmentDistance = 100.f; - const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * Pi / SegmentDistance))); + const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * osg::PI / SegmentDistance))); const size_t VerticesPerSegment = 4; const size_t IndicesPerSegment = 24; const size_t VertexCount = SegmentCount * VerticesPerSegment; const size_t IndexCount = SegmentCount * IndicesPerSegment; - const float Angle = 2 * Pi / SegmentCount; + const float Angle = 2 * osg::PI / SegmentCount; const unsigned short IndexPattern[IndicesPerSegment] = { diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 3e54093d3..10a46fc10 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -1,6 +1,7 @@ #ifndef OPENCS_VIEW_OBJECT_H #define OPENCS_VIEW_OBJECT_H +#include #include #include @@ -41,6 +42,7 @@ namespace CSMWorld namespace CSVRender { + class Actor; class Object; // An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query @@ -98,6 +100,7 @@ namespace CSVRender osg::ref_ptr mMarker[3]; int mSubMode; float mMarkerTransparency; + std::unique_ptr mActor; /// Not implemented Object (const Object&); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 1d1a7cd17..ff69656a2 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -8,6 +8,8 @@ #include +#include + #include "../../model/prefs/shortcut.hpp" #include "../../model/world/tablemimedata.hpp" @@ -506,13 +508,11 @@ void CSVRender::PagedWorldspaceWidget::moveCellSelection (int x, int y) void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, int offsetY) { - const int CellSize = 8192; - osg::Vec3f eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up); - int cellX = (int)std::floor(center.x() / CellSize) + offsetX; - int cellY = (int)std::floor(center.y() / CellSize) + offsetY; + int cellX = (int)std::floor(center.x() / Constants::CellSizeInUnits) + offsetX; + int cellY = (int)std::floor(center.y() / Constants::CellSizeInUnits) + offsetY; CSMWorld::CellCoordinates cellCoordinates(cellX, cellY); @@ -527,7 +527,7 @@ void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, in CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document) : WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"), - mControlElements(NULL), mDisplayCellCoord(true) + mControlElements(nullptr), mDisplayCellCoord(true) { QAbstractItemModel *cells = document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells); @@ -738,22 +738,18 @@ void CSVRender::PagedWorldspaceWidget::selectAllWithSameParentId (int elementMas std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const { - const int cellSize = 8192; - CSMWorld::CellCoordinates cellCoordinates ( - static_cast (std::floor (point.x()/cellSize)), - static_cast (std::floor (point.y()/cellSize))); + static_cast (std::floor (point.x() / Constants::CellSizeInUnits)), + static_cast (std::floor (point.y() / Constants::CellSizeInUnits))); return cellCoordinates.getId (mWorldspace); } CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const { - const int cellSize = 8192; - CSMWorld::CellCoordinates coords( - static_cast (std::floor (point.x()/cellSize)), - static_cast (std::floor (point.y()/cellSize))); + static_cast (std::floor (point.x() / Constants::CellSizeInUnits)), + static_cast (std::floor (point.y() / Constants::CellSizeInUnits))); std::map::const_iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 0d1780d57..6cc64f653 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -185,7 +185,7 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste bool retrieveInput) : RenderWidget(parent, f) , mResourceSystem(resourceSystem) - , mLighting(NULL) + , mLighting(nullptr) , mHasDefaultAmbient(false) , mPrevMouseX(0) , mPrevMouseY(0) @@ -425,21 +425,21 @@ void SceneWidget::selectNavigationMode (const std::string& mode) { if (mode=="1st") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mFreeCamControl; mFreeCamControl->setCamera(getCamera()); mFreeCamControl->fixUpAxis(CameraController::WorldUp); } else if (mode=="free") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mFreeCamControl; mFreeCamControl->setCamera(getCamera()); mFreeCamControl->unfixUpAxis(); } else if (mode=="orbit") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mOrbitCamControl; mOrbitCamControl->setCamera(getCamera()); mOrbitCamControl->reset(); diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 51c9dd009..e0edae774 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -18,7 +18,7 @@ namespace CSVRender // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY)); if (index == -1) - return NULL; + return nullptr; const ESM::Land& land = mData.getLand().getRecord(index).get(); return new ESMTerrain::LandObject(&land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 5b825fab1..7e405266e 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -648,11 +648,6 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) mDragX = event->posF().x(); mDragY = height() - event->posF().y(); #endif - - if (mDragMode == InteractionType_PrimaryEdit) - { - editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos - } } } else diff --git a/apps/opencs/view/widget/coloreditor.cpp b/apps/opencs/view/widget/coloreditor.cpp index 5f82f8e26..82ca2b0c9 100644 --- a/apps/opencs/view/widget/coloreditor.cpp +++ b/apps/opencs/view/widget/coloreditor.cpp @@ -1,11 +1,9 @@ #include "coloreditor.hpp" #include -#include #include #include #include -#include #include #include "colorpickerpopup.hpp" @@ -27,9 +25,7 @@ CSVWidget::ColorEditor::ColorEditor(QWidget *parent, const bool popupOnStart) mColorPicker(new ColorPickerPopup(this)), mPopupOnStart(popupOnStart) { - setCheckable(true); connect(this, SIGNAL(clicked()), this, SLOT(showPicker())); - connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid())); connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &))); } @@ -85,20 +81,7 @@ void CSVWidget::ColorEditor::setColor(const int colorInt) void CSVWidget::ColorEditor::showPicker() { - if (isChecked()) - { - mColorPicker->showPicker(calculatePopupPosition(), mColor); - } - else - { - mColorPicker->hide(); - } -} - -void CSVWidget::ColorEditor::pickerHid() -{ - setChecked(false); - emit pickingFinished(); + mColorPicker->showPicker(calculatePopupPosition(), mColor); } void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color) diff --git a/apps/opencs/view/widget/coloreditor.hpp b/apps/opencs/view/widget/coloreditor.hpp index 668f22cc9..368896e42 100644 --- a/apps/opencs/view/widget/coloreditor.hpp +++ b/apps/opencs/view/widget/coloreditor.hpp @@ -45,7 +45,6 @@ namespace CSVWidget private slots: void showPicker(); - void pickerHid(); void pickerColorChanged(const QColor &color); signals: diff --git a/apps/opencs/view/widget/colorpickerpopup.cpp b/apps/opencs/view/widget/colorpickerpopup.cpp index 8e71ce39e..a38728ef3 100644 --- a/apps/opencs/view/widget/colorpickerpopup.cpp +++ b/apps/opencs/view/widget/colorpickerpopup.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -19,7 +18,6 @@ CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent) mColorPicker->setWindowFlags(Qt::Widget); mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog); mColorPicker->installEventFilter(this); - mColorPicker->open(); connect(mColorPicker, SIGNAL(currentColorChanged(const QColor &)), this, @@ -39,14 +37,15 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo geometry.moveTo(position); setGeometry(geometry); - mColorPicker->setCurrentColor(initialColor); - show(); + // Calling getColor() creates a blocking dialog that will continue execution once the user chooses OK or Cancel + QColor color = mColorPicker->getColor(initialColor); + mColorPicker->setCurrentColor(color); } void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) { QPushButton *button = qobject_cast(parentWidget()); - if (button != NULL) + if (button != nullptr) { QStyleOptionButton option; option.init(button); @@ -63,12 +62,6 @@ void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) QFrame::mousePressEvent(event); } -void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event) -{ - QFrame::hideEvent(event); - emit hid(); -} - bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event) { if (object == mColorPicker && event->type() == QEvent::KeyPress) diff --git a/apps/opencs/view/widget/colorpickerpopup.hpp b/apps/opencs/view/widget/colorpickerpopup.hpp index 602bbdb6d..eb5653f46 100644 --- a/apps/opencs/view/widget/colorpickerpopup.hpp +++ b/apps/opencs/view/widget/colorpickerpopup.hpp @@ -20,11 +20,9 @@ namespace CSVWidget protected: virtual void mousePressEvent(QMouseEvent *event); - virtual void hideEvent(QHideEvent *event); virtual bool eventFilter(QObject *object, QEvent *event); signals: - void hid(); void colorChanged(const QColor &color); }; } diff --git a/apps/opencs/view/widget/completerpopup.cpp b/apps/opencs/view/widget/completerpopup.cpp index 5777325c8..be509bcb9 100644 --- a/apps/opencs/view/widget/completerpopup.cpp +++ b/apps/opencs/view/widget/completerpopup.cpp @@ -11,7 +11,7 @@ CSVWidget::CompleterPopup::CompleterPopup(QWidget *parent) int CSVWidget::CompleterPopup::sizeHintForRow(int row) const { - if (model() == NULL) + if (model() == nullptr) { return -1; } diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 2a710a940..a42e7ead4 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -25,7 +25,7 @@ std::string CSVWorld::CellCreator::getId() const void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const { CSMWorld::IdTree *model = dynamic_cast(getData().getTableModel(getCollectionId())); - Q_ASSERT(model != NULL); + Q_ASSERT(model != nullptr); int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell); int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior); command.addNestedValue(parentIndex, index, mType->currentIndex() == 0); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 7b198056c..b32e2c7a1 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -158,7 +158,7 @@ mNotEditableDelegate(table, parent) CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display) { - CommandDelegate *delegate = NULL; + CommandDelegate *delegate = nullptr; std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt == mDelegates.end()) { @@ -251,11 +251,11 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: variant = index.data(Qt::DisplayRole); if (!variant.isValid()) { - return NULL; + return nullptr; } } - QWidget* editor = NULL; + QWidget* editor = nullptr; if (! (mTable->flags (index) & Qt::ItemIsEditable)) { return mNotEditableDelegate.createEditor(qobject_cast(mParent), @@ -325,7 +325,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di mWidget(widget), mIdType(CSMWorld::TableMimeData::convertEnums(display)) { - Q_ASSERT(mWidget != NULL); + Q_ASSERT(mWidget != nullptr); Q_ASSERT(CSMWorld::ColumnBase::isId(display)); Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None); @@ -339,7 +339,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest())); QLineEdit *lineEdit = qobject_cast(mWidget); - if (lineEdit != NULL) + if (lineEdit != nullptr) { mContextMenu = lineEdit->createStandardContextMenu(); } @@ -360,11 +360,11 @@ QString CSVWorld::IdContextMenu::getWidgetValue() const QLabel *label = qobject_cast(mWidget); QString value = ""; - if (lineEdit != NULL) + if (lineEdit != nullptr) { value = lineEdit->text(); } - else if (label != NULL) + else if (label != nullptr) { value = label->text(); } @@ -436,7 +436,7 @@ void CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor, CSMWorld::ColumnBase::Display display, int currentRow) const { - Q_ASSERT(editor != NULL); + Q_ASSERT(editor != nullptr); if (CSMWorld::ColumnBase::isId(display) && CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None) @@ -470,11 +470,11 @@ CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, bool createAndDelete) : QScrollArea(parent), -mWidgetMapper(NULL), -mNestedTableMapper(NULL), -mDispatcher(NULL), -mNestedTableDispatcher(NULL), -mMainWidget(NULL), +mWidgetMapper(nullptr), +mNestedTableMapper(nullptr), +mDispatcher(nullptr), +mNestedTableDispatcher(nullptr), +mMainWidget(nullptr), mTable(table), mCommandDispatcher (commandDispatcher), mDocument (document) @@ -733,7 +733,7 @@ bool CSVWorld::SimpleDialogueSubView::isLocked() const CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mEditWidget(0), - mMainLayout(NULL), + mMainLayout(nullptr), mTable(dynamic_cast(document.getData().getTableModel(id))), mLocked(false), mDocument(document), diff --git a/apps/opencs/view/world/dragdroputils.cpp b/apps/opencs/view/world/dragdroputils.cpp index 7f3974e53..789d4f33d 100644 --- a/apps/opencs/view/world/dragdroputils.cpp +++ b/apps/opencs/view/world/dragdroputils.cpp @@ -12,7 +12,7 @@ const CSMWorld::TableMimeData *CSVWorld::DragDropUtils::getTableMimeData(const Q bool CSVWorld::DragDropUtils::canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type) { const CSMWorld::TableMimeData *data = getTableMimeData(event); - return data != NULL && data->holdsType(type); + return data != nullptr && data->holdsType(type); } CSMWorld::UniversalId CSVWorld::DragDropUtils::getAcceptedData(const QDropEvent &event, diff --git a/apps/opencs/view/world/dragrecordtable.cpp b/apps/opencs/view/world/dragrecordtable.cpp index 6d980e171..d795bd5de 100644 --- a/apps/opencs/view/world/dragrecordtable.cpp +++ b/apps/opencs/view/world/dragrecordtable.cpp @@ -83,7 +83,7 @@ void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event) CSMWorld::ColumnBase::Display CSVWorld::DragRecordTable::getIndexDisplayType(const QModelIndex &index) const { - Q_ASSERT(model() != NULL); + Q_ASSERT(model() != nullptr); if (index.isValid()) { diff --git a/apps/opencs/view/world/dragrecordtable.hpp b/apps/opencs/view/world/dragrecordtable.hpp index 560864ba5..9e29b6145 100644 --- a/apps/opencs/view/world/dragrecordtable.hpp +++ b/apps/opencs/view/world/dragrecordtable.hpp @@ -28,7 +28,7 @@ namespace CSVWorld bool mEditLock; public: - DragRecordTable(CSMDoc::Document& document, QWidget* parent = NULL); + DragRecordTable(CSMDoc::Document& document, QWidget* parent = nullptr); virtual std::vector getDraggedRecords() const = 0; diff --git a/apps/opencs/view/world/extendedcommandconfigurator.cpp b/apps/opencs/view/world/extendedcommandconfigurator.cpp index 2cf6222a6..894742024 100644 --- a/apps/opencs/view/world/extendedcommandconfigurator.cpp +++ b/apps/opencs/view/world/extendedcommandconfigurator.cpp @@ -95,7 +95,7 @@ void CSVWorld::ExtendedCommandConfigurator::setupGroupLayout() int divider = 1; do { - while (layout->itemAt(0) != NULL) + while (layout->itemAt(0) != nullptr) { layout->removeItem(layout->itemAt(0)); } diff --git a/apps/opencs/view/world/idcompletiondelegate.cpp b/apps/opencs/view/world/idcompletiondelegate.cpp index 7f0f4ae46..4ff850b9f 100644 --- a/apps/opencs/view/world/idcompletiondelegate.cpp +++ b/apps/opencs/view/world/idcompletiondelegate.cpp @@ -25,7 +25,7 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent, { if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid()) { - return NULL; + return nullptr; } // The completer for InfoCondVar needs to return a completer based on the first column diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp index 02bd93920..1b72211e8 100644 --- a/apps/opencs/view/world/nestedtable.cpp +++ b/apps/opencs/view/world/nestedtable.cpp @@ -23,9 +23,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, bool editable, bool fixedRows) : DragRecordTable(document, parent), - mAddNewRowAction(NULL), - mRemoveRowAction(NULL), - mEditIdAction(NULL), + mAddNewRowAction(nullptr), + mRemoveRowAction(nullptr), + mEditIdAction(nullptr), mModel(model) { mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); diff --git a/apps/opencs/view/world/nestedtable.hpp b/apps/opencs/view/world/nestedtable.hpp index 765060ea5..b39c7e560 100644 --- a/apps/opencs/view/world/nestedtable.hpp +++ b/apps/opencs/view/world/nestedtable.hpp @@ -38,7 +38,7 @@ namespace CSVWorld NestedTable(CSMDoc::Document& document, CSMWorld::UniversalId id, CSMWorld::NestedTableProxyModel* model, - QWidget* parent = NULL, + QWidget* parent = nullptr, bool editable = true, bool fixedRows = false); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index b14d708da..b03cf8fdb 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -27,7 +27,7 @@ #include "creator.hpp" CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) -: SubView (id), mScene(NULL), mLayout(new QHBoxLayout), mDocument(document), mToolbar(NULL) +: SubView (id), mScene(nullptr), mLayout(new QHBoxLayout), mDocument(document), mToolbar(nullptr) { QVBoxLayout *layout = new QVBoxLayout; @@ -35,7 +35,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); - CSVRender::WorldspaceWidget* worldspaceWidget = NULL; + CSVRender::WorldspaceWidget* worldspaceWidget = nullptr; widgetType whatWidget; if (id.getId()==ESM::CellId::sDefaultWorldspace) @@ -189,9 +189,9 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& universalIdData) { - CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; - CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; - CSVWidget::SceneToolbar* toolbar = NULL; + CSVRender::PagedWorldspaceWidget* pagedNewWidget = nullptr; + CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = nullptr; + CSVWidget::SceneToolbar* toolbar = nullptr; CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (universalIdData); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index eab37e1bf..8cdb2d2db 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -1,8 +1,6 @@ #include "util.hpp" #include -#include -#include #include #include @@ -131,7 +129,7 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM // Color columns use a custom editor, so we need to fetch selected color from it. CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); - if (colorEditor != NULL) + if (colorEditor != nullptr) { variant = colorEditor->colorInt(); } @@ -209,7 +207,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO case CSMWorld::ColumnBase::Display_Integer: { DialogueSpinBox *sb = new DialogueSpinBox(parent); - sb->setRange(INT_MIN, INT_MAX); + sb->setRange(std::numeric_limits::min(), std::numeric_limits::max()); return sb; } @@ -324,7 +322,7 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde // Color columns use a custom editor, so we need explicitly set a data for it CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); - if (colorEditor != NULL) + if (colorEditor != nullptr) { colorEditor->setColor(variant.toInt()); return; diff --git a/apps/openmw/android_main.c b/apps/openmw/android_main.c index d234a369d..9a7b6bc50 100644 --- a/apps/openmw/android_main.c +++ b/apps/openmw/android_main.c @@ -20,14 +20,14 @@ void releaseArgv(); int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) { int ret = 0; - SDL_GetMouseState(&ret, NULL); + SDL_GetMouseState(&ret, nullptr); return ret; } int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) { int ret = 0; - SDL_GetMouseState(NULL, &ret); + SDL_GetMouseState(nullptr, &ret); return ret; } diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2941ede1b..f5b4171d4 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -197,9 +197,9 @@ bool OMW::Engine::frame(float frametime) } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) - : mWindow(NULL) + : mWindow(nullptr) , mEncoding(ToUTF8::WINDOWS_1252) - , mEncoder(NULL) + , mEncoder(nullptr) , mScreenCaptureOperation(nullptr) , mSkipMenu (false) , mUseSound (true) @@ -237,18 +237,18 @@ OMW::Engine::~Engine() mEnvironment.cleanup(); delete mScriptContext; - mScriptContext = NULL; + mScriptContext = nullptr; - mWorkQueue = NULL; + mWorkQueue = nullptr; mResourceSystem.reset(); - mViewer = NULL; + mViewer = nullptr; if (mWindow) { SDL_DestroyWindow(mWindow); - mWindow = NULL; + mWindow = nullptr; } SDL_Quit(); @@ -516,7 +516,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap, - Version::getOpenmwVersionDescription(mResDir.string())); + Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string()); mEnvironment.setWindowManager (window); // Create sound system diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index fe3fc5721..8137bad95 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -90,6 +90,8 @@ namespace MWBase virtual void setPlayerClass (const ESM::Class& class_) = 0; ///< Set player class to custom class. + virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) = 0; + virtual void rest(bool sleep) = 0; ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 39eed5537..36beb25fc 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -219,6 +219,7 @@ namespace MWBase virtual const MWWorld::Ptr& getSelectedEnchantItem() const = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; virtual const MWWorld::Ptr& getSelectedWeapon() const = 0; + virtual int getFontHeight() const = 0; virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; @@ -289,6 +290,8 @@ namespace MWBase /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; + virtual void loadUserFonts() = 0; + virtual Loading::Listener* getLoadingScreen() = 0; /// Should the cursor be visible? @@ -350,7 +353,8 @@ namespace MWBase virtual const MWGui::TextColours& getTextColours() = 0; - virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text) = 0; + virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0; + virtual bool injectKeyRelease(MyGUI::KeyCode key) = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3c46298b0..e17935abc 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -412,6 +412,7 @@ namespace MWBase /// @note throws an exception when invoked on a teleport door virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; + virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) = 0; ///< get a list of actors standing on \a object virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object @@ -437,12 +438,16 @@ namespace MWBase virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; - virtual int canRest() = 0; - ///< check if the player is allowed to rest \n - /// 0 - yes \n - /// 1 - only waiting \n - /// 2 - player is underwater \n - /// 3 - enemies are nearby (not implemented) + enum RestPermitted + { + Rest_Allowed = 0, + Rest_OnlyWaiting = 1, + Rest_PlayerIsUnderwater = 2, + Rest_EnemiesAreNearby = 3 + }; + + /// check if the player is allowed to rest + virtual RestPermitted canRest() const = 0; /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; @@ -490,8 +495,8 @@ namespace MWBase virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0; virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0; - virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; + virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0; virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0; @@ -566,6 +571,8 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; + virtual void rest() = 0; + virtual void setPlayerTraveling(bool traveling) = 0; virtual bool isPlayerTraveling() const = 0; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 0d267046f..b90c1ec58 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -142,14 +142,14 @@ namespace MWClass const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - float iWeight = floor(gmst.find(typeGmst)->getFloat()); + float iWeight = floor(gmst.find(typeGmst)->mValue.getFloat()); float epsilon = 0.0005f; - if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->getFloat() + epsilon) + if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->mValue.getFloat() + epsilon) return ESM::Skill::LightArmor; - if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->getFloat() + epsilon) + if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->mValue.getFloat() + epsilon) return ESM::Skill::MediumArmor; else @@ -285,7 +285,7 @@ namespace MWClass int armorSkill = actor.getClass().getSkill(actor, armorSkillType); const MWBase::World *world = MWBase::Environment::get().getWorld(); - int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->getInt(); + int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->mValue.getInteger(); if(ref->mBase->mData.mWeight == 0) return ref->mBase->mData.mArmor; diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index efe460b00..e1784d593 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -74,7 +74,7 @@ namespace MWClass if (ref->mBase->mFlags & ESM::Container::Respawn) { MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 161711751..6d0b42bfe 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -247,13 +247,13 @@ namespace MWClass MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); - float dist = gmst.find("fCombatDistance")->getFloat(); + float dist = gmst.find("fCombatDistance")->mValue.getFloat(); if (!weapon.isEmpty()) dist *= weapon.get()->mBase->mData.mReach; // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; - if (!ptr.isEmpty() && ptr.getClass().isActor() && ptr != MWMechanics::getPlayer()) + if (!ptr.isEmpty() && ptr.getClass().isActor()) ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors); std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors); @@ -298,7 +298,7 @@ namespace MWClass bool healthdmg = true; if (!weapon.isEmpty()) { - const unsigned char *attack = NULL; + const unsigned char *attack = nullptr; if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; else if(type == ESM::Weapon::AT_Slash) @@ -325,9 +325,6 @@ namespace MWClass if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength)) damage = 0; - if (victim == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState()) - damage = 0; - MWMechanics::diseaseContact(victim, ptr); victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true); @@ -376,11 +373,6 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } - bool godmode = object == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); - - if (godmode) - damage = 0; - if (!successful) { // Missed @@ -402,9 +394,9 @@ namespace MWClass if (!attacker.isEmpty()) { // Check for knockdown - float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat(); + float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->mValue.getFloat(); float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() - * getGmst().iKnockDownOddsMult->getInt() * 0.01f + getGmst().iKnockDownOddsBase->getInt(); + * getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f + getGmst().iKnockDownOddsBase->mValue.getInteger(); if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) stats.setKnockedDown(true); else @@ -415,7 +407,7 @@ namespace MWClass if(ishealth) { - if (!attacker.isEmpty() && !godmode) + if (!attacker.isEmpty()) { damage = scaleDamage(damage, attacker, ptr); MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition); @@ -524,8 +516,8 @@ namespace MWClass const GMST& gmst = getGmst(); - float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() - * (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat()); + float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() + * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); @@ -544,9 +536,9 @@ namespace MWClass { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).getMagnitude()); - flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); + flySpeed = gmst.fMinFlySpeed->mValue.getFloat() + flySpeed*(gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat()); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); - flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } @@ -556,8 +548,8 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * - gmst.fSwimRunAthleticsMult->getFloat(); + swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * + gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } else if(running) @@ -822,8 +814,8 @@ namespace MWClass return; const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); - static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); + static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat(); + static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat(); float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay); @@ -836,7 +828,7 @@ namespace MWClass ptr.getRefData().setCount(1); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); // Reset to original position MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 4a4d9793e..b8c4cbb62 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -56,8 +56,8 @@ namespace MWClass else if (creatureStats.isDead()) { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); - static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); + static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat(); + static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat(); float delay = std::min(fCorpseRespawnDelay, fCorpseClearDelay); if (creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp()) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index d738974dd..a26118029 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -213,7 +213,7 @@ namespace MWClass closeSound, 0.5f); // Doors rotate at 90 degrees per second, so start the sound at // where it would be at the current rotation. - float offset = doorRot/(3.14159265f * 0.5f); + float offset = doorRot/(osg::PI * 0.5f); action->setSoundOffset(offset); action->setSound(openSound); } @@ -221,7 +221,7 @@ namespace MWClass { MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, openSound, 0.5f); - float offset = 1.0f - doorRot/(3.14159265f * 0.5f); + float offset = 1.0f - doorRot/(osg::PI * 0.5f); action->setSoundOffset(std::max(offset, 0.0f)); action->setSound(closeSound); } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index df7c61ebf..70339564c 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -14,8 +14,6 @@ #include "../mwworld/actioneat.hpp" #include "../mwworld/nullaction.hpp" -#include "../mwmechanics/npcstats.hpp" - #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" @@ -134,11 +132,10 @@ namespace MWClass } MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats (player); - int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); + int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); static const float fWortChanceValue = - MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); MWGui::Widgets::SpellEffectList list; for (int i=0; i<4; ++i) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 27a9f3f2b..ea0abd6f6 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -41,7 +41,7 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); - assert (ref->mBase != NULL); + assert (ref->mBase != nullptr); // TODO: add option somewhere to enable collision for placeable objects if (!model.empty() && (ref->mBase->mData.mFlags & ESM::Light::Carry) == 0) @@ -161,7 +161,12 @@ namespace MWClass std::string text; if (Settings::Manager::getBool("show effect duration","Game")) - text += "\n#{sDuration}: " + MWGui::ToolTips::toString(ptr.getClass().getRemainingUsageTime(ptr)); + { + // -1 is infinite light source, so duration makes no sense here. Other negative values are treated as 0. + float remainingTime = ptr.getClass().getRemainingUsageTime(ptr); + if (remainingTime != -1.0f) + text += "\n#{sDuration}: " + MWGui::ToolTips::toString(std::max(0.f, remainingTime)); + } text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, "#{sWeight}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c06c3f67c..4339f37e2 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -372,9 +373,9 @@ namespace MWClass if (!ref->mBase->mFaction.empty()) { static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get() - .find("iAutoRepFacMod")->getInt(); + .find("iAutoRepFacMod")->mValue.getInteger(); static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get() - .find("iAutoRepLevMod")->getInt(); + .find("iAutoRepLevMod")->mValue.getInteger(); int rank = ref->mBase->getFactionRank(); data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1)); @@ -528,7 +529,7 @@ namespace MWClass const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); - return store.find("sWerewolfPopup")->getString(); + return store.find("sWerewolfPopup")->mValue.getString(); } const MWWorld::LiveCellRef *ref = ptr.get(); @@ -565,10 +566,10 @@ namespace MWClass MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); - const float fCombatDistance = store.find("fCombatDistance")->getFloat(); + const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat(); float dist = fCombatDistance * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : - store.find("fHandToHandReach")->getFloat()); + store.find("fHandToHandReach")->mValue.getFloat()); // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; @@ -609,7 +610,7 @@ namespace MWClass float damage = 0.0f; if(!weapon.isEmpty()) { - const unsigned char *attack = NULL; + const unsigned char *attack = nullptr; if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; else if(type == ESM::Weapon::AT_Slash) @@ -638,14 +639,14 @@ namespace MWClass && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); if(unaware) { - damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); + damage *= store.find("fCombatCriticalStrikeMult")->mValue.getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } } if (othercls.getCreatureStats(victim).getKnockedDown()) - damage *= store.find("fCombatKODamageMult")->getFloat(); + damage *= store.find("fCombatKODamageMult")->mValue.getFloat(); // Apply "On hit" enchanted weapons MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition); @@ -737,14 +738,14 @@ namespace MWClass const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const GMST& gmst = getGmst(); - int chance = store.get().find("iVoiceHitOdds")->getInt(); + int chance = store.get().find("iVoiceHitOdds")->mValue.getInteger(); if (Misc::Rng::roll0to99() < chance) MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); // Check for knockdown - float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat(); + float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->mValue.getFloat(); float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() - * gmst.iKnockDownOddsMult->getInt() * 0.01f + gmst.iKnockDownOddsBase->getInt(); + * gmst.iKnockDownOddsMult->mValue.getInteger() * 0.01f + gmst.iKnockDownOddsBase->mValue.getInteger(); if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) stats.setKnockedDown(true); else @@ -772,7 +773,7 @@ namespace MWClass float unmitigatedDamage = damage; float x = damage / (damage + getArmorRating(ptr)); - damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x); + damage *= std::max(gmst.fCombatArmorMinMult->mValue.getFloat(), x); int damageDiff = static_cast(unmitigatedDamage - damage); damage = std::max(1.f, damage); damageDiff = std::max(1, damageDiff); @@ -938,18 +939,18 @@ namespace MWClass const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); - bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); + bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr) && stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); + bool running = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr) && stats.getStance(MWMechanics::CreatureStats::Stance_Run); - float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* - (gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat()); - walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; + float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* + (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); + walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance; walkSpeed = std::max(0.0f, walkSpeed); if(sneaking) - walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat(); + walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); - float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * - gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); + float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * + gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat()); float moveSpeed; if(getEncumbrance(ptr) > getCapacity(ptr)) @@ -959,22 +960,22 @@ namespace MWClass { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).getMagnitude()); - flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); - flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed = gmst.fMinFlySpeed->mValue.getFloat() + flySpeed*(gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat()); + flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } - else if(world->isSwimming(ptr)) + else if (world->isSwimming(ptr)) { float swimSpeed = walkSpeed; if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* - gmst.fSwimRunAthleticsMult->getFloat(); + swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)* + gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } - else if(running && !sneaking) + else if (running) moveSpeed = runSpeed; else moveSpeed = walkSpeed; @@ -982,7 +983,7 @@ namespace MWClass moveSpeed *= 0.75f; if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing) - moveSpeed *= gmst.fWereWolfRunMult->getFloat(); + moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat(); return moveSpeed; } @@ -992,14 +993,18 @@ namespace MWClass if(getEncumbrance(ptr) > getCapacity(ptr)) return 0.f; + const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) + return 0.f; + const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const GMST& gmst = getGmst(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); - const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() + - gmst.fJumpEncumbranceMultiplier->getFloat() * + const float encumbranceTerm = gmst.fJumpEncumbranceBase->mValue.getFloat() + + gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr)); - float a = static_cast(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified()); + float a = static_cast(getSkill(ptr, ESM::Skill::Acrobatics)); float b = 0.0f; if(a > 50.0f) { @@ -1007,16 +1012,16 @@ namespace MWClass a = 50.0f; } - float x = gmst.fJumpAcrobaticsBase->getFloat() + - std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat()); - x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat(); + float x = gmst.fJumpAcrobaticsBase->mValue.getFloat() + + std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->mValue.getFloat()); + x += 3.0f * b * gmst.fJumpAcroMultiplier->mValue.getFloat(); x += mageffects.get(ESM::MagicEffect::Jump).getMagnitude() * 64; x *= encumbranceTerm; - if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) - x *= gmst.fJumpRunMultiplier->getFloat(); + if(stats.getStance(MWMechanics::CreatureStats::Stance_Run)) + x *= gmst.fJumpRunMultiplier->mValue.getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); - x -= -627.2f;/*gravity constant*/ + x -= -Constants::GravityConst * Constants::UnitsPerMeter; x /= 3.0f; return x; @@ -1079,7 +1084,7 @@ namespace MWClass float Npc::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEncumbranceStrMult")->getFloat(); + static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEncumbranceStrMult")->mValue.getFloat(); return stats.getAttribute(ESM::Attribute::Strength).getModified()*fEncumbranceStrMult; } @@ -1122,9 +1127,9 @@ namespace MWClass MWMechanics::NpcStats &stats = getNpcStats(ptr); const MWWorld::InventoryStore &invStore = getInventoryStore(ptr); - float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat(); - float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat(); - int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); + float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); + float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); + int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); float ratings[MWWorld::InventoryStore::Slots]; for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) @@ -1241,11 +1246,10 @@ namespace MWClass { MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); - if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) + if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) return "DefaultLandWater"; - if(world->isOnGround(ptr)) - return "DefaultLand"; - return ""; + + return "DefaultLand"; } if(name == "swimleft") return "Swim Left"; @@ -1364,8 +1368,8 @@ namespace MWClass return; const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); - static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); + static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat(); + static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat(); float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay); @@ -1378,7 +1382,7 @@ namespace MWClass ptr.getRefData().setCount(1); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); // Reset to original position MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index bb1ca09a3..78678f461 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -1,6 +1,7 @@ #include "weapon.hpp" #include +#include #include #include "../mwbase/environment.hpp" @@ -65,7 +66,7 @@ namespace MWClass { const MWWorld::LiveCellRef *ref = ptr.get(); - return (ref->mBase->mData.mType < 11); // thrown weapons and arrows/bolts don't have health, only quantity + return (ref->mBase->mData.mType < ESM::Weapon::MarksmanThrown); // thrown weapons and arrows/bolts don't have health, only quantity } int Weapon::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const @@ -268,27 +269,30 @@ namespace MWClass { text += "\n#{sType} "; - std::map > mapping; - mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); - mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded"); - mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded"); - mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded"); - mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); - mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); - mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded"); - mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded"); - mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded"); - mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", ""); + static std::map > mapping; + if (mapping.empty()) + { + mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); + mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded"); + mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded"); + mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded"); + mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); + mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); + mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded"); + mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded"); + mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded"); + mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", ""); + } - std::string type = mapping[ref->mBase->mData.mType].first; - std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; + const std::string type = mapping[ref->mBase->mData.mType].first; + const std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; - text += store.get().find(type)->getString() + - ((oneOrTwoHanded != "") ? ", " + store.get().find(oneOrTwoHanded)->getString() : ""); + text += store.get().find(type)->mValue.getString() + + ((oneOrTwoHanded != "") ? ", " + store.get().find(oneOrTwoHanded)->mValue.getString() : ""); // weapon damage if (ref->mBase->mData.mType >= 9) @@ -315,21 +319,26 @@ namespace MWClass } } - if (ref->mBase->mData.mType < 11) // thrown weapons and arrows/bolts don't have health, only quantity + if (hasItemHealth(ptr)) { int remainingHealth = getItemHealth(ptr); text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); } - // add reach and attack speed for melee weapon - if (ref->mBase->mData.mType < 9 && Settings::Manager::getBool("show melee info", "Game")) + const bool verbose = Settings::Manager::getBool("show melee info", "Game"); + // add reach for melee weapon + if (ref->mBase->mData.mType < ESM::Weapon::MarksmanBow && verbose) { - // 64 game units = 1 yard = 3 ft, display value in feet - const float combatDistance = store.get().find("fCombatDistance")->getFloat() * ref->mBase->mData.mReach; - text += MWGui::ToolTips::getWeightString(combatDistance*3/64, "#{sRange}"); + // display value in feet + const float combatDistance = store.get().find("fCombatDistance")->mValue.getFloat() * ref->mBase->mData.mReach; + text += MWGui::ToolTips::getWeightString(combatDistance / Constants::UnitsPerFoot, "#{sRange}"); text += " #{sFeet}"; + } + // add attack speed for any weapon excepts arrows and bolts + if (ref->mBase->mData.mType < ESM::Weapon::Arrow && verbose) + { text += MWGui::ToolTips::getPercentString(ref->mBase->mData.mSpeed, "#{sAttributeSpeed}"); } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index c3e56c0bf..17f69d69b 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -261,7 +261,7 @@ namespace MWDialogue const MWWorld::Store& gmsts = MWBase::Environment::get().getWorld()->getStore().get(); - title = gmsts.find (modifiedTopic)->getString(); + title = gmsts.find (modifiedTopic)->mValue.getString(); } else title = topic; @@ -537,7 +537,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - callback->addResponse(gmsts.find ("sServiceRefusal")->getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); + callback->addResponse(gmsts.find ("sServiceRefusal")->mValue.getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript, mActor); return true; @@ -572,7 +572,7 @@ namespace MWDialogue const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); Filter filter(actor, 0, creatureStats.hasTalkedToPlayer()); const ESM::DialInfo *info = filter.search(*dial, false); - if(info != NULL) + if(info != nullptr) { MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); if(winMgr->getSubtitlesEnabled()) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 15020898f..042ccb019 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -621,7 +621,7 @@ const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, std::vector suitableInfos = list (dialogue, fallbackToInfoRefusal, false); if (suitableInfos.empty()) - return NULL; + return nullptr; else return suitableInfos[0]; } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 9f74d0733..5eab6d5ca 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -30,7 +30,7 @@ namespace MWDialogue { if (actor.isEmpty()) { - MWScript::InterpreterContext interpreterContext(NULL,MWWorld::Ptr()); + MWScript::InterpreterContext interpreterContext(nullptr, MWWorld::Ptr()); mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext); } else diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 80284e9b2..fa8a96185 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -25,9 +25,12 @@ namespace MWGui { + const float AlchemyWindow::sCountChangeInitialPause = 0.5f; + const float AlchemyWindow::sCountChangeInterval = 0.1f; + AlchemyWindow::AlchemyWindow() : WindowBase("openmw_alchemy_window.layout") - , mSortModel(NULL) + , mSortModel(nullptr) , mAlchemy(new MWMechanics::Alchemy()) , mApparatus (4) , mIngredients (4) @@ -43,9 +46,21 @@ namespace MWGui getWidget(mApparatus[2], "Apparatus3"); getWidget(mApparatus[3], "Apparatus4"); getWidget(mEffectsBox, "CreatedEffects"); + getWidget(mBrewCountEdit, "BrewCount"); + getWidget(mIncreaseButton, "IncreaseButton"); + getWidget(mDecreaseButton, "DecreaseButton"); getWidget(mNameEdit, "NameEdit"); getWidget(mItemView, "ItemView"); + mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged); + mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept); + mBrewCountEdit->setMinValue(1); + mBrewCountEdit->setValue(1); + + mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onIncreaseButtonPressed); + mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased); + mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onDecreaseButtonPressed); + mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased); mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem); @@ -65,6 +80,9 @@ namespace MWGui void AlchemyWindow::onAccept(MyGUI::EditBox* sender) { onCreateButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender) @@ -74,7 +92,15 @@ namespace MWGui void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) { - MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ()); + mAlchemy->setPotionName(mNameEdit->getCaption()); + int count = mAlchemy->countPotionsToBrew(); + count = std::min(count, mBrewCountEdit->getValue()); + createPotions(count); + } + + void AlchemyWindow::createPotions(int count) + { + MWMechanics::Alchemy::Result result = mAlchemy->create(mNameEdit->getCaption(), count); MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); switch (result) @@ -89,8 +115,11 @@ namespace MWGui winMgr->messageBox("#{sNotifyMessage6a}"); break; case MWMechanics::Alchemy::Result_Success: - winMgr->messageBox("#{sPotionSuccess}"); winMgr->playSound("potion success"); + if (count == 1) + winMgr->messageBox("#{sPotionSuccess}"); + else + winMgr->messageBox("#{sPotionSuccess} "+mNameEdit->getCaption()+" ("+std::to_string(count)+")"); break; case MWMechanics::Alchemy::Result_NoEffects: case MWMechanics::Alchemy::Result_RandomFailure: @@ -123,6 +152,7 @@ namespace MWGui mItemView->resetScrollBars(); mNameEdit->setCaption(""); + mBrewCountEdit->setValue(1); int index = 0; for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools()); @@ -247,4 +277,61 @@ namespace MWGui update(); } + + void AlchemyWindow::addRepeatController(MyGUI::Widget *widget) + { + MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerRepeatEvent::getClassTypeName()); + Controllers::ControllerRepeatEvent* controller = item->castType(); + controller->eventRepeatClick += MyGUI::newDelegate(this, &AlchemyWindow::onRepeatClick); + controller->setRepeat(sCountChangeInitialPause, sCountChangeInterval); + MyGUI::ControllerManager::getInstance().addItem(widget, controller); + } + + void AlchemyWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + addRepeatController(_sender); + onIncreaseButtonTriggered(); + } + + void AlchemyWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + addRepeatController(_sender); + onDecreaseButtonTriggered(); + } + + void AlchemyWindow::onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller) + { + if (widget == mIncreaseButton) + onIncreaseButtonTriggered(); + else if (widget == mDecreaseButton) + onDecreaseButtonTriggered(); + } + + void AlchemyWindow::onCountButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id) + { + MyGUI::ControllerManager::getInstance().removeItem(_sender); + } + + void AlchemyWindow::onCountValueChanged(int value) + { + mBrewCountEdit->setValue(std::abs(value)); + } + + void AlchemyWindow::onIncreaseButtonTriggered() + { + int currentCount = mBrewCountEdit->getValue(); + + // prevent overflows + if (currentCount == std::numeric_limits::max()) + return; + + mBrewCountEdit->setValue(currentCount+1); + } + + void AlchemyWindow::onDecreaseButtonTriggered() + { + int currentCount = mBrewCountEdit->getValue(); + if (currentCount > 1) + mBrewCountEdit->setValue(currentCount-1); + } } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index d1e54241a..aa23fbaa5 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -3,8 +3,13 @@ #include +#include + #include "../mwmechanics/alchemy.hpp" +#include + +#include "controllers.hpp" #include "windowbase.hpp" namespace MWMechanics @@ -28,6 +33,10 @@ namespace MWGui void onResChange(int, int) { center(); } private: + + static const float sCountChangeInitialPause; // in seconds + static const float sCountChangeInterval; // in seconds + std::string mSuggestedPotionName; ItemView* mItemView; @@ -38,17 +47,32 @@ namespace MWGui MyGUI::Widget* mEffectsBox; + MyGUI::Button* mIncreaseButton; + MyGUI::Button* mDecreaseButton; MyGUI::EditBox* mNameEdit; + Gui::NumericEditBox* mBrewCountEdit; void onCancelButtonClicked(MyGUI::Widget* _sender); void onCreateButtonClicked(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender); void onAccept(MyGUI::EditBox*); + void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onCountButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onCountValueChanged(int value); + void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller); + + void addRepeatController(MyGUI::Widget* widget); + + void onIncreaseButtonTriggered(); + void onDecreaseButtonTriggered(); void onSelectedItem(int index); void removeIngredient(MyGUI::Widget* ingredient); + void createPotions(int count); + void update(); std::unique_ptr mAlchemy; diff --git a/apps/openmw/mwgui/backgroundimage.cpp b/apps/openmw/mwgui/backgroundimage.cpp index 98828a041..55c825ebb 100644 --- a/apps/openmw/mwgui/backgroundimage.cpp +++ b/apps/openmw/mwgui/backgroundimage.cpp @@ -10,7 +10,7 @@ void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRa if (mChild) { MyGUI::Gui::getInstance().destroyWidget(mChild); - mChild = NULL; + mChild = nullptr; } if (!stretch) { diff --git a/apps/openmw/mwgui/backgroundimage.hpp b/apps/openmw/mwgui/backgroundimage.hpp index 8c963b762..3db5bab16 100644 --- a/apps/openmw/mwgui/backgroundimage.hpp +++ b/apps/openmw/mwgui/backgroundimage.hpp @@ -14,7 +14,7 @@ namespace MWGui MYGUI_RTTI_DERIVED(BackgroundImage) public: - BackgroundImage() : mChild(NULL), mAspect(0) {} + BackgroundImage() : mChild(nullptr), mAspect(0) {} /** * @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 6f16cf076..29dfe7f3a 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -1,6 +1,5 @@ #include "bookpage.hpp" -#include "MyGUI_FontManager.h" #include "MyGUI_RenderItem.h" #include "MyGUI_RenderManager.h" #include "MyGUI_TextureUtility.h" @@ -8,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { struct TypesetBookImpl; @@ -108,7 +110,7 @@ struct TypesetBookImpl : TypesetBook Contents::iterator i = mContents.insert (mContents.end (), Content (text.first, text.second)); if (i->empty()) - return Range (Utf8Point (NULL), Utf8Point (NULL)); + return Range (Utf8Point (nullptr), Utf8Point (nullptr)); Utf8Point begin = &i->front (); Utf8Point end = &i->front () + i->size (); @@ -146,7 +148,7 @@ struct TypesetBookImpl : TypesetBook template void visitRuns (int top, int bottom, Visitor const & visitor) const { - visitRuns (top, bottom, NULL, visitor); + visitRuns (top, bottom, nullptr, visitor); } /// hit test with a margin for error. only hits on interactive text fragments are reported. @@ -174,7 +176,7 @@ struct TypesetBookImpl : TypesetBook return hit; } } - return NULL; + return nullptr; } StyleImpl * hitTest (int left, int top) const @@ -211,7 +213,7 @@ struct TypesetBookImpl : TypesetBook for (Styles::iterator i = mStyles.begin (); i != mStyles.end (); ++i) if (&*i == style) return i->mFont; - return NULL; + return nullptr; } struct Typesetter; @@ -251,8 +253,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter Typesetter (size_t width, size_t height) : mPageWidth (width), mPageHeight(height), - mSection (NULL), mLine (NULL), mRun (NULL), - mCurrentContent (NULL), + mSection (nullptr), mLine (nullptr), mRun (nullptr), + mCurrentContent (nullptr), mCurrentAlignment (AlignLeft) { mBook = std::make_shared (); @@ -262,18 +264,24 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { } - Style * createStyle (char const * fontName, const Colour& fontColour) + Style * createStyle (const std::string& fontName, const Colour& fontColour, bool useBookFont) { - if (strcmp(fontName, "") == 0) - return createStyle(MyGUI::FontManager::getInstance().getDefaultFont().c_str(), fontColour); + std::string fullFontName; + if (fontName.empty()) + fullFontName = MyGUI::FontManager::getInstance().getDefaultFont(); + else + fullFontName = fontName; + + if (useBookFont) + fullFontName = "Journalbook " + fullFontName; for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) - if (i->match (fontName, fontColour, fontColour, fontColour, 0)) + if (i->match (fullFontName.c_str(), fontColour, fontColour, fontColour, 0)) return &*i; - MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fontName); + MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fullFontName); if (!font) - throw std::runtime_error(std::string("can't find font ") + fontName); + throw std::runtime_error(std::string("can't find font ") + fullFontName); StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = font; @@ -334,7 +342,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter void write (Style * style, size_t begin, size_t end) { - assert (mCurrentContent != NULL); + assert (mCurrentContent != nullptr); assert (end <= mCurrentContent->size ()); assert (begin <= mCurrentContent->size ()); @@ -350,8 +358,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter add_partial_text(); - mRun = NULL; - mLine = NULL; + mRun = nullptr; + mLine = nullptr; } void sectionBreak (int margin) @@ -360,9 +368,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mBook->mSections.size () > 0) { - mRun = NULL; - mLine = NULL; - mSection = NULL; + mRun = nullptr; + mLine = nullptr; + mSection = nullptr; if (mBook->mRect.bottom < (mBook->mSections.back ().mRect.bottom + margin)) mBook->mRect.bottom = (mBook->mSections.back ().mRect.bottom + margin); @@ -373,7 +381,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { add_partial_text(); - if (mSection != NULL) + if (mSection != nullptr) mSectionAlignment.back () = sectionAlignment; mCurrentAlignment = sectionAlignment; } @@ -483,7 +491,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { add_partial_text(); stream.consume (); - mLine = NULL, mRun = NULL; + mLine = nullptr, mRun = nullptr; continue; } @@ -497,9 +505,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) { - MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); - if (gi) - space_width += static_cast(gi->advance + gi->bearingX); + MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); + if (info.codePoint >= 0) + space_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -507,9 +515,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) { - MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); - if (gi) - word_width += static_cast(gi->advance + gi->bearingX); + MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); + if (info.codePoint >= 0) + word_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -530,6 +538,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mPartialWhitespace.empty() && mPartialWord.empty()) return; + int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); int space_width = 0; int word_width = 0; @@ -542,16 +551,15 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (left + space_width + word_width > mPageWidth) { - mLine = NULL, mRun = NULL, left = 0; + mLine = nullptr, mRun = nullptr, left = 0; } else { for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i) { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; - int line_height = i->mStyle->mFont->getDefaultHeight (); - append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + line_height); + append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + fontHeight); left = mLine->mRect.right; } @@ -560,9 +568,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i) { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; - int line_height = i->mStyle->mFont->getDefaultHeight (); - append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + line_height); + append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + fontHeight); left = mLine->mRect.right; } @@ -573,7 +580,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom) { - if (mSection == NULL) + if (mSection == nullptr) { mBook->mSections.push_back (Section ()); mSection = &mBook->mSections.back (); @@ -581,7 +588,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter mSectionAlignment.push_back (mCurrentAlignment); } - if (mLine == NULL) + if (mLine == nullptr) { mSection->mLines.push_back (Line ()); mLine = &mSection->mLines.back (); @@ -606,7 +613,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mLine->mRect.bottom < bottom) mLine->mRect.bottom = bottom; - if (mRun == NULL || mRun->mStyle != style || mRun->mRange.second != begin) + if (mRun == nullptr || mRun->mStyle != style || mRun->mRange.second != begin) { int left = mRun ? mRun->mRight : mLine->mRect.left; @@ -756,32 +763,32 @@ namespace void emitGlyph (wchar_t ch) { - MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (!gi) + if (info.codePoint < 0) return; MyGUI::FloatRect vr; - vr.left = mCursor.left + gi->bearingX; - vr.top = mCursor.top + gi->bearingY; - vr.right = vr.left + gi->width; - vr.bottom = vr.top + gi->height; + vr.left = mCursor.left + info.bearingX; + vr.top = mCursor.top + info.bearingY; + vr.right = vr.left + info.width; + vr.bottom = vr.top + info.height; - MyGUI::FloatRect tr = gi->uvRect; + MyGUI::FloatRect tr = info.uvRect; if (mRenderXform.clip (vr, tr)) quad (vr, tr); - mCursor.left += gi->bearingX + gi->advance; + mCursor.left += static_cast(info.bearingX + info.advance); } void emitSpace (wchar_t ch) { - MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (gi) - mCursor.left += gi->bearingX + gi->advance; + if (info.codePoint >= 0) + mCursor.left += static_cast(info.bearingX + info.advance); } private: @@ -836,17 +843,17 @@ protected: TextFormat (MyGUI::IFont* id, PageDisplay * display) : mFont (id), mCountVertex (0), - mTexture (NULL), - mRenderItem (NULL), + mTexture (nullptr), + mRenderItem (nullptr), mDisplay (display) { } void createDrawItem (MyGUI::ILayerNode* node) { - assert (mRenderItem == NULL); + assert (mRenderItem == nullptr); - if (mTexture != NULL) + if (mTexture != nullptr) { mRenderItem = node->addToRenderItem(mTexture, false, false); mRenderItem->addDrawItem(this, mCountVertex); @@ -855,12 +862,12 @@ protected: void destroyDrawItem (MyGUI::ILayerNode* node) { - assert (mTexture != NULL ? mRenderItem != NULL : mRenderItem == NULL); + assert (mTexture != nullptr ? mRenderItem != nullptr : mRenderItem == nullptr); - if (mTexture != NULL) + if (mTexture != nullptr) { mRenderItem->removeDrawItem (this); - mRenderItem = NULL; + mRenderItem = nullptr; } } @@ -913,9 +920,9 @@ public: resetPage (); mViewTop = 0; mViewBottom = 0; - mFocusItem = NULL; + mFocusItem = nullptr; mItemActive = false; - mNode = NULL; + mNode = nullptr; } void dirtyFocusItem () @@ -1046,7 +1053,7 @@ public: for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) { - if (mNode != NULL) + if (mNode != nullptr) i->second->destroyDrawItem (mNode); i->second.reset(); } @@ -1082,7 +1089,7 @@ public: else if (mBook && isPageDifferent (newPage)) { - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate(i->second->mRenderItem); @@ -1130,7 +1137,7 @@ public: { newBook->visitRuns (0, 0x7FFFFFFF, CreateActiveFormat (this)); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->createDrawItem (mNode); } @@ -1231,7 +1238,7 @@ public: { _checkMargin(); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate (i->second->mRenderItem); } @@ -1240,7 +1247,7 @@ public: { _checkMargin (); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate (i->second->mRenderItem); @@ -1251,7 +1258,7 @@ public: for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->destroyDrawItem (mNode); - mNode = NULL; + mNode = nullptr; } }; @@ -1262,7 +1269,7 @@ MYGUI_RTTI_DERIVED(BookPage) public: BookPageImpl() - : mPageDisplay(NULL) + : mPageDisplay(nullptr) { } diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 66d1834c7..4cf99794d 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -3,10 +3,17 @@ #include "MyGUI_Colour.h" #include "MyGUI_Widget.h" +#include "MyGUI_FontManager.h" #include #include +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { /// A formatted and paginated document to be used with @@ -28,6 +35,45 @@ namespace MWGui virtual std::pair getSize () const = 0; }; + struct GlyphInfo + { + char codePoint; + float width; + float height; + float advance; + float bearingX; + float bearingY; + MyGUI::FloatRect uvRect; + + GlyphInfo(MyGUI::IFont* font, MyGUI::Char ch) + { + static const int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); + + MyGUI::GlyphInfo* gi = font->getGlyphInfo(ch); + if (gi) + { + const float scale = font->getDefaultHeight() / (float) fontHeight; + + codePoint = gi->codePoint; + bearingX = (int) gi->bearingX / scale; + bearingY = (int) gi->bearingY / scale; + width = (int) gi->width / scale; + height = (int) gi->height / scale; + advance = (int) gi->advance / scale; + uvRect = gi->uvRect; + } + else + { + codePoint = -1; + bearingX = 0; + bearingY = 0; + width = 0; + height = 0; + advance = 0; + } + } + }; + /// A factory class for creating a typeset book instance. struct BookTypesetter { @@ -56,7 +102,7 @@ namespace MWGui static Ptr create (int pageWidth, int pageHeight); /// Create a simple text style consisting of a font and a text color. - virtual Style* createStyle (char const * Font, const Colour& Colour) = 0; + virtual Style* createStyle (const std::string& fontName, const Colour& colour, bool useBookFont=true) = 0; /// Create a hyper-link style with a user-defined identifier based on an /// existing style. The unique flag forces a new instance of this style diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 6ed7a4491..45abe889e 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -399,12 +399,12 @@ namespace MWGui CreateClassDialog::CreateClassDialog() : WindowModal("openmw_chargen_create_class.layout") - , mSpecDialog(NULL) - , mAttribDialog(NULL) - , mSkillDialog(NULL) - , mDescDialog(NULL) - , mAffectedAttribute(NULL) - , mAffectedSkill(NULL) + , mSpecDialog(nullptr) + , mAttribDialog(nullptr) + , mSkillDialog(nullptr) + , mDescDialog(nullptr) + , mAffectedAttribute(nullptr) + , mAffectedSkill(nullptr) { // Centre dialog center(); diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index c6ef2890d..b2639e938 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -37,8 +37,8 @@ namespace MWGui CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* manager) : WindowBase("openmw_companion_window.layout") - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) , mSelectedItem(-1) , mDragAndDrop(dragAndDrop) , mMessageBoxManager(manager) @@ -89,7 +89,7 @@ void CompanionWindow::onItemSelected(int index) dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); } else - dragItem (NULL, count); + dragItem (nullptr, count); } void CompanionWindow::dragItem(MyGUI::Widget* sender, int count) @@ -179,9 +179,9 @@ void CompanionWindow::onReferenceUnavailable() void CompanionWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mModel = nullptr; + mSortModel = nullptr; } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index b367c6f49..dc22e4193 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -152,12 +152,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } - void Console::setFont(const std::string &fntName) - { - mHistory->setFontName(fntName); - mCommandLine->setFontName(fntName); - } - void Console::print(const std::string &msg, const std::string& color) { mHistory->addText(color + MyGUI::TextIterator::toTagsString(msg)); diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index bbff34c8d..883bc8967 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -41,8 +41,6 @@ namespace MWGui virtual void onOpen(); - void setFont(const std::string &fntName); - void onResChange(int width, int height); // Print a message to the console, in specified color. diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 703ba309e..2f9643f74 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -31,8 +31,8 @@ namespace MWGui ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) : WindowBase("openmw_container_window.layout") , mDragAndDrop(dragAndDrop) - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) , mSelectedItem(-1) { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); @@ -83,7 +83,7 @@ namespace MWGui dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } else - dragItem (NULL, count); + dragItem (nullptr, count); } void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) @@ -151,9 +151,9 @@ namespace MWGui void ContainerWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mModel = nullptr; + mSortModel = nullptr; } void ContainerWindow::onClose() @@ -171,9 +171,11 @@ namespace MWGui void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) { - if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop) + if(mDragAndDrop != nullptr && mDragAndDrop->mIsOnDragAndDrop) return; + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); + // transfer everything into the player's inventory ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); mModel->update(); @@ -217,8 +219,10 @@ namespace MWGui void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) { - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + if(mDragAndDrop == nullptr || !mDragAndDrop->mIsOnDragAndDrop) { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); + onTakeAllButtonClicked(mTakeButton); if (mPtr.getClass().isPersistent(mPtr)) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index cf058caac..baf3a43ab 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -63,7 +63,7 @@ namespace MWGui void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { - eventOkClicked(NULL, mSlider->getScrollPosition()+1); + eventOkClicked(nullptr, mSlider->getScrollPosition()+1); setVisible(false); } @@ -72,9 +72,11 @@ namespace MWGui // Enter key void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) { - eventOkClicked(NULL, mSlider->getScrollPosition()+1); - + eventOkClicked(nullptr, mSlider->getScrollPosition()+1); setVisible(false); + + // To do not spam onEnterKeyPressed() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void CountDialog::onEditValueChanged(int value) diff --git a/apps/openmw/mwgui/cursor.cpp b/apps/openmw/mwgui/cursor.cpp index 82e0b9699..ed8a76eb8 100644 --- a/apps/openmw/mwgui/cursor.cpp +++ b/apps/openmw/mwgui/cursor.cpp @@ -10,7 +10,7 @@ namespace MWGui ResourceImageSetPointerFix::ResourceImageSetPointerFix() - : mImageSet(NULL) + : mImageSet(nullptr) , mRotation(0) { } @@ -47,7 +47,7 @@ namespace MWGui void ResourceImageSetPointerFix::setImage(MyGUI::ImageBox* _image) { - if (mImageSet != NULL) + if (mImageSet != nullptr) _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0)); } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index aeb6dfc0f..3ac8839e2 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -137,7 +137,7 @@ namespace MWGui if (mTitle != "") { const MyGUI::Colour& headerColour = MWBase::Environment::get().getWindowManager()->getTextColours().header; - BookTypesetter::Style* title = typesetter->createStyle("", headerColour); + BookTypesetter::Style* title = typesetter->createStyle("", headerColour, false); typesetter->write(title, to_utf8_span(mTitle.c_str())); typesetter->sectionBreak(); } @@ -182,7 +182,7 @@ namespace MWGui { const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); - BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal); + BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal, false); size_t formatted = 0; // points to the first character that is not laid out yet for (std::map::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it) { @@ -223,7 +223,7 @@ namespace MWGui { const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); - BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal); + BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal, false); if (topicId) @@ -239,7 +239,7 @@ namespace MWGui void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { const MyGUI::Colour& textColour = MWBase::Environment::get().getWindowManager()->getTextColours().notify; - BookTypesetter::Style* title = typesetter->createStyle("", textColour); + BookTypesetter::Style* title = typesetter->createStyle("", textColour, false); typesetter->sectionBreak(9); typesetter->write(title, to_utf8_span(mText.c_str())); } @@ -365,15 +365,15 @@ namespace MWGui const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - const std::string sPersuasion = gmst.find("sPersuasion")->getString(); - const std::string sCompanionShare = gmst.find("sCompanionShare")->getString(); - const std::string sBarter = gmst.find("sBarter")->getString(); - const std::string sSpells = gmst.find("sSpells")->getString(); - const std::string sTravel = gmst.find("sTravel")->getString(); - const std::string sSpellMakingMenuTitle = gmst.find("sSpellMakingMenuTitle")->getString(); - const std::string sEnchanting = gmst.find("sEnchanting")->getString(); - const std::string sServiceTrainingTitle = gmst.find("sServiceTrainingTitle")->getString(); - const std::string sRepair = gmst.find("sRepair")->getString(); + const std::string sPersuasion = gmst.find("sPersuasion")->mValue.getString(); + const std::string sCompanionShare = gmst.find("sCompanionShare")->mValue.getString(); + const std::string sBarter = gmst.find("sBarter")->mValue.getString(); + const std::string sSpells = gmst.find("sSpells")->mValue.getString(); + const std::string sTravel = gmst.find("sTravel")->mValue.getString(); + const std::string sSpellMakingMenuTitle = gmst.find("sSpellMakingMenuTitle")->mValue.getString(); + const std::string sEnchanting = gmst.find("sEnchanting")->mValue.getString(); + const std::string sServiceTrainingTitle = gmst.find("sServiceTrainingTitle")->mValue.getString(); + const std::string sRepair = gmst.find("sRepair")->mValue.getString(); if (topic != sPersuasion && topic != sCompanionShare && topic != sBarter && topic != sSpells && topic != sTravel && topic != sSpellMakingMenuTitle @@ -458,7 +458,7 @@ namespace MWGui void DialogueWindow::restock() { MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); - float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getFloat(); + float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->mValue.getFloat(); // Gold is restocked every 24h if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) @@ -503,31 +503,31 @@ namespace MWGui MWBase::Environment::get().getWorld()->getStore().get(); if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addItem(gmst.find("sPersuasion")->getString()); + mTopicsList->addItem(gmst.find("sPersuasion")->mValue.getString()); if (services & ESM::NPC::AllItems) - mTopicsList->addItem(gmst.find("sBarter")->getString()); + mTopicsList->addItem(gmst.find("sBarter")->mValue.getString()); if (services & ESM::NPC::Spells) - mTopicsList->addItem(gmst.find("sSpells")->getString()); + mTopicsList->addItem(gmst.find("sSpells")->mValue.getString()); if (travel) - mTopicsList->addItem(gmst.find("sTravel")->getString()); + mTopicsList->addItem(gmst.find("sTravel")->mValue.getString()); if (services & ESM::NPC::Spellmaking) - mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); + mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->mValue.getString()); if (services & ESM::NPC::Enchanting) - mTopicsList->addItem(gmst.find("sEnchanting")->getString()); + mTopicsList->addItem(gmst.find("sEnchanting")->mValue.getString()); if (services & ESM::NPC::Training) - mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); + mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->mValue.getString()); if (services & ESM::NPC::Repair) - mTopicsList->addItem(gmst.find("sRepair")->getString()); + mTopicsList->addItem(gmst.find("sRepair")->mValue.getString()); if (isCompanion()) - mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); + mTopicsList->addItem(gmst.find("sCompanionShare")->mValue.getString()); if (mTopicsList->getItemCount() > 0) mTopicsList->addSeparator(); @@ -567,7 +567,7 @@ namespace MWGui (*it)->write(typesetter, &mKeywordSearch, mTopicLinks); - BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White); + BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White, false); typesetter->sectionBreak(9); // choices @@ -592,7 +592,7 @@ namespace MWGui Goodbye* link = new Goodbye(); link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated); mLinks.push_back(link); - std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString(); + std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->mValue.getString(); BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver, textColours.answerPressed, TypesetBook::InteractiveId(link)); diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index d81b2ed00..daf9f6636 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -20,10 +20,10 @@ namespace MWGui DragAndDrop::DragAndDrop() : mIsOnDragAndDrop(false) - , mDraggedWidget(NULL) - , mSourceModel(NULL) - , mSourceView(NULL) - , mSourceSortModel(NULL) + , mDraggedWidget(nullptr) + , mSourceModel(nullptr) + , mSourceView(nullptr) + , mSourceSortModel(nullptr) , mDraggedCount(0) { } diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index f7764e0f1..98980e339 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -28,7 +28,7 @@ namespace MWGui EnchantingDialog::EnchantingDialog() : WindowBase("openmw_enchanting_dialog.layout") , EffectEditorBase(EffectEditorBase::Enchanting) - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mName, "NameEdit"); getWidget(mCancelButton, "CancelButton"); @@ -288,6 +288,9 @@ namespace MWGui void EnchantingDialog::onAccept(MyGUI::EditBox *sender) { onBuyButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) @@ -341,7 +344,7 @@ namespace MWGui MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), mPtr)) { - std::string msg = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage49")->getString(); + std::string msg = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage49")->mValue.getString(); if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); MWBase::Environment::get().getWindowManager()->messageBox(msg); diff --git a/apps/openmw/mwgui/exposedwindow.cpp b/apps/openmw/mwgui/exposedwindow.cpp index 150a8c893..1a0484e72 100644 --- a/apps/openmw/mwgui/exposedwindow.cpp +++ b/apps/openmw/mwgui/exposedwindow.cpp @@ -14,7 +14,7 @@ namespace MWGui if (widgets.empty()) { MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' not found in skin of layout '" << getName() << "'"); - return NULL; + return nullptr; } else { diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 663bd7338..78359608e 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -27,7 +27,7 @@ namespace MWGui BookTextParser::BookTextParser(const std::string & text) : mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true), mClosingTag(false) { - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + MWScript::InterpreterContext interpreterContext(nullptr, MWWorld::Ptr()); // empty arguments, because there is no locals or actor mText = Interpreter::fixDefinesBook(mText, interpreterContext); boost::algorithm::replace_all(mText, "\r", ""); @@ -377,7 +377,7 @@ namespace MWGui if (attr.find("face") != attr.end()) { std::string face = attr.at("face"); - mTextStyle.mFont = face; + mTextStyle.mFont = "Journalbook "+face; } if (attr.find("size") != attr.end()) { @@ -415,7 +415,7 @@ namespace MWGui : GraphicElement(parent, pag, blockStyle), mTextStyle(textStyle) { - MyGUI::EditBox* box = parent->createWidget("NormalText", + Gui::EditBox* box = parent->createWidget("NormalText", MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, parent->getName() + MyGUI::utility::toString(parent->getChildCount())); box->setEditStatic(true); @@ -432,15 +432,6 @@ namespace MWGui mEditBox = box; } - int TextElement::currentFontHeight() const - { - std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); - MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fontName); - if (!font) - return 0; - return font->getDefaultHeight(); - } - int TextElement::getHeight() { return mEditBox->getTextSize().height; @@ -449,7 +440,7 @@ namespace MWGui int TextElement::pageSplit() { // split lines - const int lineHeight = currentFontHeight(); + const int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()); if (lineHeight > 0) lastLine /= lineHeight; diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index d525baace..b16a4e57d 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace MWGui { namespace Formatting @@ -12,7 +14,7 @@ namespace MWGui { TextStyle() : mColour(0,0,0) - , mFont("Default") + , mFont("Journalbook Magic Cards") , mTextSize(16) { } @@ -152,7 +154,7 @@ namespace MWGui private: int currentFontHeight() const; TextStyle mTextStyle; - MyGUI::EditBox * mEditBox; + Gui::EditBox * mEditBox; }; class ImageElement : public GraphicElement diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 1841303f2..6076c1c10 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -70,20 +70,20 @@ namespace MWGui HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender) : WindowBase("openmw_hud.layout") , LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool("local map hud fog of war", "Map")) - , mHealth(NULL) - , mMagicka(NULL) - , mStamina(NULL) - , mDrowning(NULL) - , mWeapImage(NULL) - , mSpellImage(NULL) - , mWeapStatus(NULL) - , mSpellStatus(NULL) - , mEffectBox(NULL) - , mMinimap(NULL) - , mCrosshair(NULL) - , mCellNameBox(NULL) - , mDrowningFrame(NULL) - , mDrowningFlash(NULL) + , mHealth(nullptr) + , mMagicka(nullptr) + , mStamina(nullptr) + , mDrowning(nullptr) + , mWeapImage(nullptr) + , mSpellImage(nullptr) + , mWeapStatus(nullptr) + , mSpellStatus(nullptr) + , mEffectBox(nullptr) + , mMinimap(nullptr) + , mCrosshair(nullptr) + , mCellNameBox(nullptr) + , mDrowningFrame(nullptr) + , mDrowningFlash(nullptr) , mHealthManaStaminaBaseLeft(0) , mWeapBoxBaseLeft(0) , mSpellBoxBaseLeft(0) @@ -247,7 +247,7 @@ namespace MWGui float mouseY = cursorPosition.top / float(viewSize.height); WorldItemModel drop (mouseX, mouseY); - mDragAndDrop->drop(&drop, NULL); + mDragAndDrop->drop(&drop, nullptr); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); } @@ -611,7 +611,7 @@ namespace MWGui // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :) mEnemyHealth->setProgressPosition(static_cast(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100)); - static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarFade")->getFloat(); + static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarFade")->mValue.getFloat(); if (fNPCHealthBarFade > 0.f) mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade))); @@ -620,7 +620,7 @@ namespace MWGui void HUD::setEnemy(const MWWorld::Ptr &enemy) { mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); - mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarTime")->getFloat(); + mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarTime")->mValue.getFloat(); if (!mEnemyHealth->getVisible()) mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); mEnemyHealth->setVisible(true); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0b35bd9de..1877ef97d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -60,14 +60,15 @@ namespace MWGui : WindowPinnableBase("openmw_inventory_window.layout") , mDragAndDrop(dragAndDrop) , mSelectedItem(-1) - , mSortModel(NULL) - , mTradeModel(NULL) + , mSortModel(nullptr) + , mTradeModel(nullptr) , mGuiMode(GM_Inventory) , mLastXSize(0) , mLastYSize(0) , mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer())) , mTrading(false) , mScaleFactor(1.0f) + , mUpdateTimer(0.f) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); if (uiScale > 1.0) @@ -156,9 +157,9 @@ namespace MWGui void InventoryWindow::clear() { mPtr = MWWorld::Ptr(); - mTradeModel = NULL; - mSortModel = NULL; - mItemView->setModel(NULL); + mTradeModel = nullptr; + mSortModel = nullptr; + mItemView->setModel(nullptr); } void InventoryWindow::setGuiMode(GuiMode mode) @@ -295,9 +296,9 @@ namespace MWGui { mSelectedItem = index; if (mTrading) - sellItem (NULL, count); + sellItem (nullptr, count); else - dragItem (NULL, count); + dragItem (nullptr, count); } } @@ -631,6 +632,22 @@ namespace MWGui void InventoryWindow::onFrame(float dt) { updateEncumbranceBar(); + + if (mPinned) + { + mUpdateTimer += dt; + if (0.1f < mUpdateTimer) + { + mUpdateTimer = 0; + + // Update pinned inventory in-game + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + mItemView->update(); + notifyContentChanged(); + } + } + } } void InventoryWindow::setTrading(bool trading) @@ -681,6 +698,8 @@ namespace MWGui return; int count = object.getRefData().getCount(); + if (object.getClass().isGold(object)) + count *= object.getClass().getValue(object); MWWorld::Ptr player = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->breakInvisibility(player); diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index d9cf6870c..c60e37363 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -102,6 +102,7 @@ namespace MWGui bool mTrading; float mScaleFactor; + float mUpdateTimer; void onItemSelected(int index); void onItemSelectedFromSourceModel(int index); diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 3001b9ef2..10c36c73f 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -21,7 +21,7 @@ namespace MWGui { ItemChargeView::ItemChargeView() - : mScrollView(NULL), + : mScrollView(nullptr), mDisplayMode(DisplayMode_Health) { } @@ -36,7 +36,7 @@ namespace MWGui Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item charge view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index e3f38a54c..c7c10e8e4 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -31,7 +31,7 @@ namespace MWGui ItemStack::ItemStack() : mType(Type_Normal) , mFlags(0) - , mCreator(NULL) + , mCreator(nullptr) , mCount(0) { } @@ -106,7 +106,7 @@ namespace MWGui ProxyItemModel::ProxyItemModel() - : mSourceModel(NULL) + : mSourceModel(nullptr) { } @@ -167,7 +167,7 @@ namespace MWGui if (mSourceModel) { delete mSourceModel; - mSourceModel = NULL; + mSourceModel = nullptr; } mSourceModel = sourceModel; diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index effd11d55..23f1398ea 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -12,8 +12,8 @@ namespace MWGui ItemSelectionDialog::ItemSelectionDialog(const std::string &label) : WindowModal("openmw_itemselection_dialog.layout") - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) { getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &ItemSelectionDialog::onSelectedItem); diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 2cdfcada4..94dcc77c5 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -18,8 +18,8 @@ namespace MWGui { ItemView::ItemView() - : mModel(NULL) - , mScrollView(NULL) + : mModel(nullptr) + , mScrollView(nullptr) { } @@ -44,7 +44,7 @@ void ItemView::initialiseOverride() Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp index 6b5be0314..223bced4f 100644 --- a/apps/openmw/mwgui/itemwidget.cpp +++ b/apps/openmw/mwgui/itemwidget.cpp @@ -32,10 +32,10 @@ namespace MWGui { ItemWidget::ItemWidget() - : mItem(NULL) - , mItemShadow(NULL) - , mFrame(NULL) - , mText(NULL) + : mItem(nullptr) + , mItemShadow(nullptr) + , mFrame(nullptr) + , mText(nullptr) { } diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 9fa5927ac..1761e1346 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -99,9 +99,9 @@ namespace MWGui std::string message; if (mDays == 1) - message = gmst.find("sNotifyMessage42")->getString(); + message = gmst.find("sNotifyMessage42")->mValue.getString(); else - message = gmst.find("sNotifyMessage43")->getString(); + message = gmst.find("sNotifyMessage43")->mValue.getString(); std::stringstream dayStr; dayStr << mDays; @@ -110,12 +110,12 @@ namespace MWGui for (std::set::iterator it = skills.begin(); it != skills.end(); ++it) { - std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->getString(); + std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->mValue.getString(); std::stringstream skillValue; skillValue << player.getClass().getNpcStats(player).getSkill(*it).getBase(); - std::string skillMsg = gmst.find("sNotifyMessage44")->getString(); + std::string skillMsg = gmst.find("sNotifyMessage44")->mValue.getString(); if (*it == ESM::Skill::Sneak || *it == ESM::Skill::Security) - skillMsg = gmst.find("sNotifyMessage39")->getString(); + skillMsg = gmst.find("sNotifyMessage39")->mValue.getString(); if (skillMsg.find("%s") != std::string::npos) skillMsg.replace(skillMsg.find("%s"), 2, skillName); diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 1075239fa..4302740f6 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -157,7 +157,7 @@ MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) typedef TypesetBook::Ptr book; JournalBooks::JournalBooks (JournalViewModel::Ptr model, ToUTF8::FromType encoding) : - mModel (model), mEncoding(encoding) + mModel (model), mEncoding(encoding), mIndexPagesCount(0) { } @@ -229,10 +229,13 @@ book JournalBooks::createTopicIndexBook () BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () { - BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); + // Latin journal index always has two columns for now. + mIndexPagesCount = 2; + char ch = 'A'; BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); @@ -260,12 +263,23 @@ BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () { - BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); + + // for small font size split alphabet to two columns (2x15 characers), for big font size split it to three colums (3x10 characters). + int sectionBreak = 10; + mIndexPagesCount = 3; + if (fontHeight < 18) + { + sectionBreak = 15; + mIndexPagesCount = 2; + } + unsigned char ch[2] = {0xd0, 0x90}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 for (int i = 0; i < 32; ++i) @@ -287,7 +301,7 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () if (i == 26 || i == 28) continue; - if (i == 15) + if (i % sectionBreak == 0) typesetter->sectionBreak (); typesetter->write (style, to_utf8_span (buffer)); diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index aa36eecdf..05eda6e22 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -25,6 +25,7 @@ namespace MWGui Book createTopicIndexBook (); ToUTF8::FromType mEncoding; + int mIndexPagesCount; private: BookTypesetter::Ptr createTypesetter (); diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 63b48eab1..3621656bc 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -39,7 +39,7 @@ struct JournalViewModelImpl : JournalViewModel static Utf8Span toUtf8Span (std::string const & str) { if (str.size () == 0) - return Utf8Span (Utf8Point (NULL), Utf8Point (NULL)); + return Utf8Span (Utf8Point (nullptr), Utf8Point (nullptr)); Utf8Point point = reinterpret_cast (str.c_str ()); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 0776706d8..a2f6ea142 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -43,6 +43,7 @@ namespace static char const LeftBookPage [] = "LeftBookPage"; static char const RightBookPage [] = "RightBookPage"; static char const LeftTopicIndex [] = "LeftTopicIndex"; + static char const CenterTopicIndex [] = "CenterTopicIndex"; static char const RightTopicIndex [] = "RightTopicIndex"; struct JournalWindowImpl : MWGui::JournalBooks, MWGui::JournalWindow @@ -148,6 +149,7 @@ namespace callback = std::bind(&JournalWindowImpl::notifyIndexLinkClicked, this, std::placeholders::_1); getPage (LeftTopicIndex)->adviseLinkClicked (callback); + getPage (CenterTopicIndex)->adviseLinkClicked (callback); getPage (RightTopicIndex)->adviseLinkClicked (callback); } @@ -312,6 +314,7 @@ namespace setVisible (TopicsList, false); setVisible (QuestsList, mQuestMode); setVisible (LeftTopicIndex, !mQuestMode); + setVisible (CenterTopicIndex, !mQuestMode); setVisible (RightTopicIndex, !mQuestMode); setVisible (ShowAllBTN, mQuestMode && !mAllQuests); setVisible (ShowActiveBTN, mQuestMode && mAllQuests); @@ -465,8 +468,17 @@ namespace if (!mTopicIndexBook) mTopicIndexBook = createTopicIndexBook (); - getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); - getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); + if (mIndexPagesCount == 3) + { + getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); + getPage (CenterTopicIndex)->showPage (mTopicIndexBook, 1); + getPage (RightTopicIndex)->showPage (mTopicIndexBook, 2); + } + else + { + getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); + getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); + } } void notifyJournal(MyGUI::Widget* _sender) @@ -480,6 +492,7 @@ namespace void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId index) { setVisible (LeftTopicIndex, false); + setVisible (CenterTopicIndex, false); setVisible (RightTopicIndex, false); setVisible (TopicsList, true); @@ -502,6 +515,7 @@ namespace mQuestMode = false; mTopicsMode = false; setVisible (LeftTopicIndex, true); + setVisible (CenterTopicIndex, true); setVisible (RightTopicIndex, true); setVisible (TopicsList, false); setVisible (QuestsList, false); @@ -540,6 +554,7 @@ namespace mQuestMode = true; setVisible (LeftTopicIndex, false); + setVisible (CenterTopicIndex, true); setVisible (RightTopicIndex, false); setVisible (TopicsList, false); setVisible (QuestsList, true); diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index cde8a17cc..52ff0b2d9 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" @@ -49,7 +51,14 @@ KeyboardNavigation::KeyboardNavigation() KeyboardNavigation::~KeyboardNavigation() { - MyGUI::WidgetManager::getInstance().unregisterUnlinker(this); + try + { + MyGUI::WidgetManager::getInstance().unregisterUnlinker(this); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void KeyboardNavigation::saveFocus(int mode) @@ -172,7 +181,7 @@ enum Direction D_Prev }; -bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text) +bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) { if (!mEnabled) return false; @@ -192,7 +201,14 @@ bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text) case MyGUI::KeyCode::Return: case MyGUI::KeyCode::NumpadEnter: case MyGUI::KeyCode::Space: + { + // We should disable repeating for activation keys + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::None); + if (repeat) + return true; + return accept(); + } default: return false; } diff --git a/apps/openmw/mwgui/keyboardnavigation.hpp b/apps/openmw/mwgui/keyboardnavigation.hpp index 7caf25690..2a094a2df 100644 --- a/apps/openmw/mwgui/keyboardnavigation.hpp +++ b/apps/openmw/mwgui/keyboardnavigation.hpp @@ -14,7 +14,7 @@ namespace MWGui ~KeyboardNavigation(); /// @return Was the key handled by this class? - bool injectKeyPress(MyGUI::KeyCode key, unsigned int text); + bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat); void saveFocus(int mode); void restoreFocus(int mode); diff --git a/apps/openmw/mwgui/layout.hpp b/apps/openmw/mwgui/layout.hpp index 0e7cf3faa..ea51bf541 100644 --- a/apps/openmw/mwgui/layout.hpp +++ b/apps/openmw/mwgui/layout.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace MWGui { /** The Layout class is an utility class used to load MyGUI layouts @@ -16,7 +18,17 @@ namespace MWGui Layout(const std::string & _layout, MyGUI::Widget* _parent = nullptr) : mMainWidget(nullptr) { initialise(_layout, _parent); } - virtual ~Layout() { shutdown(); } + virtual ~Layout() + { + try + { + shutdown(); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } + } MyGUI::Widget* getWidget(const std::string& _name); diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index be3d477e1..38ab70d54 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -140,7 +140,7 @@ namespace MWGui // Callback removes itself when done if (renderInfo.getCurrentCamera()) - renderInfo.getCurrentCamera()->setInitialDrawCallback(NULL); + renderInfo.getCurrentCamera()->setInitialDrawCallback(nullptr); } private: @@ -208,7 +208,7 @@ namespace MWGui else mImportantLabel = false; // label was already shown on loading screen - mViewer->getSceneData()->setComputeBoundingSphereCallback(NULL); + mViewer->getSceneData()->setComputeBoundingSphereCallback(nullptr); mViewer->getSceneData()->dirtyBound(); //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 5017b8893..1b9e024ba 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -25,10 +25,10 @@ namespace MWGui : WindowBase("openmw_mainmenu.layout") , mWidth (w), mHeight (h) , mVFS(vfs), mButtonBox(0) - , mBackground(NULL) - , mVideoBackground(NULL) - , mVideo(NULL) - , mSaveGameDialog(NULL) + , mBackground(nullptr) + , mVideoBackground(nullptr) + , mVideo(nullptr) + , mSaveGameDialog(nullptr) { getWidget(mVersionText, "VersionText"); mVersionText->setCaption(versionDescription); @@ -147,13 +147,13 @@ namespace MWGui if (mVideo && !show) { MyGUI::Gui::getInstance().destroyWidget(mVideoBackground); - mVideoBackground = NULL; - mVideo = NULL; + mVideoBackground = nullptr; + mVideo = nullptr; } if (mBackground && !show) { MyGUI::Gui::getInstance().destroyWidget(mBackground); - mBackground = NULL; + mBackground = nullptr; } if (!show) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index c1ff9510d..b9b59965d 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -33,7 +33,7 @@ namespace { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; enum LocalMapWidgetDepth { @@ -161,8 +161,8 @@ namespace MWGui , mCurX(0) , mCurY(0) , mInterior(false) - , mLocalMap(NULL) - , mCompass(NULL) + , mLocalMap(nullptr) + , mCompass(nullptr) , mChanged(true) , mFogOfWarToggled(true) , mFogOfWarEnabled(fogOfWarEnabled) @@ -388,7 +388,7 @@ namespace MWGui box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } else - box->setRenderItemTexture(NULL); + box->setRenderItemTexture(nullptr); } } mMapTextures.swap(textures); @@ -598,7 +598,7 @@ namespace MWGui addDetectionMarkers(MWBase::World::Detect_Enchantment); // Add marker for the spot marked with Mark magic effect - MWWorld::CellStore* markedCell = NULL; + MWWorld::CellStore* markedCell = nullptr; ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); if (markedCell && markedCell->isExterior() == !mInterior @@ -627,11 +627,11 @@ namespace MWGui , LocalMapBase(customMarkers, localMapRender) , NoDrop(drag, mMainWidget) , mGlobalMap(0) - , mGlobalMapImage(NULL) - , mGlobalMapOverlay(NULL) + , mGlobalMapImage(nullptr) + , mGlobalMapOverlay(nullptr) , mGlobal(Settings::Manager::getBool("global", "Map")) - , mEventBoxGlobal(NULL) - , mEventBoxLocal(NULL) + , mEventBoxGlobal(nullptr) + , mEventBoxLocal(nullptr) , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue)) , mEditNoteDialog() { @@ -872,7 +872,7 @@ namespace MWGui if (!destNotes.empty()) { - MarkerUserData data (NULL); + MarkerUserData data (nullptr); data.notes = destNotes; data.caption = markerWidget->getUserString("Caption_TextOneLine"); markerWidget->setUserData(data); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index d3504ebc3..b9d4c80f4 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -20,8 +20,6 @@ namespace MWGui { -const int MerchantRepair::sLineHeight = 18; - MerchantRepair::MerchantRepair() : WindowBase("openmw_merchantrepair.layout") { @@ -39,6 +37,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) while (mList->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mList->getChildAt(0)); + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; int currentY = 0; MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -57,7 +56,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) int basePrice = iter->getClass().getValue(*iter); float fRepairMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fRepairMult")->getFloat(); + .find("fRepairMult")->mValue.getFloat(); float p = static_cast(std::max(1, basePrice)); float r = static_cast(std::max(1, static_cast(maxDurability / p))); @@ -67,28 +66,26 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true); - std::string name = iter->getClass().getName(*iter) + " - " + MyGUI::utility::toString(price) + MWBase::Environment::get().getWorld()->getStore().get() - .find("sgp")->getString(); - + .find("sgp")->mValue.getString(); MyGUI::Button* button = mList->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip 0, currentY, 0, - sLineHeight, + lineHeight, MyGUI::Align::Default ); - currentY += sLineHeight; + currentY += lineHeight; button->setUserString("Price", MyGUI::utility::toString(price)); button->setUserData(MWWorld::Ptr(*iter)); button->setCaptionWithReplacing(name); - button->setSize(mList->getWidth(),sLineHeight); + button->setSize(mList->getWidth(), lineHeight); button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel); button->setUserString("ToolTipType", "ItemPtr"); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick); diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index 4e4e7164f..26887ae2c 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -27,8 +27,6 @@ protected: void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onRepairButtonClick(MyGUI::Widget* sender); void onOkButtonClick(MyGUI::Widget* sender); - - static const int sLineHeight; }; } diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 2fbce97d4..e83c4b238 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -20,8 +20,8 @@ namespace MWGui MessageBoxManager::MessageBoxManager (float timePerChar) { - mInterMessageBoxe = NULL; - mStaticMessageBox = NULL; + mInterMessageBoxe = nullptr; + mStaticMessageBox = nullptr; mLastButtonPressed = -1; mMessageBoxSpeed = timePerChar; } @@ -42,14 +42,14 @@ namespace MWGui mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; } std::vector::iterator it(mMessageBoxes.begin()); for (; it != mMessageBoxes.end(); ++it) { if (*it == mStaticMessageBox) - mStaticMessageBox = NULL; + mStaticMessageBox = nullptr; delete *it; } mMessageBoxes.clear(); @@ -81,11 +81,11 @@ namespace MWGui ++it; } - if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { + if(mInterMessageBoxe != nullptr && mInterMessageBoxe->mMarkedToDelete) { mLastButtonPressed = mInterMessageBoxe->readPressedButton(); mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; MWBase::Environment::get().getInputManager()->changeInputMode( MWBase::Environment::get().getWindowManager()->isGuiMode()); } @@ -119,17 +119,17 @@ namespace MWGui void MessageBoxManager::removeStaticMessageBox () { removeMessageBox(mStaticMessageBox); - mStaticMessageBox = NULL; + mStaticMessageBox = nullptr; } bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) { - if (mInterMessageBoxe != NULL) + if (mInterMessageBoxe != nullptr) { Log(Debug::Warning) << "Warning: replacing an interactive message box that was not answered yet"; mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; } mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); @@ -140,7 +140,7 @@ namespace MWGui bool MessageBoxManager::isInteractiveMessageBox () { - return mInterMessageBoxe != NULL; + return mInterMessageBoxe != nullptr; } @@ -373,7 +373,7 @@ namespace MWGui } } } - return NULL; + return nullptr; } void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 8041c50c5..23ad87fd6 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -254,6 +254,8 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0)); mSelected->type = Type_MagicItem; + mSelected->id = item.getCellRef().getRefId(); + mSelected->name = item.getClass().getName(item); mSelected->button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40)); mSelected->button->setIcon(item); @@ -271,20 +273,19 @@ namespace MWGui while (mSelected->button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0)); + const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Spell* spell = esmStore.get().find(spellId); + mSelected->type = Type_Magic; mSelected->id = spellId; + mSelected->name = spell->mName; mSelected->button->setItem(MWWorld::Ptr()); mSelected->button->setUserString("ToolTipType", "Spell"); mSelected->button->setUserString("Spell", spellId); - const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); - // use the icon of the first effect - const ESM::Spell* spell = esmStore.get().find(spellId); - - const ESM::MagicEffect* effect = - esmStore.get().find(spell->mEffects.mList.front().mEffectID); + const ESM::MagicEffect* effect = esmStore.get().find(spell->mEffects.mList.front().mEffectID); std::string path = effect->mIcon; int slashPos = path.rfind('\\'); @@ -423,9 +424,7 @@ namespace MWGui if (!spells.hasSpell(spellId)) { - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + spell->mName); + MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + key->name); return; } @@ -547,28 +546,26 @@ namespace MWGui return; mSelected = &mKey[i]; - mSelected->type = (QuickKeysMenu::QuickKeyType) it->mType; - mSelected->id = it->mId; - switch (mSelected->type) + switch (it->mType) { case Type_Magic: - if (MWBase::Environment::get().getWorld()->getStore().get().search(mSelected->id)) - onAssignMagic(mSelected->id); + if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mId)) + onAssignMagic(it->mId); break; case Type_Item: case Type_MagicItem: { // Find the item by id - MWWorld::Ptr item = store.findReplacement(mSelected->id); + MWWorld::Ptr item = store.findReplacement(it->mId); if (item.isEmpty()) - unassign(&mKey[i]); + unassign(mSelected); else { - if (mSelected->type == Type_Item) + if (it->mType == Type_Item) onAssignItem(item); - else if (mSelected->type == Type_MagicItem) + else // if (it->mType == Type_MagicItem) onAssignMagicItem(item); } @@ -576,7 +573,7 @@ namespace MWGui } case Type_Unassigned: case Type_HandToHand: - unassign(&mKey[i]); + unassign(mSelected); break; } diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 96c0d7de4..be0dff660 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -129,10 +129,10 @@ namespace MWGui updateSkills(); updateSpellPowers(); - mPreviewImage->setRenderItemTexture(NULL); + mPreviewImage->setRenderItemTexture(nullptr); - mPreview.reset(NULL); - mPreviewTexture.reset(NULL); + mPreview.reset(nullptr); + mPreviewTexture.reset(nullptr); mPreview.reset(new MWRender::RaceSelectionPreview(mParent, mResourceSystem)); mPreview->rebuild(); @@ -190,10 +190,10 @@ namespace MWGui { WindowModal::onClose(); - mPreviewImage->setRenderItemTexture(NULL); + mPreviewImage->setRenderItemTexture(nullptr); - mPreviewTexture.reset(NULL); - mPreview.reset(NULL); + mPreviewTexture.reset(nullptr); + mPreview.reset(nullptr); } // widget controls @@ -212,7 +212,7 @@ namespace MWGui void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position) { - float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5f) * 3.14f * 2; + float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5f) * osg::PI * 2; mPreview->setAngle (angle); mCurrentAngle = angle; diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 26a364f72..56bd97d5e 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -18,7 +18,6 @@ #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "itemwidget.hpp" @@ -31,7 +30,7 @@ namespace MWGui Recharge::Recharge() : WindowBase("openmw_recharge_dialog.layout") - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mBox, "Box"); getWidget(mGemBox, "GemBox"); @@ -90,7 +89,7 @@ void Recharge::updateView() mBox->update(); Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); @@ -139,7 +138,6 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified(); if (luckTerm < 1|| luckTerm > 10) @@ -152,7 +150,7 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) if (intelligenceTerm < 1) intelligenceTerm = 1; - float x = (npcStats.getSkill(ESM::Skill::Enchant).getModified() + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); + float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); int roll = Misc::Rng::roll0to99(); if (roll < x) { @@ -169,19 +167,18 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) MWBase::Environment::get().getWindowManager()->playSound("Enchant Success"); player.getClass().getContainerStore(player).restack(item); - - player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); } else { MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail"); } + player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); gem.getContainerStore()->remove(gem, 1, player); if (gem.getRefData().getCount() == 0) { - std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage51")->getString(); + std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage51")->mValue.getString(); message = boost::str(boost::format(message) % gem.getClass().getName(gem)); MWBase::Environment::get().getWindowManager()->messageBox(message); diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 11a8aece2..ea79e0326 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -27,7 +27,7 @@ namespace MWGui Repair::Repair() : WindowBase("openmw_repair.layout") - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mRepairBox, "RepairBox"); getWidget(mToolBox, "ToolBox"); @@ -99,7 +99,7 @@ void Repair::updateRepairView() mRepairBox->update(); Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 61a7a2ac0..f2f1cf892 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -26,9 +26,6 @@ namespace namespace MWGui { - - const int ReviewDialog::sLineHeight = 18; - ReviewDialog::ReviewDialog() : WindowModal("openmw_chargen_review.layout"), mUpdateSkillArea(false) @@ -261,8 +258,9 @@ namespace MWGui groupWidget->setCaption(label); mSkillWidgets.push_back(groupWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) @@ -282,8 +280,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return skillValueWidget; } @@ -298,8 +297,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } void ReviewDialog::addItem(const ESM::Spell* spell, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2) @@ -312,8 +312,9 @@ namespace MWGui mSkillWidgets.push_back(widget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) @@ -378,7 +379,7 @@ namespace MWGui // starting spells std::vector spells; - const ESM::Race* race = NULL; + const ESM::Race* race = nullptr; if (!mRaceId.empty()) race = MWBase::Environment::get().getWorld()->getStore().get().find(mRaceId); diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 8e9ec0ec7..f46ad280d 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -87,8 +87,6 @@ namespace MWGui void addItem(const ESM::Spell* spell, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void updateSkillArea(); - static const int sLineHeight; - MyGUI::TextBox *mNameWidget, *mRaceWidget, *mClassWidget, *mBirthSignWidget; MyGUI::ScrollView* mSkillView; diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 45790cbf5..2027210d7 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -37,8 +37,8 @@ namespace MWGui SaveGameDialog::SaveGameDialog() : WindowModal("openmw_savegame_dialog.layout") , mSaving(true) - , mCurrentCharacter(NULL) - , mCurrentSlot(NULL) + , mCurrentCharacter(nullptr) + , mCurrentSlot(nullptr) { getWidget(mScreenshot, "Screenshot"); getWidget(mCharacterSelection, "SelectCharacter"); @@ -99,7 +99,7 @@ namespace MWGui if (mSaveList->getItemCount() == 0) { size_t previousIndex = mCharacterSelection->getIndexSelected(); - mCurrentCharacter = NULL; + mCurrentCharacter = nullptr; mCharacterSelection->removeItemAt(previousIndex); if (mCharacterSelection->getItemCount()) { @@ -127,6 +127,9 @@ namespace MWGui void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender) { accept(); + + // To do not spam onEditSelectAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void SaveGameDialog::onOpen() @@ -143,8 +146,8 @@ namespace MWGui mCharacterSelection->setCaption(""); mCharacterSelection->removeAllItems(); - mCurrentCharacter = NULL; - mCurrentSlot = NULL; + mCurrentCharacter = nullptr; + mCurrentSlot = nullptr; mSaveList->removeAllItems(); onSlotSelected(mSaveList, MyGUI::ITEM_NONE); @@ -247,12 +250,12 @@ namespace MWGui void SaveGameDialog::accept(bool reallySure) { // Remove for MyGUI 3.2.2 - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); if (mSaving) { // If overwriting an existing slot, ask for confirmation first - if (mCurrentSlot != NULL && !reallySure) + if (mCurrentSlot != nullptr && !reallySure) { ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sMessage4}"); @@ -315,7 +318,7 @@ namespace MWGui MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); unsigned int i=0; - const MWState::Character* character = NULL; + const MWState::Character* character = nullptr; for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i) { if (i == pos) @@ -324,7 +327,7 @@ namespace MWGui assert(character && "Can't find selected character"); mCurrentCharacter = character; - mCurrentSlot = NULL; + mCurrentSlot = nullptr; fillSaveList(); } @@ -376,7 +379,7 @@ namespace MWGui if (pos == MyGUI::ITEM_NONE || !mCurrentCharacter) { - mCurrentSlot = NULL; + mCurrentSlot = nullptr; mInfoText->setCaption(""); mScreenshot->setImageTexture(""); return; @@ -385,7 +388,7 @@ namespace MWGui if (mSaving) mSaveNameEdit->setCaption(sender->getItemNameAt(pos)); - mCurrentSlot = NULL; + mCurrentSlot = nullptr; unsigned int i=0; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i) { @@ -401,7 +404,7 @@ namespace MWGui timeinfo = localtime(&time); // Use system/environment locale settings for datetime formatting - char* oldLctime = setlocale(LC_TIME, NULL); + char* oldLctime = setlocale(LC_TIME, nullptr); setlocale(LC_TIME, ""); const int size=1024; diff --git a/apps/openmw/mwgui/screenfader.cpp b/apps/openmw/mwgui/screenfader.cpp index 9a9a1ae01..e75e4954e 100644 --- a/apps/openmw/mwgui/screenfader.cpp +++ b/apps/openmw/mwgui/screenfader.cpp @@ -99,7 +99,14 @@ namespace MWGui ScreenFader::~ScreenFader() { - MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + try + { + MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void ScreenFader::onFrameStart(float dt) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index d9c3a5f16..bc5d161d8 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -18,8 +18,6 @@ namespace MWGui { - const int SpellBuyingWindow::sLineHeight = 18; - SpellBuyingWindow::SpellBuyingWindow() : WindowBase("openmw_spell_buying_window.layout") , mCurrentY(0) @@ -44,7 +42,7 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int price = static_cast(spell.mData.mCost*store.get().find("fSpellValueMult")->getFloat()); + int price = static_cast(spell.mData.mCost*store.get().find("fSpellValueMult")->mValue.getFloat()); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -52,21 +50,23 @@ namespace MWGui // TODO: refactor to use MyGUI::ListBox + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + MyGUI::Button* toAdd = mSpellsView->createWidget( price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip 0, mCurrentY, 200, - sLineHeight, + lineHeight, MyGUI::Align::Default ); - mCurrentY += sLineHeight; + mCurrentY += lineHeight; toAdd->setUserData(price); toAdd->setCaptionWithReplacing(spell.mName+" - "+MyGUI::utility::toString(price)+"#{sgp}"); - toAdd->setSize(mSpellsView->getWidth(),sLineHeight); + toAdd->setSize(mSpellsView->getWidth(), lineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); toAdd->setUserString("Spell", spell.mId); diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 3414c1b94..30727a545 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -48,8 +48,6 @@ namespace MWGui void clearSpells(); int mCurrentY; - static const int sLineHeight; - void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 4aa208d8f..46d8b38ec 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -32,8 +32,8 @@ namespace const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - return gmst.find(ESM::MagicEffect::effectIdToString (id1))->getString() - < gmst.find(ESM::MagicEffect::effectIdToString (id2))->getString(); + return gmst.find(ESM::MagicEffect::effectIdToString (id1))->mValue.getString() + < gmst.find(ESM::MagicEffect::effectIdToString (id2))->mValue.getString(); } void init(ESM::ENAMstruct& effect) @@ -55,7 +55,7 @@ namespace MWGui EditEffectDialog::EditEffectDialog() : WindowModal("openmw_edit_effect.layout") , mEditing(false) - , mMagicEffect(NULL) + , mMagicEffect(nullptr) , mConstantEffect(false) { init(mEffect); @@ -147,7 +147,9 @@ namespace MWGui mDurationValue->setCaption("1"); mMagnitudeMinValue->setCaption("1"); - mMagnitudeMaxValue->setCaption("- 1"); + const std::string to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); + + mMagnitudeMaxValue->setCaption(to + " 1"); mAreaValue->setCaption("0"); setVisible(true); @@ -312,8 +314,9 @@ namespace MWGui } mEffect.mMagnMax = pos+1; + const std::string to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); - mMagnitudeMaxValue->setCaption("- " + MyGUI::utility::toString(pos+1)); + mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos+1)); eventEffectModified(mEffect); } @@ -420,6 +423,9 @@ namespace MWGui void SpellCreationDialog::onAccept(MyGUI::EditBox *sender) { onBuyButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void SpellCreationDialog::onOpen() @@ -469,13 +475,13 @@ namespace MWGui mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); float fSpellMakingValueMult = - store.get().find("fSpellMakingValueMult")->getFloat(); + store.get().find("fSpellMakingValueMult")->mValue.getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, static_cast(y * fSpellMakingValueMult),true); mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); - float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), NULL); + float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), nullptr); int intChance = std::min(100, int(chance)); mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); @@ -485,11 +491,11 @@ namespace MWGui EffectEditorBase::EffectEditorBase(Type type) - : mAvailableEffectsList(NULL) - , mUsedEffectsView(NULL) + : mAvailableEffectsList(nullptr) + , mUsedEffectsView(nullptr) , mAddEffectDialog() - , mSelectAttributeDialog(NULL) - , mSelectSkillDialog(NULL) + , mSelectAttributeDialog(nullptr) + , mSelectSkillDialog(nullptr) , mSelectedEffect(0) , mSelectedKnownEffectId(0) , mConstantEffect(false) @@ -547,7 +553,7 @@ namespace MWGui for (std::vector::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) { mAvailableEffectsList->addItem(MWBase::Environment::get().getWorld ()->getStore ().get().find( - ESM::MagicEffect::effectIdToString (*it))->getString()); + ESM::MagicEffect::effectIdToString (*it))->mValue.getString()); mButtonMapping[i] = *it; ++i; } @@ -557,7 +563,7 @@ namespace MWGui for (std::vector::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) { std::string name = MWBase::Environment::get().getWorld ()->getStore ().get().find( - ESM::MagicEffect::effectIdToString (*it))->getString(); + ESM::MagicEffect::effectIdToString (*it))->mValue.getString(); MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); ToolTips::createMagicEffectToolTip (w, *it); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 27896381e..c0f56e654 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -75,7 +75,7 @@ namespace MWGui std::string sourcesDescription; - static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get().find("fMagicStartIconBlink")->getFloat(); + static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get().find("fMagicStartIconBlink")->mValue.getFloat(); for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 0933737ca..6dadebca2 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -71,7 +71,7 @@ namespace MWGui { newSpell.mType = Spell::Type_Spell; std::string cost = std::to_string(spell->mData.mCost); - std::string chance = std::to_string(int(MWMechanics::getSpellSuccessChance(spell, mActor, NULL, true, true))); + std::string chance = std::to_string(int(MWMechanics::getSpellSuccessChance(spell, mActor, nullptr, true, true))); newSpell.mCostColumn = cost + "/" + chance; } else diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 758e6b306..879ce471f 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "tooltips.hpp" @@ -23,7 +24,7 @@ namespace MWGui } SpellView::SpellView() - : mScrollView(NULL) + : mScrollView(nullptr) , mShowCostColumn(true) , mHighlightSelected(true) { @@ -34,7 +35,7 @@ namespace MWGui Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); @@ -130,7 +131,7 @@ namespace MWGui mLines.push_back(LineInfo(t, costChance, i)); } else - mLines.push_back(LineInfo(t, (MyGUI::Widget*)NULL, i)); + mLines.push_back(LineInfo(t, (MyGUI::Widget*)nullptr, i)); t->setStateSelected(spell.mSelected); } @@ -176,7 +177,7 @@ namespace MWGui { maxSpellIndexFound = spellIndex; Gui::SharedStateButton* costButton = reinterpret_cast(it->mRightWidget); - if ((costButton != NULL) && (costButton->getCaption() != spell.mCostColumn)) + if ((costButton != nullptr) && (costButton->getCaption() != spell.mCostColumn)) { costButton->setCaption(spell.mCostColumn); } @@ -237,10 +238,10 @@ namespace MWGui MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18), MyGUI::Align::Left | MyGUI::Align::Top); separator->setNeedMouseFocus(false); - mLines.push_back(LineInfo(separator, (MyGUI::Widget*)NULL, NoSpellIndex)); + mLines.push_back(LineInfo(separator, (MyGUI::Widget*)nullptr, NoSpellIndex)); } - MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", + MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top); groupWidget->setCaptionWithReplacing(label); @@ -249,7 +250,7 @@ namespace MWGui if (label2 != "") { - MyGUI::TextBox* groupWidget2 = mScrollView->createWidget("SandBrightText", + MyGUI::TextBox* groupWidget2 = mScrollView->createWidget("SandBrightText", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top); groupWidget2->setCaptionWithReplacing(label2); @@ -259,7 +260,7 @@ namespace MWGui mLines.push_back(LineInfo(groupWidget, groupWidget2, NoSpellIndex)); } else - mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)NULL, NoSpellIndex)); + mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex)); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 3fe171e4e..2177e9e2a 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -32,7 +32,7 @@ namespace MWGui SpellWindow::SpellWindow(DragAndDrop* drag) : WindowPinnableBase("openmw_spell_window.layout") , NoDrop(drag, mMainWidget) - , mSpellView(NULL) + , mSpellView(nullptr) , mUpdateTimer(0.0f) { mSpellIcons = new SpellIcons(); @@ -72,7 +72,7 @@ namespace MWGui // Reset the filter focus when opening the window MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); if (focus == mFilterEdit) - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); updateSpells(); } @@ -86,6 +86,10 @@ namespace MWGui mUpdateTimer = 0; mSpellView->incrementalUpdate(); } + + // Update effects in-game too if the window is pinned + if (mPinned && !MWBase::Environment::get().getWindowManager()->isGuiMode()) + mSpellIcons->updateWidgets(mEffectBox, false); } void SpellWindow::updateSpells() diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index d3d487579..e58993a55 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -23,13 +23,10 @@ namespace MWGui { - - const int StatsWindow::sLineHeight = 18; - StatsWindow::StatsWindow (DragAndDrop* drag) : WindowPinnableBase("openmw_stats_window.layout") , NoDrop(drag, mMainWidget) - , mSkillView(NULL) + , mSkillView(nullptr) , mMajorSkills() , mMinorSkills() , mMiscSkills() @@ -61,7 +58,7 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); for (int i=0; names[i][0]; ++i) { - setText (names[i][0], store.get().find (names[i][1])->getString()); + setText (names[i][0], store.get().find (names[i][1])->mValue.getString()); } getWidget(mSkillView, "SkillView"); @@ -71,7 +68,7 @@ namespace MWGui for (int i = 0; i < ESM::Skill::Length; ++i) { mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue())); - mSkillWidgetMap.insert(std::make_pair(i, std::make_pair((MyGUI::TextBox*)NULL, (MyGUI::TextBox*)NULL))); + mSkillWidgetMap.insert(std::make_pair(i, std::make_pair((MyGUI::TextBox*)nullptr, (MyGUI::TextBox*)nullptr))); } MyGUI::Window* t = mMainWidget->castType(); @@ -306,7 +303,7 @@ namespace MWGui MyGUI::Widget* levelWidget; for (int i=0; i<2; ++i) { - int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->getInt(); + int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->mValue.getInteger(); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); levelWidget->setUserString("RangePosition_LevelProgress", MyGUI::utility::toString(PCstats.getLevelProgress())); levelWidget->setUserString("Range_LevelProgress", MyGUI::utility::toString(max)); @@ -376,8 +373,9 @@ namespace MWGui groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); mSkillWidgets.push_back(groupWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } std::pair StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) @@ -401,8 +399,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return std::make_pair(skillNameWidget, skillValueWidget); } @@ -421,8 +420,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return skillNameWidget; } diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 487953e00..8dab2f3d9 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -53,8 +53,6 @@ namespace MWGui void onWindowResize(MyGUI::Window* window); void onMouseWheel(MyGUI::Widget* _sender, int _rel); - static const int sLineHeight; - MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 169c9e742..54f2d3be9 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -65,6 +65,9 @@ namespace MWGui void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) { onOkClicked(_sender); + + // To do not spam onTextAccepted() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } std::string TextInputDialog::getTextInput() const diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 20814aac5..0bd0d191c 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -421,7 +422,7 @@ namespace MWGui std::string realImage = MWBase::Environment::get().getWindowManager()->correctIconPath(image); - MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); + Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); captionWidget->setEditStatic(true); captionWidget->setNeedKeyFocus(false); captionWidget->setCaptionWithReplacing(caption); @@ -429,7 +430,7 @@ namespace MWGui int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); - MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); + Gui::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); textWidget->setEditStatic(true); textWidget->setEditMultiLine(true); textWidget->setEditWordWrap(info.wordWrap); @@ -447,7 +448,7 @@ namespace MWGui MyGUI::ImageBox* icon = mDynamicToolTipBox->createWidget("MarkerButton", MyGUI::IntCoord(padding.left, totalSize.height+padding.top, 8, 8), MyGUI::Align::Default); icon->setColour(MyGUI::Colour(1.0f, 0.3f, 0.3f)); - MyGUI::EditBox* edit = mDynamicToolTipBox->createWidget("SandText", + Gui::EditBox* edit = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(padding.left+8+4, totalSize.height+padding.top, 300-padding.left-8-4, 300-totalSize.height), MyGUI::Align::Default); edit->setEditMultiLine(true); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index e11147c74..4404b2b1a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -1,7 +1,5 @@ #include "tradewindow.hpp" -#include - #include #include #include @@ -52,8 +50,8 @@ namespace MWGui TradeWindow::TradeWindow() : WindowBase("openmw_trade_window.layout") - , mSortModel(NULL) - , mTradeModel(NULL) + , mSortModel(nullptr) + , mTradeModel(nullptr) , mItemToSell(-1) , mCurrentBalance(0) , mCurrentMerchantOffer(0) @@ -96,7 +94,7 @@ namespace MWGui mTotalBalance->eventValueChanged += MyGUI::newDelegate(this, &TradeWindow::onBalanceValueChanged); mTotalBalance->eventEditSelectAccept += MyGUI::newDelegate(this, &TradeWindow::onAccept); - mTotalBalance->setMinValue(INT_MIN+1); // disallow INT_MIN since abs(INT_MIN) is undefined + mTotalBalance->setMinValue(std::numeric_limits::min()+1); // disallow INT_MIN since abs(INT_MIN) is undefined setCoord(400, 0, 400, 300); } @@ -207,7 +205,7 @@ namespace MWGui else { mItemToSell = mSortModel->mapToSource(index); - sellItem (NULL, count); + sellItem (nullptr, count); } } @@ -313,7 +311,7 @@ namespace MWGui { if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(), mPtr)) { - std::string msg = gmst.find("sNotifyMessage49")->getString(); + std::string msg = gmst.find("sNotifyMessage49")->mValue.getString(); 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); @@ -331,8 +329,8 @@ namespace MWGui // apply disposition change if merchant is NPC if ( mPtr.getClass().isNpc() ) { int dispositionDelta = offerAccepted - ? gmst.find("iBarterSuccessDisposition")->getInt() - : gmst.find("iBarterFailDisposition")->getInt(); + ? gmst.find("iBarterSuccessDisposition")->mValue.getInteger() + : gmst.find("iBarterFailDisposition")->mValue.getInteger(); MWBase::Environment::get().getDialogueManager()->applyBarterDispositionChange(dispositionDelta); } @@ -367,6 +365,9 @@ namespace MWGui void TradeWindow::onAccept(MyGUI::EditBox *sender) { onOfferButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender) @@ -428,7 +429,7 @@ namespace MWGui void TradeWindow::onIncreaseButtonTriggered() { // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined - if (mCurrentBalance == INT_MAX || mCurrentBalance == INT_MIN+1) + if (mCurrentBalance == std::numeric_limits::max() || mCurrentBalance == std::numeric_limits::min()+1) return; if (mCurrentBalance < 0) mCurrentBalance -= 1; else mCurrentBalance += 1; @@ -513,8 +514,8 @@ namespace MWGui void TradeWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mTradeModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mTradeModel = nullptr; + mSortModel = nullptr; } } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 630ba8ce1..60e6584c7 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -73,14 +73,12 @@ namespace MWGui mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); - MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); - // NPC can train you in his best 3 skills std::vector< std::pair > skills; for (int i=0; igetBarterOffer - (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); + (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->mValue.getInteger(),true); MyGUI::Button* button = mTrainingOptions->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); @@ -136,14 +134,13 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); + int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->mValue.getInteger(); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; - MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats (mPtr); - if (npcStats.getSkill (skillId).getModified () <= pcStats.getSkill (skillId).getBase ()) + if (mPtr.getClass().getSkill(mPtr, skillId) <= pcStats.getSkill (skillId).getBase ()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}"); return; @@ -168,6 +165,7 @@ namespace MWGui player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool + MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats(mPtr); npcStats.setGoldPool(npcStats.getGoldPool() + price); // advance time diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index cf4fb1b5e..5f5430524 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -22,8 +22,6 @@ namespace MWGui { - const int TravelWindow::sLineHeight = 18; - TravelWindow::TravelWindow() : WindowBase("openmw_travel_window.layout") , mCurrentY(0) @@ -58,13 +56,13 @@ namespace MWGui if (!mPtr.getCell()->isExterior()) { - price = gmst.find("fMagesGuildTravel")->getInt(); + price = gmst.find("fMagesGuildTravel")->mValue.getInteger(); } else { ESM::Position PlayerPos = player.getRefData().getPosition(); float d = sqrt(pow(pos.pos[0] - PlayerPos.pos[0], 2) + pow(pos.pos[1] - PlayerPos.pos[1], 2) + pow(pos.pos[2] - PlayerPos.pos[2], 2)); - price = static_cast(d / gmst.find("fTravelMult")->getFloat()); + price = static_cast(d / gmst.find("fTravelMult")->mValue.getFloat()); } price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); @@ -79,9 +77,11 @@ namespace MWGui else price *= std::max(1, static_cast(followers.size())); - MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + + MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, lineHeight, MyGUI::Align::Default); toAdd->setEnabled(price <= playerGold); - mCurrentY += sLineHeight; + mCurrentY += lineHeight; if(interior) toAdd->setUserString("interior","y"); else @@ -92,7 +92,7 @@ namespace MWGui toAdd->setUserString("price",oss.str()); toAdd->setCaptionWithReplacing("#{sCell=" + name + "} - " + MyGUI::utility::toString(price)+"#{sgp}"); - toAdd->setSize(mDestinationsView->getWidth(),sLineHeight); + toAdd->setSize(mDestinationsView->getWidth(),lineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); toAdd->setUserString("Destination", name); toAdd->setUserData(pos); @@ -176,7 +176,7 @@ namespace MWGui { ESM::Position playerPos = player.getRefData().getPosition(); float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length(); - int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); + int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->mValue.getFloat()); for(int i = 0;i < hours;i++) { MWBase::Environment::get().getMechanicsManager ()->rest (true); diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 5ae466047..dcf0b7727 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -41,8 +41,6 @@ namespace MWGui void clearDestinations(); int mCurrentY; - static const int sLineHeight; - void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 516f5cfcc..5dec6e48a 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -16,12 +16,14 @@ namespace MWGui { VideoWidget::VideoWidget() - : mVFS(NULL) + : mVFS(nullptr) { mPlayer.reset(new Video::VideoPlayer()); setNeedKeyFocus(true); } +VideoWidget::~VideoWidget() = default; + void VideoWidget::setVFS(const VFS::Manager *vfs) { mVFS = vfs; diff --git a/apps/openmw/mwgui/videowidget.hpp b/apps/openmw/mwgui/videowidget.hpp index ac240e69d..20af579a2 100644 --- a/apps/openmw/mwgui/videowidget.hpp +++ b/apps/openmw/mwgui/videowidget.hpp @@ -25,6 +25,8 @@ namespace MWGui MYGUI_RTTI_DERIVED(VideoWidget) VideoWidget(); + + ~VideoWidget(); /// Set the VFS (virtual file system) to find the videos on. void setVFS(const VFS::Manager* vfs); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 52575e25c..037fcb19b 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -83,7 +83,7 @@ namespace MWGui void WaitDialog::setPtr(const MWWorld::Ptr &ptr) { - setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == 0); + setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == MWBase::World::Rest_Allowed); if (mUntilHealedButton->getVisible()) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton); @@ -120,9 +120,14 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->popGuiMode (); } - int canRest = MWBase::Environment::get().getWorld ()->canRest (); + MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld ()->canRest (); - if (canRest == 2) + if (canRest == MWBase::World::Rest_EnemiesAreNearby) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); + MWBase::Environment::get().getWindowManager()->popGuiMode (); + } + else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) { // resting underwater or mid-air not allowed MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); @@ -183,10 +188,10 @@ namespace MWGui { // figure out if player will be woken while sleeping int x = Misc::Rng::rollDice(hoursToWait); - float fSleepRandMod = world->getStore().get().find("fSleepRandMod")->getFloat(); + float fSleepRandMod = world->getStore().get().find("fSleepRandMod")->mValue.getFloat(); if (x < fSleepRandMod * hoursToWait) { - float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->getFloat(); + float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->mValue.getFloat(); int interruptAtHoursRemaining = int(fSleepRestMod * hoursToWait); if (interruptAtHoursRemaining != 0) { @@ -252,7 +257,7 @@ namespace MWGui // trigger levelup if possible const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->getInt()) + if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->mValue.getInteger()) { MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } @@ -276,7 +281,7 @@ namespace MWGui mSleeping = canRest; Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); center(); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 024a91fc6..c3bc1ec19 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -23,8 +23,8 @@ namespace MWGui MWSkill::MWSkill() : mSkillId(ESM::Skill::Length) - , mSkillNameWidget(NULL) - , mSkillValueWidget(NULL) + , mSkillNameWidget(nullptr) + , mSkillValueWidget(nullptr) { } @@ -114,8 +114,8 @@ namespace MWGui MWAttribute::MWAttribute() : mId(-1) - , mAttributeNameWidget(NULL) - , mAttributeValueWidget(NULL) + , mAttributeNameWidget(nullptr) + , mAttributeValueWidget(nullptr) { } @@ -204,7 +204,7 @@ namespace MWGui /* MWSpell */ MWSpell::MWSpell() - : mSpellNameWidget(NULL) + : mSpellNameWidget(nullptr) { } @@ -286,7 +286,7 @@ namespace MWGui { // We don't know the width of all the elements beforehand, so we do it in // 2 steps: first, create all widgets and check their width.... - MWSpellEffectPtr effect = NULL; + MWSpellEffectPtr effect = nullptr; int maxwidth = coord.width; for (SpellEffectList::iterator it=mEffectList.begin(); @@ -359,8 +359,8 @@ namespace MWGui /* MWSpellEffect */ MWSpellEffect::MWSpellEffect() - : mImageWidget(NULL) - , mTextWidget(NULL) + : mImageWidget(nullptr) + , mTextWidget(nullptr) , mRequestedWidth(0) { } @@ -489,9 +489,9 @@ namespace MWGui MWDynamicStat::MWDynamicStat() : mValue(0) , mMax(1) - , mTextWidget(NULL) - , mBarWidget(NULL) - , mBarTextWidget(NULL) + , mTextWidget(nullptr) + , mBarWidget(nullptr) + , mBarTextWidget(nullptr) { } diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 93440a50a..86c780325 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -33,11 +33,11 @@ void WindowBase::setVisible(bool visible) if (!visible) { MyGUI::Widget* keyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); - while (keyFocus != mMainWidget && keyFocus != NULL) + while (keyFocus != mMainWidget && keyFocus != nullptr) keyFocus = keyFocus->getParent(); if (keyFocus == mMainWidget) - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); } } diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index fde1f2ac9..7d8fb2f1e 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -23,7 +23,7 @@ namespace MWGui public: WindowBase(const std::string& parLayout); - virtual MyGUI::Widget* getDefaultKeyFocus() { return NULL; } + virtual MyGUI::Widget* getDefaultKeyFocus() { return nullptr; } // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e1bf9627d..e4515fdc3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -38,13 +38,13 @@ #include #include +#include #include #include #include #include -#include #include #include @@ -132,46 +132,46 @@ namespace MWGui WindowManager::WindowManager( osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription) - : mStore(NULL) + const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, + ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription, const std::string& userDataPath) + : mStore(nullptr) , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) , mViewer(viewer) , mConsoleOnlyScripts(consoleOnlyScripts) , mCurrentModals() - , mHud(NULL) - , mMap(NULL) - , mLocalMapRender(NULL) - , mToolTips(NULL) - , mStatsWindow(NULL) - , mMessageBoxManager(NULL) - , mConsole(NULL) - , mDialogueWindow(NULL) - , mDragAndDrop(NULL) - , mInventoryWindow(NULL) - , mScrollWindow(NULL) - , mBookWindow(NULL) - , mCountDialog(NULL) - , mTradeWindow(NULL) - , mSettingsWindow(NULL) - , mConfirmationDialog(NULL) - , mSpellWindow(NULL) - , mQuickKeysMenu(NULL) - , mLoadingScreen(NULL) - , mWaitDialog(NULL) - , mSoulgemDialog(NULL) - , mVideoBackground(NULL) - , mVideoWidget(NULL) - , mWerewolfFader(NULL) - , mBlindnessFader(NULL) - , mHitFader(NULL) - , mScreenFader(NULL) - , mDebugWindow(NULL) - , mJailScreen(NULL) + , mHud(nullptr) + , mMap(nullptr) + , mLocalMapRender(nullptr) + , mToolTips(nullptr) + , mStatsWindow(nullptr) + , mMessageBoxManager(nullptr) + , mConsole(nullptr) + , mDialogueWindow(nullptr) + , mDragAndDrop(nullptr) + , mInventoryWindow(nullptr) + , mScrollWindow(nullptr) + , mBookWindow(nullptr) + , mCountDialog(nullptr) + , mTradeWindow(nullptr) + , mSettingsWindow(nullptr) + , mConfirmationDialog(nullptr) + , mSpellWindow(nullptr) + , mQuickKeysMenu(nullptr) + , mLoadingScreen(nullptr) + , mWaitDialog(nullptr) + , mSoulgemDialog(nullptr) + , mVideoBackground(nullptr) + , mVideoWidget(nullptr) + , mWerewolfFader(nullptr) + , mBlindnessFader(nullptr) + , mHitFader(nullptr) + , mScreenFader(nullptr) + , mDebugWindow(nullptr) + , mJailScreen(nullptr) , mTranslationDataStorage (translationDataStorage) - , mCharGen(NULL) - , mInputBlocker(NULL) + , mCharGen(nullptr) + , mInputBlocker(nullptr) , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHitFaderEnabled(Settings::Manager::getBool ("hit fader", "GUI")) @@ -185,9 +185,9 @@ namespace MWGui , mPlayerMajorSkills() , mPlayerMinorSkills() , mPlayerSkillValues() - , mGui(NULL) + , mGui(nullptr) , mGuiModes() - , mCursorManager(NULL) + , mCursorManager(nullptr) , mGarbageDialogs() , mShown(GW_ALL) , mForceHidden(GW_None) @@ -196,6 +196,7 @@ namespace MWGui , mFallbackMap(fallbackMap) , mShowOwned(0) , mEncoding(encoding) + , mFontHeight(16) , mVersionDescription(versionDescription) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); @@ -210,8 +211,8 @@ namespace MWGui MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); // Load fonts - mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS())); - mFontLoader->loadAllFonts(exportFonts); + mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath)); + mFontLoader->loadBitmapFonts(exportFonts); //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); @@ -233,11 +234,19 @@ namespace MWGui SpellView::registerComponents(); Gui::registerAllWidgets(); + int fontSize = Settings::Manager::getInt("font size", "GUI"); + fontSize = std::min(std::max(12, fontSize), 20); + mFontHeight = fontSize; + + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = newDelegate(this, &WindowManager::loadFontDelegate); + MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); MyGUI::ResourceManager::getInstance().load("core.xml"); + loadUserFonts(); bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI"); mKeyboardNavigation.reset(new KeyboardNavigation()); @@ -284,6 +293,99 @@ namespace MWGui mShowOwned = Settings::Manager::getInt("show owned", "Game"); } + void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) + { + const std::string templateName = "Journalbook "; + MyGUI::xml::ElementEnumerator font = _node->getElementEnumerator(); + bool createCopy = false; + while (font.next("Resource")) + { + std::string type, name; + font->findAttribute("type", type); + font->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + createCopy = true; + + // For TrueType fonts we should override Size and Resolution properties + // to allow to configure font size via config file, without need to edit XML files. + // Also we should take UI scaling factor in account. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::min(960, std::max(48, resolution)); + + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = font->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + MyGUI::xml::ElementPtr sizeNode = font->createChild("Property"); + sizeNode->addAttribute("key", "Size"); + sizeNode->addAttribute("value", std::to_string(mFontHeight)); + } + else if (Misc::StringUtils::ciEqual(type, "ResourceSkin")) + { + // We should adjust line height for MyGUI widgets depending on font size + MyGUI::xml::ElementPtr heightNode = font->createChild("Property"); + heightNode->addAttribute("key", "HeightLine"); + heightNode->addAttribute("value", std::to_string(mFontHeight+2)); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); + + if (createCopy) + { + MyGUI::xml::ElementPtr copy = _node->createCopy(); + + MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator(); + while (copyFont.next("Resource")) + { + std::string type, name; + copyFont->findAttribute("type", type); + copyFont->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + // Since the journal and books use the custom scaling factor depending on resolution, + // setup separate fonts with different Resolution to fit these windows. + // These fonts have an internal prefix. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::min(960, std::max(48, resolution)); + + float currentX = Settings::Manager::getInt("resolution x", "Video"); + float currentY = Settings::Manager::getInt("resolution y", "Video"); + // TODO: read size from openmw_layout.xml + float heightScale = (currentY / 520); + float widthScale = (currentX / 600); + float uiScale = std::min(widthScale, heightScale); + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + copyFont->setAttribute("name", "Journalbook " + name); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy, _file, _version); + } + } + + void WindowManager::loadUserFonts() + { + mFontLoader->loadTrueTypeFonts(); + } + void WindowManager::initUI() { // Get size info from the Gui object @@ -358,7 +460,7 @@ namespace MWGui mGuiModeStates[GM_Journal].mCloseSound = "book close"; mGuiModeStates[GM_Journal].mOpenSound = "book open"; - mMessageBoxManager = new MessageBoxManager(mStore->get().find("fMessageTimePerChar")->getFloat()); + mMessageBoxManager = new MessageBoxManager(mStore->get().find("fMessageTimePerChar")->mValue.getFloat()); SpellBuyingWindow* spellBuyingWindow = new SpellBuyingWindow(); mWindows.push_back(spellBuyingWindow); @@ -504,6 +606,11 @@ namespace MWGui updateVisible(); } + int WindowManager::getFontHeight() const + { + return mFontHeight; + } + void WindowManager::setNewGame(bool newgame) { if (newgame) @@ -518,35 +625,43 @@ namespace MWGui WindowManager::~WindowManager() { - mKeyboardNavigation.reset(); + try + { + mKeyboardNavigation.reset(); - MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); - MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); - MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); - MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); - MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); + MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); + MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); + MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); + MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); - for (WindowBase* window : mWindows) - delete window; - mWindows.clear(); + for (WindowBase* window : mWindows) + delete window; + mWindows.clear(); - delete mMessageBoxManager; - delete mLocalMapRender; - delete mCharGen; - delete mDragAndDrop; - delete mSoulgemDialog; - delete mCursorManager; - delete mToolTips; + delete mMessageBoxManager; + delete mLocalMapRender; + delete mCharGen; + delete mDragAndDrop; + delete mSoulgemDialog; + delete mCursorManager; + delete mToolTips; - cleanupGarbage(); + cleanupGarbage(); - mFontLoader.reset(); + mFontLoader.reset(); - mGui->shutdown(); - delete mGui; + mGui->shutdown(); + delete mGui; - mGuiPlatform->shutdown(); - delete mGuiPlatform; + mGuiPlatform->shutdown(); + delete mGuiPlatform; + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void WindowManager::setStore(const MWWorld::ESMStore &store) @@ -589,7 +704,7 @@ namespace MWGui setCursorVisible(!gameMode); if (gameMode) - setKeyFocusWidget (NULL); + setKeyFocusWidget (nullptr); // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); @@ -1268,8 +1383,11 @@ namespace MWGui void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { mSelectedWeapon = item; - int durabilityPercent = - static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); + int durabilityPercent = 100; + if (item.getClass().hasItemHealth(item)) + { + durabilityPercent = static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); + } mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } @@ -1533,7 +1651,7 @@ namespace MWGui // Remove this method for MyGUI 3.2.2 void WindowManager::setKeyFocusWidget(MyGUI::Widget *widget) { - if (widget == NULL) + if (widget == nullptr) MyGUI::InputManager::getInstance().resetKeyFocusWidget(); else MyGUI::InputManager::getInstance().setKeyFocusWidget(widget); @@ -1795,7 +1913,7 @@ namespace MWGui } if (mCurrentModals.empty()) { - mKeyboardNavigation->setModalWindow(NULL); + mKeyboardNavigation->setModalWindow(nullptr); mKeyboardNavigation->restoreFocus(getMode()); } else @@ -2060,9 +2178,9 @@ namespace MWGui return mTextColours; } - bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text) + bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) { - if (!mKeyboardNavigation->injectKeyPress(key, text)) + if (!mKeyboardNavigation->injectKeyPress(key, text, repeat)) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); bool widgetActive = MyGUI::InputManager::getInstance().injectKeyPress(key, text); @@ -2091,6 +2209,11 @@ namespace MWGui return true; } + bool WindowManager::injectKeyRelease(MyGUI::KeyCode key) + { + return MyGUI::InputManager::getInstance().injectKeyRelease(key); + } + void WindowManager::GuiModeState::update(bool visible) { for (unsigned int i=0; i FactionList; WindowManager(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription); + const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, + ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription, const std::string& localPath); virtual ~WindowManager(); /// Set the ESMStore to use for retrieving of GUI-related strings. void setStore (const MWWorld::ESMStore& store); void initUI(); + virtual void loadUserFonts(); virtual Loading::Listener* getLoadingScreen(); @@ -246,6 +247,7 @@ namespace MWGui virtual const MWWorld::Ptr& getSelectedEnchantItem() const; virtual void setSelectedWeapon(const MWWorld::Ptr& item); virtual const MWWorld::Ptr& getSelectedWeapon() const; + virtual int getFontHeight() const; virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); @@ -379,7 +381,8 @@ namespace MWGui virtual const MWGui::TextColours& getTextColours(); - virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text); + virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat=false); + virtual bool injectKeyRelease(MyGUI::KeyCode key); private: const MWWorld::ESMStore* mStore; @@ -401,6 +404,8 @@ namespace MWGui MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedWeapon; + void loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); + std::vector mCurrentModals; // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). @@ -513,6 +518,8 @@ namespace MWGui ToUTF8::FromType mEncoding; + int mFontHeight; + std::string mVersionDescription; MWGui::TextColours mTextColours; diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp index a6984b5a4..271d4e3a8 100644 --- a/apps/openmw/mwgui/windowpinnablebase.cpp +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -14,7 +14,7 @@ namespace MWGui mPinButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonPressed); - MyGUI::Button* button = NULL; + MyGUI::Button* button = nullptr; MyGUI::VectorWidgetPtr widgets = window->getSkinWidgetsByName("Action"); for (MyGUI::VectorWidgetPtr::iterator it = widgets.begin(); it != widgets.end(); ++it) { diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 65fc0c098..0d017ba90 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -46,9 +46,9 @@ namespace MWInput , mScreenCaptureHandler(screenCaptureHandler) , mScreenCaptureOperation(screenCaptureOperation) , mJoystickLastUsed(false) - , mPlayer(NULL) - , mInputManager(NULL) - , mVideoWrapper(NULL) + , mPlayer(nullptr) + , mInputManager(nullptr) + , mVideoWrapper(nullptr) , mUserFile(userFile) , mDragDrop(false) , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) @@ -84,7 +84,7 @@ namespace MWInput Settings::Manager::getFloat("contrast", "Video")); std::string file = userFileExists ? userFile : ""; - mInputBinder = new ICS::InputControlSystem(file, true, this, NULL, A_Last); + mInputBinder = new ICS::InputControlSystem(file, true, this, nullptr, A_Last); loadKeyDefaults(); loadControllerDefaults(); @@ -214,7 +214,7 @@ namespace MWInput break; } - MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0); + MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); } void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) @@ -676,6 +676,9 @@ namespace MWInput Settings::Manager::getInt("resolution y", "Video"), Settings::Manager::getBool("fullscreen", "Video"), Settings::Manager::getBool("window border", "Video")); + + // We should reload TrueType fonts to fit new resolution + MWBase::Environment::get().getWindowManager()->loadUserFonts(); } } @@ -720,7 +723,7 @@ namespace MWInput bool consumed = false; if (kc != OIS::KC_UNASSIGNED && !mInputBinder->detectingBindingState()) { - consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); + consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0, arg.repeat); if (SDL_IsTextInputActive() && // Little trick to check if key is printable ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) consumed = true; @@ -1026,10 +1029,6 @@ namespace MWInput if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) return; - if(mPlayer->enemiesNearby()) {//Check if in combat - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); //Nope, - return; - } MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI } @@ -1108,6 +1107,7 @@ namespace MWInput if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu + && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); @@ -1153,7 +1153,7 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { if (!SDL_IsTextInputActive() && !isLeftOrRightButton(A_Activate, mInputBinder, mFakeDeviceID, mJoystickLastUsed)) - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0); + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0, false); } else if (mControlSwitch["playercontrols"]) mPlayer->activate(); @@ -1192,7 +1192,7 @@ namespace MWInput void InputManager::updateIdleTime(float dt) { static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get() - .find("fVanityDelay")->getFloat(); + .find("fVanityDelay")->mValue.getFloat(); if (mTimeIdle >= 0.f) mTimeIdle += dt; if (mTimeIdle > vanityDelay) { diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 90d29f686..57b009689 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -197,8 +197,14 @@ namespace MWMechanics void ActiveSpells::removeEffects(const std::string &id) { - mSpells.erase(Misc::StringUtils::lowerCase(id)); - mSpellsChanged = true; + for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell) + { + if (spell->first == id) + { + spell->second.mEffects.clear(); + mSpellsChanged = true; + } + } } void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6f5b4eeb8..2a68591a8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -54,22 +54,22 @@ int getBoundItemSlot (const std::string& itemId) static std::map boundItemsMap; if (boundItemsMap.empty()) { - std::string boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundBootsID")->getString(); + std::string boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundBootsID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Boots; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundCuirassID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundCuirassID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Cuirass; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundLeftGauntletID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundLeftGauntletID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_LeftGauntlet; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundRightGauntletID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundRightGauntletID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_RightGauntlet; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundHelmID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundHelmID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Helmet; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundShieldID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundShieldID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_CarriedLeft; } @@ -138,7 +138,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float magicka = 0; if (!stunted) { - float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); + float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); } } @@ -179,7 +179,7 @@ namespace MWMechanics if (caster.isEmpty() || !caster.getClass().isActor()) return; - static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->getFloat(); + static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->mValue.getFloat(); int creatureSoulValue = mCreature.get()->mBase->mData.mSoul; if (creatureSoulValue == 0) @@ -313,9 +313,9 @@ namespace MWMechanics MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance) { static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get() - .find("fMaxHeadTrackDistance")->getFloat(); + .find("fMaxHeadTrackDistance")->mValue.getFloat(); static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fInteriorHeadTrackMult")->getFloat(); + .find("fInteriorHeadTrackMult")->mValue.getFloat(); float maxDistance = fMaxHeadTrackDistance; const ESM::Cell* currentCell = actor.getCell()->getCell(); if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx)) @@ -470,7 +470,7 @@ namespace MWMechanics if (actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc()) { // Check if the creature is too far - static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get().find("fAlarmRadius")->getFloat(); + static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get().find("fAlarmRadius")->mValue.getFloat(); if (sqrDist > fAlarmRadius * fAlarmRadius) return; @@ -501,12 +501,6 @@ namespace MWMechanics } } - void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration) - { - updateDrowning(ptr, duration); - calculateNpcStatModifiers(ptr, duration); - } - void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) { CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature); @@ -534,9 +528,9 @@ namespace MWMechanics float base = 1.f; if (ptr == getPlayer()) - base = MWBase::Environment::get().getWorld()->getStore().get().find("fPCbaseMagickaMult")->getFloat(); + base = MWBase::Environment::get().getWorld()->getStore().get().find("fPCbaseMagickaMult")->mValue.getFloat(); else - base = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCbaseMagickaMult")->getFloat(); + base = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCbaseMagickaMult")->mValue.getFloat(); double magickaFactor = base + creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1; @@ -551,10 +545,10 @@ namespace MWMechanics void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep) { - if (ptr.getClass().getCreatureStats(ptr).isDead()) + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); + if (stats.isDead()) return; - MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); if (sleep) @@ -571,12 +565,6 @@ namespace MWMechanics stats.setMagicka(stat); } - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - - float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); - if (normalizedEncumbrance > 1) - normalizedEncumbrance = 1; - // Current fatigue can be above base value due to a fortify effect. // In that case stop here and don't try to restore. DynamicStat fatigue = stats.getFatigue(); @@ -584,9 +572,15 @@ namespace MWMechanics return; // Restore fatigue - float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); - float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); - float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); + float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); + float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); + float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); + + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); + if (normalizedEncumbrance > 1) + normalizedEncumbrance = 1; float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; @@ -611,8 +605,8 @@ namespace MWMechanics // Restore fatigue int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); - static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); + static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); + static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float x = fFatigueReturnBase + fFatigueReturnMult * endurance; @@ -853,14 +847,14 @@ namespace MWMechanics std::string itemGmst = it->second; std::string item = MWBase::Environment::get().getWorld()->getStore().get().find( - itemGmst)->getString(); + itemGmst)->mValue.getString(); magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); if (it->first == ESM::MagicEffect::BoundGloves) { item = MWBase::Environment::get().getWorld()->getStore().get().find( - "sMagicBoundRightGauntletID")->getString(); + "sMagicBoundRightGauntletID")->mValue.getString(); magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); } } @@ -926,23 +920,19 @@ namespace MWMechanics return ctrl->isSneaking(); } - void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) + void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer) { - PtrActorMap::iterator it = mActors.find(ptr); - if (it == mActors.end()) - return; - CharacterController* ctrl = it->second->getCharacterController(); - NpcStats &stats = ptr.getClass().getNpcStats(ptr); // When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST - static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->getFloat(); + static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->mValue.getFloat(); if (stats.getTimeToStartDrowning() == -1.f) stats.setTimeToStartDrowning(fHoldBreathTime); - if (ptr.getClass().isNpc() && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) + if (stats.getTimeToStartDrowning() < fHoldBreathTime / 2) { - if(ptr != MWMechanics::getPlayer() ) { + if(!isPlayer) + { MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdBreathe) //Only add it once seq.stack(MWMechanics::AiBreathe(), ptr); @@ -950,7 +940,7 @@ namespace MWMechanics } MWBase::World *world = MWBase::Environment::get().getWorld(); - bool knockedOutUnderwater = (ctrl->isKnockedOut() && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); + bool knockedOutUnderwater = (isKnockedOut && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); if((world->isSubmerged(ptr) || knockedOutUnderwater) && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0) { @@ -965,12 +955,12 @@ namespace MWMechanics stats.setTimeToStartDrowning(timeLeft); } - bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); if(timeLeft == 0.0f && !godmode) { // If drowning, apply 3 points of damage per second - static const float fSuffocationDamage = world->getStore().get().find("fSuffocationDamage")->getFloat(); + static const float fSuffocationDamage = world->getStore().get().find("fSuffocationDamage")->mValue.getFloat(); DynamicStat health = stats.getHealth(); health.setCurrent(health.getCurrent() - fSuffocationDamage*duration); stats.setHealth(health); @@ -980,7 +970,7 @@ namespace MWMechanics if(!sndmgr->getSoundPlaying(ptr, "drown")) sndmgr->playSound3D(ptr, "drown", 1.0f, 1.0f); - if(ptr == world->getPlayerPtr()) + if(isPlayer) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); } } @@ -1096,7 +1086,7 @@ namespace MWMechanics && creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - static const int cutoff = esmStore.get().find("iCrimeThreshold")->getInt(); + static const int cutoff = esmStore.get().find("iCrimeThreshold")->mValue.getInteger(); // Force dialogue on sight if bounty is greater than the cutoff // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty) if ( player.getClass().getNpcStats(player).getBounty() >= cutoff @@ -1104,7 +1094,7 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - static const int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->getInt(); + static const int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->mValue.getInteger(); if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) { MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); @@ -1242,21 +1232,24 @@ namespace MWMechanics void Actors::updateCombatMusic () { MWWorld::Ptr player = getPlayer(); - int hostilesCount = 0; // need to know this to play Battle music + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); + bool hasHostiles = false; // need to know this to play Battle music + bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive(); - for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + if (aiActive) { - if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() - <= sqrAiProcessingDistance; + if (iter->first == player) continue; - if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) + bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= sqrAiProcessingDistance; + if (inProcessingRange) { - if (iter->first != player) + MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); + if (!stats.isDead() && stats.getAiSequence().isInCombat()) { - MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); - if (stats.getAiSequence().isInCombat() && !stats.isDead()) hostilesCount++; + hasHostiles = true; + break; } } } @@ -1265,13 +1258,13 @@ namespace MWMechanics // check if we still have any player enemies to switch music static int currentMusic = 0; - if (currentMusic != 1 && hostilesCount == 0 && !(player.getClass().getCreatureStats(player).isDead() && + if (currentMusic != 1 && !hasHostiles && !(player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getSoundManager()->isMusicPlaying())) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); currentMusic = 1; } - else if (currentMusic != 2 && hostilesCount > 0) + else if (currentMusic != 2 && hasHostiles) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle")); currentMusic = 2; @@ -1298,17 +1291,29 @@ namespace MWMechanics bool showTorches = MWBase::Environment::get().getWorld()->useTorches(); MWWorld::Ptr player = getPlayer(); + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); /// \todo move update logic to Actor class where appropriate std::map > cachedAllies; // will be filled as engageCombat iterates + bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive(); + int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId(); + if (attackedByPlayerId != -1) + { + const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(attackedByPlayerId); + + if (!playerHitAttemptActor.isInCell()) + player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); + } + // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { bool isPlayer = iter->first == player; + CharacterController* ctrl = iter->second->getCharacterController(); - float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) // This distance could be made configurable later, but the setting must be marked with a big warning: @@ -1316,7 +1321,7 @@ namespace MWMechanics bool inProcessingRange = distSqr <= sqrAiProcessingDistance; if (isPlayer) - iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); + ctrl->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player. if (iter->first != player && (iter->first.getClass().getCreatureStats(iter->first).isDead() @@ -1328,11 +1333,6 @@ namespace MWMechanics player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); } - const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(player.getClass().getCreatureStats(player).getHitAttemptActorId()); - - if (!playerHitAttemptActor.isInCell()) - player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); - if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { bool cellChanged = MWBase::Environment::get().getWorld()->hasCellChanged(); @@ -1343,7 +1343,7 @@ namespace MWMechanics return; // for now abort update of the old cell when cell changes by teleportation magic effect // a better solution might be to apply cell changes at the end of the frame } - if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) + if (aiActive && inProcessingRange) { if (timerUpdateAITargets == 0) { @@ -1381,7 +1381,7 @@ namespace MWMechanics } } - iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); + ctrl->setHeadTrackTarget(headTrackTarget); } if (iter->first.getClass().isNpc() && iter->first != player) @@ -1391,13 +1391,14 @@ namespace MWMechanics { CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); if (isConscious(iter->first)) - stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), duration); + stats.getAiSequence().execute(iter->first, *ctrl, duration); } } if(iter->first.getTypeName() == typeid(ESM::NPC).name()) { - updateNpc(iter->first, duration); + updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer); + calculateNpcStatModifiers(iter->first, duration); if (timerUpdateEquippedLight == 0) updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches); @@ -1419,11 +1420,11 @@ namespace MWMechanics iter->second->getCharacterController()->updateContinuousVfx(); // Animation/movement update - CharacterController* playerCharacter = NULL; + CharacterController* playerCharacter = nullptr; for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation. - const float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + const float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); bool isPlayer = iter->first == player; bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance); int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) @@ -1437,22 +1438,24 @@ namespace MWMechanics inAnimationRange = true; active = std::max(1, active); } - iter->second->getCharacterController()->setActive(active); + + CharacterController* ctrl = iter->second->getCharacterController(); + ctrl->setActive(active); if (!inAnimationRange) continue; if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) - iter->second->getCharacterController()->skipAnim(); + ctrl->skipAnim(); // Handle player last, in case a cell transition occurs by casting a teleportation spell // (would invalidate the iterator) if (iter->first == getPlayer()) { - playerCharacter = iter->second->getCharacterController(); + playerCharacter = ctrl; continue; } - iter->second->getCharacterController()->update(duration); + ctrl->update(duration); } if (playerCharacter) @@ -1487,9 +1490,9 @@ namespace MWMechanics static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice" const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - const int radius = esmStore.get().find("fSneakUseDist")->getInt(); + const int radius = esmStore.get().find("fSneakUseDist")->mValue.getInteger(); - static float fSneakUseDelay = esmStore.get().find("fSneakUseDelay")->getFloat(); + static float fSneakUseDelay = esmStore.get().find("fSneakUseDelay")->mValue.getFloat(); if (sneakTimer >= fSneakUseDelay) sneakTimer = 0.f; @@ -1512,7 +1515,7 @@ namespace MWMechanics continue; // is the player in range and can they be detected - if ((observer.getRefData().getPosition().asVec3() - player.getRefData().getPosition().asVec3()).length2() <= radius*radius + if ((observer.getRefData().getPosition().asVec3() - playerPos).length2() <= radius*radius && MWBase::Environment::get().getWorld()->getLOS(player, observer)) { if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, observer)) @@ -1656,17 +1659,19 @@ namespace MWMechanics void Actors::rest(bool sleep) { float duration = 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (iter->first.getClass().getCreatureStats(iter->first).isDead()) continue; - restoreDynamicStats(iter->first, sleep); + if (!sleep || iter->first == player) + restoreDynamicStats(iter->first, sleep); if ((!iter->first.getRefData().getBaseNode()) || - (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) + (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) continue; adjustMagicEffects (iter->first); @@ -1679,7 +1684,10 @@ namespace MWMechanics MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first); if (animation) - animation->updateEffects(duration); + { + animation->removeEffects(); + MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first); + } } @@ -1782,6 +1790,8 @@ namespace MWMechanics if (iteratedActor == getPlayer()) continue; + const bool sameActor = (iteratedActor == actor); + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; @@ -1790,14 +1800,13 @@ namespace MWMechanics // Actors that are targeted by this actor's Follow or Escort packages also side with them for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - const MWWorld::Ptr &target = (*package)->getTarget(); - if ((*package)->sideWithTarget() && !target.isEmpty()) + if ((*package)->sideWithTarget() && !(*package)->getTarget().isEmpty()) { - if (iteratedActor == actor) + if (sameActor) { - list.push_back(target); + list.push_back((*package)->getTarget()); } - else if (target == actor) + else if ((*package)->getTarget() == actor) { list.push_back(iteratedActor); } @@ -1816,7 +1825,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Ptr &iteratedActor = iter->first; - if (iteratedActor == getPlayer()) + if (iteratedActor == getPlayer() || iteratedActor == actor) continue; const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); @@ -1879,7 +1888,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Ptr &iteratedActor = iter->first; - if (iteratedActor == getPlayer()) + if (iteratedActor == getPlayer() || iteratedActor == actor) continue; const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); @@ -1909,8 +1918,11 @@ namespace MWMechanics getObjectsInRange(position, aiProcessingDistance, neighbors); for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { + if (*neighbor == actor) + continue; + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); - if (stats.isDead() || *neighbor == actor) + if (stats.isDead()) continue; if (stats.getAiSequence().isInCombat(actor)) @@ -1975,7 +1987,7 @@ namespace MWMechanics for (; it != mActors.end(); ++it) { delete it->second; - it->second = NULL; + it->second = nullptr; } mActors.clear(); mDeathCount.clear(); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 8c94ce45f..a1e0e511d 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -28,8 +28,6 @@ namespace MWMechanics void addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor); void removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor); - void updateNpc(const MWWorld::Ptr &ptr, float duration); - void adjustMagicEffects (const MWWorld::Ptr& creature); void calculateDynamicStats (const MWWorld::Ptr& ptr); @@ -39,7 +37,7 @@ namespace MWMechanics void calculateRestoration (const MWWorld::Ptr& ptr, float duration); - void updateDrowning (const MWWorld::Ptr& ptr, float duration); + void updateDrowning (const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer); void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip); diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index bb03ff53b..e7d1ecee1 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -33,9 +33,9 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont float distance = x * x + y * y + z * z; if(distance < 10 * 10) { //Got stuck, didn't move if(mAdjAngle == 0) //Try going in various directions - mAdjAngle = 1.57079632679f; //pi/2 - else if (mAdjAngle == 1.57079632679f) - mAdjAngle = -1.57079632679f; + mAdjAngle = osg::PI / 2; + else if (mAdjAngle == osg::PI / 2) + mAdjAngle = -osg::PI / 2; else mAdjAngle = 0; mDuration = 1; //reset timer diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index 4e0076824..36acc75d5 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -19,7 +19,7 @@ MWMechanics::AiBreathe::AiBreathe() bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { - static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->getFloat(); + static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->mValue.getFloat(); const MWWorld::Class& actorClass = actor.getClass(); if (actorClass.isNpc()) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 438f4f7c6..8f9545f99 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -351,7 +351,7 @@ namespace MWMechanics case AiCombatStorage::FleeState_RunToDestination: { - static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fFleeDistance")->getFloat(); + static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fFleeDistance")->mValue.getFloat(); float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length(); if ((dist > fFleeDistance && !storage.mLOS) @@ -520,13 +520,13 @@ namespace MWMechanics const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - float baseDelay = store.get().find("fCombatDelayCreature")->getFloat(); + float baseDelay = store.get().find("fCombatDelayCreature")->mValue.getFloat(); if (actor.getClass().isNpc()) { - baseDelay = store.get().find("fCombatDelayNPC")->getFloat(); + baseDelay = store.get().find("fCombatDelayNPC")->mValue.getFloat(); //say a provoking combat phrase - int chance = store.get().find("iVoiceAttackOdds")->getInt(); + int chance = store.get().find("iVoiceAttackOdds")->mValue.getInteger(); if (Misc::Rng::roll0to99() < chance) { MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); @@ -586,7 +586,7 @@ std::string chooseBestAttack(const ESM::Weapon* weapon) { std::string attackType; - if (weapon != NULL) + if (weapon != nullptr) { //the more damage attackType deals the more probability it has int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; @@ -616,21 +616,21 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t // get projectile speed (depending on weapon type) if (weapType == ESM::Weapon::MarksmanThrown) { - static float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); - static float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); + static float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->mValue.getFloat(); + static float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->mValue.getFloat(); projSpeed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength; } else if (weapType != 0) { - static float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); - static float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); + static float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->mValue.getFloat(); + static float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat(); projSpeed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; } else // weapType is 0 ==> it's a target spell projectile { - projSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat(); + projSpeed = gmst.find("fTargetSpellMaxSpeed")->mValue.getFloat(); } // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 88feba481..f89e71678 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -64,7 +64,7 @@ namespace MWMechanics mAttackRange(0.0f), mCombatMove(false), mLastTargetPos(0,0,0), - mCell(NULL), + mCell(nullptr), mCurrentAction(), mActionCooldown(0.0f), mStrength(), diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 2685e0e0c..804e0dd05 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -23,8 +23,8 @@ namespace MWMechanics { float suggestCombatRange(int rangeTypes) { - static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->getFloat(); - static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->getFloat(); + static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->mValue.getFloat(); + static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->mValue.getFloat(); // This distance is a possible distance of melee attack static float distance = fCombatDistance * std::max(2.f, fHandToHandReach); @@ -114,13 +114,13 @@ namespace MWMechanics { isRanged = false; - static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->getFloat(); - static const float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->getFloat(); + static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->mValue.getFloat(); + static const float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->mValue.getFloat(); if (mWeapon.isEmpty()) { static float fHandToHandReach = - MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->mValue.getFloat(); return fHandToHandReach * fCombatDistance; } @@ -138,7 +138,7 @@ namespace MWMechanics const ESM::Weapon* ActionWeapon::getWeapon() const { if (mWeapon.isEmpty()) - return NULL; + return nullptr; return mWeapon.get()->mBase; } @@ -190,11 +190,6 @@ namespace MWMechanics for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - std::vector equipmentSlots = it->getClass().getEquipmentSlots(*it).first; - if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight) - == equipmentSlots.end()) - continue; - float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating); if (rating > bestActionRating) { @@ -215,14 +210,12 @@ namespace MWMechanics for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - const ESM::Spell* spell = it->first; - - float rating = rateSpell(spell, actor, enemy); + float rating = rateSpell(it->first, actor, enemy); if (rating > bestActionRating) { bestActionRating = rating; - bestAction.reset(new ActionSpell(spell->mId)); - antiFleeRating = vanillaRateSpell(spell, actor, enemy); + bestAction.reset(new ActionSpell(it->first->mId)); + antiFleeRating = vanillaRateSpell(it->first, actor, enemy); } } @@ -265,11 +258,6 @@ namespace MWMechanics for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - std::vector equipmentSlots = it->getClass().getEquipmentSlots(*it).first; - if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight) - == equipmentSlots.end()) - continue; - float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating); if (rating > bestActionRating) { @@ -280,9 +268,7 @@ namespace MWMechanics for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - const ESM::Spell* spell = it->first; - - float rating = rateSpell(spell, actor, enemy); + float rating = rateSpell(it->first, actor, enemy); if (rating > bestActionRating) { bestActionRating = rating; @@ -336,7 +322,7 @@ namespace MWMechanics float dist = 1.0f; if (activeWeapon.isEmpty() && !selectedSpellId.empty() && !selectedEnchItem.isEmpty()) { - static const float fHandToHandReach = gmst.find("fHandToHandReach")->getFloat(); + static const float fHandToHandReach = gmst.find("fHandToHandReach")->mValue.getFloat(); dist = fHandToHandReach; } else if (stats.getDrawState() == MWMechanics::DrawState_Spell) @@ -375,7 +361,7 @@ namespace MWMechanics } } - static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat(); + static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->mValue.getFloat(); dist *= std::max(1000.0f, fTargetSpellMaxSpeed); } else if (!activeWeapon.isEmpty()) @@ -383,7 +369,7 @@ namespace MWMechanics const ESM::Weapon* esmWeap = activeWeapon.get()->mBase; if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow) { - static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); + static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat(); dist = fTargetSpellMaxSpeed; if (!activeAmmo.isEmpty()) { @@ -399,8 +385,8 @@ namespace MWMechanics dist = (dist > 0.f) ? dist : 1.0f; - static const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); - static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->getFloat(); + static const float fCombatDistance = gmst.find("fCombatDistance")->mValue.getFloat(); + static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->mValue.getFloat(); float combatDistance = fCombatDistance; if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) @@ -485,20 +471,20 @@ namespace MWMechanics if (flee >= 100) return flee; - static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->getFloat(); - static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->getFloat(); + static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->mValue.getFloat(); + static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->mValue.getFloat(); float healthPercentage = (stats.getHealth().getModified() == 0.0f) ? 1.0f : stats.getHealth().getCurrent() / stats.getHealth().getModified(); float rating = (1.0f - healthPercentage) * fAIFleeHealthMult + flee * fAIFleeFleeMult; - static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt(); + static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->mValue.getInteger(); if (actor.getClass().isNpc() && enemy.getClass().isNpc()) { if (enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack) { - static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt(); + static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->mValue.getInteger(); rating = iWereWolfFleeMod; } } diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp index 466ae2dc4..cf7dc78fd 100644 --- a/apps/openmw/mwmechanics/aicombataction.hpp +++ b/apps/openmw/mwmechanics/aicombataction.hpp @@ -17,7 +17,7 @@ namespace MWMechanics virtual void prepare(const MWWorld::Ptr& actor) = 0; virtual float getCombatRange (bool& isRanged) const = 0; virtual float getActionCooldown() { return 0.f; } - virtual const ESM::Weapon* getWeapon() const { return NULL; }; + virtual const ESM::Weapon* getWeapon() const { return nullptr; }; virtual bool isAttackingOrSpell() const { return true; } virtual bool isFleeing() const { return false; } }; diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index e6cca0523..9a42f191e 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -275,7 +275,7 @@ bool MWMechanics::AiPackage::shortcutPath(const ESM::Pathgrid::Point& startPoint static_cast(startPoint.mX), static_cast(startPoint.mY), static_cast(startPoint.mZ), static_cast(endPoint.mX), static_cast(endPoint.mY), static_cast(endPoint.mZ)); - if (destInLOS != NULL) *destInLOS = isPathClear; + if (destInLOS != nullptr) *destInLOS = isPathClear; if (!isPathClear) return false; diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index f1941ff1d..5a468ae30 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -114,7 +114,7 @@ namespace MWMechanics /// Check if there aren't any obstacles along the path to make shortcut possible /// If a shortcut is possible then path will be cleared and filled with the destination point. - /// \param destInLOS If not NULL function will return ray cast check result + /// \param destInLOS If not nullptr function will return ray cast check result /// \return If can shortcut the path bool shortcutPath(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor, bool *destInLOS, bool isPathClear); diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index b64b3568f..cf572abc0 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -228,7 +228,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac { if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; - MWWorld::Ptr target = static_cast(*it)->getTarget(); + MWWorld::Ptr target = (*it)->getTarget(); // target disappeared (e.g. summoned creatures) if (target.isEmpty()) @@ -242,11 +242,11 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac const ESM::Position &targetPos = target.getRefData().getPosition(); - float distTo = (targetPos.asVec3() - vActorPos).length(); + float distTo = (targetPos.asVec3() - vActorPos).length2(); // Small threshold for changing target if (it == mPackages.begin()) - distTo = std::max(0.f, distTo - 50.f); + distTo = std::max(0.f, distTo - 2500.f); // if a target has higher priority than current target or has same priority but closer if (rating > bestRating || ((distTo < nearestDist) && rating == bestRating)) diff --git a/apps/openmw/mwmechanics/aistate.hpp b/apps/openmw/mwmechanics/aistate.hpp index 19f0ecf99..40f4ab1d4 100644 --- a/apps/openmw/mwmechanics/aistate.hpp +++ b/apps/openmw/mwmechanics/aistate.hpp @@ -59,7 +59,7 @@ namespace MWMechanics bool empty() const { - return mStorage == NULL; + return mStorage == nullptr; } const std::type_info& getType() const @@ -67,16 +67,12 @@ namespace MWMechanics return typeid(mStorage); } - - DerivedClassStorage():mStorage(NULL){} + DerivedClassStorage():mStorage(nullptr){} ~DerivedClassStorage() { if(mStorage) delete mStorage; } - - - }; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 67468453a..caacf8cb6 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -300,9 +300,8 @@ namespace MWMechanics bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); do { // Determine a random location within radius of original position - const float pi = 3.14159265359f; const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance; - const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * pi; + const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI; const float destinationX = mInitialActorPosition.x() + wanderRadius * std::cos(randomDirection); const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection); const float destinationZ = mInitialActorPosition.z(); @@ -495,7 +494,7 @@ namespace MWMechanics MWWorld::Ptr player = getPlayer(); static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() - .get().find("fVoiceIdleOdds")->getFloat(); + .get().find("fVoiceIdleOdds")->mValue.getFloat(); float roll = Misc::Rng::rollProbability() * 10000.0f; @@ -522,7 +521,7 @@ namespace MWMechanics int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); float helloDistance = static_cast(hello); static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore() - .get().find("iGreetDistanceMultiplier")->getInt(); + .get().find("iGreetDistanceMultiplier")->mValue.getInteger(); helloDistance *= iGreetDistanceMultiplier; @@ -700,7 +699,7 @@ namespace MWMechanics for(unsigned int counter = 0; counter < mIdle.size(); counter++) { static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore() - .get().find("fIdleChanceMultiplier")->getFloat(); + .get().find("fIdleChanceMultiplier")->mValue.getFloat(); unsigned short idleChance = static_cast(fIdleChanceMultiplier * mIdle[counter]); unsigned short randSelect = (int)(Misc::Rng::rollProbability() * int(100 / fIdleChanceMultiplier)); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 4a0811f50..479ed4869 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -77,7 +77,7 @@ namespace MWMechanics mReaction(0), mSaidGreeting(Greet_None), mGreetingTimer(0), - mCell(NULL), + mCell(nullptr), mState(Wander_ChooseAction), mIsWanderingManually(false), mCanWanderAlongPathGrid(true), diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 48705dc72..c199bfb3f 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -26,10 +26,10 @@ #include "magiceffects.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" MWMechanics::Alchemy::Alchemy() : mValue(0) + , mPotionName("") { } @@ -140,11 +140,11 @@ void MWMechanics::Alchemy::updateEffects() float x = getAlchemyFactor(); x *= mTools[ESM::Apparatus::MortarPestle].get()->mBase->mData.mQuality; - x *= MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionStrengthMult")->getFloat(); + x *= MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionStrengthMult")->mValue.getFloat(); // value mValue = static_cast ( - x * MWBase::Environment::get().getWorld()->getStore().get().find ("iAlchemyMod")->getFloat()); + x * MWBase::Environment::get().getWorld()->getStore().get().find ("iAlchemyMod")->mValue.getFloat()); // build quantified effect list for (std::set::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) @@ -160,13 +160,13 @@ void MWMechanics::Alchemy::updateEffects() } float fPotionT1MagMul = - MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->mValue.getFloat(); if (fPotionT1MagMul<=0) throw std::runtime_error ("invalid gmst: fPotionT1MagMul"); float fPotionT1DurMult = - MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1DurMult")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1DurMult")->mValue.getFloat(); if (fPotionT1DurMult<=0) throw std::runtime_error ("invalid gmst: fPotionT1DurMult"); @@ -317,10 +317,9 @@ void MWMechanics::Alchemy::increaseSkill() float MWMechanics::Alchemy::getAlchemyFactor() const { const CreatureStats& creatureStats = mAlchemist.getClass().getCreatureStats (mAlchemist); - const NpcStats& npcStats = mAlchemist.getClass().getNpcStats (mAlchemist); return - (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + (mAlchemist.getClass().getSkill(mAlchemist, ESM::Skill::Alchemy) + 0.1f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()); } @@ -336,6 +335,25 @@ int MWMechanics::Alchemy::countIngredients() const return ingredients; } +int MWMechanics::Alchemy::countPotionsToBrew() const +{ + Result readyStatus = getReadyStatus(); + if (readyStatus != Result_Success) + return 0; + + int toBrew = -1; + + for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) + if (!iter->isEmpty()) + { + int count = iter->getRefData().getCount(); + if ((count > 0 && count < toBrew) || toBrew < 0) + toBrew = count; + } + + return toBrew; +} + void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc) { mAlchemist = npc; @@ -396,6 +414,12 @@ void MWMechanics::Alchemy::clear() mTools.clear(); mIngredients.clear(); mEffects.clear(); + setPotionName(""); +} + +void MWMechanics::Alchemy::setPotionName(const std::string& name) +{ + mPotionName = name; } int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient) @@ -446,17 +470,16 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) { - MWMechanics::NpcStats& npcStats = npc.getClass().getNpcStats(npc); - int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); + int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); static const float fWortChanceValue = - MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) || (potionEffectIndex <= 3 && alchemySkill >= fWortChanceValue*2) || (potionEffectIndex <= 5 && alchemySkill >= fWortChanceValue*3) || (potionEffectIndex <= 7 && alchemySkill >= fWortChanceValue*4); } -MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name) +MWMechanics::Alchemy::Result MWMechanics::Alchemy::getReadyStatus() const { if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) return Result_NoMortarAndPestle; @@ -464,15 +487,43 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na if (countIngredients()<2) return Result_LessThanTwoIngredients; - if (name.empty()) + if (mPotionName.empty()) return Result_NoName; if (listEffects().empty()) - { - removeIngredients(); return Result_NoEffects; + + return Result_Success; +} + +MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name, int& count) +{ + setPotionName(name); + Result readyStatus = getReadyStatus(); + + if (readyStatus == Result_NoEffects) + removeIngredients(); + + if (readyStatus != Result_Success) + return readyStatus; + + Result result = Result_RandomFailure; + int brewedCount = 0; + for (int i = 0; i < count; ++i) + { + if (createSingle() == Result_Success) + { + result = Result_Success; + brewedCount++; + } } + count = brewedCount; + return result; +} + +MWMechanics::Alchemy::Result MWMechanics::Alchemy::createSingle () +{ if (beginEffects() == endEffects()) { // all effects were nullified due to insufficient skill @@ -486,7 +537,7 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na return Result_RandomFailure; } - addPotion (name); + addPotion(mPotionName); removeIngredients(); @@ -503,5 +554,5 @@ std::string MWMechanics::Alchemy::suggestPotionName() int effectId = effects.begin()->mId; return MWBase::Environment::get().getWorld()->getStore().get().find( - ESM::MagicEffect::effectIdToString(effectId))->getString(); + ESM::MagicEffect::effectIdToString(effectId))->mValue.getString(); } diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp index f351881e0..9f9f0b21c 100644 --- a/apps/openmw/mwmechanics/alchemy.hpp +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -51,11 +51,14 @@ namespace MWMechanics TIngredientsContainer mIngredients; TEffectsContainer mEffects; int mValue; + std::string mPotionName; void applyTools (int flags, float& value) const; void updateEffects(); + Result getReadyStatus() const; + const ESM::Potion *getRecord(const ESM::Potion& toFind) const; ///< Try to find a potion record similar to \a toFind in the record store, or return 0 if not found /// \note Does not account for record ID, model or icon @@ -70,6 +73,10 @@ namespace MWMechanics void increaseSkill(); ///< Increase alchemist's skill. + Result createSingle (); + ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and + /// adjust the skills of the alchemist accordingly. + float getAlchemyFactor() const; int countIngredients() const; @@ -79,6 +86,8 @@ namespace MWMechanics TEffectsIterator endEffects() const; public: + int countPotionsToBrew() const; + ///< calculates maximum amount of potions, which you can make from selected ingredients static bool knownEffect (unsigned int potionEffectIndex, const MWWorld::Ptr& npc); ///< Does npc have sufficient alchemy skill to know about this potion effect? @@ -100,6 +109,9 @@ namespace MWMechanics void clear(); ///< Remove alchemist, tools and ingredients. + void setPotionName(const std::string& name); + ///< Set name of potion to create + std::set listEffects() const; ///< List all effects shared by at least two ingredients. @@ -115,8 +127,8 @@ namespace MWMechanics std::string suggestPotionName (); ///< Suggest a name for the potion, based on the current effects - Result create (const std::string& name); - ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and + Result create (const std::string& name, int& count); + ///< Try to create potions from the ingredients, place them in the inventory of the alchemist and /// adjust the skills of the alchemist accordingly. /// \param name must not be an empty string, or Result_NoName is returned }; diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index f655a68b4..144449cf0 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -1,7 +1,6 @@ #include "autocalcspell.hpp" #include "spellcasting.hpp" -#include #include #include "../mwworld/esmstore.hpp" @@ -25,7 +24,7 @@ namespace MWMechanics std::vector autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race) { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat(); + static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat(); float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; static const std::string schools[] = { @@ -38,7 +37,7 @@ namespace MWMechanics for (int i=0; i<6; ++i) { const std::string& gmstName = "iAutoSpell" + schools[i] + "Max"; - iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt(); + iAutoSpellSchoolMax[i] = gmst.find(gmstName)->mValue.getInteger(); } init = true; } @@ -50,7 +49,7 @@ namespace MWMechanics caps.mCount = 0; caps.mLimit = iAutoSpellSchoolMax[i]; caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0; - caps.mMinCost = INT_MAX; + caps.mMinCost = std::numeric_limits::max(); caps.mWeakestSpell.clear(); schoolCaps[i] = caps; } @@ -70,7 +69,7 @@ namespace MWMechanics continue; if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc)) continue; - static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt(); + static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->mValue.getInteger(); if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost) continue; @@ -89,7 +88,7 @@ namespace MWMechanics if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost) continue; - static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat(); + static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->mValue.getFloat(); if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance) continue; @@ -101,7 +100,7 @@ namespace MWMechanics if (found != selectedSpells.end()) selectedSpells.erase(found); - cap.mMinCost = INT_MAX; + cap.mMinCost = std::numeric_limits::max(); for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = spells.find(*weakIt); @@ -146,12 +145,12 @@ namespace MWMechanics { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - static const float fPCbaseMagickaMult = esmStore.get().find("fPCbaseMagickaMult")->getFloat(); + static const float fPCbaseMagickaMult = esmStore.get().find("fPCbaseMagickaMult")->mValue.getFloat(); float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; bool reachedLimit = false; - const ESM::Spell* weakestSpell = NULL; - int minCost = INT_MAX; + const ESM::Spell* weakestSpell = nullptr; + int minCost = std::numeric_limits::max(); std::vector selectedSpells; @@ -173,7 +172,7 @@ namespace MWMechanics if (baseMagicka < spell->mData.mCost) continue; - static const float fAutoPCSpellChance = esmStore.get().find("fAutoPCSpellChance")->getFloat(); + static const float fAutoPCSpellChance = esmStore.get().find("fAutoPCSpellChance")->mValue.getFloat(); if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance) continue; @@ -188,7 +187,7 @@ namespace MWMechanics if (it != selectedSpells.end()) selectedSpells.erase(it); - minCost = INT_MAX; + minCost = std::numeric_limits::max(); for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = esmStore.get().find(*weakIt); @@ -206,7 +205,7 @@ namespace MWMechanics weakestSpell = spell; minCost = weakestSpell->mData.mCost; } - static const unsigned int iAutoPCSpellMax = esmStore.get().find("iAutoPCSpellMax")->getInt(); + static const unsigned int iAutoPCSpellMax = esmStore.get().find("iAutoPCSpellMax")->mValue.getInteger(); if (selectedSpells.size() == iAutoPCSpellMax) reachedLimit = true; } @@ -221,7 +220,7 @@ namespace MWMechanics for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); - static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get().find("iAutoSpellAttSkillMin")->getInt(); + static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get().find("iAutoSpellAttSkillMin")->mValue.getInteger(); if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)) { @@ -278,7 +277,7 @@ namespace MWMechanics duration = effect.mDuration; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() - .get().find("fEffectCostMult")->getFloat(); + .get().find("fEffectCostMult")->mValue.getFloat(); float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); x *= 0.1 * magicEffect->mData.mBaseCost; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c7c6b57d0..7ad8d61cd 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -123,16 +123,16 @@ 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(); + const float fallDistanceMin = store.find("fFallDamageDistanceMin")->mValue.getFloat(); if (fallHeight >= fallDistanceMin) { const float acrobaticsSkill = static_cast(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(); + const float fallAcroBase = store.find("fFallAcroBase")->mValue.getFloat(); + const float fallAcroMult = store.find("fFallAcroMult")->mValue.getFloat(); + const float fallDistanceBase = store.find("fFallDistanceBase")->mValue.getFloat(); + const float fallDistanceMult = store.find("fFallDistanceMult")->mValue.getFloat(); float x = fallHeight - fallDistanceMin; x -= (1.5f * acrobaticsSkill) + jumpSpellBonus; @@ -243,7 +243,7 @@ std::string CharacterController::chooseRandomGroup (const std::string& prefix, i return prefix + toString(roll); } -void CharacterController::refreshHitRecoilAnims() +void CharacterController::refreshHitRecoilAnims(CharacterState& idle) { bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); @@ -348,92 +348,130 @@ void CharacterController::refreshHitRecoilAnims() mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0); } if (mHitState != CharState_None) - mIdleState = CharState_None; + idle = CharState_None; } -void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, bool force) +void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, CharacterState& movement, bool force) { - if(force || jump != mJumpState) + if (!force && jump == mJumpState && idle == CharState_None && movement == CharState_None) + return; + + if (jump != JumpState_None && !(mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) // FIXME { - mIdleState = CharState_None; - bool startAtLoop = (jump == mJumpState); - mJumpState = jump; + idle = CharState_None; + movement = CharState_None; + } - std::string jumpAnimName; - MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; - if(mJumpState != JumpState_None) + std::string jumpAnimName; + MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; + if (jump != JumpState_None) + { + jumpAnimName = "jump"; + if(weap != sWeaponTypeListEnd) { - jumpAnimName = "jump"; - if(weap != sWeaponTypeListEnd) + jumpAnimName += weap->shortgroup; + if(!mAnimation->hasAnimation(jumpAnimName)) { - jumpAnimName += weap->shortgroup; - if(!mAnimation->hasAnimation(jumpAnimName)) - { - jumpmask = MWRender::Animation::BlendMask_LowerBody; - jumpAnimName = "jump"; + jumpmask = MWRender::Animation::BlendMask_LowerBody; + jumpAnimName = "jump"; - // For crossbow animations use 1h ones as fallback - if (mWeaponType == WeapType_Crossbow) - jumpAnimName += "1h"; - } + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; + + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + jumpAnimName += "1h"; } } + } - if (!mCurrentJump.empty()) - { - mAnimation->disable(mCurrentJump); - mCurrentJump.clear(); - } + if (!force && jump == mJumpState) + return; - if(mJumpState == JumpState_InAir) + mJumpState = jump; + + if (!mCurrentJump.empty()) + { + mAnimation->disable(mCurrentJump); + mCurrentJump.clear(); + } + + if(mJumpState == JumpState_InAir) + { + if (mAnimation->hasAnimation(jumpAnimName)) { - if (mAnimation->hasAnimation(jumpAnimName)) - { - mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, - 1.0f, (startAtLoop?"loop start":"start"), "stop", 0.0f, ~0ul); - mCurrentJump = jumpAnimName; - } + mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, + 1.0f, "start", "stop", 0.f, ~0ul); + mCurrentJump = jumpAnimName; } - else if (mJumpState == JumpState_Landing) + } + else if (mJumpState == JumpState_Landing) + { + if (mAnimation->hasAnimation(jumpAnimName)) { - if (mAnimation->hasAnimation(jumpAnimName)) - { - mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, - 1.0f, "loop stop", "stop", 0.0f, 0); - mCurrentJump = jumpAnimName; - } + mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, + 1.0f, "loop stop", "stop", 0.0f, 0); + mCurrentJump = jumpAnimName; } } } -void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, bool force) +void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force) { - if(force || movement != mMovementState) + if (movement == mMovementState && idle == mIdleState && !force) + return; + + std::string movementAnimName; + MWRender::Animation::BlendMask movemask; + const StateInfo *movestate; + + movemask = MWRender::Animation::BlendMask_All; + movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement)); + if(movestate != sMovementListEnd) { - mMovementState = movement; - std::string movementAnimName; - MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All; - const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState)); - if(movestate != sMovementListEnd) + movementAnimName = movestate->groupname; + if(weap != sWeaponTypeListEnd) { - movementAnimName = movestate->groupname; - if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) + std::string::size_type swimpos = movementAnimName.find("swim"); + if (swimpos == std::string::npos) { - if (mWeaponType == WeapType_Spell && (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)) // Spellcasting stance turning is a special case + if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case movementAnimName = weap->shortgroup + movementAnimName; else movementAnimName += weap->shortgroup; - if(!mAnimation->hasAnimation(movementAnimName)) + } + + if(!mAnimation->hasAnimation(movementAnimName)) + { + movemask = MWRender::Animation::BlendMask_LowerBody; + movementAnimName = movestate->groupname; + + if (swimpos == std::string::npos) { - movemask = MWRender::Animation::BlendMask_LowerBody; - movementAnimName = movestate->groupname; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; // For crossbow animations use 1h ones as fallback if (mWeaponType == WeapType_Crossbow) movementAnimName += "1h"; } + else if (idle == CharState_None) + { + idle = CharState_IdleSwim; + } } + } + } + if(force || movement != mMovementState) + { + mMovementState = movement; + if(movestate != sMovementListEnd) + { if(!mAnimation->hasAnimation(movementAnimName)) { std::string::size_type swimpos = movementAnimName.find("swim"); @@ -451,6 +489,10 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } else { + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + movementAnimName += "1h"; + movementAnimName.erase(swimpos, 4); if (weap != sWeaponTypeListEnd) { @@ -468,10 +510,9 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } // If we're playing the same animation, start it from the point it ended - bool sameAnim = (movementAnimName == mCurrentMovement); - float startPoint = 0.f; - if (sameAnim) - mAnimation->getInfo(mCurrentMovement, &startPoint); + float startpoint = 0.f; + if (!mCurrentMovement.empty() && movementAnimName == mCurrentMovement) + mAnimation->getInfo(mCurrentMovement, &startpoint); mMovementAnimationControlled = true; @@ -520,14 +561,22 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false, - 1.f, (!sameAnim ? "start" : "loop start"), "stop", startPoint, ~0ul, true); + 1.f, "start", "stop", startpoint, ~0ul, true); } } } void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force) { - if(force || idle != mIdleState || mIdleState == CharState_None || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) + // 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 && mUpperBodyState != UpperCharState_WeapEquiped) + || (mMovementState != CharState_None && !isTurning()) + || mHitState != CharState_None) + && !mPtr.getClass().isBipedal(mPtr)) + idle = CharState_None; + + if(force || idle != mIdleState || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) { mIdleState = idle; size_t numLoops = ~0ul; @@ -586,24 +635,16 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat return; if (mPtr.getClass().isActor()) - refreshHitRecoilAnims(); + refreshHitRecoilAnims(idle); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); if (!mPtr.getClass().hasInventoryStore(mPtr)) weap = sWeaponTypeListEnd; - refreshJumpAnims(weap, jump, force); - refreshMovementAnims(weap, movement, force); + refreshJumpAnims(weap, jump, idle, movement, force); + refreshMovementAnims(weap, movement, idle, force); // 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 && mUpperBodyState != UpperCharState_WeapEquiped) - || (mMovementState != CharState_None && !isTurning()) - || mHitState != CharState_None) - && !mPtr.getClass().isBipedal(mPtr)) - idle = CharState_None; - refreshIdleAnims(weap, idle, force); } @@ -890,7 +931,7 @@ CharacterController::~CharacterController() if (mAnimation) { persistAnimationState(); - mAnimation->setTextKeyListener(NULL); + mAnimation->setTextKeyListener(nullptr); } } @@ -938,11 +979,14 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } } + if (soundgen == "land") // Morrowind ignores land soundgen for some reason + return; + std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0) + if(soundgen == "left" || soundgen == "right") { // Don't make foot sounds local for the player, it makes sense to keep them // positioned on the ground. @@ -1115,7 +1159,7 @@ bool CharacterController::updateCreatureState() if (!spellid.empty() && canCast) { - MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell); + MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); cast.playSpellCastingEffects(spellid); if (!mAnimation->hasAnimation("spellcast")) @@ -1192,7 +1236,7 @@ bool CharacterController::updateCarriedLeftVisible(WeaponType weaptype) const } } -bool CharacterController::updateWeaponState() +bool CharacterController::updateWeaponState(CharacterState& idle) { const MWWorld::Class &cls = mPtr.getClass(); CreatureStats &stats = cls.getCreatureStats(mPtr); @@ -1391,6 +1435,14 @@ bool CharacterController::updateWeaponState() MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackStrength = 0; + // Randomize attacks for non-bipedal creatures with Weapon flag + if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() && + !mPtr.getClass().isBipedal(mPtr) && + (!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon))) + { + mCurrentWeapon = chooseRandomAttackAnimation(); + } + if(mWeaponType == WeapType_Spell) { // Unset casting flag, otherwise pressing the mouse button down would @@ -1411,7 +1463,7 @@ bool CharacterController::updateWeaponState() if(!spellid.empty() && canCast) { - MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell); + MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); cast.playSpellCastingEffects(spellid); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -1554,11 +1606,11 @@ bool CharacterController::updateWeaponState() // We should reset player's idle animation in the first-person mode. if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson()) - mIdleState = CharState_None; + idle = CharState_None; // In other cases we should not break swim and sneak animations if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) - mIdleState = CharState_None; + idle = CharState_None; animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) @@ -1614,7 +1666,9 @@ bool CharacterController::updateWeaponState() } mAnimation->setPitchFactor(0.f); - if (mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) + if (mWeaponType == WeapType_BowAndArrow || + mWeaponType == WeapType_Thrown || + mWeaponType == WeapType_Crossbow) { switch (mUpperBodyState) { @@ -1628,29 +1682,14 @@ bool CharacterController::updateWeaponState() break; case UpperCharState_FollowStartToFollowStop: if (animPlaying) - mAnimation->setPitchFactor(1.f-complete); - break; - default: - break; - } - } - else if (mWeaponType == WeapType_Crossbow) - { - switch (mUpperBodyState) - { - case UpperCharState_EquipingWeap: - mAnimation->setPitchFactor(complete); - break; - case UpperCharState_UnEquipingWeap: - mAnimation->setPitchFactor(1.f-complete); - break; - case UpperCharState_WeapEquiped: - case UpperCharState_StartToMinAttack: - case UpperCharState_MinAttackToMaxAttack: - case UpperCharState_MaxAttackToMinHit: - case UpperCharState_MinHitToHit: - case UpperCharState_FollowStartToFollowStop: - mAnimation->setPitchFactor(1.f); + { + // technically we do not need a pitch for crossbow reload animation, + // but we should avoid abrupt repositioning + if (mWeaponType == WeapType_Crossbow) + mAnimation->setPitchFactor(std::max(0.f, 1.f-complete*10.f)); + else + mAnimation->setPitchFactor(1.f-complete); + } break; default: break; @@ -1814,17 +1853,19 @@ void CharacterController::update(float duration) if (isKnockedOut()) mTimeUntilWake -= duration; - bool godmode = mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + bool isPlayer = mPtr == MWMechanics::getPlayer(); + bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); if(!cls.isActor()) updateAnimQueue(); else if(!cls.getCreatureStats(mPtr).isDead()) { bool onground = world->isOnGround(mPtr); + bool incapacitated = (cls.getCreatureStats(mPtr).isParalyzed() || cls.getCreatureStats(mPtr).getKnockedDown()); bool inwater = world->isSwimming(mPtr); - bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool flying = world->isFlying(mPtr); - // Can't run while flying (see speed formula in Npc/Creature::getSpeed) + // Can't run and sneak while flying (see speed formula in Npc/Creature::getSpeed) + bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !flying; bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !flying; CreatureStats &stats = cls.getCreatureStats(mPtr); @@ -1878,7 +1919,7 @@ void CharacterController::update(float duration) isrunning = isrunning && mHasMovedInXY; // advance athletics - if(mHasMovedInXY && mPtr == getPlayer()) + if(mHasMovedInXY && isPlayer) { if(inwater) { @@ -1903,14 +1944,14 @@ void CharacterController::update(float duration) // reduce fatigue const MWWorld::Store &gmst = world->getStore().get(); float fatigueLoss = 0; - static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->getFloat(); - static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->getFloat(); - static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->getFloat(); - static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->getFloat(); - static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->getFloat(); - static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->getFloat(); - static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->getFloat(); - static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->getFloat(); + static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->mValue.getFloat(); + static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->mValue.getFloat(); + static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->mValue.getFloat(); + static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->mValue.getFloat(); + static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->mValue.getFloat(); + static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->mValue.getFloat(); + static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->mValue.getFloat(); + static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->mValue.getFloat(); if (cls.getEncumbrance(mPtr) <= cls.getCapacity(mPtr)) { @@ -1939,7 +1980,7 @@ void CharacterController::update(float duration) cls.getCreatureStats(mPtr).setFatigue(fatigue); } - if(sneak || inwater || flying) + if(sneak || inwater || flying || incapacitated) vec.z() = 0.0f; bool inJump = true; @@ -1950,8 +1991,8 @@ void CharacterController::update(float duration) forcestateupdate = (mJumpState != JumpState_InAir); jumpstate = JumpState_InAir; - static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat(); - static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat(); + static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->mValue.getFloat(); + static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->mValue.getFloat(); float factor = fJumpMoveBase + fJumpMoveMult * mPtr.getClass().getSkill(mPtr, ESM::Skill::Acrobatics)/100.f; factor = std::min(1.f, factor); vec.x() *= factor; @@ -1974,12 +2015,16 @@ void CharacterController::update(float duration) } // advance acrobatics - if (mPtr == getPlayer()) + // also set jumping flag to allow GetPCJumping works + if (isPlayer) + { cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0); + MWBase::Environment::get().getWorld()->getPlayer().setJumping(true); + } // decrease fatigue - const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat(); - const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat(); + const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat(); + const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat(); float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr); if (normalizedEncumbrance > 1) normalizedEncumbrance = 1; @@ -1998,7 +2043,7 @@ void CharacterController::update(float duration) jumpstate = JumpState_Landing; vec.z() = 0.0f; - float height = cls.getCreatureStats(mPtr).land(); + float height = cls.getCreatureStats(mPtr).land(isPlayer); float healthLost = getFallDamage(mPtr, height); if (healthLost > 0.0f) @@ -2008,25 +2053,29 @@ void CharacterController::update(float duration) // inflict fall damages if (!godmode) { - DynamicStat health = cls.getCreatureStats(mPtr).getHealth(); float realHealthLost = static_cast(healthLost * (1.0f - 0.25f * fatigueTerm)); - health.setCurrent(health.getCurrent() - realHealthLost); - cls.getCreatureStats(mPtr).setHealth(health); cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); } const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) { - cls.getCreatureStats(mPtr).setKnockedDown(true); + if (!godmode) + cls.getCreatureStats(mPtr).setKnockedDown(true); } else { // report acrobatics progression - if (mPtr == getPlayer()) + if (isPlayer) cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); } } + + // Play landing sound + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + std::string sound = cls.getSoundIdFromSndGen(mPtr, "land"); + if (!sound.empty()) + sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); } else { @@ -2036,6 +2085,12 @@ void CharacterController::update(float duration) inJump = false; + // Do not play turning animation for player if rotation speed is very slow. + // Actual threshold should take framerate in account. + float rotationThreshold = 0; + if (isPlayer) + rotationThreshold = 0.015 * 60 * duration; + if(std::abs(vec.x()/2.0f) > std::abs(vec.y())) { if(vec.x() > 0.0f) @@ -2058,17 +2113,23 @@ void CharacterController::update(float duration) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); } - else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) + else if(rot.z() != 0.0f) { - if(rot.z() > 0.0f) - movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; - else if(rot.z() < 0.0f) - movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; + // It seems only bipedal actors use turning animations. + // Also do not use turning animations in the first-person view and when sneaking. + bool isFirstPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); + if (!sneak && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr)) + { + if(rot.z() > rotationThreshold) + movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; + else if(rot.z() < -rotationThreshold) + movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; + } } } // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering - if (mPtr == getPlayer()) + if (isPlayer) { float threshold = mCurrentMovement.find("swim") == std::string::npos ? 0.4f : 0.8f; float complete; @@ -2081,16 +2142,21 @@ void CharacterController::update(float duration) } else { - mTurnAnimationThreshold -= duration; - if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft || - movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft) + if (mPtr.getClass().isBipedal(mPtr)) { - mTurnAnimationThreshold = 0.05f; - } - else if (movestate == CharState_None && isTurning() - && mTurnAnimationThreshold > 0) - { - movestate = mMovementState; + if (mTurnAnimationThreshold > 0) + mTurnAnimationThreshold -= duration; + + if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft || + movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft) + { + mTurnAnimationThreshold = 0.05f; + } + else if (movestate == CharState_None && isTurning() + && mTurnAnimationThreshold > 0) + { + movestate = mMovementState; + } } } @@ -2099,14 +2165,13 @@ void CharacterController::update(float duration) if(mAnimQueue.empty() || inwater || sneak) { - // Note: turning animations should not interrupt idle ones. - // Also movement should not stop idle animation for spellcasting stance. - if (inwater) + // Note: turning animations should not interrupt idle ones + if (movestate != CharState_None && !isTurning()) + idlestate = CharState_None; + else if (inwater) idlestate = CharState_IdleSwim; else if (sneak && !inJump) idlestate = CharState_IdleSneak; - else if (movestate != CharState_None && !isTurning() && mWeaponType != WeapType_Spell) - idlestate = CharState_None; else idlestate = CharState_Idle; } @@ -2117,7 +2182,7 @@ void CharacterController::update(float duration) { // bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used. if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr)) - forcestateupdate = updateWeaponState() || forcestateupdate; + forcestateupdate = updateWeaponState(idlestate) || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; @@ -2131,9 +2196,11 @@ void CharacterController::update(float duration) if (isTurning()) { // Adjust animation speed from 1.0 to 1.5 multiplier - float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI)); if (duration > 0) + { + float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI)); mAnimation->adjustSpeedMult(mCurrentMovement, std::max(turnSpeed, 1.0f)); + } } else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed) { @@ -2210,9 +2277,6 @@ void CharacterController::update(float duration) moved *= (l / newLength); } - if (mSkipAnim) - mAnimation->updateEffects(duration); - if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr)) moved.z() = 1.0; @@ -2242,7 +2306,7 @@ void CharacterController::persistAnimationState() { anim.mLoopCount = mAnimation->getCurrentLoopCount(anim.mGroup); float complete; - mAnimation->getInfo(anim.mGroup, &complete, NULL); + mAnimation->getInfo(anim.mGroup, &complete, nullptr); anim.mTime = complete; } else @@ -2374,7 +2438,7 @@ bool CharacterController::isPersistentAnimPlaying() bool CharacterController::isAnimPlaying(const std::string &groupName) { - if(mAnimation == NULL) + if(mAnimation == nullptr) return false; return mAnimation->isPlaying(groupName); } @@ -2671,9 +2735,9 @@ void CharacterController::updateHeadTracking(float duration) if (const MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) { const osg::Node* node = anim->getNode("Head"); - if (node == NULL) + if (node == nullptr) node = anim->getNode("Bip01 Head"); - if (node != NULL) + if (node != nullptr) { nodepaths = node->getParentalNodePaths(); if (!nodepaths.empty()) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 43d26e52f..f97614ef4 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -213,14 +213,14 @@ class CharacterController : public MWRender::Animation::TextKeyListener void setAttackTypeBasedOnMovement(); void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); - void refreshHitRecoilAnims(); - void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, bool force=false); - void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, bool force=false); + void refreshHitRecoilAnims(CharacterState& idle); + void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, CharacterState& movement, bool force=false); + void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force=false); void refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force=false); void clearAnimQueue(bool clearPersistAnims = false); - bool updateWeaponState(); + bool updateWeaponState(CharacterState& idle); bool updateCreatureState(); void updateIdleStormState(bool inwater); @@ -240,8 +240,8 @@ class CharacterController : public MWRender::Animation::TextKeyListener void playRandomDeath(float startpoint = 0.0f); /// choose a random animation group with \a prefix and numeric suffix - /// @param num if non-NULL, the chosen animation number will be written here - std::string chooseRandomGroup (const std::string& prefix, int* num = NULL) const; + /// @param num if non-nullptr, the chosen animation number will be written here + std::string chooseRandomGroup (const std::string& prefix, int* num = nullptr) const; bool updateCarriedLeftVisible(WeaponType weaptype) const; diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 06ed99f5a..be55b681f 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -82,9 +82,9 @@ namespace MWMechanics osg::Vec3f(0,0,1))); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - if (angleDegrees < gmst.find("fCombatBlockLeftAngle")->getFloat()) + if (angleDegrees < gmst.find("fCombatBlockLeftAngle")->mValue.getFloat()) return false; - if (angleDegrees > gmst.find("fCombatBlockRightAngle")->getFloat()) + if (angleDegrees > gmst.find("fCombatBlockRightAngle")->mValue.getFloat()) return false; MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); @@ -92,11 +92,11 @@ namespace MWMechanics float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2f * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() + 0.1f * blockerStats.getAttribute(ESM::Attribute::Luck).getModified(); float enemySwing = attackStrength; - float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->getFloat() + gmst.find("fSwingBlockBase")->getFloat(); + float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->mValue.getFloat() + gmst.find("fSwingBlockBase")->mValue.getFloat(); float blockerTerm = blockTerm * swingTerm; if (blocker.getClass().getMovementSettings(blocker).mPosition[1] <= 0) - blockerTerm *= gmst.find("fBlockStillBonus")->getFloat(); + blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat(); blockerTerm *= blockerStats.getFatigueTerm(); int attackerSkill = 0; @@ -109,8 +109,8 @@ namespace MWMechanics attackerTerm *= attackerStats.getFatigueTerm(); int x = int(blockerTerm - attackerTerm); - int iBlockMaxChance = gmst.find("iBlockMaxChance")->getInt(); - int iBlockMinChance = gmst.find("iBlockMinChance")->getInt(); + int iBlockMaxChance = gmst.find("iBlockMaxChance")->mValue.getInteger(); + int iBlockMinChance = gmst.find("iBlockMinChance")->mValue.getInteger(); x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x)); if (Misc::Rng::roll0to99() < x) @@ -126,9 +126,9 @@ namespace MWMechanics inv.unequipItem(*shield, blocker); } // Reduce blocker fatigue - const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat(); - const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat(); - const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat(); + const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->mValue.getFloat(); + const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->mValue.getFloat(); + const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->mValue.getFloat(); MWMechanics::DynamicStat fatigue = blockerStats.getFatigue(); float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker); normalizedEncumbrance = std::min(1.f, normalizedEncumbrance); @@ -166,7 +166,7 @@ namespace MWMechanics if ((weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver) && actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) - damage *= MWBase::Environment::get().getWorld()->getStore().get().find("fWereWolfSilverWeaponDamageMult")->getFloat(); + damage *= MWBase::Environment::get().getWorld()->getStore().get().find("fWereWolfSilverWeaponDamageMult")->mValue.getFloat(); if (damage == 0 && attacker == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}"); @@ -219,14 +219,14 @@ namespace MWMechanics if (unaware) { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + damage *= gmst.find("fCombatCriticalStrikeMult")->mValue.getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } } if (victim.getClass().getCreatureStats(victim).getKnockedDown()) - damage *= gmst.find("fCombatKODamageMult")->getFloat(); + damage *= gmst.find("fCombatKODamageMult")->mValue.getFloat(); } reduceWeaponCondition(damage, validVictim, weapon, attacker); @@ -241,7 +241,7 @@ namespace MWMechanics // Non-enchanted arrows shot at enemies have a chance to turn up in their inventory if (victim != getPlayer() && !appliedEnchantment) { - float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat(); + float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->mValue.getFloat(); if (Misc::Rng::rollProbability() < fProjectileThrownStoreChance / 100.f) victim.getClass().getContainerStore(victim).add(projectile, 1, victim); } @@ -273,10 +273,10 @@ namespace MWMechanics defenseTerm = victimStats.getEvasion(); } defenseTerm += std::min(100.f, - gmst.find("fCombatInvisoMult")->getFloat() * + gmst.find("fCombatInvisoMult")->mValue.getFloat() * victimStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude()); defenseTerm += std::min(100.f, - gmst.find("fCombatInvisoMult")->getFloat() * + gmst.find("fCombatInvisoMult")->mValue.getFloat() * victimStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude()); } float attackTerm = skillValue + @@ -322,7 +322,7 @@ namespace MWMechanics x = std::min(100.f, x + elementResistance); - static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->getFloat(); + static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->mValue.getFloat(); x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); // Note swapped victim and attacker, since the attacker takes the damage here. @@ -352,7 +352,7 @@ namespace MWMechanics // weapon condition does not degrade when godmode is on if (!godmode) { - const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get().find("fWeaponDamageMult")->getFloat(); + const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get().find("fWeaponDamageMult")->mValue.getFloat(); float x = std::max(1.f, fWeaponDamageMult * damage); weaphealth -= std::min(int(x), weaphealth); @@ -379,9 +379,9 @@ namespace MWMechanics } static const float fDamageStrengthBase = MWBase::Environment::get().getWorld()->getStore().get() - .find("fDamageStrengthBase")->getFloat(); + .find("fDamageStrengthBase")->mValue.getFloat(); static const float fDamageStrengthMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fDamageStrengthMult")->getFloat(); + .find("fDamageStrengthMult")->mValue.getFloat(); damage *= fDamageStrengthBase + (attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1f); } @@ -389,8 +389,8 @@ namespace MWMechanics void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg, float attackStrength) { const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - float minstrike = store.get().find("fMinHandToHandMult")->getFloat(); - float maxstrike = store.get().find("fMaxHandToHandMult")->getFloat(); + float minstrike = store.get().find("fMinHandToHandMult")->mValue.getFloat(); + float maxstrike = store.get().find("fMaxHandToHandMult")->mValue.getFloat(); damage = static_cast(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand)); damage *= minstrike + ((maxstrike-minstrike)*attackStrength); @@ -415,7 +415,7 @@ namespace MWMechanics damage *= MWBase::Environment::get().getWorld()->getGlobalFloat("werewolfclawmult"); } if(healthdmg) - damage *= store.get().find("fHandtoHandHealthPer")->getFloat(); + damage *= store.get().find("fHandtoHandHealthPer")->mValue.getFloat(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(isWerewolf) @@ -432,9 +432,9 @@ namespace MWMechanics { // somewhat of a guess, but using the weapon weight makes sense const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); - const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat(); - const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat(); - const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat(); + const float fFatigueAttackBase = store.find("fFatigueAttackBase")->mValue.getFloat(); + const float fFatigueAttackMult = store.find("fFatigueAttackMult")->mValue.getFloat(); + const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->mValue.getFloat(); CreatureStats& stats = attacker.getClass().getCreatureStats(attacker); MWMechanics::DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = attacker.getClass().getNormalizedEncumbrance(attacker); @@ -459,9 +459,9 @@ namespace MWMechanics float d = (pos1 - pos2).length(); static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( - "iFightDistanceBase")->getInt(); + "iFightDistanceBase")->mValue.getInteger(); static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find( - "fFightDistanceMultiplier")->getFloat(); + "fFightDistanceMultiplier")->mValue.getFloat(); return (iFightDistanceBase - fFightDistanceMultiplier * d); } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index d1128860c..7cc6ea784 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -7,6 +7,7 @@ #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -48,8 +49,8 @@ namespace MWMechanics const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fFatigueBase = gmst.find("fFatigueBase")->getFloat(); - static const float fFatigueMult = gmst.find("fFatigueMult")->getFloat(); + static const float fFatigueBase = gmst.find("fFatigueBase")->mValue.getFloat(); + static const float fFatigueMult = gmst.find("fFatigueMult")->mValue.getFloat(); return fFatigueBase - fFatigueMult * (1-normalised); } @@ -387,8 +388,11 @@ namespace MWMechanics mFallHeight += height; } - float CreatureStats::land() + float CreatureStats::land(bool isPlayer) { + if (isPlayer) + MWBase::Environment::get().getWorld()->getPlayer().setJumping(false); + float height = mFallHeight; mFallHeight = 0; return height; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 057a6f602..503ac7d68 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -95,7 +95,7 @@ namespace MWMechanics /// Reset the fall height /// @return total fall height - float land(); + float land(bool isPlayer=false); const AttributeValue & getAttribute(int index) const; diff --git a/apps/openmw/mwmechanics/difficultyscaling.cpp b/apps/openmw/mwmechanics/difficultyscaling.cpp index 53cf47dea..237698974 100644 --- a/apps/openmw/mwmechanics/difficultyscaling.cpp +++ b/apps/openmw/mwmechanics/difficultyscaling.cpp @@ -17,7 +17,7 @@ float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr difficultySetting = std::min(difficultySetting, 500); difficultySetting = std::max(difficultySetting, -500); - static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get().find("fDifficultyMult")->getFloat(); + static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get().find("fDifficultyMult")->mValue.getFloat(); float difficultyTerm = 0.01f * difficultySetting; diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp index 5746af2a2..fec3bdd37 100644 --- a/apps/openmw/mwmechanics/disease.hpp +++ b/apps/openmw/mwmechanics/disease.hpp @@ -28,7 +28,7 @@ namespace MWMechanics float fDiseaseXferChance = MWBase::Environment::get().getWorld()->getStore().get().find( - "fDiseaseXferChance")->getFloat(); + "fDiseaseXferChance")->mValue.getFloat(); MagicEffects& actorEffects = actor.getClass().getCreatureStats(actor).getMagicEffects(); @@ -59,7 +59,7 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).getSpells().add(it->first); std::string msg = "sMagicContractDisease"; - msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); + msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->mValue.getString(); if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, spell->mName); MWBase::Environment::get().getWindowManager()->messageBox(msg); diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 08f30aba1..e4bb0cf3f 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -10,7 +10,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" #include "spellcasting.hpp" #include "actorutil.hpp" @@ -107,7 +106,7 @@ namespace MWMechanics } const bool powerfulSoul = getGemCharge() >= \ - MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->getInt(); + MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->mValue.getInteger(); if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name())) { // Armor or Clothing switch(mCastStyle) @@ -184,7 +183,7 @@ namespace MWMechanics float magnitudeCost = (magMin + magMax) * baseCost * 0.05f; if (mCastStyle == ESM::Enchantment::ConstantEffect) { - magnitudeCost *= store.get().find("fEnchantmentConstantDurationMult")->getFloat(); + magnitudeCost *= store.get().find("fEnchantmentConstantDurationMult")->mValue.getFloat(); } else { @@ -193,7 +192,7 @@ namespace MWMechanics float areaCost = area * 0.05f * baseCost; - const float fEffectCostMult = store.get().find("fEffectCostMult")->getFloat(); + const float fEffectCostMult = store.get().find("fEffectCostMult")->mValue.getFloat(); cost += (magnitudeCost + areaCost) * fEffectCostMult; @@ -230,7 +229,7 @@ namespace MWMechanics if(mEnchanter.isEmpty()) return 0; - float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->getFloat(); + float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->mValue.getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast(getEnchantPoints() * priceMultipler), true); return price; } @@ -256,7 +255,7 @@ namespace MWMechanics const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - return static_cast(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get().find("fEnchantmentMult")->getFloat()); + return static_cast(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get().find("fEnchantmentMult")->mValue.getFloat()); } bool Enchanting::soulEmpty() const { @@ -280,16 +279,16 @@ namespace MWMechanics float Enchanting::getEnchantChance() const { - const NpcStats& npcStats = mEnchanter.getClass().getNpcStats (mEnchanter); + const CreatureStats& stats = mEnchanter.getClass().getCreatureStats(mEnchanter); - float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() + - (0.25f * npcStats.getAttribute (ESM::Attribute::Intelligence).getModified()) - + (0.125f * npcStats.getAttribute (ESM::Attribute::Luck).getModified())); + float chance1 = (mEnchanter.getClass().getSkill(mEnchanter, ESM::Skill::Enchant) + + (0.25f * stats.getAttribute (ESM::Attribute::Intelligence).getModified()) + + (0.125f * stats.getAttribute (ESM::Attribute::Luck).getModified())); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - float chance2 = 7.5f / (gmst.find("fEnchantmentChanceMult")->getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ? - gmst.find("fEnchantmentConstantChanceMult")->getFloat() : 1.0f )) + float chance2 = 7.5f / (gmst.find("fEnchantmentChanceMult")->mValue.getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ? + gmst.find("fEnchantmentConstantChanceMult")->mValue.getFloat() : 1.0f )) * getEnchantPoints(); return (chance1-chance2); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index e7343e23a..619ed91b3 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -36,7 +36,7 @@ namespace float getFightDispositionBias(float disposition) { static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fFightDispMult")->getFloat(); + "fFightDispMult")->mValue.getFloat(); return ((50.f - disposition) * fFightDispMult); } @@ -45,11 +45,11 @@ namespace const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->getFloat(); - float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->getFloat(); - float repTerm = stats.getReputation() * gmst.find("fReputationMod")->getFloat(); + float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->mValue.getFloat(); + float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->mValue.getFloat(); + float repTerm = stats.getReputation() * gmst.find("fReputationMod")->mValue.getFloat(); float fatigueTerm = stats.getFatigueTerm(); - float levelTerm = stats.getLevel() * gmst.find("fLevelMod")->getFloat(); + float levelTerm = stats.getLevel() * gmst.find("fLevelMod")->mValue.getFloat(); rating1 = (repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; @@ -203,7 +203,7 @@ namespace MWMechanics } // F_PCStart spells - const ESM::Race* race = NULL; + const ESM::Race* race = nullptr; if (mRaceSelected) race = esmStore.get().find(player->mRace); @@ -342,7 +342,7 @@ namespace MWMechanics if(timeToDrown != mWatchedTimeToStartDrowning) { static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get() - .find("fHoldBreathTime")->getFloat(); + .find("fHoldBreathTime")->mValue.getFloat(); mWatchedTimeToStartDrowning = timeToDrown; @@ -453,9 +453,17 @@ namespace MWMechanics void MechanicsManager::rest(bool sleep) { + if (sleep) + MWBase::Environment::get().getWorld()->rest(); + mActors.rest(sleep); } + void MechanicsManager::restoreDynamicStats(MWWorld::Ptr actor, bool sleep) + { + mActors.restoreDynamicStats(actor, sleep); + } + int MechanicsManager::getHoursToRest() const { return mActors.getHoursToRest(mWatched); @@ -543,12 +551,12 @@ namespace MWMechanics const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fDispRaceMod = gmst.find("fDispRaceMod")->getFloat(); + static const float fDispRaceMod = gmst.find("fDispRaceMod")->mValue.getFloat(); if (Misc::StringUtils::ciEqual(npc->mBase->mRace, player->mBase->mRace)) x += fDispRaceMod; - static const float fDispPersonalityMult = gmst.find("fDispPersonalityMult")->getFloat(); - static const float fDispPersonalityBase = gmst.find("fDispPersonalityBase")->getFloat(); + static const float fDispPersonalityMult = gmst.find("fDispPersonalityMult")->mValue.getFloat(); + static const float fDispPersonalityBase = gmst.find("fDispPersonalityBase")->mValue.getFloat(); x += fDispPersonalityMult * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - fDispPersonalityBase); float reaction = 0; @@ -592,20 +600,20 @@ namespace MWMechanics rank = 0; } - static const float fDispFactionRankMult = gmst.find("fDispFactionRankMult")->getFloat(); - static const float fDispFactionRankBase = gmst.find("fDispFactionRankBase")->getFloat(); - static const float fDispFactionMod = gmst.find("fDispFactionMod")->getFloat(); + static const float fDispFactionRankMult = gmst.find("fDispFactionRankMult")->mValue.getFloat(); + static const float fDispFactionRankBase = gmst.find("fDispFactionRankBase")->mValue.getFloat(); + static const float fDispFactionMod = gmst.find("fDispFactionMod")->mValue.getFloat(); x += (fDispFactionRankMult * rank + fDispFactionRankBase) * fDispFactionMod * reaction; - static const float fDispCrimeMod = gmst.find("fDispCrimeMod")->getFloat(); - static const float fDispDiseaseMod = gmst.find("fDispDiseaseMod")->getFloat(); + static const float fDispCrimeMod = gmst.find("fDispCrimeMod")->mValue.getFloat(); + static const float fDispDiseaseMod = gmst.find("fDispDiseaseMod")->mValue.getFloat(); x -= fDispCrimeMod * playerStats.getBounty(); if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease()) x += fDispDiseaseMod; - static const float fDispWeaponDrawn = gmst.find("fDispWeaponDrawn")->getFloat(); + static const float fDispWeaponDrawn = gmst.find("fDispWeaponDrawn")->mValue.getFloat(); if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon) x += fDispWeaponDrawn; @@ -631,10 +639,10 @@ namespace MWMechanics // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = getDerivedDisposition(ptr); - float a = static_cast(std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100)); + float a = static_cast(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100)); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = static_cast(std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100)); + float d = static_cast(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100)); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); @@ -673,16 +681,16 @@ namespace MWMechanics float target2 = d * (playerRating2 - npcRating2 + 50); float bribeMod; - if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat(); - else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); - else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); + if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->mValue.getFloat(); + else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->mValue.getFloat(); + else bribeMod = gmst.find("fBribe1000Mod")->mValue.getFloat(); float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; - float iPerMinChance = floor(gmst.find("iPerMinChance")->getFloat()); - float iPerMinChange = floor(gmst.find("iPerMinChange")->getFloat()); - float fPerDieRollMult = gmst.find("fPerDieRollMult")->getFloat(); - float fPerTempMult = gmst.find("fPerTempMult")->getFloat(); + float iPerMinChance = floor(gmst.find("iPerMinChance")->mValue.getFloat()); + float iPerMinChange = floor(gmst.find("iPerMinChange")->mValue.getFloat()); + float fPerDieRollMult = gmst.find("fPerDieRollMult")->mValue.getFloat(); + float fPerTempMult = gmst.find("fPerTempMult")->mValue.getFloat(); float x = 0; float y = 0; @@ -867,7 +875,7 @@ namespace MWMechanics continue; // All sMagicBound* GMST's should be of type string - std::string currentGMSTValue = currentSetting.getString(); + std::string currentGMSTValue = currentSetting.mValue.getString(); Misc::StringUtils::lowerCaseInPlace(currentGMSTValue); boundItemIDCache.insert(currentGMSTValue); @@ -1163,7 +1171,7 @@ namespace MWMechanics osg::Vec3f from (player.getRefData().getPosition().asVec3()); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - float radius = esmStore.get().find("fAlarmRadius")->getFloat(); + float radius = esmStore.get().find("fAlarmRadius")->mValue.getFloat(); mActors.getObjectsInRange(from, radius, neighbors); @@ -1261,29 +1269,29 @@ namespace MWMechanics float disp = 0.f, dispVictim = 0.f; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) { - arg = store.find("iCrimeTresspass")->getInt(); - disp = dispVictim = store.find("iDispTresspass")->getFloat(); + arg = store.find("iCrimeTresspass")->mValue.getInteger(); + disp = dispVictim = store.find("iDispTresspass")->mValue.getFloat(); } else if (type == OT_Pickpocket) { - arg = store.find("iCrimePickPocket")->getInt(); - disp = dispVictim = store.find("fDispPickPocketMod")->getFloat(); + arg = store.find("iCrimePickPocket")->mValue.getInteger(); + disp = dispVictim = store.find("fDispPickPocketMod")->mValue.getFloat(); } else if (type == OT_Assault) { - arg = store.find("iCrimeAttack")->getInt(); - disp = store.find("iDispAttackMod")->getFloat(); - dispVictim = store.find("fDispAttacking")->getFloat(); + arg = store.find("iCrimeAttack")->mValue.getInteger(); + disp = store.find("iDispAttackMod")->mValue.getFloat(); + dispVictim = store.find("fDispAttacking")->mValue.getFloat(); } else if (type == OT_Murder) { - arg = store.find("iCrimeKilling")->getInt(); - disp = dispVictim = store.find("iDispKilling")->getFloat(); + arg = store.find("iCrimeKilling")->mValue.getInteger(); + disp = dispVictim = store.find("iDispKilling")->mValue.getFloat(); } else if (type == OT_Theft) { - disp = dispVictim = store.find("fDispStealing")->getFloat() * arg; - arg = static_cast(arg * store.find("fCrimeStealing")->getFloat()); + disp = dispVictim = store.find("fDispStealing")->mValue.getFloat() * arg; + arg = static_cast(arg * store.find("fCrimeStealing")->mValue.getFloat()); arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen } @@ -1293,7 +1301,7 @@ namespace MWMechanics const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); osg::Vec3f from (player.getRefData().getPosition().asVec3()); - float radius = esmStore.get().find("fAlarmRadius")->getFloat(); + float radius = esmStore.get().find("fAlarmRadius")->mValue.getFloat(); mActors.getObjectsInRange(from, radius, neighbors); @@ -1307,21 +1315,21 @@ namespace MWMechanics // Controls whether witnesses will engage combat with the criminal. int fight = 0, fightVictim = 0; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) - fight = fightVictim = esmStore.get().find("iFightTrespass")->getInt(); + fight = fightVictim = esmStore.get().find("iFightTrespass")->mValue.getInteger(); else if (type == OT_Pickpocket) { - fight = esmStore.get().find("iFightPickpocket")->getInt(); - fightVictim = esmStore.get().find("iFightPickpocket")->getInt() * 4; // *4 according to research wiki + fight = esmStore.get().find("iFightPickpocket")->mValue.getInteger(); + fightVictim = esmStore.get().find("iFightPickpocket")->mValue.getInteger() * 4; // *4 according to research wiki } else if (type == OT_Assault) { - fight = esmStore.get().find("iFightAttacking")->getInt(); - fightVictim = esmStore.get().find("iFightAttack")->getInt(); + fight = esmStore.get().find("iFightAttacking")->mValue.getInteger(); + fightVictim = esmStore.get().find("iFightAttack")->mValue.getInteger(); } else if (type == OT_Murder) - fight = fightVictim = esmStore.get().find("iFightKilling")->getInt(); + fight = fightVictim = esmStore.get().find("iFightKilling")->mValue.getInteger(); else if (type == OT_Theft) - fight = fightVictim = esmStore.get().find("fFightStealing")->getInt(); + fight = fightVictim = esmStore.get().find("fFightStealing")->mValue.getInteger(); bool reported = false; @@ -1552,8 +1560,8 @@ namespace MWMechanics && !MWBase::Environment::get().getWorld()->isSwimming(ptr) && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { - static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); - static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); + static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); + static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); @@ -1568,8 +1576,8 @@ namespace MWMechanics sneakTerm = fSneakSkillMult * sneak + 0.2f * agility + 0.1f * luck + bootWeight * fSneakBootMult; } - static float fSneakDistBase = store.find("fSneakDistanceBase")->getFloat(); - static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->getFloat(); + static float fSneakDistBase = store.find("fSneakDistanceBase")->mValue.getFloat(); + static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->mValue.getFloat(); osg::Vec3f pos1 (ptr.getRefData().getPosition().asVec3()); osg::Vec3f pos2 (observer.getRefData().getPosition().asVec3()); @@ -1587,8 +1595,8 @@ namespace MWMechanics float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; // is ptr behind the observer? - static float fSneakNoViewMult = store.find("fSneakNoViewMult")->getFloat(); - static float fSneakViewMult = store.find("fSneakViewMult")->getFloat(); + static float fSneakNoViewMult = store.find("fSneakNoViewMult")->mValue.getFloat(); + static float fSneakViewMult = store.find("fSneakViewMult")->mValue.getFloat(); float y = 0; osg::Vec3f vec = pos1 - pos2; if (observer.getRefData().getBaseNode()) @@ -1748,7 +1756,7 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getGlobalInt("pcknownwerewolf"))) { const ESM::GameSetting * iWerewolfFightMod = MWBase::Environment::get().getWorld()->getStore().get().find("iWerewolfFightMod"); - fight += iWerewolfFightMod->getInt(); + fight += iWerewolfFightMod->mValue.getInteger(); } } @@ -1841,7 +1849,7 @@ namespace MWMechanics // Witnesses of the player's transformation will make them a globally known werewolf std::vector closeActors; const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->getFloat(), closeActors); + getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->mValue.getFloat(), closeActors); bool detected = false, reported = false; for (std::vector::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it) @@ -1866,7 +1874,7 @@ namespace MWMechanics if (reported) { npcStats.setBounty(npcStats.getBounty()+ - gmst.find("iWereWolfBounty")->getInt()); + gmst.find("iWereWolfBounty")->mValue.getInteger()); windowManager->messageBox("#{sCrimeMessage}"); } } @@ -1878,7 +1886,7 @@ namespace MWMechanics const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); MWMechanics::NpcStats &stats = actor.getClass().getNpcStats(actor); - stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getInt()); + stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->mValue.getInteger()); } void MechanicsManager::cleanupSummonedCreature(const MWWorld::Ptr &caster, int creatureActorId) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index af12d4d98..3682e97ce 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -91,6 +91,8 @@ namespace MWMechanics virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. + virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep); + virtual void rest(bool sleep); ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index f15152759..47d886e3f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -149,12 +149,12 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - float typeFactor = gmst.find ("fMiscSkillBonus")->getFloat(); + float typeFactor = gmst.find ("fMiscSkillBonus")->mValue.getFloat(); for (int i=0; i<5; ++i) if (class_.mData.mSkills[i][0]==skillIndex) { - typeFactor = gmst.find ("fMinorSkillBonus")->getFloat(); + typeFactor = gmst.find ("fMinorSkillBonus")->mValue.getFloat(); break; } @@ -162,7 +162,7 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const for (int i=0; i<5; ++i) if (class_.mData.mSkills[i][1]==skillIndex) { - typeFactor = gmst.find ("fMajorSkillBonus")->getFloat(); + typeFactor = gmst.find ("fMajorSkillBonus")->mValue.getFloat(); break; } @@ -178,7 +178,7 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const MWBase::Environment::get().getWorld()->getStore().get().find (skillIndex); if (skill->mData.mSpecialization==class_.mData.mSpecialization) { - specialisationFactor = gmst.find ("fSpecialSkillBonus")->getFloat(); + specialisationFactor = gmst.find ("fSpecialSkillBonus")->mValue.getFloat(); if (specialisationFactor<=0) throw std::runtime_error ("invalid skill specialisation factor"); @@ -227,21 +227,21 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWorld()->getStore().get(); // is this a minor or major skill? - int increase = gmst.find("iLevelupMiscMultAttriubte")->getInt(); // Note: GMST has a typo + int increase = gmst.find("iLevelupMiscMultAttriubte")->mValue.getInteger(); // Note: GMST has a typo for (int k=0; k<5; ++k) { if (class_.mData.mSkills[k][0] == skillIndex) { - mLevelProgress += gmst.find("iLevelUpMinorMult")->getInt(); - increase = gmst.find("iLevelUpMajorMultAttribute")->getInt(); + mLevelProgress += gmst.find("iLevelUpMinorMult")->mValue.getInteger(); + increase = gmst.find("iLevelUpMajorMultAttribute")->mValue.getInteger(); } } for (int k=0; k<5; ++k) { if (class_.mData.mSkills[k][1] == skillIndex) { - mLevelProgress += gmst.find("iLevelUpMajorMult")->getInt(); - increase = gmst.find("iLevelUpMinorMultAttribute")->getInt(); + mLevelProgress += gmst.find("iLevelUpMajorMult")->mValue.getInteger(); + increase = gmst.find("iLevelUpMinorMultAttribute")->mValue.getInteger(); } } @@ -249,7 +249,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWorld ()->getStore ().get().find(skillIndex); mSkillIncreases[skill->mData.mAttribute] += increase; - mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->getInt(); + mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger(); // Play sound & skill progress notification /// \todo check if character is the player, if levelling is ever implemented for NPCs @@ -266,7 +266,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never); - if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) + if (mLevelProgress >= gmst.find("iLevelUpTotal")->mValue.getInteger()) { // levelup is possible now MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never); @@ -287,7 +287,7 @@ void MWMechanics::NpcStats::levelUp() const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - mLevelProgress -= gmst.find("iLevelUpTotal")->getInt(); + mLevelProgress -= gmst.find("iLevelUpTotal")->mValue.getInteger(); mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console for (int i=0; igetFloat()); + // Note: we should add bonus Health points to current level too. + float healthGain = endurance * gmst.find("fLevelUpHealthEndMult")->mValue.getFloat(); + MWMechanics::DynamicStat health(getHealth()); + health.setBase(getHealth().getBase() + healthGain); + health.setCurrent(std::max(1.f, getHealth().getCurrent() + healthGain)); + setHealth(health); setLevel(getLevel()+1); } @@ -324,7 +329,7 @@ int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const std::stringstream gmst; gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult"; - return MWBase::Environment::get().getWorld()->getStore().get().find(gmst.str())->getInt(); + return MWBase::Environment::get().getWorld()->getStore().get().find(gmst.str())->mValue.getInteger(); } int MWMechanics::NpcStats::getSkillIncreasesForSpecialization(int spec) const diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index d8821276e..e95c0a704 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -20,7 +20,7 @@ Objects::~Objects() for (; it != mObjects.end();++it) { delete it->second; - it->second = NULL; + it->second = nullptr; } } diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 6a84e0ef9..c55374501 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -16,7 +16,7 @@ namespace MWMechanics bool proximityToDoor(const MWWorld::Ptr& actor, float minDist); /// Returns door pointer within range. No guarantee is given as to which one - /** \return Pointer to the door, or NULL if none exists **/ + /** \return Pointer to the door, or empty pointer if none exists **/ const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist); class ObstacleCheck diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index b1b04f304..1da97a645 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -120,8 +120,8 @@ namespace MWMechanics } PathFinder::PathFinder() - : mPathgrid(NULL) - , mCell(NULL) + : mPathgrid(nullptr) + , mCell(nullptr) { } diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index ea4c973b7..6b5db64ea 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -50,8 +50,8 @@ namespace namespace MWMechanics { PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell) - : mCell(NULL) - , mPathgrid(NULL) + : mCell(nullptr) + , mPathgrid(nullptr) , mIsExterior(0) , mGraph(0) , mIsGraphConstructed(false) diff --git a/apps/openmw/mwmechanics/pickpocket.cpp b/apps/openmw/mwmechanics/pickpocket.cpp index eca24606e..ed6469106 100644 --- a/apps/openmw/mwmechanics/pickpocket.cpp +++ b/apps/openmw/mwmechanics/pickpocket.cpp @@ -37,9 +37,9 @@ namespace MWMechanics float pcSneak = static_cast(mThief.getClass().getSkill(mThief, ESM::Skill::Sneak)); int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get() - .find("iPickMinChance")->getInt(); + .find("iPickMinChance")->mValue.getInteger(); int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get() - .find("iPickMaxChance")->getInt(); + .find("iPickMaxChance")->mValue.getInteger(); int roll = Misc::Rng::roll0to99(); if (t < pcSneak / iPickMinChance) @@ -57,7 +57,7 @@ namespace MWMechanics { float stackValue = static_cast(item.getClass().getValue(item) * count); float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get() - .find("fPickPocketMod")->getFloat(); + .find("fPickPocketMod")->mValue.getFloat(); float valueTerm = 10 * fPickPocketMod * stackValue; return getDetected(valueTerm); diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 5c6123e82..8c5e69896 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -14,7 +14,6 @@ #include "../mwworld/esmstore.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" #include "actorutil.hpp" namespace MWMechanics @@ -34,15 +33,14 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) mTool.getCellRef().setCharge(uses-1); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); float fatigueTerm = stats.getFatigueTerm(); int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - int armorerSkill = npcStats.getSkill(ESM::Skill::Armorer).getModified(); + int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fRepairAmountMult")->getFloat(); + .find("fRepairAmountMult")->mValue.getFloat(); float toolQuality = ref->mBase->mData.mQuality; @@ -87,7 +85,7 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) store.remove(mTool, 1, player); std::string message = MWBase::Environment::get().getWorld()->getStore().get() - .find("sNotifyMessage51")->getString(); + .find("sNotifyMessage51")->mValue.getString(); MWBase::Environment::get().getWindowManager()->messageBox((boost::format(message) % mTool.getClass().getName(mTool)).str()); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index 3e38bc521..18d4ec2e5 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -11,7 +11,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "npcstats.hpp" #include "creaturestats.hpp" namespace MWMechanics @@ -21,10 +20,9 @@ namespace MWMechanics : mActor(actor) { CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - NpcStats& npcStats = actor.getClass().getNpcStats(actor); mAgility = static_cast(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); mLuck = static_cast(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); - mSecuritySkill = static_cast(npcStats.getSkill(ESM::Skill::Security).getModified()); + mSecuritySkill = static_cast(actor.getClass().getSkill(actor, ESM::Skill::Security)); mFatigueTerm = creatureStats.getFatigueTerm(); } @@ -40,7 +38,7 @@ namespace MWMechanics float pickQuality = lockpick.get()->mBase->mData.mQuality; - float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get().find("fPickLockMult")->getFloat(); + float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get().find("fPickLockMult")->mValue.getFloat(); float x = 0.2f * mAgility + 0.1f * mLuck + mSecuritySkill; x *= pickQuality * mFatigueTerm; @@ -81,7 +79,7 @@ namespace MWMechanics const ESM::Spell* trapSpell = MWBase::Environment::get().getWorld()->getStore().get().find(trap.getCellRef().getTrap()); int trapSpellPoints = trapSpell->mData.mCost; - float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fTrapCostMult")->getFloat(); + float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fTrapCostMult")->mValue.getFloat(); float x = 0.2f * mAgility + 0.1f * mLuck + mSecuritySkill; x += fTrapCostMult * trapSpellPoints; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 76140013d..a91aa2f31 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -34,13 +34,17 @@ namespace MWMechanics { ESM::Skill::SkillEnum spellSchoolToSkill(int school) { - std::map schoolSkillMap; // maps spell school to skill id - schoolSkillMap[0] = ESM::Skill::Alteration; - schoolSkillMap[1] = ESM::Skill::Conjuration; - schoolSkillMap[3] = ESM::Skill::Illusion; - schoolSkillMap[2] = ESM::Skill::Destruction; - schoolSkillMap[4] = ESM::Skill::Mysticism; - schoolSkillMap[5] = ESM::Skill::Restoration; + static std::map schoolSkillMap; // maps spell school to skill id + if (schoolSkillMap.empty()) + { + schoolSkillMap[0] = ESM::Skill::Alteration; + schoolSkillMap[1] = ESM::Skill::Conjuration; + schoolSkillMap[3] = ESM::Skill::Illusion; + schoolSkillMap[2] = ESM::Skill::Destruction; + schoolSkillMap[4] = ESM::Skill::Mysticism; + schoolSkillMap[5] = ESM::Skill::Restoration; + } + assert(schoolSkillMap.find(school) != schoolSkillMap.end()); return schoolSkillMap[school]; } @@ -48,7 +52,11 @@ namespace MWMechanics float calcEffectCost(const ESM::ENAMstruct& effect) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); + return calcEffectCost(effect, magicEffect); + } + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) + { int minMagn = 1; int maxMagn = 1; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) @@ -57,12 +65,12 @@ namespace MWMechanics maxMagn = effect.mMagnMax; } - int duration = 0; + int duration = 1; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) duration = effect.mDuration; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() - .get().find("fEffectCostMult")->getFloat(); + .get().find("fEffectCostMult")->mValue.getFloat(); float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); x *= 0.1 * magicEffect->mData.mBaseCost; @@ -93,7 +101,7 @@ namespace MWMechanics if (it->mRange == ESM::RT_Target) x *= 1.5f; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fEffectCostMult")->getFloat(); + "fEffectCostMult")->mValue.getFloat(); x *= fEffectCostMult; float s = 2.0f * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); @@ -234,9 +242,9 @@ namespace MWMechanics // This makes spells that are easy to cast harder to resist and vice versa float castChance = 100.f; - if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor()) + if (spell != nullptr && !caster.isEmpty() && caster.getClass().isActor()) { - castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance + castChance = getSpellSuccessChance(spell, caster, nullptr, false); // Uncapped casting chance } if (castChance > 0) x *= 50 / castChance; @@ -574,7 +582,7 @@ namespace MWMechanics ActiveSpells::ActiveEffect effect_ = effect; effect_.mMagnitude *= -1; absorbEffects.push_back(effect_); - if (reflected && Settings::Manager::getBool("classic reflect absorb attribute behavior", "Game")) + if (reflected && Settings::Manager::getBool("classic reflected absorb spells behavior", "Game")) target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); else @@ -729,7 +737,7 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::Recall) { - MWWorld::CellStore* markedCell = NULL; + MWWorld::CellStore* markedCell = nullptr; ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); @@ -883,8 +891,8 @@ namespace MWMechanics if (!godmode) { // Reduce fatigue (note that in the vanilla game, both GMSTs are 0, and there's no fatigue loss) - static const float fFatigueSpellBase = store.get().find("fFatigueSpellBase")->getFloat(); - static const float fFatigueSpellMult = store.get().find("fFatigueSpellMult")->getFloat(); + static const float fFatigueSpellBase = store.get().find("fFatigueSpellBase")->mValue.getFloat(); + static const float fFatigueSpellMult = store.get().find("fFatigueSpellMult")->mValue.getFloat(); DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster); @@ -956,10 +964,9 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( effect.mEffectID); - const MWMechanics::NpcStats& npcStats = mCaster.getClass().getNpcStats(mCaster); const MWMechanics::CreatureStats& creatureStats = mCaster.getClass().getCreatureStats(mCaster); - float x = (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + float x = (mCaster.getClass().getSkill(mCaster, ESM::Skill::Alchemy) + 0.2f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()) * creatureStats.getFatigueTerm(); @@ -968,7 +975,7 @@ namespace MWMechanics if (roll > x) { // "X has no effect on you" - std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage50")->getString(); + std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage50")->mValue.getString(); message = boost::str(boost::format(message) % ingredient->mName); MWBase::Environment::get().getWindowManager()->messageBox(message); return false; @@ -1251,7 +1258,7 @@ namespace MWMechanics float damageScale = 1.f - timeDiff / 7.f; // When cloudy, the sun damage effect is halved static float fMagicSunBlockedMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fMagicSunBlockedMult")->getFloat(); + "fMagicSunBlockedMult")->mValue.getFloat(); int weather = MWBase::Environment::get().getWorld()->getCurrentWeather(); if (weather > 1) @@ -1348,7 +1355,7 @@ namespace MWMechanics if (it == summonMap.end()) return std::string(); else - return MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->getString(); + return MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->mValue.getString(); } void ApplyLoopingParticlesVisitor::visit (MWMechanics::EffectKey key, diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 72f6a1f4a..5ac406368 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwworld/ptr.hpp" @@ -25,6 +26,7 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); float calcEffectCost(const ESM::ENAMstruct& effect); + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect); bool isSummoningEffect(int effectId); @@ -37,8 +39,8 @@ namespace MWMechanics * @note actor can be an NPC or a creature * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned. */ - float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true, bool checkMagicka=false); - float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true, bool checkMagicka=false); + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=false); + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=false); int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); @@ -56,14 +58,14 @@ namespace MWMechanics /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); + const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); /// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional). /// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness) /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); + const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 7bedb1e37..3b242266c 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -14,7 +14,7 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/cellstore.hpp" -#include "npcstats.hpp" +#include "creaturestats.hpp" #include "spellcasting.hpp" #include "combat.hpp" @@ -232,20 +232,59 @@ namespace MWMechanics case ESM::MagicEffect::CommandHumanoid: return 0.f; + case ESM::MagicEffect::Blind: + { + if (enemy.isEmpty()) + return 0.f; + + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + + // Enemy can't attack + if (stats.isParalyzed() || stats.getKnockedDown()) + return 0.f; + + // Enemy doesn't attack + if (stats.getDrawState() != MWMechanics::DrawState_Weapon) + return 0.f; + + break; + } + case ESM::MagicEffect::Sound: { if (enemy.isEmpty()) return 0.f; - // there is no need to cast sound if enemy is not able to cast spells - CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + // Enemy can't cast spells if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0) return 0.f; - if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + if (stats.isParalyzed() || stats.getKnockedDown()) return 0.f; + // Enemy doesn't cast spells + if (stats.getDrawState() != MWMechanics::DrawState_Spell) + return 0.f; + + break; + } + + case ESM::MagicEffect::Silence: + { + if (enemy.isEmpty()) + return 0.f; + + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + + // Enemy can't cast spells + if (stats.isParalyzed() || stats.getKnockedDown()) + return 0.f; + + // Enemy doesn't cast spells + if (stats.getDrawState() != MWMechanics::DrawState_Spell) + return 0.f; break; } @@ -353,9 +392,10 @@ namespace MWMechanics int priority = 1; if (effect.mEffectID == ESM::MagicEffect::RestoreHealth) priority = 10; - const DynamicStat& current = actor.getClass().getCreatureStats(actor). - getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); - float toHeal = (effect.mMagnMin + effect.mMagnMax)/2.f * effect.mDuration; + const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + const DynamicStat& current = stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); + const float magnitude = (effect.mMagnMin + effect.mMagnMax)/2.f; + const float toHeal = magnitude * effect.mDuration; // 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) @@ -498,7 +538,7 @@ namespace MWMechanics case ESM::MagicEffect::DrainSkill: if (enemy.isEmpty() || !enemy.getClass().isNpc()) return 0.f; - if (enemy.getClass().getNpcStats(enemy).getSkill(effect.mSkill).getModified() <= 0) + if (enemy.getClass().getSkill(enemy, effect.mSkill) <= 0) return 0.f; break; @@ -515,8 +555,6 @@ namespace MWMechanics return 0.f; } - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - // Underwater casting not possible if (effect.mRange == ESM::RT_Target) { @@ -530,6 +568,7 @@ namespace MWMechanics return 0.f; } + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) { rating *= -1.f; @@ -565,7 +604,7 @@ namespace MWMechanics } } - rating *= calcEffectCost(effect); + rating *= calcEffectCost(effect, magicEffect); // Currently treating all "on target" or "on touch" effects to target the enemy actor. // Combat AI is egoistic, so doesn't consider applying positive effects to friendly actors. @@ -578,13 +617,19 @@ namespace MWMechanics float rateEffects(const ESM::EffectList &list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy) { // NOTE: enemy may be empty + float rating = 0.f; + float ratingMult = 1.f; // NB: this multiplier is applied to the effect rating, not the final rating + + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->mValue.getFloat(); + static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->mValue.getFloat(); + for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) { - rating += rateEffect(*it, actor, enemy); + ratingMult = (it->mRange == ESM::RT_Target) ? fAIRangeMagicSpellMult : fAIMagicSpellMult; - if (it->mRange == ESM::RT_Target) - rating *= 1.5f; + rating += rateEffect(*it, actor, enemy) * ratingMult; } return rating; } @@ -593,8 +638,8 @@ namespace MWMechanics { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->getFloat(); - static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->getFloat(); + static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->mValue.getFloat(); + static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->mValue.getFloat(); float mult = fAIMagicSpellMult; diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index ff397f69d..0a11ed641 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -279,6 +279,25 @@ namespace MWMechanics } } + void Spells::removeEffects(const std::string &id) + { + if (isSpellActive(id)) + { + for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell) + { + if (spell->first == getSpell(id)) + { + for (long unsigned int i = 0; i != spell->first->mEffects.mList.size(); i++) + { + spell->second.mPurgedEffects.insert(i); + } + } + } + + mSpellsChanged = true; + } + } + void Spells::visitEffectSources(EffectSourceVisitor &visitor) const { if (mSpellsChanged) { diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index c1705b38a..e635f4f56 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -124,6 +124,8 @@ namespace MWMechanics bool hasBlightDisease() const; + void removeEffects(const std::string& id); + void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; void readState (const ESM::SpellState& state); diff --git a/apps/openmw/mwmechanics/trading.cpp b/apps/openmw/mwmechanics/trading.cpp index eee5e6449..b824d7c45 100644 --- a/apps/openmw/mwmechanics/trading.cpp +++ b/apps/openmw/mwmechanics/trading.cpp @@ -50,11 +50,11 @@ namespace MWMechanics float e1 = 0.1f * merchantStats.getAttribute(ESM::Attribute::Luck).getModified(); float f1 = 0.2f * merchantStats.getAttribute(ESM::Attribute::Personality).getModified(); - float dispositionTerm = gmst.find("fDispositionMod")->getFloat() * (clampedDisposition - 50); + float dispositionTerm = gmst.find("fDispositionMod")->mValue.getFloat() * (clampedDisposition - 50); float pcTerm = (dispositionTerm + a1 + b1 + c1) * playerStats.getFatigueTerm(); float npcTerm = (d1 + e1 + f1) * merchantStats.getFatigueTerm(); - float x = gmst.find("fBargainOfferMulti")->getFloat() * d - + gmst.find("fBargainOfferBase")->getFloat() + float x = gmst.find("fBargainOfferMulti")->mValue.getFloat() * d + + gmst.find("fBargainOfferBase")->mValue.getFloat() + int(pcTerm - npcTerm); int roll = Misc::Rng::rollDice(100) + 1; diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 9030d6254..aaa06f6e4 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -21,7 +21,10 @@ namespace MWMechanics float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type, float arrowRating, float boltRating) { - if (item.getTypeName() != typeid(ESM::Weapon).name()) + if (enemy.isEmpty() || item.getTypeName() != typeid(ESM::Weapon).name()) + return 0.f; + + if (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) == 0) return 0.f; const ESM::Weapon* weapon = item.get()->mBase; @@ -29,59 +32,46 @@ namespace MWMechanics if (type != -1 && weapon->mData.mType != type) return 0.f; + const MWBase::World* world = MWBase::Environment::get().getWorld(); + const MWWorld::Store& gmst = world->getStore().get(); + + if (type == -1 && (weapon->mData.mType == ESM::Weapon::Arrow || weapon->mData.mType == ESM::Weapon::Bolt)) + return 0.f; + float rating=0.f; - float rangedMult=1.f; + static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); + float ratingMult = fAIMeleeWeaponMult; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown) { - // Range weapon is useless under water - if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f)) - return 0.f; - - if (enemy.isEmpty()) - return 0.f; - - if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) + // Underwater ranged combat is impossible + if (world->isUnderwater(MWWorld::ConstPtr(actor), 0.75f) + || world->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) return 0.f; + // Use a higher rating multiplier if the actor is out of enemy's reach, use the normal mult otherwise if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy)) - rangedMult = 1.5f; + { + static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); + ratingMult = fAIRangeMeleeWeaponMult; + } } + const float chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) - { - float rangedDamage = weapon->mData.mChop[0] + weapon->mData.mChop[1]; - MWMechanics::adjustWeaponDamage(rangedDamage, item, actor); - - rating = rangedDamage / 2.f; - - if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown) - MWMechanics::resistNormalWeapon(enemy, actor, item, rating); - } + rating = chop; else { - float meleeDamage = 0.f; - - for (int i=0; i<2; ++i) - { - meleeDamage += weapon->mData.mSlash[i]; - meleeDamage += weapon->mData.mThrust[i]; - meleeDamage += weapon->mData.mChop[i]; - } - - MWMechanics::adjustWeaponDamage(meleeDamage, item, actor); - rating = meleeDamage / 6.f; - - MWMechanics::resistNormalWeapon(enemy, actor, item, rating); + const float slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1]) / 2.f; + const float thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1]) / 2.f; + rating = (slash * slash + thrust * thrust + chop * chop) / (slash + thrust + chop); } - if (item.getClass().hasItemHealth(item)) - { - if (item.getClass().getItemHealth(item) == 0) - return 0.f; - } + adjustWeaponDamage(rating, item, actor); - if (weapon->mData.mType == ESM::Weapon::MarksmanBow) + if (weapon->mData.mType != ESM::Weapon::MarksmanBow && weapon->mData.mType != ESM::Weapon::MarksmanCrossbow) + resistNormalWeapon(enemy, actor, item, rating); + else if (weapon->mData.mType == ESM::Weapon::MarksmanBow) { if (arrowRating <= 0.f) rating = 0.f; @@ -98,7 +88,7 @@ namespace MWMechanics if (!weapon->mEnchant.empty()) { - const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(weapon->mEnchant); + const ESM::Enchantment* enchantment = world->getStore().get().find(weapon->mEnchant); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { int castCost = getEffectiveEnchantmentCastCost(static_cast(enchantment->mData.mCost), actor); @@ -108,22 +98,31 @@ namespace MWMechanics } } + if (enemy.getClass().isNpc()) + { + static const float fCombatArmorMinMult = gmst.find("fCombatArmorMinMult")->mValue.getFloat(); + rating *= std::max(fCombatArmorMinMult, rating / (rating + enemy.getClass().getArmorRating(enemy))); + } + + int value = 50.f; if (actor.getClass().isNpc()) { int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) - { - int value = actor.getClass().getSkill(actor, skill); - rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f; - } + value = actor.getClass().getSkill(actor, skill); } else { MWWorld::LiveCellRef *ref = actor.get(); - rating *= MWMechanics::getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f; + value = ref->mBase->mData.mCombat; } - return rating * rangedMult; + rating *= getHitChance(actor, enemy, value) / 100.f; + + if (weapon->mData.mType < ESM::Weapon::Arrow) + rating *= weapon->mData.mSpeed; + + return rating * ratingMult; } float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType) @@ -157,9 +156,9 @@ namespace MWMechanics { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->getFloat(); - static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->getFloat(); - static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->getFloat(); + static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); + static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->mValue.getFloat(); + static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); if (weapon.isEmpty()) return 0.f; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 13a0f23ac..363f28e70 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -303,7 +304,7 @@ namespace MWPhysics position.z() += halfExtents.z(); static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get() - .find("fSwimHeightScale")->getFloat(); + .find("fSwimHeightScale")->mValue.getFloat(); float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale); ActorTracer tracer; @@ -339,7 +340,7 @@ namespace MWPhysics osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length()))); static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fStromWalkMult")->getFloat(); + .find("fStromWalkMult")->mValue.getFloat(); velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f)); } @@ -486,7 +487,7 @@ namespace MWPhysics physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f)); else { - inertia.z() += time * -627.2f; + inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter; if (inertia.z() < 0) inertia.z() *= slowFall; if (slowFall < 1.f) { @@ -650,7 +651,6 @@ namespace MWPhysics osg::NodePath& nodePath = nodePathFound->second; osg::Matrixf matrix = osg::computeLocalToWorld(nodePath); - osg::Vec3f scale = matrix.getScale(); matrix.orthoNormalize(matrix); btTransform transform; @@ -659,8 +659,8 @@ namespace MWPhysics for (int j=0; j<3; ++j) transform.getBasis()[i][j] = matrix(j,i); // NB column/row major difference - if (compound->getLocalScaling() * toBullet(scale) != compound->getChildShape(shapeIndex)->getLocalScaling()) - compound->getChildShape(shapeIndex)->setLocalScaling(compound->getLocalScaling() * toBullet(scale)); + // Note: we can not apply scaling here for now since we treat scaled shapes + // as new shapes (btScaledBvhTriangleMeshShape) with 1.0 scale for now if (!(transform == compound->getChildTransform(shapeIndex))) compound->updateChildTransform(shapeIndex, transform); } @@ -810,7 +810,7 @@ namespace MWPhysics btScalar mLeastDistSqr; DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3 &origin) - : mMe(me), mTargets(targets), mOrigin(origin), mObject(NULL), mContactPoint(0,0,0), + : mMe(me), mTargets(targets), mOrigin(origin), mObject(nullptr), mContactPoint(0,0,0), mLeastDistSqr(std::numeric_limits::max()) { } @@ -861,8 +861,8 @@ namespace MWPhysics // Use cone shape as fallback const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); - btConeShape shape (osg::DegreesToRadians(store.find("fCombatAngleXY")->getFloat()/2.0f), queryDistance); - shape.setLocalScaling(btVector3(1, 1, osg::DegreesToRadians(store.find("fCombatAngleZ")->getFloat()/2.0f) / + btConeShape shape (osg::DegreesToRadians(store.find("fCombatAngleXY")->mValue.getFloat()/2.0f), queryDistance); + shape.setLocalScaling(btVector3(1, 1, osg::DegreesToRadians(store.find("fCombatAngleZ")->mValue.getFloat()/2.0f) / shape.getRadius())); // The shape origin is its center, so we have to move it forward by half the length. The @@ -873,7 +873,7 @@ namespace MWPhysics object.setCollisionShape(&shape); object.setWorldTransform(btTransform(toBullet(orient), toBullet(center))); - const btCollisionObject* me = NULL; + const btCollisionObject* me = nullptr; std::vector targetCollisionObjects; const Actor* physactor = getActor(actor); @@ -906,7 +906,7 @@ namespace MWPhysics float PhysicsSystem::getHitDistance(const osg::Vec3f &point, const MWWorld::ConstPtr &target) const { - btCollisionObject* targetCollisionObj = NULL; + btCollisionObject* targetCollisionObj = nullptr; const Actor* actor = getActor(target); if (actor) targetCollisionObj = actor->getCollisionObject(); @@ -968,7 +968,7 @@ namespace MWPhysics btVector3 btFrom = toBullet(from); btVector3 btTo = toBullet(to); - const btCollisionObject* me = NULL; + const btCollisionObject* me = nullptr; std::vector targetCollisionObjects; if (!ignore.isEmpty()) @@ -1127,7 +1127,7 @@ namespace MWPhysics std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const { - btCollisionObject* me = NULL; + btCollisionObject* me = nullptr; ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) @@ -1255,7 +1255,7 @@ namespace MWPhysics ActorMap::iterator found = mActors.find(ptr); if (found != mActors.end()) return found->second; - return NULL; + return nullptr; } const Actor *PhysicsSystem::getActor(const MWWorld::ConstPtr &ptr) const @@ -1263,7 +1263,7 @@ namespace MWPhysics ActorMap::const_iterator found = mActors.find(ptr); if (found != mActors.end()) return found->second; - return NULL; + return nullptr; } const Object* PhysicsSystem::getObject(const MWWorld::ConstPtr &ptr) const @@ -1271,7 +1271,7 @@ namespace MWPhysics ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) return found->second; - return NULL; + return nullptr; } void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr) @@ -1395,6 +1395,7 @@ namespace MWPhysics mStandingCollisions.clear(); } + const MWWorld::Ptr player = MWMechanics::getPlayer(); const MWBase::World *world = MWBase::Environment::get().getWorld(); PtrVelocityList::iterator iter = mMovementQueue.begin(); for(;iter != mMovementQueue.end();++iter) @@ -1452,7 +1453,7 @@ namespace MWPhysics MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); if ((wasOnGround && physicActor->getOnGround()) || flying || world->isSwimming(iter->first) || slowFall < 1) - stats.land(); + stats.land(iter->first == player); else if (heightDiff < 0) stats.addToFallHeight(-heightDiff); diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp index 14a5dac48..204ec1467 100644 --- a/apps/openmw/mwphysics/trace.cpp +++ b/apps/openmw/mwphysics/trace.cpp @@ -86,7 +86,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f); mFraction = 1.0f; mHitPoint = end; - mHitObject = NULL; + mHitObject = nullptr; } } diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index d92b95a71..e6aff8d19 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -40,6 +40,9 @@ ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr addHiddenItemLight(*iter, light); } } + + // Make sure we cleaned object from effects, just in cast if we re-use node + removeEffects(); } ActorAnimation::~ActorAnimation() diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 4da3ec4a8..6e2c76d1d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -91,32 +91,6 @@ namespace std::vector > mToRemove; }; - class NodeMapVisitor : public osg::NodeVisitor - { - public: - typedef std::map > NodeMap; - - NodeMapVisitor(NodeMap& map) - : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) - , mMap(map) - {} - - void apply(osg::MatrixTransform& trans) - { - // Take transformation for first found node in file - const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName()); - if (mMap.find(nodeName) == mMap.end()) - { - mMap[nodeName] = &trans; - } - - traverse(trans); - } - - private: - NodeMap& mMap; - }; - NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) { NifOsg::TextKeyMap::const_iterator iter(keys.begin()); @@ -208,15 +182,17 @@ namespace class RemoveFinishedCallbackVisitor : public RemoveVisitor { public: + bool mHasMagicEffects; + RemoveFinishedCallbackVisitor() : RemoveVisitor() - , mEffectId(-1) + , mHasMagicEffects(false) { } RemoveFinishedCallbackVisitor(int effectId) : RemoveVisitor() - , mEffectId(effectId) + , mHasMagicEffects(false) { } @@ -234,11 +210,68 @@ namespace { // We should remove empty transformation nodes and finished callbacks here MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); - bool finished = vfxCallback && vfxCallback->mFinished; - bool toRemove = vfxCallback && mEffectId >= 0 && vfxCallback->mParams.mEffectId == mEffectId; - if (finished || toRemove) + if (vfxCallback) { - mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + if (vfxCallback->mFinished) + mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + else + mHasMagicEffects = true; + } + } + } + + virtual void apply(osg::MatrixTransform &node) + { + traverse(node); + } + + virtual void apply(osg::Geometry&) + { + } + + private: + int mEffectId; + }; + + class RemoveCallbackVisitor : public RemoveVisitor + { + public: + bool mHasMagicEffects; + + RemoveCallbackVisitor() + : RemoveVisitor() + , mHasMagicEffects(false) + , mEffectId(-1) + { + } + + RemoveCallbackVisitor(int effectId) + : RemoveVisitor() + , mHasMagicEffects(false) + , mEffectId(effectId) + { + } + + virtual void apply(osg::Node &node) + { + traverse(node); + } + + virtual void apply(osg::Group &group) + { + traverse(group); + + osg::Callback* callback = group.getUpdateCallback(); + if (callback) + { + MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); + if (vfxCallback) + { + bool toRemove = mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId; + if (toRemove) + mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + else + mHasMagicEffects = true; } } } @@ -334,7 +367,7 @@ namespace void applyNode(osg::Node& node) { if (node.getStateSet()) - node.setStateSet(NULL); + node.setStateSet(nullptr); if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) mToRemove.push_back(std::make_pair(&node, node.getParent(0))); @@ -461,7 +494,7 @@ namespace MWRender } if (mDone) return; - + // Set the starting time to measure glow duration from if this is a temporary glow if ((mDuration >= 0) && mStartingTime == 0) mStartingTime = nv->getFrameStamp()->getSimulationTime(); @@ -565,8 +598,8 @@ namespace MWRender } else { - // Remove effect immediately - mParams.mObjects.reset(); + // Hide effect immediately + node->setNodeMask(0); mFinished = true; } } @@ -602,14 +635,15 @@ namespace MWRender Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem) : mInsert(parentNode) - , mSkeleton(NULL) + , mSkeleton(nullptr) , mNodeMapCreated(false) , mPtr(ptr) , mResourceSystem(resourceSystem) , mAccumulate(1.f, 1.f, 0.f) - , mTextKeyListener(NULL) + , mTextKeyListener(nullptr) , mHeadYawRadians(0.f) , mHeadPitchRadians(0.f) + , mHasMagicEffects(false) , mAlpha(1.f) { for(size_t i = 0;i < sNumBlendMasks;i++) @@ -792,7 +826,7 @@ namespace MWRender for(size_t i = 0;i < sNumBlendMasks;i++) mAnimationTimePtr[i]->setTimePtr(std::shared_ptr()); - mAccumCtrl = NULL; + mAccumCtrl = nullptr; mAnimSources.clear(); @@ -1034,7 +1068,7 @@ namespace MWRender { if (!mNodeMapCreated && mObjectRoot) { - NodeMapVisitor visitor(mNodeMap); + SceneUtil::NodeMapVisitor visitor(mNodeMap); mObjectRoot->accept(visitor); mNodeMapCreated = true; } @@ -1050,12 +1084,12 @@ namespace MWRender node->removeUpdateCallback(it->second); // Should be no longer needed with OSG 3.4 - it->second->setNestedCallback(NULL); + it->second->setNestedCallback(nullptr); } mActiveControllers.clear(); - mAccumCtrl = NULL; + mAccumCtrl = nullptr; for(size_t blendMask = 0;blendMask < sNumBlendMasks;blendMask++) { @@ -1305,7 +1339,7 @@ namespace MWRender if(state.getTime() >= state.mLoopStopTime) break; - } + } if(timepassed <= 0.0f) break; @@ -1321,7 +1355,7 @@ namespace MWRender ++stateiter; } - updateEffects(duration); + updateEffects(); if (mHeadController) { @@ -1357,7 +1391,7 @@ namespace MWRender { osg::ref_ptr created = sceneMgr->getInstance(model); - CleanObjectRootVisitor removeDrawableVisitor; + SceneUtil::CleanObjectRootVisitor removeDrawableVisitor; created->accept(removeDrawableVisitor); removeDrawableVisitor.remove(); @@ -1382,14 +1416,14 @@ namespace MWRender previousStateset = mObjectRoot->getStateSet(); mObjectRoot->getParent(0)->removeChild(mObjectRoot); } - mObjectRoot = NULL; - mSkeleton = NULL; + mObjectRoot = nullptr; + mSkeleton = nullptr; mNodeMap.clear(); mNodeMapCreated = false; mActiveControllers.clear(); - mAccumRoot = NULL; - mAccumCtrl = NULL; + mAccumRoot = nullptr; + mAccumCtrl = nullptr; if (!forceskeleton) { @@ -1426,7 +1460,7 @@ namespace MWRender if (isCreature) { - RemoveTriBipVisitor removeTriBipVisitor; + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; mObjectRoot->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); } @@ -1520,9 +1554,9 @@ namespace MWRender osg::ref_ptr glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, mResourceSystem); mGlowUpdater = glowUpdater; node->addUpdateCallback(glowUpdater); - + // set a texture now so that the ShaderVisitor can find it - osg::ref_ptr writableStateSet = NULL; + osg::ref_ptr writableStateSet = nullptr; if (!node->getStateSet()) writableStateSet = node->getOrCreateStateSet(); else @@ -1626,25 +1660,36 @@ namespace MWRender params.mLoop = loop; params.mEffectId = effectId; params.mBoneName = bonename; - params.mObjects = PartHolderPtr(new PartHolder(node)); params.mAnimTime = std::shared_ptr(new EffectAnimationTime); trans->addUpdateCallback(new UpdateVfxCallback(params)); SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::shared_ptr(params.mAnimTime)); node->accept(assignVisitor); + // Notify that this animation has attached magic effects + mHasMagicEffects = true; + overrideFirstRootTexture(texture, mResourceSystem, node); } void Animation::removeEffect(int effectId) { - RemoveFinishedCallbackVisitor visitor(effectId); + RemoveCallbackVisitor visitor(effectId); mInsert->accept(visitor); visitor.remove(); + mHasMagicEffects = visitor.mHasMagicEffects; + } + + void Animation::removeEffects() + { + removeEffect(-1); } void Animation::getLoopingEffects(std::vector &out) const { + if (!mHasMagicEffects) + return; + FindVfxCallbacksVisitor visitor; mInsert->accept(visitor); @@ -1657,13 +1702,19 @@ namespace MWRender } } - void Animation::updateEffects(float duration) + void Animation::updateEffects() { + // We do not need to visit scene every frame. + // We can use a bool flag to check in spellcasting effect found. + if (!mHasMagicEffects) + return; + // TODO: objects without animation still will have // transformation nodes with finished callbacks RemoveFinishedCallbackVisitor visitor; mInsert->accept(visitor); visitor.remove(); + mHasMagicEffects = visitor.mHasMagicEffects; } bool Animation::upperBodyReady() const @@ -1684,7 +1735,7 @@ namespace MWRender std::string lowerName = Misc::StringUtils::lowerCase(name); NodeMap::const_iterator found = getNodeMap().find(lowerName); if (found == getNodeMap().end()) - return NULL; + return nullptr; else return found->second; } @@ -1715,7 +1766,7 @@ namespace MWRender } else { - mObjectRoot->setStateSet(NULL); + mObjectRoot->setStateSet(nullptr); mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } @@ -1742,7 +1793,7 @@ namespace MWRender if (mGlowLight) { mInsert->removeChild(mGlowLight); - mGlowLight = NULL; + mGlowLight = nullptr; } } else @@ -1756,7 +1807,7 @@ namespace MWRender if (mGlowLight) { mInsert->removeChild(mGlowLight); - mGlowLight = NULL; + mGlowLight = nullptr; } osg::ref_ptr light (new osg::Light); @@ -1777,7 +1828,7 @@ namespace MWRender void Animation::addControllers() { - mHeadController = NULL; + mHeadController = nullptr; if (mPtr.getClass().isBipedal(mPtr)) { @@ -1897,12 +1948,12 @@ namespace MWRender PartHolder::~PartHolder() { if (mNode.get() && !mNode->getNumParents()) - Log(Debug::Verbose) << "Part has no parents" ; + Log(Debug::Verbose) << "Part \"" << mNode->getName() << "\" has no parents" ; if (mNode.get() && mNode->getNumParents()) { if (mNode->getNumParents() > 1) - Log(Debug::Verbose) << "Part has multiple (" << mNode->getNumParents() << ") parents"; + Log(Debug::Verbose) << "Part \"" << mNode->getName() << "\" has multiple (" << mNode->getNumParents() << ") parents"; mNode->getParent(0)->removeChild(mNode); } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 1da1fe4ef..47edbdc0b 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -74,7 +74,6 @@ typedef std::shared_ptr PartHolderPtr; struct EffectParams { std::string mModelName; // Just here so we don't add the same effect twice - PartHolderPtr mObjects; std::shared_ptr mAnimTime; float mMaxControllerLength; int mEffectId; @@ -263,6 +262,7 @@ protected: osg::ref_ptr mHeadController; float mHeadYawRadians; float mHeadPitchRadians; + bool mHasMagicEffects; osg::ref_ptr mGlowLight; osg::ref_ptr mGlowUpdater; @@ -369,6 +369,7 @@ public: */ void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "", float scale = 1.0f); void removeEffect (int effectId); + void removeEffects (); void getLoopingEffects (std::vector& out) const; // Add a spell casting glow to an object. From measuring video taken from the original engine, @@ -424,7 +425,7 @@ public: * \param speedmult Stores the animation speed multiplier * \return True if the animation is active, false otherwise. */ - bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; + bool getInfo(const std::string &groupname, float *complete=nullptr, float *speedmult=nullptr) const; /// Get the absolute position in the animation track of the first text key with the given group. float getStartTime(const std::string &groupname) const; @@ -450,9 +451,9 @@ public: void setLoopingEnabled(const std::string &groupname, bool enabled); /// This is typically called as part of runAnimation, but may be called manually if needed. - void updateEffects(float duration); + void updateEffects(); - /// Return a node with the specified name, or NULL if not existing. + /// Return a node with the specified name, or nullptr if not existing. /// @note The matching is case-insensitive. const osg::Node* getNode(const std::string& name) const; diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index eb3775cb4..4cf76e473 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -54,9 +54,9 @@ void DebugDrawer::destroyGeometry() if (mGeometry) { mParentNode->removeChild(mGeometry); - mGeometry = NULL; - mVertices = NULL; - mDrawArrays = NULL; + mGeometry = nullptr; + mVertices = nullptr; + mDrawArrays = nullptr; } } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index cb6188a54..74f2ba911 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -45,7 +45,7 @@ namespace MWRender Camera::Camera (osg::Camera* camera) : mHeightScale(1.f), mCamera(camera), - mAnimation(NULL), + mAnimation(nullptr), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 60eff215d..8727830fd 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -242,7 +242,7 @@ namespace MWRender void CharacterPreview::rebuild() { - mAnimation = NULL; + mAnimation = nullptr; mAnimation = new NpcAnimation(mCharacter, mNode, mResourceSystem, true, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); @@ -383,7 +383,7 @@ namespace MWRender void InventoryPreview::updatePtr(const MWWorld::Ptr &ptr) { - mCharacter = MWWorld::Ptr(ptr.getBase(), NULL); + mCharacter = MWWorld::Ptr(ptr.getBase(), nullptr); } void InventoryPreview::onSetup() @@ -406,7 +406,7 @@ namespace MWRender , mRef(&mBase) , mPitchRadians(osg::DegreesToRadians(6.f)) { - mCharacter = MWWorld::Ptr(&mRef, NULL); + mCharacter = MWWorld::Ptr(&mRef, nullptr); } RaceSelectionPreview::~RaceSelectionPreview() diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 6db223bd5..a262d0021 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -170,7 +170,7 @@ void CreatureWeaponAnimation::releaseArrow(float attackStrength) osg::Group *CreatureWeaponAnimation::getArrowBone() { if (!mWeapon) - return NULL; + return nullptr; SceneUtil::FindByNameVisitor findVisitor ("ArrowBone"); mWeapon->getNode()->accept(findVisitor); @@ -180,7 +180,7 @@ osg::Group *CreatureWeaponAnimation::getArrowBone() osg::Node *CreatureWeaponAnimation::getWeaponNode() { - return mWeapon ? mWeapon->getNode().get() : NULL; + return mWeapon ? mWeapon->getNode().get() : nullptr; } Resource::ResourceSystem *CreatureWeaponAnimation::getResourceSystem() diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index fae524faa..d1b6cd239 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -1,7 +1,5 @@ #include "globalmap.hpp" -#include - #include #include #include @@ -273,9 +271,9 @@ namespace MWRender void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY) { - imageX = float(x / 8192.f - mMinX) / (mMaxX - mMinX + 1); + imageX = float(x / float(Constants::CellSizeInUnits) - mMinX) / (mMaxX - mMinX + 1); - imageY = 1.f-float(z / 8192.f - mMinY) / (mMaxY - mMinY + 1); + imageY = 1.f-float(z / float(Constants::CellSizeInUnits) - mMinY) / (mMaxY - mMinY + 1); } void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY) @@ -565,7 +563,7 @@ namespace MWRender requestOverlayTextureUpdate(0, 0, mWidth, mHeight, osg::ref_ptr(), true, false); - mWorkItem = NULL; + mWorkItem = nullptr; } } diff --git a/apps/openmw/mwrender/landmanager.cpp b/apps/openmw/mwrender/landmanager.cpp index 5edce2c3f..38c28a72a 100644 --- a/apps/openmw/mwrender/landmanager.cpp +++ b/apps/openmw/mwrender/landmanager.cpp @@ -14,7 +14,7 @@ namespace MWRender { LandManager::LandManager(int loadFlags) - : ResourceManager(NULL) + : ResourceManager(nullptr) , mLoadFlags(loadFlags) { } @@ -32,7 +32,7 @@ osg::ref_ptr LandManager::getLand(int x, int y) { const ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search(x,y); if (!land) - return NULL; + return nullptr; osg::ref_ptr landObj (new ESMTerrain::LandObject(land, mLoadFlags)); mCache->addEntryToObjectCache(idstr, landObj.get()); return landObj; diff --git a/apps/openmw/mwrender/landmanager.hpp b/apps/openmw/mwrender/landmanager.hpp index 392a8b406..81253bba3 100644 --- a/apps/openmw/mwrender/landmanager.hpp +++ b/apps/openmw/mwrender/landmanager.hpp @@ -19,7 +19,7 @@ namespace MWRender public: LandManager(int loadFlags); - /// @note Will return NULL if not found. + /// @note Will return nullptr if not found. osg::ref_ptr getLand(int x, int y); virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 566835ffd..a7f9247f7 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -72,11 +73,16 @@ namespace MWRender LocalMap::LocalMap(osg::Group* root) : mRoot(root) , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) - , mMapWorldSize(8192.f) + , mMapWorldSize(Constants::CellSizeInUnits) , mCellDistance(Settings::Manager::getInt("local map cell distance", "Map")) , mAngle(0.f) , mInterior(false) { + // Increase map resolution, if use UI scaling + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + if (uiScale > 1.0) + mMapResolution *= uiScale; + SceneUtil::FindByNameVisitor find("Scene Root"); mRoot->accept(find); mSceneRoot = find.mFoundNode; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 5d1a2b9cf..fa0b5d183 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -71,7 +72,7 @@ std::string getVampireHead(const std::string& race, bool female) } if (sVampireMapping.find(thisCombination) == sVampireMapping.end()) - sVampireMapping[thisCombination] = NULL; + sVampireMapping[thisCombination] = nullptr; const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination]; if (!bodyPart) @@ -304,7 +305,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr par void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) { assert(viewMode != VM_HeadOnly); - if(mViewMode == viewMode) + if(mViewMode == viewMode) return; mViewMode = viewMode; @@ -456,37 +457,15 @@ void NpcAnimation::updateNpcBase() } } + bool is1stPerson = mViewMode == VM_FirstPerson; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel; - if (mViewMode != VM_FirstPerson) - { - if (isWerewolf) - smodel = "meshes\\wolf\\skin.nif"; - else if (isBeast) - smodel = "meshes\\base_animkna.nif"; - else if (isFemale) - smodel = "meshes\\base_anim_female.nif"; - else - smodel = "meshes\\base_anim.nif"; - } - else - { - if (isWerewolf) - smodel = "meshes\\wolf\\skin.1st.nif"; - else if (isBeast) - smodel = "meshes\\base_animkna.1st.nif"; - else if (isFemale) - smodel = "meshes\\base_anim_female.1st.nif"; - else - smodel = "meshes\\base_anim.1st.nif"; - } - + std::string smodel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); setObjectRoot(smodel, true, true, false); - if(mViewMode != VM_FirstPerson) + if(!is1stPerson) { const std::string base = "meshes\\xbase_anim.nif"; if (smodel != base) @@ -561,7 +540,7 @@ void NpcAnimation::updateParts() }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); - bool wasArrowAttached = (mAmmunition.get() != NULL); + bool wasArrowAttached = (mAmmunition.get() != nullptr); mAmmunition.reset(); const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); @@ -680,7 +659,7 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st } osg::Vec3f NpcAnimation::runAnimation(float timepassed) -{ +{ osg::Vec3f ret = Animation::runAnimation(timepassed); mHeadAnimationTime->update(timepassed); @@ -849,7 +828,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) - bodypart = NULL; + bodypart = nullptr; } else if (!bodypart) Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mFemale << "'"; @@ -864,7 +843,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) - bodypart = NULL; + bodypart = nullptr; } else if (!bodypart) Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mMale << "'"; @@ -881,7 +860,7 @@ void NpcAnimation::addControllers() { Animation::addControllers(); - mFirstPersonNeckController = NULL; + mFirstPersonNeckController = nullptr; WeaponAnimation::deleteControllers(); if (mViewMode == VM_FirstPerson) @@ -973,7 +952,7 @@ osg::Group* NpcAnimation::getArrowBone() { PartHolderPtr part = mObjectParts[ESM::PRT_Weapon]; if (!part) - return NULL; + return nullptr; SceneUtil::FindByNameVisitor findVisitor ("ArrowBone"); part->getNode()->accept(findVisitor); @@ -985,7 +964,7 @@ osg::Node* NpcAnimation::getWeaponNode() { PartHolderPtr part = mObjectParts[ESM::PRT_Weapon]; if (!part) - return NULL; + return nullptr; return part->getNode(); } @@ -1109,7 +1088,7 @@ const std::vector& NpcAnimation::getBodyParts(const std:: sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); } - parts.resize(ESM::PRT_Count, NULL); + parts.resize(ESM::PRT_Count, nullptr); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 7f3a8da2d..335ca5d5a 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -75,16 +75,16 @@ private: void updateNpcBase(); PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename, - const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=NULL); + const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=nullptr); void removeIndividualPart(ESM::PartReferenceType type); void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority); bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh, - bool enchantedGlow=false, osg::Vec4f* glowColor=NULL); + bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); void removePartGroup(int group); void addPartGroup(int group, int priority, const std::vector &parts, - bool enchantedGlow=false, osg::Vec4f* glowColor=NULL); + bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); virtual void setRenderBin(); @@ -155,7 +155,7 @@ public: virtual void updatePtr(const MWWorld::Ptr& updated); /// Get a list of body parts that may be used by an NPC of given race and gender. - /// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain NULL body parts. + /// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain nullptr body parts. static const std::vector& getBodyParts(const std::string& raceId, bool female, bool firstperson, bool werewolf); }; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index ddeea4abd..92cd33a5a 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -127,14 +127,14 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr) if (ptr.getClass().isActor()) { if (ptr.getClass().hasInventoryStore(ptr)) - ptr.getClass().getInventoryStore(ptr).setInvListener(NULL, ptr); + ptr.getClass().getInventoryStore(ptr).setInvListener(nullptr, ptr); - ptr.getClass().getContainerStore(ptr).setContListener(NULL); + ptr.getClass().getContainerStore(ptr).setContListener(nullptr); } ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode()); - ptr.getRefData().setBaseNode(NULL); + ptr.getRefData().setBaseNode(nullptr); return true; } return false; @@ -154,8 +154,8 @@ void Objects::removeCell(const MWWorld::CellStore* store) if (ptr.getClass().isNpc() && ptr.getRefData().getCustomData()) { MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); - invStore.setInvListener(NULL, ptr); - invStore.setContListener(NULL); + invStore.setInvListener(nullptr, ptr); + invStore.setContListener(nullptr); } mObjects.erase(iter++); @@ -219,7 +219,7 @@ Animation* Objects::getAnimation(const MWWorld::Ptr &ptr) if(iter != mObjects.end()) return iter->second; - return NULL; + return nullptr; } const Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const @@ -228,7 +228,7 @@ const Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const if(iter != mObjects.end()) return iter->second; - return NULL; + return nullptr; } } diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index aae97fe35..ee4120d6f 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -27,8 +27,8 @@ namespace MWRender Pathgrid::Pathgrid(osg::ref_ptr root) : mPathgridEnabled(false) , mRootNode(root) - , mPathGridRoot(NULL) - , mInteriorPathgridNode(NULL) + , mPathGridRoot(nullptr) + , mInteriorPathgridNode(nullptr) { } @@ -94,7 +94,7 @@ void Pathgrid::togglePathgrid() if (mPathGridRoot) { mRootNode->removeChild(mPathGridRoot); - mPathGridRoot = NULL; + mPathGridRoot = nullptr; } } } @@ -124,7 +124,7 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) } else { - assert(mInteriorPathgridNode == NULL); + assert(mInteriorPathgridNode == nullptr); mInteriorPathgridNode = cellPathGrid; } } @@ -146,7 +146,7 @@ void Pathgrid::disableCellPathgrid(const MWWorld::CellStore *store) if (mInteriorPathgridNode) { mPathGridRoot->removeChild(mInteriorPathgridNode); - mInteriorPathgridNode = NULL; + mInteriorPathgridNode = nullptr; } } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2134b0220..a186ed8e9 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -254,7 +254,7 @@ namespace MWRender mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get())); - if (getenv("OPENMW_DONT_PRECOMPILE") == NULL) + if (getenv("OPENMW_DONT_PRECOMPILE") == nullptr) { mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); @@ -358,7 +358,7 @@ namespace MWRender RenderingManager::~RenderingManager() { // let background loading thread finish before we delete anything else - mWorkQueue = NULL; + mWorkQueue = nullptr; } MWRender::Objects& RenderingManager::getObjects() @@ -858,7 +858,7 @@ namespace MWRender stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON); quad->setStateSet(stateset); - quad->setUpdateCallback(NULL); + quad->setUpdateCallback(nullptr); screenshotCamera->addChild(quad); @@ -1001,7 +1001,7 @@ namespace MWRender result.mHitNormalWorld = intersection.getWorldIntersectNormal(); result.mRatio = intersection.ratio; - PtrHolder* ptrHolder = NULL; + PtrHolder* ptrHolder = nullptr; for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); @@ -1139,7 +1139,7 @@ namespace MWRender void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) { - NpcAnimation *anim = NULL; + NpcAnimation *anim = nullptr; if(ptr == mPlayerAnimation->getPtr()) anim = mPlayerAnimation.get(); else diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 141a3eb87..21bd48d9a 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -196,7 +196,7 @@ void RippleSimulation::emitRipple(const osg::Vec3f &pos) { if (std::abs(pos.z() - mParticleNode->getPosition().z()) < 20) { - osgParticle::Particle* p = mParticleSystem->createParticle(NULL); + osgParticle::Particle* p = mParticleSystem->createParticle(nullptr); p->setPosition(osg::Vec3f(pos.x(), pos.y(), 0.f)); p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI)); } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index dab3bb7d9..6a2a4ca5b 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -630,7 +630,7 @@ private: if (mSunFlashNode) { mSunFlashNode->removeCullCallback(mSunFlashCallback); - mSunFlashCallback = NULL; + mSunFlashCallback = nullptr; } } @@ -672,7 +672,7 @@ private: if (mSunGlareNode) { mSunGlareNode->removeCullCallback(mSunGlareCallback); - mSunGlareCallback = NULL; + mSunGlareCallback = nullptr; } } @@ -1096,8 +1096,8 @@ private: SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager) : mSceneManager(sceneManager) - , mCamera(NULL) - , mRainIntensityUniform(NULL) + , mCamera(nullptr) + , mRainIntensityUniform(nullptr) , mAtmosphereNightRoll(0.f) , mCreated(false) , mIsStorm(false) @@ -1304,7 +1304,7 @@ public: { if (stateset->getAttribute(osg::StateAttribute::MATERIAL)) { - SceneUtil::CompositeStateSetUpdater* composite = NULL; + SceneUtil::CompositeStateSetUpdater* composite = nullptr; osg::Callback* callback = node.getUpdateCallback(); while (callback) @@ -1385,12 +1385,12 @@ public: virtual osg::Object *cloneType() const override { - return NULL; + return nullptr; } virtual osg::Object *clone(const osg::CopyOp &op) const override { - return NULL; + return nullptr; } virtual void operate(osgParticle::Particle *P, double dt) override @@ -1522,10 +1522,10 @@ void SkyManager::destroyRain() return; mRootNode->removeChild(mRainNode); - mRainNode = NULL; - mRainParticleSystem = NULL; - mRainShooter = NULL; - mRainFader = NULL; + mRainNode = nullptr; + mRainParticleSystem = nullptr; + mRainShooter = nullptr; + mRainFader = nullptr; } SkyManager::~SkyManager() @@ -1533,7 +1533,7 @@ SkyManager::~SkyManager() if (mRootNode) { mRootNode->getParent(0)->removeChild(mRootNode); - mRootNode = NULL; + mRootNode = nullptr; } } @@ -1556,7 +1556,7 @@ bool SkyManager::isEnabled() bool SkyManager::hasRain() { - return mRainNode != NULL; + return mRainNode != nullptr; } void SkyManager::update(float duration) @@ -1660,7 +1660,7 @@ void SkyManager::setWeather(const WeatherResult& weather) if (mParticleEffect) { mParticleNode->removeChild(mParticleEffect); - mParticleEffect = NULL; + mParticleEffect = nullptr; mParticleFaders.clear(); } @@ -1669,7 +1669,7 @@ void SkyManager::setWeather(const WeatherResult& weather) if (mParticleNode) { mRootNode->removeChild(mParticleNode); - mParticleNode = NULL; + mParticleNode = nullptr; } } else diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 605a71e46..9ad7dc8dd 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -27,6 +27,8 @@ #include +#include + #include #include @@ -401,7 +403,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem { mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback)); - mWaterGeom = SceneUtil::createWaterGeometry(CELL_SIZE*150, 40, 900); + mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900); mWaterGeom->setDrawCallback(new DepthClampCallback); mWaterGeom->setNodeMask(Mask_Water); @@ -439,13 +441,13 @@ void Water::updateWaterMaterial() { mReflection->removeChildren(0, mReflection->getNumChildren()); mParent->removeChild(mReflection); - mReflection = NULL; + mReflection = nullptr; } if (mRefraction) { mRefraction->removeChildren(0, mRefraction->getNumChildren()); mParent->removeChild(mRefraction); - mRefraction = NULL; + mRefraction = nullptr; } if (Settings::Manager::getBool("shader", "Water")) @@ -577,7 +579,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON); node->setStateSet(shaderStateset); - node->setUpdateCallback(NULL); + node->setUpdateCallback(nullptr); } void Water::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -593,13 +595,13 @@ Water::~Water() { mReflection->removeChildren(0, mReflection->getNumChildren()); mParent->removeChild(mReflection); - mReflection = NULL; + mReflection = nullptr; } if (mRefraction) { mRefraction->removeChildren(0, mRefraction->getNumChildren()); mParent->removeChild(mRefraction); - mRefraction = NULL; + mRefraction = nullptr; } } @@ -679,7 +681,8 @@ bool Water::isUnderwater(const osg::Vec3f &pos) const osg::Vec3f Water::getSceneNodeCoordinates(int gridX, int gridY) { - return osg::Vec3f(static_cast(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast(gridY * CELL_SIZE + (CELL_SIZE / 2)), mTop); + return osg::Vec3f(static_cast(gridX * Constants::CellSizeInUnits + (Constants::CellSizeInUnits / 2)), + static_cast(gridY * Constants::CellSizeInUnits + (Constants::CellSizeInUnits / 2)), mTop); } void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force) diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index e2413cfa0..32a7977d2 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -50,8 +50,6 @@ namespace MWRender /// Water rendering class Water { - static const int CELL_SIZE = 8192; - osg::ref_ptr mRainIntensityUniform; osg::ref_ptr mParent; diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 0b3d000f3..d5a8cb10d 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -119,11 +119,12 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) return; osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans(); - float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); - float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); + float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->mValue.getFloat(); + float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->mValue.getFloat(); float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength; - MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed, attackStrength); + MWWorld::Ptr weaponPtr = *weapon; + MWBase::Environment::get().getWorld()->launchProjectile(actor, weaponPtr, launchPos, orient, weaponPtr, speed, attackStrength); showWeapon(false); @@ -145,13 +146,15 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) return; osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans(); - float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); - float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); + float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->mValue.getFloat(); + float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat(); float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength; - MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed, attackStrength); + MWWorld::Ptr weaponPtr = *weapon; + MWWorld::Ptr ammoPtr = *ammo; + MWBase::Environment::get().getWorld()->launchProjectile(actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength); - inv.remove(*ammo, 1, actor); + inv.remove(ammoPtr, 1, actor); mAmmunition.reset(); } } @@ -161,7 +164,7 @@ void WeaponAnimation::addControllers(const std::map >::const_iterator found = nodes.find(i == 0 ? "bip01 spine1" : "bip01 spine2"); if (found != nodes.end()) @@ -177,7 +180,7 @@ void WeaponAnimation::addControllers(const std::mapgetPlayerPtr(); if (world->findExteriorPosition(cell, pos)) { - MWWorld::ActionTeleport("", pos, false).execute(world->getPlayerPtr()); - world->fixPosition(world->getPlayerPtr()); + MWWorld::ActionTeleport("", pos, false).execute(playerPtr); + world->adjustPosition(playerPtr, false); } else { // Change to interior even if findInteriorPosition() // yields false. In this case position will be zero-point. world->findInteriorPosition(cell, pos); - MWWorld::ActionTeleport(cell, pos, false).execute(world->getPlayerPtr()); + MWWorld::ActionTeleport(cell, pos, false).execute(playerPtr); } } }; @@ -76,14 +77,15 @@ namespace MWScript ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Ptr playerPtr = world->getPlayerPtr(); world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); pos.pos[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - MWWorld::ActionTeleport("", pos, false).execute(world->getPlayerPtr()); - world->fixPosition(world->getPlayerPtr()); + MWWorld::ActionTeleport("", pos, false).execute(playerPtr); + world->adjustPosition(playerPtr, false); } }; diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index f4e729da1..07b7a3bff 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -288,10 +288,17 @@ namespace MWScript return "None"; } - std::string InterpreterContext::getNPCName() const + std::string InterpreterContext::getActorName() const { - ESM::NPC npc = *getReferenceImp().get()->mBase; - return npc.mName; + const MWWorld::Ptr& ptr = getReferenceImp(); + if (ptr.getClass().isNpc()) + { + const ESM::NPC* npc = ptr.get()->mBase; + return npc->mName; + } + + const ESM::Creature* creature = ptr.get()->mBase; + return creature->mName; } std::string InterpreterContext::getNPCRace() const diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index ac8e1833b..5f1f4f7ab 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -93,7 +93,7 @@ namespace MWScript virtual std::string getActionBinding(const std::string& action) const; - virtual std::string getNPCName() const; + virtual std::string getActorName() const; virtual std::string getNPCRace() const; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 3d1978d62..7250e9fcf 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -107,8 +107,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayerPtr(); - runtime.push (!world->isOnGround(player) && !world->isFlying(player)); + runtime.push (world->getPlayer().getJumping()); } }; @@ -1064,7 +1063,13 @@ namespace MWScript runtime.pop(); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find (spellId); - if (spell && spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power) + if (!spell) + { + runtime.getContext().report("spellcasting failed: can not find spell \""+spellId+"\""); + return; + } + + if (spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power) { runtime.getContext().report("spellcasting failed: you can cast only spells and powers."); return; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 5df767da8..53316c44c 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -501,6 +501,7 @@ namespace MWScript runtime.pop(); ptr.getClass().getCreatureStats (ptr).getActiveSpells().removeEffects(spellid); + ptr.getClass().getCreatureStats (ptr).getSpells().removeEffects(spellid); } }; @@ -1155,7 +1156,7 @@ namespace MWScript // HACK: disable/enable object to re-add it to the scene properly (need a new Animation). MWBase::Environment::get().getWorld()->disable(ptr); // resets runtime state such as inventory, stats and AI. does not reset position in the world - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); if (wasEnabled) MWBase::Environment::get().getWorld()->enable(ptr); } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 8f0c72519..2695aed76 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -29,6 +29,18 @@ namespace MWScript { namespace Transformation { + void moveStandingActors(const MWWorld::Ptr &ptr, const osg::Vec3f& diff) + { + std::vector actors; + MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors); + for (auto& actor : actors) + { + osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); + actorPos += diff; + MWBase::Environment::get().getWorld()->moveObject(actor, actorPos.x(), actorPos.y(), actorPos.z()); + } + } + template class OpSetScale : public Interpreter::Opcode0 { @@ -472,7 +484,7 @@ namespace MWScript if (!player.isInCell()) throw std::runtime_error("player not in a cell"); - MWWorld::CellStore* store = NULL; + MWWorld::CellStore* store = nullptr; if (player.getCell()->isExterior()) { int cx,cy; @@ -666,6 +678,10 @@ namespace MWScript osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange; osg::Vec3f worldPos(ptr.getRefData().getPosition().asVec3()); worldPos += diff; + + // We should move actors, standing on moving object, too. + // This approach can be used to create elevators. + moveStandingActors(ptr, diff); MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z()); } }; @@ -688,22 +704,21 @@ namespace MWScript runtime.pop(); const float *objPos = ptr.getRefData().getPosition().pos; + osg::Vec3f diff; - MWWorld::Ptr updated; if (axis == "x") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]); - } + diff.x() += movement; else if (axis == "y") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]); - } + diff.y() += movement; else if (axis == "z") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement); - } + diff.z() += movement; else throw std::runtime_error ("invalid movement axis: " + axis); + + // We should move actors, standing on moving object, too. + // This approach can be used to create elevators. + moveStandingActors(ptr, diff); + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z()); } }; @@ -717,14 +732,13 @@ namespace MWScript } }; - template class OpFixme : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = R()(runtime); + const MWWorld::Ptr ptr = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->fixPosition(ptr); } }; @@ -769,8 +783,7 @@ namespace MWScript interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngle, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngleExplicit, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeResetActors, new OpResetActors); - interpreter.installSegment5(Compiler::Transformation::opcodeFixme, new OpFixme); - interpreter.installSegment5(Compiler::Transformation::opcodeFixmeExplicit, new OpFixme); + interpreter.installSegment5(Compiler::Transformation::opcodeFixme, new OpFixme); } } } diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 7dffd685a..53452c305 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -122,7 +122,7 @@ bool FFmpeg_Decoder::getAVAudioData() if(!mDataBuf || mDataBufLen < mFrame->nb_samples) { av_freep(&mDataBuf); - if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout), + if(av_samples_alloc(&mDataBuf, nullptr, av_get_channel_layout_nb_channels(mOutputChannelLayout), mFrame->nb_samples, mOutputSampleFormat, 0) < 0) return false; else @@ -181,34 +181,34 @@ void FFmpeg_Decoder::open(const std::string &fname) close(); mDataStream = mResourceMgr->get(fname); - if((mFormatCtx=avformat_alloc_context()) == NULL) + if((mFormatCtx=avformat_alloc_context()) == nullptr) throw std::runtime_error("Failed to allocate context"); try { - mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); - if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0) + mFormatCtx->pb = avio_alloc_context(nullptr, 0, 0, this, readPacket, writePacket, seek); + if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), nullptr, nullptr) != 0) { // "Note that a user-supplied AVFormatContext will be freed on failure". if (mFormatCtx) { - if (mFormatCtx->pb != NULL) + if (mFormatCtx->pb != nullptr) { - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; } avformat_free_context(mFormatCtx); } - mFormatCtx = NULL; + mFormatCtx = nullptr; throw std::runtime_error("Failed to allocate input stream"); } - if(avformat_find_stream_info(mFormatCtx, NULL) < 0) + if(avformat_find_stream_info(mFormatCtx, nullptr) < 0) throw std::runtime_error("Failed to find stream info in "+fname); for(size_t j = 0;j < mFormatCtx->nb_streams;j++) @@ -231,7 +231,7 @@ void FFmpeg_Decoder::open(const std::string &fname) std::to_string((*mStream)->codec->codec_id); throw std::runtime_error(ss); } - if(avcodec_open2((*mStream)->codec, codec, NULL) < 0) + if(avcodec_open2((*mStream)->codec, codec, nullptr) < 0) throw std::runtime_error(std::string("Failed to open audio codec ") + codec->long_name); @@ -255,17 +255,17 @@ void FFmpeg_Decoder::open(const std::string &fname) { if(mStream) avcodec_close((*mStream)->codec); - mStream = NULL; + mStream = nullptr; - if (mFormatCtx != NULL) + if (mFormatCtx != nullptr) { - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; avformat_close_input(&mFormatCtx); } @@ -276,7 +276,7 @@ void FFmpeg_Decoder::close() { if(mStream) avcodec_close((*mStream)->codec); - mStream = NULL; + mStream = nullptr; av_free_packet(&mPacket); av_freep(&mFrame); @@ -285,20 +285,20 @@ void FFmpeg_Decoder::close() if(mFormatCtx) { - if (mFormatCtx->pb != NULL) + if (mFormatCtx->pb != nullptr) { // mFormatCtx->pb->buffer must be freed by hand, // if not, valgrind will show memleak, see: // // https://trac.ffmpeg.org/ticket/1357 // - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; } avformat_close_input(&mFormatCtx); } @@ -373,7 +373,7 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * (*mStream)->codec->sample_fmt, // input sample format (*mStream)->codec->sample_rate, // input sample rate 0, // logging level offset - NULL); // log context + nullptr); // log context if(!mSwr) throw std::runtime_error("Couldn't allocate SwrContext"); if(swr_init(mSwr) < 0) @@ -417,17 +417,17 @@ size_t FFmpeg_Decoder::getSampleOffset() FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs) : Sound_Decoder(vfs) - , mFormatCtx(NULL) - , mStream(NULL) - , mFrame(NULL) + , mFormatCtx(nullptr) + , mStream(nullptr) + , mFrame(nullptr) , mFrameSize(0) , mFramePos(0) , mNextPts(0.0) , mSwr(0) , mOutputSampleFormat(AV_SAMPLE_FMT_NONE) , mOutputChannelLayout(0) - , mDataBuf(NULL) - , mFrameData(NULL) + , mDataBuf(nullptr) + , mFrameData(nullptr) , mDataBufLen(0) { memset(&mPacket, 0, sizeof(mPacket)); diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index 497562516..faa8d94ca 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -19,7 +19,7 @@ namespace MWSound { public: MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder) - : Sound_Decoder(NULL) + : Sound_Decoder(nullptr) , mDecoder(decoder) { } diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 3569cd3da..39e872b0c 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -34,10 +35,6 @@ namespace { -// The game uses 64 units per yard, or approximately 69.99125109 units per meter. -// Should this be defined publically somewhere? -const float UnitsPerMeter = 69.99125109f; - const int sLoudnessFPS = 20; // loudness values per second of audio ALCenum checkALCError(ALCdevice *device, const char *func, int line) @@ -575,10 +572,10 @@ std::vector OpenAL_Output::enumerate() std::vector devlist; const ALCchar *devnames; - if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) - devnames = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); + if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) + devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); else - devnames = alcGetString(NULL, ALC_DEVICE_SPECIFIER); + devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); while(devnames && *devnames) { devlist.push_back(devnames); @@ -818,13 +815,13 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname LoadEffect(mWaterEffect, EFX_REVERB_PRESET_UNDERWATER); } - alListenerf(AL_METERS_PER_UNIT, 1.0f / UnitsPerMeter); + alListenerf(AL_METERS_PER_UNIT, 1.0f / Constants::UnitsPerMeter); } skip_efx: alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); - // Speed of sound is in units per second. Given the default speed of sound is 343.3 (assumed + // Speed of sound is in units per second. Take the sound speed in air (assumed // meters per second), multiply by the units per meter to get the speed in u/s. - alSpeedOfSound(343.3f * UnitsPerMeter); + alSpeedOfSound(Constants::SoundSpeedInAir * Constants::UnitsPerMeter); alGetError(); mInitialized = true; @@ -1145,14 +1142,24 @@ bool OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset) initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getUseEnv()); + alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } - alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcePlay(source); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } mFreeSources.pop_front(); sound->mHandle = MAKE_PTRID(source); @@ -1175,14 +1182,24 @@ bool OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset) initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getUseEnv()); + alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } - alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcePlay(source); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } mFreeSources.pop_front(); sound->mHandle = MAKE_PTRID(source); @@ -1197,9 +1214,8 @@ void OpenAL_Output::finishSound(Sound *sound) ALuint source = GET_PTRID(sound->mHandle); sound->mHandle = 0; - // Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state, - // which works around a bug in the MacOS OpenAL implementation which would otherwise think - // the initial queue already played when it hasn't. + // Rewind the stream to put the source back into an AL_INITIAL state, for + // the next time it's used. alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); getALError(); @@ -1302,9 +1318,8 @@ void OpenAL_Output::finishStream(Stream *sound) sound->mHandle = 0; mStreamThread->remove(stream); - // Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state, - // which works around a bug in the MacOS OpenAL implementation which would otherwise think - // the initial queue already played when it hasn't. + // Rewind the stream to put the source back into an AL_INITIAL state, for + // the next time it's used. alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); getALError(); @@ -1382,8 +1397,7 @@ void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdi if(env != mListenerEnv) { - // Speed of sound in water is 1484m/s, and in air is 343.3m/s (roughly) - alSpeedOfSound(((env == Env_Underwater) ? 1484.0f : 343.3f) * UnitsPerMeter); + alSpeedOfSound(((env == Env_Underwater) ? Constants::SoundSpeedUnderwater : Constants::SoundSpeedInAir) * Constants::UnitsPerMeter); // Update active sources with the environment's direct filter if(mWaterFilter) @@ -1465,7 +1479,8 @@ void OpenAL_Output::resumeSounds(int types) OpenAL_Output::OpenAL_Output(SoundManager &mgr) - : Sound_Output(mgr), mDevice(0), mContext(0) + : Sound_Output(mgr) + , mDevice(0), mContext(0) , mListenerPos(0.0f, 0.0f, 0.0f), mListenerEnv(Env_Normal) , mWaterFilter(0), mWaterEffect(0), mDefaultEffect(0), mEffectSlot(0) , mStreamThread(new StreamThread) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index afc869733..b6a26c99a 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -26,10 +26,10 @@ namespace MWSound struct { bool EXT_EFX : 1; bool SOFT_HRTF : 1; - } ALC; + } ALC = {false, false}; struct { bool SOFT_source_spatialize : 1; - } AL; + } AL = {false}; typedef std::deque IDDq; IDDq mFreeSources; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index d6dc2ba99..8e3488906 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -139,10 +139,10 @@ namespace MWSound Sound_Buffer *SoundManager::insertSound(const std::string &soundId, const ESM::Sound *sound) { MWBase::World* world = MWBase::Environment::get().getWorld(); - static const float fAudioDefaultMinDistance = world->getStore().get().find("fAudioDefaultMinDistance")->getFloat(); - static const float fAudioDefaultMaxDistance = world->getStore().get().find("fAudioDefaultMaxDistance")->getFloat(); - static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); - static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); + static const float fAudioDefaultMinDistance = world->getStore().get().find("fAudioDefaultMinDistance")->mValue.getFloat(); + static const float fAudioDefaultMaxDistance = world->getStore().get().find("fAudioDefaultMaxDistance")->mValue.getFloat(); + static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->mValue.getFloat(); + static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->mValue.getFloat(); float volume, min, max; volume = static_cast(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); @@ -296,10 +296,10 @@ namespace MWSound Stream *SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal) { 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(); + static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->mValue.getFloat(); + static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->mValue.getFloat(); + static const float fAudioVoiceDefaultMinDistance = world->getStore().get().find("fAudioVoiceDefaultMinDistance")->mValue.getFloat(); + static const float fAudioVoiceDefaultMaxDistance = world->getStore().get().find("fAudioVoiceDefaultMaxDistance")->mValue.getFloat(); static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f); static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance); @@ -819,7 +819,7 @@ namespace MWSound } const ESM::Region *regn = world->getStore().get().search(regionName); - if(regn == NULL) + if(regn == nullptr) return; if(total == 0) diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index a91bd4e8c..d440ba869 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -46,7 +46,7 @@ void MWState::CharacterManager::deleteSlot(const MWState::Character *character, // All slots deleted, cleanup and remove this character it->cleanup(); if (character == mCurrent) - mCurrent = NULL; + mCurrent = nullptr; mCharacters.erase(it); } } @@ -96,7 +96,7 @@ std::list::iterator MWState::CharacterManager::findCharacter void MWState::CharacterManager::setCurrentCharacter (const Character *character) { if (!character) - mCurrent = NULL; + mCurrent = nullptr; else { std::list::iterator it = findCharacter(character); diff --git a/apps/openmw/mwstate/quicksavemanager.cpp b/apps/openmw/mwstate/quicksavemanager.cpp index 59658ce6e..df078e026 100644 --- a/apps/openmw/mwstate/quicksavemanager.cpp +++ b/apps/openmw/mwstate/quicksavemanager.cpp @@ -20,7 +20,7 @@ void MWState::QuickSaveManager::visitSave(const Slot *saveSlot) bool MWState::QuickSaveManager::isOldestSave(const Slot *compare) { - if(mOldestSlotVisited == NULL) + if(mOldestSlotVisited == nullptr) return true; return (compare->mTimeStamp <= mOldestSlotVisited->mTimeStamp); } @@ -33,6 +33,6 @@ bool MWState::QuickSaveManager::shouldCreateNewSlot() const MWState::Slot *MWState::QuickSaveManager::getNextQuickSaveSlot() { if(shouldCreateNewSlot()) - return NULL; + return nullptr; return mOldestSlotVisited; } diff --git a/apps/openmw/mwstate/quicksavemanager.hpp b/apps/openmw/mwstate/quicksavemanager.hpp index e52cd609f..a5237d7c3 100644 --- a/apps/openmw/mwstate/quicksavemanager.hpp +++ b/apps/openmw/mwstate/quicksavemanager.hpp @@ -28,7 +28,7 @@ namespace MWState{ const Slot *getNextQuickSaveSlot(); ///< Get the slot that the next quicksave should use. /// - ///\return Either the oldest quicksave slot visited, or NULL if a new slot can be made + ///\return Either the oldest quicksave slot visited, or nullptr if a new slot can be made }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 560433820..9804531d7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -55,7 +55,7 @@ void MWState::StateManager::cleanup (bool force) MWBase::Environment::get().getMechanicsManager()->clear(); mState = State_NoGame; - mCharacterManager.setCurrentCharacter(NULL); + mCharacterManager.setCurrentCharacter(nullptr); mTimePlayed = 0; MWMechanics::CreatureStats::cleanup(); diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index d858859a6..a173e87fb 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -5,6 +5,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwgui/inventorywindow.hpp" + #include "class.hpp" #include "containerstore.hpp" @@ -14,6 +16,17 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { + // When in GUI mode, we should use drag and drop + if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + if (mode == MWGui::GM_Inventory || mode == MWGui::GM_Container) + { + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(getTarget()); + return; + } + } + MWBase::Environment::get().getMechanicsManager()->itemTaken( actor, getTarget(), MWWorld::Ptr(), getTarget().getRefData().getCount()); MWWorld::Ptr newitem = *actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 93705f005..f54edc8cb 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -36,7 +36,7 @@ namespace MWWorld void ActionTeleport::teleport(const Ptr &actor) { MWBase::World* world = MWBase::Environment::get().getWorld(); - actor.getClass().getCreatureStats(actor).land(); + actor.getClass().getCreatureStats(actor).land(actor == world->getPlayerPtr()); if(actor == world->getPlayerPtr()) { world->getPlayer().setTeleported(true); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 9163f1f2e..a06c208b5 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -206,13 +206,13 @@ namespace MWWorld { mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); - mTerrainPreloadItem = NULL; + mTerrainPreloadItem = nullptr; } if (mUpdateCacheItem) { mUpdateCacheItem->waitTillDone(); - mUpdateCacheItem = NULL; + mUpdateCacheItem = nullptr; } for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 5cac12b9c..2b032e1d7 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -151,6 +151,19 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) return &result->second; } +void MWWorld::Cells::rest () +{ + for (auto &interior : mInteriors) + { + interior.second.rest(); + } + + for (auto &exterior : mExteriors) + { + exterior.second.rest(); + } +} + MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) { if (id.mPaged) @@ -327,7 +340,7 @@ public: } catch (...) { - return NULL; + return nullptr; } } }; diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0b7d9444f..bd730329b 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -61,6 +61,7 @@ namespace MWWorld /// @note name must be lower case Ptr getPtr (const std::string& name); + void rest (); /// Get all Ptrs referencing \a name in exterior cells /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 40e904ec2..239d714a0 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -17,6 +17,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -155,7 +156,7 @@ namespace ESM::RefNum mRefNumToFind; SearchByRefNumVisitor(const ESM::RefNum& toFind) - : mFound(NULL) + : mFound(nullptr) , mRefNumToFind(toFind) { } @@ -258,7 +259,7 @@ namespace MWWorld { MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo, object.getRefData().getCount()); object.getRefData().setCount(0); - object.getRefData().setBaseNode(NULL); + object.getRefData().setBaseNode(nullptr); return copied; } @@ -900,7 +901,7 @@ namespace MWWorld CellStore* otherCell = callback->getCellStore(movedTo); - if (otherCell == NULL) + if (otherCell == nullptr) { Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; @@ -944,7 +945,7 @@ namespace MWWorld void clearCorpse(const MWWorld::Ptr& ptr) { const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); - static const float fCorpseClearDelay = MWBase::Environment::get().getWorld()->getStore().get().find("fCorpseClearDelay")->getFloat(); + static const float fCorpseClearDelay = MWBase::Environment::get().getWorld()->getStore().get().find("fCorpseClearDelay")->mValue.getFloat(); if (creatureStats.isDead() && creatureStats.isDeathAnimationFinished() && !ptr.getClass().isPersistent(ptr) && @@ -954,11 +955,34 @@ namespace MWWorld } } + void CellStore::rest() + { + if (mState == State_Loaded) + { + for (CellRefList::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true); + } + } + for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true); + } + } + } + } + void CellStore::respawn() { if (mState == State_Loaded) { - static const int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get().find("iMonthsToRespawn")->getInt(); + static const int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get().find("iMonthsToRespawn")->mValue.getInteger(); if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn) { mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index dd54bdd6a..1c4d8f5d8 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -64,7 +64,7 @@ namespace MWWorld // Even though fog actually belongs to the player and not cells, // it makes sense to store it here since we need it once for each cell. - // Note this is NULL until the cell is explored to save some memory + // Note this is nullptr until the cell is explored to save some memory std::shared_ptr mFogState; const ESM::Cell *mCell; @@ -183,6 +183,8 @@ namespace MWWorld /// @return updated MWWorld::Ptr with the new CellStore pointer set. MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); + void rest(); + /// Make a copy of the given object and insert it into this cell. /// @note If you get a linker error here, this means the given type can not be inserted into a cell. /// The supported types are defined at the bottom of this file. @@ -365,7 +367,7 @@ namespace MWWorld struct GetCellStoreCallback { public: - ///@note must return NULL if the cell is not found + ///@note must return nullptr if the cell is not found virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0; }; diff --git a/apps/openmw/mwworld/cellvisitors.hpp b/apps/openmw/mwworld/cellvisitors.hpp index cfb07f749..e68b383b7 100644 --- a/apps/openmw/mwworld/cellvisitors.hpp +++ b/apps/openmw/mwworld/cellvisitors.hpp @@ -17,7 +17,7 @@ namespace MWWorld { if (ptr.getRefData().getBaseNode()) { - ptr.getRefData().setBaseNode(NULL); + ptr.getRefData().setBaseNode(nullptr); mObjects.push_back (ptr); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8fa16ff51..6a24ae4f0 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -114,7 +114,7 @@ void MWWorld::ContainerStore::storeStates (const CellRefList& collection, const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; -MWWorld::ContainerStore::ContainerStore() : mListener(NULL), mCachedWeight (0), mWeightUpToDate (false) {} +MWWorld::ContainerStore::ContainerStore() : mListener(nullptr), mCachedWeight (0), mWeightUpToDate (false) {} MWWorld::ContainerStore::~ContainerStore() {} @@ -286,7 +286,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr MWWorld::Ptr item = *it; // we may have copied an item from the world, so reset a few things first - item.getRefData().setBaseNode(NULL); // Especially important, otherwise scripts on the item could think that it's actually in a cell + item.getRefData().setBaseNode(nullptr); // Especially important, otherwise scripts on the item could think that it's actually in a cell ESM::Position pos; pos.rot[0] = 0; pos.rot[1] = 0; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 6cfffcde6..647ae261b 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -99,7 +99,7 @@ void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIt } MWWorld::InventoryStore::InventoryStore() - : mListener(NULL) + : mListener(nullptr) , mUpdatesEnabled (true) , mFirstAutoEquip(true) , mSelectedEnchantItem(end()) @@ -250,11 +250,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); - MWMechanics::NpcStats& stats = actor.getClass().getNpcStats(actor); - static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat(); - static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat(); - int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); + static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); + static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); + int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); @@ -384,7 +383,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) for (int j = 0; j < static_cast(weaponSkillsLength); ++j) { - int skillValue = stats.getSkill(static_cast(weaponSkills[j])).getModified(); + int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); if (skillValue > max && !weaponSkillVisited[j]) { @@ -921,7 +920,7 @@ void MWWorld::InventoryStore::rechargeItems(float duration) continue; static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get().find( - "fMagicItemRechargePerSecond")->getFloat(); + "fMagicItemRechargePerSecond")->mValue.getFloat(); if (it->first->getCellRef().getEnchantmentCharge() <= it->second) { diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 2631f513f..4b61c0a08 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -76,11 +76,11 @@ namespace MWWorld template struct LiveCellRef : public LiveCellRefBase { - LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) + LiveCellRef(const ESM::CellRef& cref, const X* b = nullptr) : LiveCellRefBase(typeid(X).name(), cref), mBase(b) {} - LiveCellRef(const X* b = NULL) + LiveCellRef(const X* b = nullptr) : LiveCellRefBase(typeid(X).name()), mBase(b) {} diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 44b78336d..85181e998 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -30,13 +30,14 @@ namespace MWWorld Player::Player (const ESM::NPC *player) : mCellStore(0), mLastKnownExteriorPosition(0,0,0), - mMarkedCell(NULL), + mMarkedCell(nullptr), mAutoMove(false), mForwardBackward(0), mTeleported(false), mCurrentCrimeId(-1), mPaidCrimeId(-1), - mAttackingOrSpell(false) + mAttackingOrSpell(false), + mJumping(false) { ESM::CellRef cellRef; cellRef.blank(); @@ -64,7 +65,7 @@ namespace MWWorld MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer()); MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer()); MWMechanics::DynamicStat health = creatureStats.getDynamic(0); - creatureStats.setHealth(int(health.getBase() / gmst.find("fWereWolfHealth")->getFloat())); + creatureStats.setHealth(int(health.getBase() / gmst.find("fWereWolfHealth")->mValue.getFloat())); for (int i=0; i health = creatureStats.getDynamic(0); - creatureStats.setHealth(int(health.getBase() * gmst.find("fWereWolfHealth")->getFloat())); + creatureStats.setHealth(int(health.getBase() * gmst.find("fWereWolfHealth")->mValue.getFloat())); for(size_t i = 0;i < ESM::Attribute::Length;++i) { // Oh, Bethesda. It's "Intelligence". @@ -85,7 +86,7 @@ namespace MWWorld ESM::Attribute::sAttributeNames[i]); MWMechanics::AttributeValue value = npcStats.getAttribute(i); - value.setBase(int(gmst.find(name)->getFloat())); + value.setBase(int(gmst.find(name)->mValue.getFloat())); npcStats.setAttribute(i, value); } @@ -100,7 +101,7 @@ namespace MWWorld ESM::Skill::sSkillNames[i]); MWMechanics::SkillValue value = npcStats.getSkill(i); - value.setBase(int(gmst.find(name)->getFloat())); + value.setBase(int(gmst.find(name)->mValue.getFloat())); npcStats.setSkill(i, value); } } @@ -255,6 +256,16 @@ namespace MWWorld return mAttackingOrSpell; } + void Player::setJumping(bool jumping) + { + mJumping = jumping; + } + + bool Player::getJumping() const + { + return mJumping; + } + bool Player::isInCombat() { return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0; } @@ -286,6 +297,7 @@ namespace MWWorld mForwardBackward = 0; mTeleported = false; mAttackingOrSpell = false; + mJumping = false; mCurrentCrimeId = -1; mPaidCrimeId = -1; mPreviousItems.clear(); @@ -394,7 +406,7 @@ namespace MWWorld { Log(Debug::Warning) << "Warning: Player cell '" << player.mCellId.mWorldspace << "' no longer exists"; // Cell no longer exists. The loader will have to choose a default cell. - mCellStore = NULL; + mCellStore = nullptr; } if (!player.mBirthsign.empty()) diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d4a9f6050..7f37de010 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -38,7 +38,7 @@ namespace MWWorld osg::Vec3f mLastKnownExteriorPosition; ESM::Position mMarkedPosition; - // If no position was marked, this is NULL + // If no position was marked, this is nullptr CellStore* mMarkedCell; bool mAutoMove; @@ -56,6 +56,7 @@ namespace MWWorld MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length]; bool mAttackingOrSpell; + bool mJumping; public: @@ -111,6 +112,9 @@ namespace MWWorld void setAttackingOrSpell(bool attackingOrSpell); bool getAttackingOrSpell() const; + void setJumping(bool jumping); + bool getJumping() const; + ///Checks all nearby actors to see if anyone has an aipackage against you bool isInCombat(); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index f150ff0ba..4698ba011 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include @@ -385,7 +387,7 @@ namespace MWWorld { osg::Quat orient = it->mNode->getAttitude(); static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() - .find("fTargetSpellMaxSpeed")->getFloat(); + .find("fTargetSpellMaxSpeed")->mValue.getFloat(); float speed = fTargetSpellMaxSpeed * it->mSpeed; osg::Vec3f direction = orient * osg::Vec3f(0,1,0); direction.normalize(); @@ -460,7 +462,7 @@ namespace MWWorld { // gravity constant - must be way lower than the gravity affecting actors, since we're not // simulating aerodynamics at all - it->mVelocity -= osg::Vec3f(0, 0, 627.2f * 0.1f) * duration; + it->mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration; osg::Vec3f pos(it->mNode->getPosition()); osg::Vec3f newPos = pos + it->mVelocity * duration; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ed6dde310..30e73df58 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -94,7 +94,7 @@ namespace void updateObjectScale(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) { - if (ptr.getRefData().getBaseNode() != NULL) + if (ptr.getRefData().getBaseNode() != nullptr) { float scale = ptr.getCellRef().getScale(); osg::Vec3f scaleVec (scale, scale, scale); @@ -330,7 +330,7 @@ namespace MWWorld while (active!=mActiveCells.end()) unloadCell (active++); assert(mActiveCells.empty()); - mCurrentCell = NULL; + mCurrentCell = nullptr; mPreloader->clear(); } @@ -345,7 +345,7 @@ namespace MWWorld getGridCenter(cellX, cellY); float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); - const float maxDistance = 8192/2 + mCellLoadingThreshold; // 1/2 cell size + threshold + const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); if (distance > maxDistance) { @@ -519,7 +519,7 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) { CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); - bool loadcell = (mCurrentCell == NULL); + bool loadcell = (mCurrentCell == nullptr); if(!loadcell) loadcell = *mCurrentCell != *cell; @@ -793,7 +793,7 @@ namespace MWWorld float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y())); dist = std::min(dist,std::max(std::abs(thisCellCenterX - predictedPos.x()), std::abs(thisCellCenterY - predictedPos.y()))); - float loadDist = 8192/2 + 8192 - mCellLoadingThreshold + mPreloadDistance; + float loadDist = Constants::CellSizeInUnits / 2 + Constants::CellSizeInUnits - mCellLoadingThreshold + mPreloadDistance; if (dist < loadDist) preloadCell(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy)); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 2ac78bb85..b6bf2b7eb 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -96,7 +96,7 @@ namespace MWWorld typename Static::const_iterator it = mStatic.find(index); if (it != mStatic.end()) return &(it->second); - return NULL; + return nullptr; } template const T *IndexedStore::find(int index) const @@ -165,7 +165,7 @@ namespace MWWorld std::for_each(mShared.begin(), mShared.end(), GetRecords(id, &results)); if(!results.empty()) return results[Misc::Rng::rollDice(results.size())]; - return NULL; + return nullptr; } template const T *Store::find(const std::string &id) const @@ -358,7 +358,7 @@ namespace MWWorld const LandTextureList <exl = mStatic[plugin]; if (index >= ltexl.size()) - return NULL; + return nullptr; return <exl[index]; } const ESM::LandTexture *Store::find(size_t index, size_t plugin) const @@ -867,7 +867,7 @@ namespace MWWorld //========================================================================= Store::Store() - : mCells(NULL) + : mCells(nullptr) { } @@ -887,7 +887,7 @@ namespace MWWorld // 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; + bool interior = mCells->search(pathgrid.mCell) != nullptr; // Try to overwrite existing record if (interior) @@ -917,14 +917,14 @@ namespace MWWorld Exterior::const_iterator it = mExt.find(std::make_pair(x,y)); if (it != mExt.end()) return &(it->second); - return NULL; + return nullptr; } const ESM::Pathgrid *Store::search(const std::string& name) const { Interior::const_iterator it = mInt.find(name); if (it != mInt.end()) return &(it->second); - return NULL; + return nullptr; } const ESM::Pathgrid *Store::find(int x, int y) const { diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 00ea6f66f..2ed81af48 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -166,7 +166,7 @@ namespace MWWorld */ bool isDynamic(const std::string &id) const; - /** Returns a random record that starts with the named ID, or NULL if not found. */ + /** Returns a random record that starts with the named ID, or nullptr if not found. */ const T *searchRandom(const std::string &id) const; const T *find(const std::string &id) const; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index d12e633be..7d3f41894 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -751,13 +751,16 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings, "Fog"); - float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2; - if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime) - mRendering.getSkyManager()->setGlareTimeOfDayFade(0); + float peakHour = mSunriseTime + (mTimeSettings.mNightStart - mSunriseTime) / 2; + float glareFade = 1.f; + if (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart) + glareFade = 0.f; else if (time.getHour() < peakHour) - mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (peakHour - time.getHour()) / (peakHour - mSunriseTime)); + glareFade = 1.f - (peakHour - time.getHour()) / (peakHour - mSunriseTime); else - mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (time.getHour() - peakHour) / (mSunsetTime - peakHour)); + glareFade = 1.f - (time.getHour() - peakHour) / (mTimeSettings.mNightStart - peakHour); + + mRendering.getSkyManager()->setGlareTimeOfDayFade(glareFade); mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time)); mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time)); @@ -765,7 +768,7 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor, mResult.mDLFogOffset/100.0f, mResult.mFogColor); mRendering.setAmbientColour(mResult.mAmbientColor); - mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView); + mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView * glareFade); mRendering.getSkyManager()->setWeather(mResult); @@ -911,7 +914,7 @@ inline void WeatherManager::addWeather(const std::string& name, float dlFactor, float dlOffset, const std::string& particleEffect) { - static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->getFloat(); + static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->mValue.getFloat(); Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c2ebac8fc..0872e589d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -190,7 +191,7 @@ namespace MWWorld mStore.setUp(true); mStore.movePlayerRecord(); - mSwimHeightScale = mStore.get().find("fSwimHeightScale")->getFloat(); + mSwimHeightScale = mStore.get().find("fSwimHeightScale")->mValue.getFloat(); mWeatherManager.reset(new MWWorld::WeatherManager(*mRendering, mFallback, mStore)); @@ -245,7 +246,7 @@ namespace MWWorld if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos, true); - fixPosition(getPlayerPtr()); + adjustPosition(getPlayerPtr(), false); } else { @@ -260,7 +261,7 @@ namespace MWWorld if (!getPlayerPtr().isInCell()) { ESM::Position pos; - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; pos.pos[0] = cellSize/2; pos.pos[1] = cellSize/2; pos.pos[2] = 0; @@ -1026,7 +1027,7 @@ namespace MWWorld if (mActivationDistanceOverride >= 0) return static_cast(mActivationDistanceOverride); - static const int iMaxActivateDist = getStore().get().find("iMaxActivateDist")->getInt(); + static const int iMaxActivateDist = getStore().get().find("iMaxActivateDist")->mValue.getInteger(); return static_cast(iMaxActivateDist); } @@ -1110,7 +1111,7 @@ namespace MWWorld void World::deleteObject (const Ptr& ptr) { - if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == NULL) + if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == nullptr) { if (ptr == getPlayerPtr()) throw std::runtime_error("can not delete player object"); @@ -1159,11 +1160,17 @@ namespace MWWorld osg::Vec3f vec(x, y, z); - CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup + CellStore *currCell = ptr.isInCell() ? ptr.getCell() : nullptr; // currCell == nullptr should only happen for player, during initial startup bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = isPlayer || (currCell && mWorldScene->isCellActive(*currCell)); MWWorld::Ptr newPtr = ptr; + if (!isPlayer && !currCell) + throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId() + "\" to another cell: current cell is nullptr"); + + if (!newCell) + throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId() + "\" to another cell: new cell is nullptr"); + if (currCell != newCell) { removeContainerScripts(ptr); @@ -1185,10 +1192,10 @@ namespace MWWorld addContainerScripts (getPlayerPtr(), newCell); newPtr = getPlayerPtr(); } - else if (currCell) + else { bool currCellActive = mWorldScene->isCellActive(*currCell); - bool newCellActive = newCell && mWorldScene->isCellActive(*newCell); + bool newCellActive = mWorldScene->isCellActive(*newCell); if (!currCellActive && newCellActive) { newPtr = currCell->moveTo(ptr, newCell); @@ -1336,7 +1343,7 @@ namespace MWWorld if (force || !isFlying(ptr)) { - osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500); + osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits); if (traced.z() < pos.z()) pos.z() = traced.z(); } @@ -1346,13 +1353,37 @@ namespace MWWorld void World::fixPosition(const Ptr &actor) { - const float dist = 8000; - osg::Vec3f pos (actor.getRefData().getPosition().asVec3()); - pos.z() += dist; + const float distance = 128.f; + ESM::Position esmPos = actor.getRefData().getPosition(); + osg::Quat orientation(esmPos.rot[2], osg::Vec3f(0,0,-1)); + osg::Vec3f pos (esmPos.asVec3()); - osg::Vec3f traced = mPhysics->traceDown(actor, pos, dist*1.1f); + int direction = 0; + int fallbackDirections[4] = {direction, (direction+3)%4, (direction+2)%4, (direction+1)%4}; + + osg::Vec3f targetPos = pos; + for (int i=0; i<4; ++i) + { + direction = fallbackDirections[i]; + if (direction == 0) targetPos = pos + (orientation * osg::Vec3f(0,1,0)) * distance; + else if(direction == 1) targetPos = pos - (orientation * osg::Vec3f(0,1,0)) * distance; + else if(direction == 2) targetPos = pos - (orientation * osg::Vec3f(1,0,0)) * distance; + else if(direction == 3) targetPos = pos + (orientation * osg::Vec3f(1,0,0)) * distance; + + // destination is free + if (!castRay(pos.x(), pos.y(), pos.z(), targetPos.x(), targetPos.y(), targetPos.z())) + break; + } + + targetPos.z() += distance / 2.f; // move up a bit to get out from geometry, will snap down later + osg::Vec3f traced = mPhysics->traceDown(actor, targetPos, Constants::CellSizeInUnits); if (traced != pos) - moveObject(actor, actor.getCell(), traced.x(), traced.y(), traced.z()); + { + esmPos.pos[0] = traced.x(); + esmPos.pos[1] = traced.y(); + esmPos.pos[2] = traced.z(); + MWWorld::ActionTeleport("", esmPos, false).execute(actor); + } } void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) @@ -1422,13 +1453,13 @@ namespace MWWorld ipos.rot[2] = referenceObject.getRefData().getPosition().rot[2]; MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false); - placed.getClass().adjustPosition(placed, true); // snap to ground + adjustPosition(placed, true); // snap to ground return placed; } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; x = static_cast(cellSize * cellX); y = static_cast(cellSize * cellY); @@ -1442,10 +1473,8 @@ namespace MWWorld void World::positionToIndex (float x, float y, int &cellX, int &cellY) const { - const int cellSize = 8192; - - cellX = static_cast(std::floor(x / cellSize)); - cellY = static_cast(std::floor(y / cellSize)); + cellX = static_cast(std::floor(x / Constants::CellSizeInUnits)); + cellY = static_cast(std::floor(y / Constants::CellSizeInUnits)); } void World::queueMovement(const Ptr &ptr, const osg::Vec3f &velocity) @@ -1552,7 +1581,13 @@ namespace MWWorld bool World::toggleCollisionMode() { - return mPhysics->toggleCollisionMode(); + if (mPhysics->toggleCollisionMode()) + { + adjustPosition(getPlayerPtr(), true); + return true; + } + + return false; } bool World::toggleRenderMode (MWRender::RenderMode mode) @@ -1706,11 +1741,11 @@ namespace MWWorld // Sink the camera while sneaking bool sneaking = player.getClass().getCreatureStats(getPlayerPtr()).getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool inair = !isOnGround(player); bool swimming = isSwimming(player); + bool flying = isFlying(player); - static const float i1stPersonSneakDelta = getStore().get().find("i1stPersonSneakDelta")->getFloat(); - if (sneaking && !(swimming || inair)) + static const float i1stPersonSneakDelta = getStore().get().find("i1stPersonSneakDelta")->mValue.getFloat(); + if (sneaking && !swimming && !flying) mRendering->getCamera()->setSneakOffset(i1stPersonSneakDelta); else mRendering->getCamera()->setSneakOffset(0.f); @@ -1783,7 +1818,7 @@ namespace MWWorld osg::Vec3f forward = listenerOrient * osg::Vec3f(0,1,0); osg::Vec3f up = listenerOrient * osg::Vec3f(0,0,1); - bool underwater = isUnderwater(getPlayerPtr().getCell(), listenerPos); + bool underwater = isUnderwater(getPlayerPtr().getCell(), mRendering->getCameraPosition()); MWBase::Environment::get().getSoundManager()->setListenerPosDir(listenerPos, forward, up, underwater); } @@ -2163,7 +2198,7 @@ namespace MWWorld pos.z() += heightRatio*2*mPhysics->getRenderingHalfExtents(object).z(); - const CellStore *currCell = object.isInCell() ? object.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup + const CellStore *currCell = object.isInCell() ? object.getCell() : nullptr; // currCell == nullptr should only happen for player, during initial startup return isUnderwater(currCell, pos); } @@ -2292,7 +2327,7 @@ namespace MWWorld applyLoopingParticles(player); } - int World::canRest () + World::RestPermitted World::canRest () const { CellStore *currentCell = mWorldScene->getCurrentCell(); @@ -2304,13 +2339,16 @@ namespace MWWorld if (!actor) throw std::runtime_error("can't find player"); + if(mPlayer->enemiesNearby()) + return Rest_EnemiesAreNearby; + if ((actor->getCollisionMode() && !mPhysics->isOnSolidGround(player)) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) - return 2; + return Rest_PlayerIsUnderwater; if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || player.getClass().getNpcStats(player).isWerewolf()) - return 1; + return Rest_OnlyWaiting; - return 0; + return Rest_Allowed; } MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr) @@ -2377,6 +2415,11 @@ namespace MWWorld return !actors.empty(); } + void World::getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) + { + mPhysics->getActorsStandingOn(object, actors); + } + bool World::getPlayerCollidingWith (const MWWorld::ConstPtr& object) { MWWorld::Ptr player = getPlayerPtr(); @@ -2594,31 +2637,52 @@ namespace MWWorld return false; } + std::vector sortedDoors; const DoorList &doors = cellStore->getReadOnlyDoors().mList; - for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) { - if (!it->mRef.getTeleport()) { + for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) + { + if (!it->mRef.getTeleport()) + { continue; } + sortedDoors.push_back(&it->mRef); + } + + // Sort teleporting doors alphabetically, first by ID, then by destination cell to make search consistent + std::sort(sortedDoors.begin(), sortedDoors.end(), [] (const MWWorld::CellRef *lhs, const MWWorld::CellRef *rhs) + { + if (lhs->getRefId() != rhs->getRefId()) + return lhs->getRefId() < rhs->getRefId(); + + return lhs->getDestCell() < rhs->getDestCell(); + }); + + for (std::vector::const_iterator it = sortedDoors.begin(); it != sortedDoors.end(); ++it) + { MWWorld::CellStore *source = 0; // door to exterior - if (it->mRef.getDestCell().empty()) { + if ((*it)->getDestCell().empty()) + { int x, y; - ESM::Position doorDest = it->mRef.getDoorDest(); + ESM::Position doorDest = (*it)->getDoorDest(); positionToIndex(doorDest.pos[0], doorDest.pos[1], x, y); source = getExterior(x, y); } // door to interior - else { - source = getInterior(it->mRef.getDestCell()); + else + { + source = getInterior((*it)->getDestCell()); } - if (0 != source) { + if (0 != source) + { // Find door leading to our current teleport door // and use its destination to position inside cell. const DoorList &destinationDoors = source->getReadOnlyDoors().mList; - for (DoorList::const_iterator jt = destinationDoors.begin(); jt != destinationDoors.end(); ++jt) { - if (it->mRef.getTeleport() && + for (DoorList::const_iterator jt = destinationDoors.begin(); jt != destinationDoors.end(); ++jt) + { + if ((*it)->getTeleport() && Misc::StringUtils::ciEqual(name, jt->mRef.getDestCell())) { /// \note Using _any_ door pointed to the interior, @@ -2798,7 +2862,7 @@ namespace MWWorld if (!actor.isEmpty() && actor != MWMechanics::getPlayer() && !manualSpell) stats.getAiSequence().getCombatTargets(targetActors); - const float fCombatDistance = getStore().get().find("fCombatDistance")->getFloat(); + const float fCombatDistance = getStore().get().find("fCombatDistance")->mValue.getFloat(); osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3(); @@ -2809,7 +2873,7 @@ namespace MWWorld // if the faced object can not be activated, do not use it if (!target.isEmpty() && !target.getClass().canBeActivated(target)) - target = NULL; + target = nullptr; if (target.isEmpty()) { @@ -2862,14 +2926,14 @@ namespace MWWorld target = result1.first; hitPosition = result1.second; if (dist1 > getMaxActivationDistance()) - target = NULL; + target = nullptr; } else if (result2.mHit) { target = result2.mHitObject; hitPosition = result2.mHitPointWorld; if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target)) - target = NULL; + target = nullptr; } } } @@ -2892,10 +2956,34 @@ namespace MWWorld } } - void World::launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) + void World::launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) { - mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); + // An initial position of projectile can be outside shooter's collision box, so any object between shooter and launch position will be ignored. + // To avoid this issue, we should check for impact immediately before launch the projectile. + // So we cast a 1-yard-length ray from shooter to launch position and check if there are collisions in this area. + // TODO: as a better solutuon we should handle projectiles during physics update, not during world update. + const osg::Vec3f sourcePos = worldPos + orient * osg::Vec3f(0,-1,0) * 64.f; + + // Early out if the launch position is underwater + bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos); + if (underwater) + { + mRendering->emitWaterRipple(worldPos); + return; + } + + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!actor.isEmpty() && actor.getClass().isActor() && actor != MWMechanics::getPlayer()) + actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors); + + // Check for impact, if yes, handle hit, if not, launch projectile + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(sourcePos, worldPos, actor, targetActors, 0xff, MWPhysics::CollisionType_Projectile); + if (result.mHit) + MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength); + else + mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); } void World::launchMagicBolt (const std::string &spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) @@ -3081,6 +3169,10 @@ namespace MWWorld return closestMarker; } + void World::rest() + { + mCells.rest(); + } void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id) @@ -3210,9 +3302,9 @@ namespace MWWorld float World::feetToGameUnits(float feet) { - // Looks like there is no GMST for this. This factor was determined in experiments - // with the Telekinesis effect. - return feet * 22; + // Original engine rounds size upward + static const int unitsPerFoot = ceil(Constants::UnitsPerFoot); + return feet * unitsPerFoot; } float World::getActivationDistancePlusTelekinesis() @@ -3238,8 +3330,8 @@ namespace MWWorld int bounty = player.getClass().getNpcStats(player).getBounty(); int playerGold = player.getClass().getContainerStore(player).count(ContainerStore::sGoldId); - float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->getFloat(); - float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->getFloat(); + float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->mValue.getFloat(); + float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->mValue.getFloat(); int discount = static_cast(bounty * fCrimeGoldDiscountMult); int turnIn = static_cast(bounty * fCrimeGoldTurnInMult); @@ -3304,7 +3396,7 @@ namespace MWWorld mPlayer->recordCrimeId(); confiscateStolenItems(player); - int iDaysinPrisonMod = getStore().get().find("iDaysinPrisonMod")->getInt(); + int iDaysinPrisonMod = getStore().get().find("iDaysinPrisonMod")->mValue.getInteger(); mDaysInPrison = std::max(1, bounty / iDaysinPrisonMod); return; @@ -3362,7 +3454,7 @@ namespace MWWorld { const ESM::CreatureLevList* list = getStore().get().find(creatureList); - int iNumberCreatures = getStore().get().find("iNumberCreatures")->getInt(); + int iNumberCreatures = getStore().get().find("iNumberCreatures")->mValue.getInteger(); int numCreatures = 1 + Misc::Rng::rollDice(iNumberCreatures); // [1, iNumberCreatures] for (int i=0; i &actors) override; ///< get a list of actors standing on \a object bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object bool getActorStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if any actor is standing on \a object bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) override; ///< @return true if the player is colliding with \a object @@ -550,12 +551,10 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - int canRest() override; - ///< check if the player is allowed to rest \n - /// 0 - yes \n - /// 1 - only waiting \n - /// 2 - player is underwater \n - /// 3 - enemies are nearby (not implemented) + RestPermitted canRest() const override; + ///< check if the player is allowed to rest + + void rest() override; /// \todo Probably shouldn't be here MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override; @@ -607,8 +606,8 @@ namespace MWWorld void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) override; void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override; - void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) override; + void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) override; void applyLoopingParticles(const MWWorld::Ptr& ptr) override; diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index ac21470de..63e4bd6af 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -28,7 +28,7 @@ struct ContentFileTest : public ::testing::Test for (std::vector::const_iterator it = mContentFiles.begin(); it != mContentFiles.end(); ++it) { ESM::ESMReader lEsm; - lEsm.setEncoder(NULL); + lEsm.setEncoder(nullptr); lEsm.setIndex(index); lEsm.setGlobalReaderList(&readerList); lEsm.open(it->string()); @@ -309,7 +309,7 @@ TEST_F(StoreTest, overwrite_test) // verify that changes were actually applied const RecordType* overwrittenRec = mEsmStore.get().search(recordId); - ASSERT_TRUE (overwrittenRec != NULL); + ASSERT_TRUE (overwrittenRec != nullptr); ASSERT_TRUE (overwrittenRec && overwrittenRec->mModel == "the_new_model"); } diff --git a/apps/wizard/existinginstallationpage.cpp b/apps/wizard/existinginstallationpage.cpp index a04e721d8..3ec98a935 100644 --- a/apps/wizard/existinginstallationpage.cpp +++ b/apps/wizard/existinginstallationpage.cpp @@ -96,7 +96,7 @@ void Wizard::ExistingInstallationPage::on_browseButton_clicked() tr("Select master file"), QDir::currentPath(), QString(tr("Morrowind master file (*.esm)")), - NULL, + nullptr, QFileDialog::DontResolveSymlinks); if (selectedFile.isEmpty()) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index bf3682aa9..f9e33a3ca 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -50,7 +50,8 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller - lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer shadow mwshadowtechnique + lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer + actorutil shadow mwshadowtechnique ) add_component_dir (nif @@ -77,7 +78,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 inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate + aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate mappings ) add_component_dir (esmterrain @@ -85,7 +86,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - utf8stream stringops resourcehelpers rng messageformatparser + constants utf8stream stringops resourcehelpers rng messageformatparser weakcache ) add_component_dir (debug @@ -130,7 +131,7 @@ add_component_dir (myguiplatform ) add_component_dir (widgets - box imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets + box fontwrapper imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets ) add_component_dir (fontloader diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index ffac252d5..e85c8c3ec 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -24,7 +24,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke if (type!=' ') { /// \todo add option to make re-declared local variables an error - getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)", + getErrorHandler().warning ("ignoring local variable re-declaration", loc); mState = State_End; diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 7cb0abfd1..6b849ec3a 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -422,7 +422,7 @@ namespace Compiler { if (!hasExplicit) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -791,7 +791,7 @@ namespace Compiler ++optionalCount; } else - getErrorHandler().warning ("Ignoring extra argument", + getErrorHandler().warning ("ignoring extra argument", stringParser.getTokenLoc()); } else if (*iter=='X') @@ -805,7 +805,7 @@ namespace Compiler if (parser.isEmpty()) break; else - getErrorHandler().warning("Ignoring extra argument", parser.getTokenLoc()); + getErrorHandler().warning("ignoring extra argument", parser.getTokenLoc()); } else if (*iter=='z') { @@ -817,7 +817,7 @@ namespace Compiler if (discardParser.isEmpty()) break; else - getErrorHandler().warning("Ignoring extra argument", discardParser.getTokenLoc()); + getErrorHandler().warning("ignoring extra argument", discardParser.getTokenLoc()); } else if (*iter=='j') { diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 7638d0f78..54b8c03cb 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -276,7 +276,7 @@ namespace Compiler extensions.registerInstruction ("gotojail", "", opcodeGoToJail); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit); - extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); + extensions.registerInstruction ("addsoulgem", "ccz", opcodeAddSoulGem, opcodeAddSoulGemExplicit); extensions.registerInstruction ("removesoulgem", "c/l", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit); extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); @@ -463,7 +463,7 @@ namespace Compiler extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); extensions.registerInstruction ("addspell", "cz", opcodeAddSpell, opcodeAddSpellExplicit); - extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, + extensions.registerInstruction ("removespell", "cz", opcodeRemoveSpell, opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, opcodeRemoveSpellEffectsExplicit); @@ -546,7 +546,7 @@ namespace Compiler extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit); extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); extensions.registerInstruction("resetactors","",opcodeResetActors); - extensions.registerInstruction("fixme","",opcodeFixme, opcodeFixmeExplicit); + extensions.registerInstruction("fixme","",opcodeFixme); extensions.registerInstruction("ra","",opcodeResetActors); } } diff --git a/components/compiler/junkparser.cpp b/components/compiler/junkparser.cpp index 7608e9bab..5910cca43 100644 --- a/components/compiler/junkparser.cpp +++ b/components/compiler/junkparser.cpp @@ -29,7 +29,7 @@ bool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& l bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==mIgnoreKeyword) - reportWarning ("found junk (ignoring it)", loc); + reportWarning ("ignoring found junk", loc); else scanner.putbackKeyword (keyword, loc); @@ -39,7 +39,7 @@ bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scann bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_member) - reportWarning ("found junk (ignoring it)", loc); + reportWarning ("ignoring found junk", loc); else scanner.putbackSpecial (code, loc); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 602bb826f..3f9d2e790 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -38,7 +38,7 @@ namespace Compiler { case 'l': - Generator::report (mCode, mLiterals, "%g"); + Generator::report (mCode, mLiterals, "%d"); break; case 'f': @@ -88,7 +88,7 @@ namespace Compiler { if (mState==PotentialEndState) { - getErrorHandler().warning ("stray string argument (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray string argument", loc); mState = EndState; return true; } @@ -233,7 +233,7 @@ namespace Compiler if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) { - getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc); + getErrorHandler().warning ("unknown variable, ignoring set instruction", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; @@ -286,7 +286,7 @@ namespace Compiler { if (!hasExplicit && mState==ExplicitState) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -344,7 +344,7 @@ namespace Compiler { if (!hasExplicit && !mExplicit.empty()) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -360,7 +360,7 @@ namespace Compiler if (mState==ExplicitState) { // drop stray explicit reference - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mState = BeginState; mExplicit.clear(); } @@ -412,19 +412,19 @@ namespace Compiler case Scanner::K_else: - getErrorHandler().warning ("stray else (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray else", loc); mState = EndState; return true; case Scanner::K_endif: - getErrorHandler().warning ("stray endif (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray endif", loc); mState = EndState; return true; case Scanner::K_begin: - getErrorHandler().warning ("stray begin (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray begin", loc); mState = EndState; return true; } @@ -491,7 +491,7 @@ namespace Compiler { if (mState==EndState && code==Scanner::S_open) { - getErrorHandler().warning ("stray '[' or '(' at the end of the line (ignoring it)", + getErrorHandler().warning ("ignoring stray '[' or '(' at the end of the line", loc); return true; } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index aef92b311..c141eec68 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -504,7 +504,6 @@ namespace Compiler const int opcodeMoveWorldExplicit = 0x2000209; const int opcodeResetActors = 0x20002f4; const int opcodeFixme = 0x2000302; - const int opcodeFixmeExplicit = 0x2000303; } namespace User diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index bb0fb9374..b56dbb95b 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -306,10 +306,22 @@ namespace Compiler int i = 0; std::string lowerCase = Misc::StringUtils::lowerCase(name); - + bool isKeyword = false; for (; sKeywords[i]; ++i) if (lowerCase==sKeywords[i]) + { + isKeyword = true; break; + } + + // Russian localization and some mods use a quirk - add newline character directly + // to compiled bytecode via HEX-editor to implement multiline messageboxes. + // Of course, original editor will not compile such script. + // Allow messageboxes to bybass the "incomplete string or name" error. + if (lowerCase == "messagebox") + enableIgnoreNewlines(); + else if (isKeyword) + mIgnoreNewline = false; if (sKeywords[i]) { @@ -357,9 +369,14 @@ namespace Compiler // } else if (c=='\n') { - error = true; - mErrorHandler.error ("incomplete string or name", mLoc); - break; + if (mIgnoreNewline) + mErrorHandler.warning ("string contains newline character, make sure that it is intended", mLoc); + else + { + error = true; + mErrorHandler.error ("incomplete string or name", mLoc); + break; + } } } else if (!(c=='"' && name.empty())) @@ -502,6 +519,11 @@ namespace Compiler if (get (c) && c!='=') // <== is a allowed as an alternative to <= :( putback (c); } + else if (c == '<' || c == '>') // Treat <> and << as < + { + special = S_cmpLT; + mErrorHandler.warning (std::string("invalid operator <") + c + ", treating it as <", mLoc); + } else { putback (c); @@ -525,6 +547,11 @@ namespace Compiler if (get (c) && c!='=') // >== is a allowed as an alternative to >= :( putback (c); } + else if (c == '<' || c == '>') // Treat >< and >> as > + { + special = S_cmpGT; + mErrorHandler.warning (std::string("invalid operator >") + c + ", treating it as >", mLoc); + } else { putback (c); @@ -578,7 +605,7 @@ namespace Compiler const Extensions *extensions) : mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions), mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0), - mStrictKeywords (false), mTolerantNames (false) + mStrictKeywords (false), mTolerantNames (false), mIgnoreNewline(false) { } @@ -631,6 +658,11 @@ namespace Compiler mExtensions->listKeywords (keywords); } + void Scanner::enableIgnoreNewlines() + { + mIgnoreNewline = true; + } + void Scanner::enableStrictKeywords() { mStrictKeywords = true; diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index 49fbaa96a..a431cabb2 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -39,6 +39,7 @@ namespace Compiler TokenLoc mPutbackLoc; bool mStrictKeywords; bool mTolerantNames; + bool mIgnoreNewline; public: @@ -126,6 +127,11 @@ namespace Compiler void listKeywords (std::vector& keywords); ///< Append all known keywords to \a keywords. + /// Treat newline character as a part of script command. + /// + /// \attention This mode lasts only until the next keyword is reached. + void enableIgnoreNewlines(); + /// Do not accept keywords in quotation marks anymore. /// /// \attention This mode lasts only until the next newline is reached. diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index bc785a276..80cd6e4c3 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -48,6 +48,8 @@ namespace ContentSelectorModel QModelIndex indexFromItem(const EsmFile *item) const; const EsmFile *item(const QString &name) const; + const EsmFile *item(int row) const; + EsmFile *item(int row); QStringList gameFiles() const; bool isEnabled (QModelIndex index) const; @@ -65,8 +67,6 @@ namespace ContentSelectorModel private: void addFile(EsmFile *file); - const EsmFile *item(int row) const; - EsmFile *item(int row); void sortFiles(); diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 5e16064f4..4f95b7fe4 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,7 @@ void ContentSelectorView::ContentSelector::buildContextMenu() mContextMenu = new QMenu(ui.addonView); mContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems())); mContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems())); + mContextMenu->addAction(tr("&Copy Path(s) to Clipboard"), this, SLOT(slotCopySelectedItemsPaths())); } void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList) @@ -111,6 +113,11 @@ void ContentSelectorView::ContentSelector::clearCheckStates() mContentModel->uncheckAll(); } +void ContentSelectorView::ContentSelector::setEncoding(const QString &encoding) +{ + mContentModel->setEncoding(encoding); +} + void ContentSelectorView::ContentSelector::setContentList(const QStringList &list) { if (list.isEmpty()) @@ -189,7 +196,7 @@ void ContentSelectorView::ContentSelector::setGameFileSelected(int index, bool s { QString fileName = ui.gameFileView->itemText(index); const ContentSelectorModel::EsmFile* file = mContentModel->item(fileName); - if (file != NULL) + if (file != nullptr) { QModelIndex index2(mContentModel->indexFromItem(file)); mContentModel->setData(index2, selected, Qt::UserRole + 1); @@ -239,4 +246,21 @@ void ContentSelectorView::ContentSelector::slotUncheckMultiSelectedItems() void ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems() { setCheckStateForMultiSelectedItems(true); -} \ No newline at end of file +} + +void ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString filepaths; + foreach (const QModelIndex& index, ui.addonView->selectionModel()->selectedIndexes()) + { + int row = mAddonProxyModel->mapToSource(index).row(); + const ContentSelectorModel::EsmFile *file = mContentModel->item(row); + filepaths += file->filePath() + "\n"; + } + + if (!filepaths.isEmpty()) + { + clipboard->setText(filepaths); + } +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 323f926ed..bc47224e4 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -32,6 +32,7 @@ namespace ContentSelectorView void setProfileContent (const QStringList &fileList); void clearCheckStates(); + void setEncoding (const QString &encoding); void setContentList(const QStringList &list); ContentSelectorModel::ContentFileList selectedFiles() const; @@ -69,6 +70,7 @@ namespace ContentSelectorView void slotShowContextMenu(const QPoint& pos); void slotCheckMultiSelectedItems(); void slotUncheckMultiSelectedItems(); + void slotCopySelectedItemsPaths(); }; } diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 04b239a7f..2e2ddd1f2 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -71,7 +71,7 @@ static const struct { { "Illegal instruction", SIGILL }, { "FPU exception", SIGFPE }, { "System BUS error", SIGBUS }, -{ NULL, 0 } +{ nullptr, 0 } }; static const struct { @@ -88,7 +88,7 @@ static const struct { { ILL_COPROC, "Coprocessor error" }, { ILL_BADSTK, "Internal stack error" }, #endif - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -103,7 +103,7 @@ static const struct { { FPE_FLTRES, "Floating point inexact result" }, { FPE_FLTINV, "Floating point invalid operation" }, { FPE_FLTSUB, "Subscript out of range" }, - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -114,7 +114,7 @@ static const struct { { SEGV_MAPERR, "Address not mapped to object" }, { SEGV_ACCERR, "Invalid permissions for mapped object" }, #endif - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -126,7 +126,7 @@ static const struct { { BUS_ADRERR, "Non-existent physical address" }, { BUS_OBJERR, "Object specific hardware error" }, #endif - { 0, NULL } + { 0, nullptr } }; static int (*cc_user_info)(char*, char*); @@ -140,7 +140,7 @@ static void gdb_info(pid_t pid) /* Create a temp file to put gdb commands into */ strcpy(respfile, "/tmp/gdb-respfile-XXXXXX"); - if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) + if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != nullptr) { fprintf(f, "attach %d\n" "shell echo \"\"\n" @@ -175,17 +175,18 @@ static void gdb_info(pid_t pid) fflush(stdout); /* Clean up */ - remove(respfile); + if (remove(respfile) != 0) + Log(Debug::Warning) << "Warning: can not remove file '" << respfile << "': " << std::strerror(errno); } else { /* Error creating temp file */ if(fd >= 0) { - if (close(fd) == 0) - remove(respfile); - else - Log(Debug::Warning) << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno); + if (close(fd) != 0) + Log(Debug::Warning) << "Warning: can not close file '" << respfile << "': " << std::strerror(errno); + else if (remove(respfile) != 0) + Log(Debug::Warning) << "Warning: can not remove file '" << respfile << "': " << std::strerror(errno); } printf("!!! Could not create gdb command file\n"); } @@ -266,7 +267,7 @@ static void crash_catcher(int signum, siginfo_t *siginfo, void *context) close(fd[0]); close(fd[1]); - execl(argv0, argv0, crash_switch, NULL); + execl(argv0, argv0, crash_switch, nullptr); safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); _exit(1); @@ -406,7 +407,7 @@ static void crash_handler(const char *logfile) if(logfile) { std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; - SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); + SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr); } exit(0); @@ -443,7 +444,7 @@ int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *sig altss.ss_sp = altstack; altss.ss_flags = 0; altss.ss_size = sizeof(altstack); - sigaltstack(&altss, NULL); + sigaltstack(&altss, nullptr); memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = crash_catcher; @@ -454,7 +455,7 @@ int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *sig while(num_signals--) { if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && *signals != SIGABRT && - *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) + *signals != SIGBUS) || sigaction(*signals, &sa, nullptr) == -1) { *signals = 0; retval = -1; @@ -501,7 +502,7 @@ static bool is_debugger_present() // Call sysctl. size = sizeof(info); - junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0); assert(junk == 0); // We're being debugged if the P_TRACED flag is set. @@ -515,7 +516,7 @@ void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath) if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_present()) { int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; - if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), NULL) == -1) + if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), nullptr) == -1) { Log(Debug::Warning) << "Installing crash handler failed"; } diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index a4c59c221..e89a65956 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -101,7 +101,7 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) if (!isatty(fileno(stdin))) #endif - SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), NULL); + SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), nullptr); Log(Debug::Error) << "Error: " << e.what(); diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp index 59536d685..f47f58e45 100644 --- a/components/debug/debugging.hpp +++ b/components/debug/debugging.hpp @@ -41,8 +41,6 @@ namespace Debug { return size; } - - char mDebugLevel; }; #if defined(_WIN32) && defined(_DEBUG) diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index 1ea18aa9b..f4a8e17be 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -43,14 +43,6 @@ public: return *this; } - template - Log& operator<<(const T& rhs) - { - if (mLevel <= Debug::CurrentDebugLevel) - std::cout << std::forward(rhs); - - return *this; - } ~Log() { diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 67b9d6a38..a6b947dd3 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -23,8 +23,8 @@ ESMReader::ESMReader() : mIdx(0) , mRecordFlags(0) , mBuffer(50*1024) - , mGlobalReaderList(NULL) - , mEncoder(NULL) + , mGlobalReaderList(nullptr) + , mEncoder(nullptr) , mFileSize(0) { } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 4772aeb6f..6e84fa7d4 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -33,8 +33,8 @@ public: int getVer() const { return mHeader.mData.version; } int getRecordCount() const { return mHeader.mData.records; } float getFVer() const { return (mHeader.mData.version == VER_12) ? 1.2f : 1.3f; } - const std::string getAuthor() const { return mHeader.mData.author.toString(); } - const std::string getDesc() const { return mHeader.mData.desc.toString(); } + const std::string getAuthor() const { return mHeader.mData.author; } + const std::string getDesc() const { return mHeader.mData.desc; } const std::vector &getGameFiles() const { return mHeader.mMaster; } const Header& getHeader() const { return mHeader; } int getFormat() const; diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index b8c78a2b9..09fca4b26 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -10,9 +10,9 @@ namespace ESM { ESMWriter::ESMWriter() : mRecords() - , mStream(NULL) + , mStream(nullptr) , mHeaderPos() - , mEncoder(NULL) + , mEncoder(nullptr) , mRecordCount(0) , mCounting(true) , mHeader() diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 249d812b1..bc5016718 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -78,14 +78,14 @@ struct Cell struct DATAstruct { - int mFlags; - int mX, mY; + int mFlags {0}; + int mX {0}, mY {0}; }; struct AMBIstruct { - Color mAmbient, mSunlight, mFog; - float mFogDensity; + Color mAmbient {0}, mSunlight {0}, mFog {0}; + float mFogDensity {0.f}; }; Cell() : mName(""), diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index 1ebb002e6..da8d256e7 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -22,21 +22,6 @@ namespace ESM mValue.write (esm, ESM::Variant::Format_Gmst); } - int GameSetting::getInt() const - { - return mValue.getInteger(); - } - - float GameSetting::getFloat() const - { - return mValue.getFloat(); - } - - std::string GameSetting::getString() const - { - return mValue.getString(); - } - void GameSetting::blank() { mValue.setType (ESM::VT_None); diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index 73a723e81..c40d348fe 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -28,17 +28,6 @@ struct GameSetting void load(ESMReader &esm, bool &isDeleted); - /// \todo remove the get* functions (redundant, since mValue has equivalent functions now). - - int getInt() const; - ///< Throws an exception if GMST is not of type int or float. - - float getFloat() const; - ///< Throws an exception if GMST is not of type int or float. - - std::string getString() const; - ///< Throwns an exception if GMST is not of type string. - void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index a91dfe3d3..9e5e9d07e 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -16,7 +16,7 @@ namespace ESM , mY(0) , mPlugin(0) , mDataTypes(0) - , mLandData(NULL) + , mLandData(nullptr) { } @@ -73,7 +73,7 @@ namespace ESM mContext = esm.getContext(); - mLandData = NULL; + mLandData = nullptr; // Skip the land data here. Load it when the cell is loaded. while (esm.hasMoreSubs()) @@ -298,7 +298,7 @@ namespace ESM if (mLandData) { delete mLandData; - mLandData = NULL; + mLandData = nullptr; } } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index cccb472de..b4b66c601 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -3,6 +3,8 @@ #include +#include + #include "esmcommon.hpp" namespace ESM @@ -53,7 +55,7 @@ struct Land static const int LAND_SIZE = 65; // cell terrain size in world coords - static const int REAL_SIZE = 8192; + static const int REAL_SIZE = Constants::CellSizeInUnits; // total number of vertices static const int LAND_NUM_VERTS = LAND_SIZE * LAND_SIZE; @@ -127,9 +129,9 @@ struct Land /** * Actually loads data into target - * If target is NULL, assumed target is mLandData + * If target is nullptr, assumed target is mLandData */ - void loadData(int flags, LandData* target = NULL) const; + void loadData(int flags, LandData* target = nullptr) const; /** * Frees memory allocated for mLandData diff --git a/components/esm/loadmgef.cpp b/components/esm/loadmgef.cpp index 9f8ad94e1..75a94f828 100644 --- a/components/esm/loadmgef.cpp +++ b/components/esm/loadmgef.cpp @@ -274,43 +274,46 @@ short MagicEffect::getResistanceEffect(short effect) // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute // - std::map effects; - effects[DisintegrateArmor] = Sanctuary; - effects[DisintegrateWeapon] = Sanctuary; - - for (int i=0; i<5; ++i) - effects[DrainAttribute+i] = ResistMagicka; - for (int i=0; i<5; ++i) - effects[DamageAttribute+i] = ResistMagicka; - for (int i=0; i<5; ++i) - effects[AbsorbAttribute+i] = ResistMagicka; - for (int i=0; i<10; ++i) - effects[WeaknessToFire+i] = ResistMagicka; - - effects[Burden] = ResistMagicka; - effects[Charm] = ResistMagicka; - effects[Silence] = ResistMagicka; - effects[Blind] = ResistMagicka; - effects[Sound] = ResistMagicka; - - for (int i=0; i<2; ++i) + static std::map effects; + if (effects.empty()) { - effects[CalmHumanoid+i] = ResistMagicka; - effects[FrenzyHumanoid+i] = ResistMagicka; - effects[DemoralizeHumanoid+i] = ResistMagicka; - effects[RallyHumanoid+i] = ResistMagicka; + effects[DisintegrateArmor] = Sanctuary; + effects[DisintegrateWeapon] = Sanctuary; + + for (int i=0; i<5; ++i) + effects[DrainAttribute+i] = ResistMagicka; + for (int i=0; i<5; ++i) + effects[DamageAttribute+i] = ResistMagicka; + for (int i=0; i<5; ++i) + effects[AbsorbAttribute+i] = ResistMagicka; + for (int i=0; i<10; ++i) + effects[WeaknessToFire+i] = ResistMagicka; + + effects[Burden] = ResistMagicka; + effects[Charm] = ResistMagicka; + effects[Silence] = ResistMagicka; + effects[Blind] = ResistMagicka; + effects[Sound] = ResistMagicka; + + for (int i=0; i<2; ++i) + { + effects[CalmHumanoid+i] = ResistMagicka; + effects[FrenzyHumanoid+i] = ResistMagicka; + effects[DemoralizeHumanoid+i] = ResistMagicka; + effects[RallyHumanoid+i] = ResistMagicka; + } + + effects[TurnUndead] = ResistMagicka; + + effects[FireDamage] = ResistFire; + effects[FrostDamage] = ResistFrost; + effects[ShockDamage] = ResistShock; + effects[Vampirism] = ResistCommonDisease; + effects[Corprus] = ResistCorprusDisease; + effects[Poison] = ResistPoison; + effects[Paralyze] = ResistParalysis; } - effects[TurnUndead] = ResistMagicka; - - effects[FireDamage] = ResistFire; - effects[FrostDamage] = ResistFrost; - effects[ShockDamage] = ResistShock; - effects[Vampirism] = ResistCommonDisease; - effects[Corprus] = ResistCorprusDisease; - effects[Poison] = ResistPoison; - effects[Paralyze] = ResistParalysis; - if (effects.find(effect) != effects.end()) return effects[effect]; else @@ -319,42 +322,44 @@ short MagicEffect::getResistanceEffect(short effect) short MagicEffect::getWeaknessEffect(short effect) { - std::map effects; - - for (int i=0; i<5; ++i) - effects[DrainAttribute+i] = WeaknessToMagicka; - for (int i=0; i<5; ++i) - effects[DamageAttribute+i] = WeaknessToMagicka; - for (int i=0; i<5; ++i) - effects[AbsorbAttribute+i] = WeaknessToMagicka; - for (int i=0; i<10; ++i) - effects[WeaknessToFire+i] = WeaknessToMagicka; - - effects[Burden] = WeaknessToMagicka; - effects[Charm] = WeaknessToMagicka; - effects[Silence] = WeaknessToMagicka; - effects[Blind] = WeaknessToMagicka; - effects[Sound] = WeaknessToMagicka; - - for (int i=0; i<2; ++i) + static std::map effects; + if (effects.empty()) { - effects[CalmHumanoid+i] = WeaknessToMagicka; - effects[FrenzyHumanoid+i] = WeaknessToMagicka; - effects[DemoralizeHumanoid+i] = WeaknessToMagicka; - effects[RallyHumanoid+i] = WeaknessToMagicka; + for (int i=0; i<5; ++i) + effects[DrainAttribute+i] = WeaknessToMagicka; + for (int i=0; i<5; ++i) + effects[DamageAttribute+i] = WeaknessToMagicka; + for (int i=0; i<5; ++i) + effects[AbsorbAttribute+i] = WeaknessToMagicka; + for (int i=0; i<10; ++i) + effects[WeaknessToFire+i] = WeaknessToMagicka; + + effects[Burden] = WeaknessToMagicka; + effects[Charm] = WeaknessToMagicka; + effects[Silence] = WeaknessToMagicka; + effects[Blind] = WeaknessToMagicka; + effects[Sound] = WeaknessToMagicka; + + for (int i=0; i<2; ++i) + { + effects[CalmHumanoid+i] = WeaknessToMagicka; + effects[FrenzyHumanoid+i] = WeaknessToMagicka; + effects[DemoralizeHumanoid+i] = WeaknessToMagicka; + effects[RallyHumanoid+i] = WeaknessToMagicka; + } + + effects[TurnUndead] = WeaknessToMagicka; + + effects[FireDamage] = WeaknessToFire; + effects[FrostDamage] = WeaknessToFrost; + effects[ShockDamage] = WeaknessToShock; + effects[Vampirism] = WeaknessToCommonDisease; + effects[Corprus] = WeaknessToCorprusDisease; + effects[Poison] = WeaknessToPoison; + + effects[Paralyze] = -1; } - effects[TurnUndead] = WeaknessToMagicka; - - effects[FireDamage] = WeaknessToFire; - effects[FrostDamage] = WeaknessToFrost; - effects[ShockDamage] = WeaknessToShock; - effects[Vampirism] = WeaknessToCommonDisease; - effects[Corprus] = WeaknessToCorprusDisease; - effects[Poison] = WeaknessToPoison; - - effects[Paralyze] = -1; - if (effects.find(effect) != effects.end()) return effects[effect]; else diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 5f567d999..fbe1dca1f 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -90,7 +90,7 @@ struct NPC char mFactionID; unsigned short mHealth, mMana, mFatigue; - signed char mDisposition, mReputation, mRank; + unsigned char mDisposition, mReputation, mRank; char mUnknown; int mGold; }; // 52 bytes @@ -101,7 +101,7 @@ struct NPC { short mLevel; // see above - signed char mDisposition, mReputation, mRank; + unsigned char mDisposition, mReputation, mRank; char mUnknown1, mUnknown2, mUnknown3; int mGold; }; // 12 bytes diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index b16145467..d953f1dc2 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -32,8 +32,8 @@ void ESM::Header::load (ESMReader &esm) esm.getSubHeader(); esm.getT(mData.version); esm.getT(mData.type); - mData.author.assign( esm.getString(mData.author.data_size()) ); - mData.desc.assign( esm.getString(mData.desc.data_size()) ); + mData.author.assign( esm.getString(32) ); + mData.desc.assign( esm.getString(256) ); esm.getT(mData.records); } @@ -73,8 +73,8 @@ void ESM::Header::save (ESMWriter &esm) esm.startSubRecord("HEDR"); esm.writeT(mData.version); esm.writeT(mData.type); - esm.writeFixedSizeString(mData.author.toString(), mData.author.data_size()); - esm.writeFixedSizeString(mData.desc.toString(), mData.desc.data_size()); + esm.writeFixedSizeString(mData.author, 32); + esm.writeFixedSizeString(mData.desc, 256); esm.writeT(mData.records); esm.endRecord("HEDR"); diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp index 896936cf5..5b26ac7d2 100644 --- a/components/esm/loadtes3.hpp +++ b/components/esm/loadtes3.hpp @@ -21,8 +21,8 @@ namespace ESM */ unsigned int version; int type; // 0=esp, 1=esm, 32=ess (unused) - NAME32 author; // Author's name - NAME256 desc; // File description + std::string author; // Author's name + std::string desc; // File description int records; // Number of records }; diff --git a/components/esm/mappings.cpp b/components/esm/mappings.cpp new file mode 100644 index 000000000..440e73573 --- /dev/null +++ b/components/esm/mappings.cpp @@ -0,0 +1,134 @@ +#include "mappings.hpp" + +#include + +namespace ESM +{ + ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Head: + return ESM::BodyPart::MP_Head; + case ESM::PRT_Hair: + return ESM::BodyPart::MP_Hair; + case ESM::PRT_Neck: + return ESM::BodyPart::MP_Neck; + case ESM::PRT_Cuirass: + return ESM::BodyPart::MP_Chest; + case ESM::PRT_Groin: + return ESM::BodyPart::MP_Groin; + case ESM::PRT_RHand: + return ESM::BodyPart::MP_Hand; + case ESM::PRT_LHand: + return ESM::BodyPart::MP_Hand; + case ESM::PRT_RWrist: + return ESM::BodyPart::MP_Wrist; + case ESM::PRT_LWrist: + return ESM::BodyPart::MP_Wrist; + case ESM::PRT_RForearm: + return ESM::BodyPart::MP_Forearm; + case ESM::PRT_LForearm: + return ESM::BodyPart::MP_Forearm; + case ESM::PRT_RUpperarm: + return ESM::BodyPart::MP_Upperarm; + case ESM::PRT_LUpperarm: + return ESM::BodyPart::MP_Upperarm; + case ESM::PRT_RFoot: + return ESM::BodyPart::MP_Foot; + case ESM::PRT_LFoot: + return ESM::BodyPart::MP_Foot; + case ESM::PRT_RAnkle: + return ESM::BodyPart::MP_Ankle; + case ESM::PRT_LAnkle: + return ESM::BodyPart::MP_Ankle; + case ESM::PRT_RKnee: + return ESM::BodyPart::MP_Knee; + case ESM::PRT_LKnee: + return ESM::BodyPart::MP_Knee; + case ESM::PRT_RLeg: + return ESM::BodyPart::MP_Upperleg; + case ESM::PRT_LLeg: + return ESM::BodyPart::MP_Upperleg; + case ESM::PRT_Tail: + return ESM::BodyPart::MP_Tail; + default: + throw std::runtime_error("PartReferenceType " + + std::to_string(type) + " not associated with a mesh part"); + } + } + + std::string getBoneName(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Head: + return "head"; + case ESM::PRT_Hair: + return "head"; // This is purposeful. + case ESM::PRT_Neck: + return "neck"; + case ESM::PRT_Cuirass: + return "chest"; + case ESM::PRT_Groin: + return "groin"; + case ESM::PRT_Skirt: + return "groin"; + case ESM::PRT_RHand: + return "right hand"; + case ESM::PRT_LHand: + return "left hand"; + case ESM::PRT_RWrist: + return "right wrist"; + case ESM::PRT_LWrist: + return "left wrist"; + case ESM::PRT_Shield: + return "shield bone"; + case ESM::PRT_RForearm: + return "right forearm"; + case ESM::PRT_LForearm: + return "left forearm"; + case ESM::PRT_RUpperarm: + return "right upper arm"; + case ESM::PRT_LUpperarm: + return "left upper arm"; + case ESM::PRT_RFoot: + return "right foot"; + case ESM::PRT_LFoot: + return "left foot"; + case ESM::PRT_RAnkle: + return "right ankle"; + case ESM::PRT_LAnkle: + return "left ankle"; + case ESM::PRT_RKnee: + return "right knee"; + case ESM::PRT_LKnee: + return "left knee"; + case ESM::PRT_RLeg: + return "right upper leg"; + case ESM::PRT_LLeg: + return "left upper leg"; + case ESM::PRT_RPauldron: + return "right clavicle"; + case ESM::PRT_LPauldron: + return "left clavicle"; + case ESM::PRT_Weapon: + return "weapon bone"; + case ESM::PRT_Tail: + return "tail"; + default: + throw std::runtime_error("unknown PartReferenceType"); + } + } + + std::string getMeshFilter(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Hair: + return "hair"; + default: + return getBoneName(type); + } + } +} diff --git a/components/esm/mappings.hpp b/components/esm/mappings.hpp new file mode 100644 index 000000000..f930fef15 --- /dev/null +++ b/components/esm/mappings.hpp @@ -0,0 +1,16 @@ +#ifndef OPENMW_ESM_MAPPINGS_H +#define OPENMW_ESM_MAPPINGS_H + +#include + +#include +#include + +namespace ESM +{ + ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type); + std::string getBoneName(ESM::PartReferenceType type); + std::string getMeshFilter(ESM::PartReferenceType type); +} + +#endif diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index f77e66276..dfdc9718d 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -50,7 +50,7 @@ namespace ESMTerrain const ESM::Land::LandData *LandObject::getData(int flags) const { if ((mData.mDataLoaded & flags) != flags) - return NULL; + return nullptr; return &mData; } @@ -267,8 +267,8 @@ namespace ESMTerrain height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; (*positions)[static_cast(vertX*numVerts + vertY)] - = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * 8192, - (vertY / float(numVerts - 1) - 0.5f) * size * 8192, + = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, + (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, height); if (normalData) @@ -477,8 +477,8 @@ namespace ESMTerrain float Storage::getHeightAt(const osg::Vec3f &worldPos) { - int cellX = static_cast(std::floor(worldPos.x() / 8192.f)); - int cellY = static_cast(std::floor(worldPos.y() / 8192.f)); + int cellX = static_cast(std::floor(worldPos.x() / float(Constants::CellSizeInUnits))); + int cellY = static_cast(std::floor(worldPos.y() / float(Constants::CellSizeInUnits))); osg::ref_ptr land = getLand(cellX, cellY); if (!land) @@ -491,8 +491,8 @@ namespace ESMTerrain // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell - float nX = (worldPos.x() - (cellX * 8192))/8192.f; - float nY = (worldPos.y() - (cellY * 8192))/8192.f; + float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); + float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); // get left / bottom points (rounded down) float factor = ESM::Land::LAND_SIZE - 1.0f; @@ -524,10 +524,10 @@ namespace ESMTerrain */ // Build all 4 positions in normalized cell space, using point-sampled height - osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / 8192.f); - osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / 8192.f); - osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / 8192.f); - osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / 8192.f); + osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / float(Constants::CellSizeInUnits)); // define this plane in terrain space osg::Plane plane; // FIXME: deal with differing triangle alignment @@ -555,7 +555,7 @@ namespace ESMTerrain // Solve plane equation for z return (-plane.getNormal().x() * nX -plane.getNormal().y() * nY - - plane[3]) / plane.getNormal().z() * 8192; + - plane[3]) / plane.getNormal().z() * Constants::CellSizeInUnits; } diff --git a/components/files/androidpath.cpp b/components/files/androidpath.cpp index 84886f473..bf5c98577 100644 --- a/components/files/androidpath.cpp +++ b/components/files/androidpath.cpp @@ -40,15 +40,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/constrainedfilestream.cpp b/components/files/constrainedfilestream.cpp index b239ec6a1..419af0d6c 100644 --- a/components/files/constrainedfilestream.cpp +++ b/components/files/constrainedfilestream.cpp @@ -103,21 +103,16 @@ namespace Files }; - ConstrainedFileStream::ConstrainedFileStream(const char *filename, size_t start, size_t length) - : std::istream(new ConstrainedFileStreamBuf(filename, start, length)) + ConstrainedFileStream::ConstrainedFileStream(std::unique_ptr buf) + : std::istream(buf.get()) + , mBuf(std::move(buf)) { - } - ConstrainedFileStream::~ConstrainedFileStream() - { - delete rdbuf(); - } - - IStreamPtr openConstrainedFileStream(const char *filename, size_t start, size_t length) { - return IStreamPtr(new ConstrainedFileStream(filename, start, length)); + auto buf = std::unique_ptr(new ConstrainedFileStreamBuf(filename, start, length)); + return IStreamPtr(new ConstrainedFileStream(std::move(buf))); } } diff --git a/components/files/constrainedfilestream.hpp b/components/files/constrainedfilestream.hpp index 05ae0fbec..bf67c7b97 100644 --- a/components/files/constrainedfilestream.hpp +++ b/components/files/constrainedfilestream.hpp @@ -11,9 +11,11 @@ namespace Files class ConstrainedFileStream : public std::istream { public: - ConstrainedFileStream(const char *filename, - size_t start=0, size_t length=0xFFFFFFFF); - virtual ~ConstrainedFileStream(); + ConstrainedFileStream(std::unique_ptr buf); + virtual ~ConstrainedFileStream() {}; + +private: + std::unique_ptr mBuf; }; typedef std::shared_ptr IStreamPtr; diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 1f6a3d913..3743eef4c 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -14,15 +14,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 169a6f813..aff15777f 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -21,22 +21,22 @@ LowLevelFile::LowLevelFile () { - mHandle = NULL; + mHandle = nullptr; } LowLevelFile::~LowLevelFile () { - if (mHandle != NULL) + if (mHandle != nullptr) fclose (mHandle); } void LowLevelFile::open (char const * filename) { - assert (mHandle == NULL); + assert (mHandle == nullptr); mHandle = fopen (filename, "rb"); - if (mHandle == NULL) + if (mHandle == nullptr) { std::ostringstream os; os << "Failed to open '" << filename << "' for reading."; @@ -46,16 +46,16 @@ void LowLevelFile::open (char const * filename) void LowLevelFile::close () { - assert (mHandle != NULL); + assert (mHandle != nullptr); fclose (mHandle); - mHandle = NULL; + mHandle = nullptr; } size_t LowLevelFile::size () { - assert (mHandle != NULL); + assert (mHandle != nullptr); long oldPosition = ftell (mHandle); @@ -78,7 +78,7 @@ size_t LowLevelFile::size () void LowLevelFile::seek (size_t Position) { - assert (mHandle != NULL); + assert (mHandle != nullptr); if (fseek (mHandle, Position, SEEK_SET) != 0) throw std::runtime_error ("A seek operation on a file failed."); @@ -86,7 +86,7 @@ void LowLevelFile::seek (size_t Position) size_t LowLevelFile::tell () { - assert (mHandle != NULL); + assert (mHandle != nullptr); long Position = ftell (mHandle); @@ -98,7 +98,7 @@ size_t LowLevelFile::tell () size_t LowLevelFile::read (void * data, size_t size) { - assert (mHandle != NULL); + assert (mHandle != nullptr); int amount = fread (data, 1, size, mHandle); @@ -296,7 +296,7 @@ void LowLevelFile::seek (size_t Position) { assert (mHandle != INVALID_HANDLE_VALUE); - if (SetFilePointer (mHandle, Position, NULL, SEEK_SET) == INVALID_SET_FILE_POINTER) + if (SetFilePointer (mHandle, Position, nullptr, SEEK_SET) == INVALID_SET_FILE_POINTER) if (GetLastError () != NO_ERROR) throw std::runtime_error ("A seek operation on a file failed."); } @@ -305,7 +305,7 @@ size_t LowLevelFile::tell () { assert (mHandle != INVALID_HANDLE_VALUE); - DWORD value = SetFilePointer (mHandle, 0, NULL, SEEK_CUR); + DWORD value = SetFilePointer (mHandle, 0, nullptr, SEEK_CUR); if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) throw std::runtime_error ("A query operation on a file failed."); @@ -319,7 +319,7 @@ size_t LowLevelFile::read (void * data, size_t size) DWORD read; - if (!ReadFile (mHandle, data, size, &read, NULL)) + if (!ReadFile (mHandle, data, size, &read, nullptr)) throw std::runtime_error ("A read operation on a file failed."); return read; diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index b49b72e46..32c35c98f 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -14,15 +14,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 4b7c6c50a..2354e6f31 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -42,7 +42,7 @@ boost::filesystem::path WindowsPath::getUserConfigPath() const WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); - if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) + if(SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, nullptr, 0, path))) { userPath = boost::filesystem::path(bconv::utf_to_utf(path)); } @@ -63,7 +63,7 @@ boost::filesystem::path WindowsPath::getGlobalConfigPath() const WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); - if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, 0, path))) + if(SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, nullptr, 0, path))) { globalPath = boost::filesystem::path(bconv::utf_to_utf(path)); } @@ -99,7 +99,7 @@ boost::filesystem::path WindowsPath::getInstallPath() const std::vector buf(512); int len = 512; - if (RegQueryValueEx(hKey, TEXT("Installed Path"), NULL, NULL, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS) + if (RegQueryValueEx(hKey, TEXT("Installed Path"), nullptr, nullptr, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS) { installPath = &buf[0]; } diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 790df7fa8..0378e294e 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -145,8 +145,9 @@ namespace namespace Gui { - FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs) + FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath) : mVFS(vfs) + , mUserDataPath(userDataPath) { if (encoding == ToUTF8::WINDOWS_1252) mEncoding = ToUTF8::CP437; @@ -161,11 +162,21 @@ namespace Gui mTextures.clear(); for (std::vector::iterator it = mFonts.begin(); it != mFonts.end(); ++it) - MyGUI::ResourceManager::getInstance().removeByName((*it)->getResourceName()); + { + try + { + MyGUI::ResourceManager::getInstance().removeByName((*it)->getResourceName()); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } + } + mFonts.clear(); } - void FontLoader::loadAllFonts(bool exportToFile) + void FontLoader::loadBitmapFonts(bool exportToFile) { const std::map& index = mVFS->getIndex(); @@ -188,6 +199,25 @@ namespace Gui } } + void FontLoader::loadTrueTypeFonts() + { + osgMyGUI::DataManager* dataManager = dynamic_cast(&osgMyGUI::DataManager::getInstance()); + if (!dataManager) + { + Log(Debug::Error) << "Can not load TrueType fonts: osgMyGUI::DataManager is not available."; + return; + } + + const std::string cfg = dataManager->getDataPath(""); + const std::string fontFile = mUserDataPath + "/" + "Fonts" + "/" + "openmw_font.xml"; + if (!boost::filesystem::exists(fontFile)) + return; + + dataManager->setResourcePath(mUserDataPath + "/" + "Fonts"); + MyGUI::ResourceManager::getInstance().load("openmw_font.xml"); + dataManager->setResourcePath(cfg); + } + typedef struct { @@ -470,6 +500,14 @@ namespace Gui font->deserialization(root, MyGUI::Version(3,2,0)); + // Setup "book" version of font as fallback if we will not use TrueType fonts + MyGUI::ResourceManualFont* bookFont = static_cast( + MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); + mFonts.push_back(bookFont); + bookFont->deserialization(root, MyGUI::Version(3,2,0)); + bookFont->setResourceName("Journalbook " + resourceName); + + // Remove automatically registered fonts for (std::vector::iterator it = mFonts.begin(); it != mFonts.end();) { if ((*it)->getResourceName() == font->getResourceName()) @@ -477,10 +515,17 @@ namespace Gui MyGUI::ResourceManager::getInstance().removeByName(font->getResourceName()); it = mFonts.erase(it); } + else if ((*it)->getResourceName() == bookFont->getResourceName()) + { + MyGUI::ResourceManager::getInstance().removeByName(bookFont->getResourceName()); + it = mFonts.erase(it); + } else ++it; } + MyGUI::ResourceManager::getInstance().addResource(font); + MyGUI::ResourceManager::getInstance().addResource(bookFont); } } diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp index b92815f13..39301f9f5 100644 --- a/components/fontloader/fontloader.hpp +++ b/components/fontloader/fontloader.hpp @@ -1,6 +1,9 @@ #ifndef OPENMW_COMPONENTS_FONTLOADER_H #define OPENMW_COMPONENTS_FONTLOADER_H +#include "boost/filesystem/operations.hpp" + +#include #include namespace VFS @@ -23,15 +26,17 @@ namespace Gui class FontLoader { public: - FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs); + FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath); ~FontLoader(); /// @param exportToFile export the converted fonts (Images and XML with glyph metrics) to files? - void loadAllFonts (bool exportToFile); + void loadBitmapFonts (bool exportToFile); + void loadTrueTypeFonts (); private: ToUTF8::FromType mEncoding; const VFS::Manager* mVFS; + std::string mUserDataPath; std::vector mTextures; std::vector mFonts; diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 881687366..4c320879e 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -55,7 +55,7 @@ namespace Interpreter virtual std::string getActionBinding(const std::string& action) const = 0; - virtual std::string getNPCName() const = 0; + virtual std::string getActorName() const = 0; virtual std::string getNPCRace() const = 0; diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 3c6226d9c..0ceed80d5 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -138,7 +138,7 @@ namespace Interpreter{ retval << context.getNPCRace(); } else if((found = check(temp, "name", &i, &start))){ - retval << context.getNPCName(); + retval << context.getActorName(); } } else { // In messagebox or book, not dialogue diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 03b7e186f..77e5a0079 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -65,23 +65,18 @@ namespace Interpreter } else if (notation == ShortestNotation) { - std::string scientific; - std::string fixed; - - out << std::scientific << value; - - scientific = out.str(); + out << value; + std::string standard = out.str(); out.str(std::string()); out.clear(); - out << std::fixed << value; + out << std::scientific << value; + std::string scientific = out.str(); - fixed = out.str(); - - mFormattedMessage += fixed.length() < scientific.length() ? fixed : scientific; + mFormattedMessage += standard.length() < scientific.length() ? standard : scientific; } - else + else { out << std::scientific << value; mFormattedMessage += out.str(); diff --git a/components/misc/constants.hpp b/components/misc/constants.hpp new file mode 100644 index 000000000..7174ae888 --- /dev/null +++ b/components/misc/constants.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_CONSTANTS_H +#define OPENMW_CONSTANTS_H + +namespace Constants +{ + +// The game uses 64 units per yard +const float UnitsPerMeter = 69.99125109f; +const float UnitsPerFoot = 21.33333333f; + +// Sound speed in meters per second +const float SoundSpeedInAir = 343.3f; +const float SoundSpeedUnderwater = 1484.0f; + +// Gravity constant in m/sec^2 +// Note: 8.96 m/sec^2 = 9.8 yards/sec^2 +// Probaly original engine's developers just forgot +// that their engine uses yards instead of meters +// and used standart gravity value as it is +const float GravityConst = 8.96f; + +// Size of one exterior cell in game units +const int CellSizeInUnits = 8192; + +} + +#endif diff --git a/components/misc/weakcache.hpp b/components/misc/weakcache.hpp new file mode 100644 index 000000000..022a722db --- /dev/null +++ b/components/misc/weakcache.hpp @@ -0,0 +1,138 @@ +#ifndef OPENMW_COMPONENTS_WEAKCACHE_HPP +#define OPENMW_COMPONENTS_WEAKCACHE_HPP + +#include +#include +#include + +namespace Misc +{ + /// \class WeakCache + /// Provides a container to weakly store pointers to shared data. + template + class WeakCache + { + public: + using WeakPtr = std::weak_ptr; + using StrongPtr = std::shared_ptr; + using Map = std::unordered_map; + + class iterator + { + public: + iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end); + iterator& operator++(); + bool operator==(const iterator& other); + bool operator!=(const iterator& other); + StrongPtr operator*(); + private: + WeakCache* mCache; + typename Map::iterator mCurrent, mEnd; + StrongPtr mPtr; + }; + + /// Stores a weak pointer to the item. + void insert(Key key, StrongPtr value, bool prune=true); + + /// Retrieves the item associated with the key. + /// \return An item or null. + StrongPtr get(Key key); + + iterator begin(); + iterator end(); + + /// Removes known invalid entries + void prune(); + + private: + Map mData; + std::vector mDirty; + }; + + + template + WeakCache::iterator::iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end) + : mCache(cache) + , mCurrent(current) + , mEnd(end) + { + // Move to 1st available valid item + for ( ; mCurrent != mEnd; ++mCurrent) + { + mPtr = mCurrent->second.lock(); + if (mPtr) break; + else mCache->mDirty.push_back(mCurrent->first); + } + } + + template + typename WeakCache::iterator& WeakCache::iterator::operator++() + { + auto next = mCurrent; + ++next; + return *this = iterator(mCache, next, mEnd); + } + + template + bool WeakCache::iterator::operator==(const iterator& other) + { + return mCurrent == other.mCurrent; + } + + template + bool WeakCache::iterator::operator!=(const iterator& other) + { + return !(*this == other); + } + + template + typename WeakCache::StrongPtr WeakCache::iterator::operator*() + { + return mPtr; + } + + + template + void WeakCache::insert(Key key, StrongPtr value, bool shouldPrune) + { + mData[key] = WeakPtr(value); + if (shouldPrune) prune(); + } + + template + typename WeakCache::StrongPtr WeakCache::get(Key key) + { + auto searchIt = mData.find(key); + if (searchIt != mData.end()) + return searchIt->second.lock(); + else + return StrongPtr(); + } + + template + typename WeakCache::iterator WeakCache::begin() + { + return iterator(this, mData.begin(), mData.end()); + } + + template + typename WeakCache::iterator WeakCache::end() + { + return iterator(this, mData.end(), mData.end()); + } + + template + void WeakCache::prune() + { + // Remove empty entries + for (auto& key : mDirty) + { + auto it = mData.find(key); + if (it != mData.end() && it->second.use_count() == 0) + mData.erase(it); + } + mDirty.clear(); + } +} + +#endif diff --git a/components/myguiplatform/additivelayer.cpp b/components/myguiplatform/additivelayer.cpp index aa2ef08b3..49558cf8e 100644 --- a/components/myguiplatform/additivelayer.cpp +++ b/components/myguiplatform/additivelayer.cpp @@ -27,7 +27,7 @@ namespace osgMyGUI MyGUI::OverlappedLayer::renderToTarget(_target, _update); - renderManager.setInjectState(NULL); + renderManager.setInjectState(nullptr); } } diff --git a/components/myguiplatform/myguidatamanager.cpp b/components/myguiplatform/myguidatamanager.cpp index ddd7ca342..c90e09221 100644 --- a/components/myguiplatform/myguidatamanager.cpp +++ b/components/myguiplatform/myguidatamanager.cpp @@ -24,7 +24,7 @@ MyGUI::IDataStream *DataManager::getData(const std::string &name) if (stream->fail()) { Log(Debug::Error) << "DataManager::getData: Failed to open '" << name << "'"; - return NULL; + return nullptr; } return new MyGUI::DataFileStream(stream.release()); } diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 5ab7ab736..845d0c484 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -52,7 +52,7 @@ public: { public: FrameUpdate() - : mRenderManager(NULL) + : mRenderManager(nullptr) { } @@ -76,7 +76,7 @@ public: { public: CollectDrawCalls() - : mRenderManager(NULL) + : mRenderManager(nullptr) { } @@ -134,9 +134,9 @@ public: { state->bindVertexBufferObject(bufferobject); - glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)NULL); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)NULL + 12); - glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)NULL + 16); + glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)nullptr + 12); + glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr + 16); } else { @@ -355,7 +355,7 @@ RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, R , mUpdate(false) , mIsInitialise(false) , mInvScalingFactor(1.f) - , mInjectState(NULL) + , mInjectState(nullptr) { if (scalingFactor != 0.f) mInvScalingFactor = 1.f / scalingFactor; @@ -537,7 +537,7 @@ void RenderManager::destroyTexture(MyGUI::ITexture *texture) MyGUI::ITexture* RenderManager::getTexture(const std::string &name) { if (name.empty()) - return NULL; + return nullptr; MapTexture::const_iterator item = mTextures.find(name); if(item == mTextures.end()) diff --git a/components/myguiplatform/myguirendermanager.hpp b/components/myguiplatform/myguirendermanager.hpp index 4a0aae3cd..8c9e3e30f 100644 --- a/components/myguiplatform/myguirendermanager.hpp +++ b/components/myguiplatform/myguirendermanager.hpp @@ -98,7 +98,7 @@ public: /** @see IRenderTarget::doRender */ virtual void doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *texture, size_t count); - /** specify a StateSet to inject for rendering. The StateSet will be used by future doRender calls until you reset it to NULL again. */ + /** specify a StateSet to inject for rendering. The StateSet will be used by future doRender calls until you reset it to nullptr again. */ void setInjectState(osg::StateSet* stateSet); /** @see IRenderTarget::getInfo */ diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index 6c53a9699..756893974 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -23,7 +23,7 @@ namespace osgMyGUI } OSGTexture::OSGTexture(osg::Texture2D *texture) - : mImageManager(NULL) + : mImageManager(nullptr) , mTexture(texture) , mFormat(MyGUI::PixelFormat::Unknow) , mUsage(MyGUI::TextureUsage::Default) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 4061247b5..66bbfdb65 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -162,7 +162,7 @@ void NIFFile::parse(Files::IStreamPtr stream) for(size_t i = 0;i < recNum;i++) { - Record *r = NULL; + Record *r = nullptr; std::string rec = nif.getString(); if(rec.empty()) @@ -182,7 +182,7 @@ void NIFFile::parse(Files::IStreamPtr stream) else fail("Unknown record type " + rec); - assert(r != NULL); + assert(r != nullptr); assert(r->recType != RC_MISSING); r->recName = rec; r->recIndex = i; @@ -203,7 +203,7 @@ void NIFFile::parse(Files::IStreamPtr stream) } else { - roots[i] = NULL; + roots[i] = nullptr; warn("Null Root found"); } } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index c32969d1b..d9afbbed7 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -53,7 +53,7 @@ public: boundXYZ = nif->getVector3(); } - parent = NULL; + parent = nullptr; isBone = false; } @@ -64,7 +64,7 @@ public: props.post(nif); } - // Parent node, or NULL for the root node. As far as I'm aware, only + // Parent node, or nullptr for the root node. As far as I'm aware, only // NiNodes (or types derived from NiNodes) can be parents. NiNode *parent; diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 09c380987..e8aa8cb5b 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -40,25 +40,25 @@ public: void post(NIFFile *nif) { if(index < 0) - ptr = NULL; + ptr = nullptr; else { Record *r = nif->getRecord(index); // And cast it ptr = dynamic_cast(r); - assert(ptr != NULL); + assert(ptr != nullptr); } } /// Look up the actual object from the index const X* getPtr() const { - assert(ptr != NULL); + assert(ptr != nullptr); return ptr; } X* getPtr() { - assert(ptr != NULL); + assert(ptr != nullptr); return ptr; } @@ -75,7 +75,7 @@ public: /// Pointers are allowed to be empty bool empty() const - { return ptr == NULL; } + { return ptr == nullptr; } }; /** A list of references to other records. These are read as a list, diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 5239e93fc..7b206e40c 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -25,7 +25,7 @@ namespace osg::Matrixf getWorldTransform(const Nif::Node *node) { - if(node->parent != NULL) + if(node->parent != nullptr) return node->trafo.toMatrix() * getWorldTransform(node->parent); return node->trafo.toMatrix(); } @@ -61,8 +61,8 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) { mShape = new Resource::BulletShape; - mCompoundShape = NULL; - mStaticMesh = NULL; + mCompoundShape = nullptr; + mStaticMesh = nullptr; if (nif.numRoots() < 1) { @@ -71,10 +71,10 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) } Nif::Record *r = nif.getRoot(0); - assert(r != NULL); + assert(r != nullptr); Nif::Node *node = dynamic_cast(r); - if (node == NULL) + if (node == nullptr) { warn("First root in file was not a node, but a " + r->recName + ". Skipping file."); @@ -216,7 +216,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n { // Get the next extra data in the list e = e->extra.getPtr(); - assert(e != NULL); + assert(e != nullptr); if (e->recType == Nif::RC_NiStringExtraData) { @@ -263,7 +263,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf &transform, bool isAnimated) { - assert(shape != NULL); + assert(shape != nullptr); // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 262966e95..83841e0e5 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -409,7 +409,7 @@ FlipController::FlipController(const FlipController ©, const osg::CopyOp &co void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) { - if (hasInput() && mDelta != 0) + if (hasInput() && mDelta != 0 && !mTextures.empty()) { int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3198e995c..eb20b7702 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -200,7 +200,7 @@ namespace NifOsg } const Nif::Record *r = nif->getRoot(0); - assert(r != NULL); + assert(r != nullptr); if(r->recType != Nif::RC_NiSequenceStreamHelper) { @@ -257,12 +257,12 @@ namespace NifOsg const Nif::Record* r = nif->getRoot(0); const Nif::Node* nifNode = dynamic_cast(r); - if (nifNode == NULL) + if (nifNode == nullptr) nif->fail("First root was not a node, but a " + r->recName); osg::ref_ptr textkeys (new TextKeyMapHolder); - osg::ref_ptr created = handleNode(nifNode, NULL, imageManager, std::vector(), 0, false, false, false, &textkeys->mTextKeys); + osg::ref_ptr created = handleNode(nifNode, nullptr, imageManager, std::vector(), 0, false, false, false, &textkeys->mTextKeys); if (nif->getUseSkinning()) { @@ -297,7 +297,7 @@ namespace NifOsg { // Get the lowest numbered recIndex of the NiTexturingProperty root node. // This is what is overridden when a spell effect "particle texture" is used. - if (nifNode->parent == NULL && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty) + if (nifNode->parent == nullptr && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty) { mFirstRootTextureIndex = props[i].getPtr()->recIndex; mFoundFirstRootTexturingProperty = true; @@ -338,7 +338,7 @@ namespace NifOsg osg::ref_ptr handleSourceTexture(const Nif::NiSourceTexture* st, Resource::ImageManager* imageManager) { if (!st) - return NULL; + return nullptr; osg::ref_ptr image; if (!st->external && !st->data.empty()) @@ -464,10 +464,10 @@ namespace NifOsg } osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager, - std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=NULL) + std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=nullptr) { - if (rootNode != NULL && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) - return NULL; + if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) + return nullptr; osg::ref_ptr node = createNode(nifNode); @@ -791,7 +791,7 @@ namespace NifOsg // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors. void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) { - const Nif::NiAutoNormalParticlesData *particledata = NULL; + const Nif::NiAutoNormalParticlesData *particledata = nullptr; if(nifNode->recType == Nif::RC_NiAutoNormalParticles) particledata = static_cast(nifNode)->data.getPtr(); else if(nifNode->recType == Nif::RC_NiRotatingParticles) @@ -873,7 +873,7 @@ namespace NifOsg osg::ref_ptr partsys (new ParticleSystem); partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); - const Nif::NiParticleSystemController* partctrl = NULL; + const Nif::NiParticleSystemController* partctrl = nullptr; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) @@ -1193,11 +1193,11 @@ namespace NifOsg break; default: Log(Debug::Info) << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename; - return NULL; + return nullptr; } if (pixelData->mipmaps.empty()) - return NULL; + return nullptr; int width = 0; int height = 0; @@ -1211,7 +1211,7 @@ namespace NifOsg if (mipSize + mip.dataOffset > pixelData->data.size()) { Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture"; - return NULL; + return nullptr; } if (i != 0) @@ -1226,7 +1226,7 @@ namespace NifOsg if (width <= 0 || height <= 0) { Log(Debug::Info) << "Internal Texture Width and height must be non zero, ignoring texture"; - return NULL; + return nullptr; } unsigned char* data = new unsigned char[pixelData->data.size()]; diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 03e836a0f..f5c055a15 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -39,7 +39,7 @@ osgParticle::Particle* ParticleSystem::createParticle(const osgParticle::Particl { if (numParticles()-numDeadParticles() < mQuota) return osgParticle::ParticleSystem::createParticle(ptemplate); - return NULL; + return nullptr; } void InverseWorldMatrix::operator()(osg::Node *node, osg::NodeVisitor *nv) @@ -312,7 +312,7 @@ void Emitter::emitParticles(double dt) FindGroupByRecIndex::FindGroupByRecIndex(int recIndex) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - , mFound(NULL) + , mFound(nullptr) , mRecIndex(recIndex) { } diff --git a/components/resource/bulletshape.cpp b/components/resource/bulletshape.cpp index dbdbf0c6e..15a747dd3 100644 --- a/components/resource/bulletshape.cpp +++ b/components/resource/bulletshape.cpp @@ -12,7 +12,7 @@ namespace Resource { BulletShape::BulletShape() - : mCollisionShape(NULL) + : mCollisionShape(nullptr) { } @@ -32,7 +32,7 @@ BulletShape::~BulletShape() void BulletShape::deleteShape(btCollisionShape* shape) { - if(shape!=NULL) + if(shape!=nullptr) { if(shape->isCompound()) { diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index 622506d6b..4193cd5b4 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -24,7 +24,7 @@ namespace Resource struct GetTriangleFunctor { GetTriangleFunctor() - : mTriMesh(NULL) + : mTriMesh(nullptr) { } diff --git a/components/resource/multiobjectcache.hpp b/components/resource/multiobjectcache.hpp index 527247bf9..b73e29d50 100644 --- a/components/resource/multiobjectcache.hpp +++ b/components/resource/multiobjectcache.hpp @@ -30,7 +30,7 @@ namespace Resource void addEntryToObjectCache(const std::string& filename, osg::Object* object); - /** Take an Object from cache. Return NULL if no object found. */ + /** Take an Object from cache. Return nullptr if no object found. */ osg::ref_ptr takeFromObjectCache(const std::string& fileName); /** call releaseGLObjects on all objects attached to the object cache.*/ diff --git a/components/sceneutil/actorutil.cpp b/components/sceneutil/actorutil.cpp new file mode 100644 index 000000000..988a61f60 --- /dev/null +++ b/components/sceneutil/actorutil.cpp @@ -0,0 +1,30 @@ +#include "actorutil.hpp" + +namespace SceneUtil +{ + std::string getActorSkeleton(bool firstPerson, bool isFemale, bool isBeast, bool isWerewolf) + { + if (!firstPerson) + { + if (isWerewolf) + return "meshes\\wolf\\skin.nif"; + else if (isBeast) + return "meshes\\base_animkna.nif"; + else if (isFemale) + return "meshes\\base_anim_female.nif"; + else + return "meshes\\base_anim.nif"; + } + else + { + if (isWerewolf) + return "meshes\\wolf\\skin.1st.nif"; + else if (isBeast) + return "meshes\\base_animkna.1st.nif"; + else if (isFemale) + return "meshes\\base_anim_female.1st.nif"; + else + return "meshes\\base_anim.1st.nif"; + } + } +} diff --git a/components/sceneutil/actorutil.hpp b/components/sceneutil/actorutil.hpp new file mode 100644 index 000000000..7bdbbaa92 --- /dev/null +++ b/components/sceneutil/actorutil.hpp @@ -0,0 +1,11 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP +#define OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP + +#include + +namespace SceneUtil +{ + std::string getActorSkeleton(bool firstPerson, bool female, bool beast, bool werewolf); +} + +#endif diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index 08f36cfcf..99dd7bad3 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -26,7 +26,7 @@ namespace SceneUtil osg::StateSet* CopyOp::operator ()(const osg::StateSet* stateset) const { if (!stateset) - return NULL; + return nullptr; if (stateset->getDataVariance() == osg::StateSet::DYNAMIC) return osg::clone(stateset, *this); return const_cast(stateset); diff --git a/components/sceneutil/controller.cpp b/components/sceneutil/controller.cpp index 8fef00333..dfc72918a 100644 --- a/components/sceneutil/controller.cpp +++ b/components/sceneutil/controller.cpp @@ -19,7 +19,7 @@ namespace SceneUtil bool Controller::hasInput() const { - return mSource.get() != NULL; + return mSource.get() != nullptr; } float Controller::getInputValue(osg::NodeVisitor* nv) diff --git a/components/sceneutil/lightcontroller.cpp b/components/sceneutil/lightcontroller.cpp index e3ea93843..199b408d7 100644 --- a/components/sceneutil/lightcontroller.cpp +++ b/components/sceneutil/lightcontroller.cpp @@ -26,7 +26,7 @@ namespace float v = 0.0f; for(int i = 0;i < 3;++i) - v += std::sin(fb*time*f[i] + o[1])*m[i]; + v += std::sin(fb*time*f[i] + o[i])*m[i]; return v * s; } @@ -73,17 +73,15 @@ namespace SceneUtil float cycle_time; float time_distortion; - const float pi = 3.14159265359; - if(mType == LT_Pulse || mType == LT_PulseSlow) { - cycle_time = 2.0f * pi; - time_distortion = mType == LT_Pulse ? 20.0f : 4.f; + cycle_time = 2.0f * osg::PI; + time_distortion = 3.0f; } else { - static const float fa = 0.785398f; - static const float phase_wavelength = 120.0f * pi / fa; + static const float fa = osg::PI / 4.0f; + static const float phase_wavelength = 120.0f * osg::PI / fa; cycle_time = 500.0f; mPhase = std::fmod(mPhase + dt, phase_wavelength); @@ -94,12 +92,14 @@ namespace SceneUtil if(mDirection > 0 && mDeltaCount > +cycle_time) { mDirection = -1.0f; - mDeltaCount = 2.0f*cycle_time - mDeltaCount; + float extra = mDeltaCount - cycle_time; + mDeltaCount -= 2*extra; } if(mDirection < 0 && mDeltaCount < -cycle_time) { mDirection = +1.0f; - mDeltaCount = -2.0f*cycle_time - mDeltaCount; + float extra = cycle_time - mDeltaCount; + mDeltaCount += 2*extra; } static const float fast = 4.0f/1.0f; diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 018abeefc..e102653b9 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -102,7 +102,7 @@ namespace SceneUtil if (LightManager* lightManager = dynamic_cast(path[i])) return lightManager; } - return NULL; + return nullptr; } // Set on a LightSource. Adds the light source to its light manager for the current frame. @@ -299,7 +299,7 @@ namespace SceneUtil virtual osg::Object* cloneType() const { return new DisableLight(mIndex); } virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new DisableLight(*this,copyop); } - virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=nullptr; } virtual const char* libraryName() const { return "SceneUtil"; } virtual const char* className() const { return "DisableLight"; } virtual Type getType() const { return LIGHT; } @@ -323,17 +323,17 @@ namespace SceneUtil virtual void apply(osg::State& state) const { int lightNum = GL_LIGHT0 + mIndex; - glLightfv( lightNum, GL_AMBIENT, mNull.ptr() ); - glLightfv( lightNum, GL_DIFFUSE, mNull.ptr() ); - glLightfv( lightNum, GL_SPECULAR, mNull.ptr() ); + glLightfv( lightNum, GL_AMBIENT, mnullptr.ptr() ); + glLightfv( lightNum, GL_DIFFUSE, mnullptr.ptr() ); + glLightfv( lightNum, GL_SPECULAR, mnullptr.ptr() ); LightStateCache* cache = getLightStateCache(state.getContextID()); - cache->lastAppliedLight[mIndex] = NULL; + cache->lastAppliedLight[mIndex] = nullptr; } private: unsigned int mIndex; - osg::Vec4f mNull; + osg::Vec4f mnullptr; }; void LightManager::setStartLight(int start) @@ -447,7 +447,7 @@ namespace SceneUtil { unsigned int maxLights = static_cast (8 - mLightManager->getStartLight()); - osg::StateSet* stateset = NULL; + osg::StateSet* stateset = nullptr; if (mLightList.size() > maxLights) { diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index 55d0c69cd..d35aa6058 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -158,7 +158,7 @@ namespace SceneUtil { public: LightListCallback() - : mLightManager(NULL) + : mLightManager(nullptr) , mLastFrameNumber(0) {} LightListCallback(const LightListCallback& copy, const osg::CopyOp& copyop) diff --git a/components/sceneutil/lightutil.cpp b/components/sceneutil/lightutil.cpp index 5ad9bd6dc..d44724e8f 100644 --- a/components/sceneutil/lightutil.cpp +++ b/components/sceneutil/lightutil.cpp @@ -45,7 +45,7 @@ namespace SceneUtil SceneUtil::FindByNameVisitor visitor("AttachLight"); node->accept(visitor); - osg::Group* attachTo = NULL; + osg::Group* attachTo = nullptr; if (visitor.mFoundNode) { attachTo = visitor.mFoundNode; diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index b86744599..5d50c369d 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -166,7 +166,7 @@ class CollectLowestTransformsVisitor : public BaseOptimizerVisitor } else { - // for all current objects mark a NULL transform for them. + // for all current objects mark a nullptr transform for them. registerWithCurrentObjects(0); } } @@ -826,7 +826,7 @@ void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Transform& transform) isOperationPermissible(transform)) { osg::Matrix matrix; - transform.computeWorldToLocalMatrix(matrix,NULL); + transform.computeWorldToLocalMatrix(matrix,nullptr); if (matrix.isIdentity()) { _redundantNodeList.insert(&transform); @@ -1000,7 +1000,7 @@ struct LessGeometryPrimitiveType }; -/// Shortcut to get size of an array, even if pointer is NULL. +/// Shortcut to get size of an array, even if pointer is nullptr. inline unsigned int getSize(const osg::Array * a) { return a ? a->getNumElements() : 0; } /// When merging geometries, tests if two arrays can be merged, regarding to their number of components, and the number of vertices. @@ -1170,7 +1170,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) MergeList::iterator eachMergeList=mergeListTmp.begin(); for(;eachMergeList!=mergeListTmp.end();++eachMergeList) { - if (!eachMergeList->empty() && eachMergeList->front()!=NULL + if (!eachMergeList->empty() && eachMergeList->front()!=nullptr && isAbleToMerge(*eachMergeList->front(),*geomToPush)) { eachMergeList->push_back(geomToPush); diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index f98481ef8..742a64475 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -8,11 +8,36 @@ #include "skeleton.hpp" #include "util.hpp" +namespace +{ + inline void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, const float weight, osg::Matrixf& result) + { + osg::Matrixf m = invBindMatrix * matrix; + float* ptr = m.ptr(); + float* ptrresult = result.ptr(); + ptrresult[0] += ptr[0] * weight; + ptrresult[1] += ptr[1] * weight; + ptrresult[2] += ptr[2] * weight; + + ptrresult[4] += ptr[4] * weight; + ptrresult[5] += ptr[5] * weight; + ptrresult[6] += ptr[6] * weight; + + ptrresult[8] += ptr[8] * weight; + ptrresult[9] += ptr[9] * weight; + ptrresult[10] += ptr[10] * weight; + + ptrresult[12] += ptr[12] * weight; + ptrresult[13] += ptr[13] * weight; + ptrresult[14] += ptr[14] * weight; + } +} + namespace SceneUtil { RigGeometry::RigGeometry() - : mSkeleton(NULL) + : mSkeleton(nullptr) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { @@ -22,7 +47,7 @@ RigGeometry::RigGeometry() RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) : Drawable(copy, copyop) - , mSkeleton(NULL) + , mSkeleton(nullptr) , mInfluenceMap(copy.mInfluenceMap) , mLastFrameNumber(0) , mBoundsFirstFrame(true) @@ -75,7 +100,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX); } else - mSourceTangents = NULL; + mSourceTangents = nullptr; } } @@ -143,28 +168,6 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv) return true; } -void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, float weight, osg::Matrixf& result) -{ - osg::Matrixf m = invBindMatrix * matrix; - float* ptr = m.ptr(); - float* ptrresult = result.ptr(); - ptrresult[0] += ptr[0] * weight; - ptrresult[1] += ptr[1] * weight; - ptrresult[2] += ptr[2] * weight; - - ptrresult[4] += ptr[4] * weight; - ptrresult[5] += ptr[5] * weight; - ptrresult[6] += ptr[6] * weight; - - ptrresult[8] += ptr[8] * weight; - ptrresult[9] += ptr[9] * weight; - ptrresult[10] += ptr[10] * weight; - - ptrresult[12] += ptr[12] * weight; - ptrresult[13] += ptr[13] * weight; - ptrresult[14] += ptr[14] * weight; -} - void RigGeometry::cull(osg::NodeVisitor* nv) { if (!mSkeleton) @@ -175,7 +178,8 @@ void RigGeometry::cull(osg::NodeVisitor* nv) return; } - if ((!mSkeleton->getActive() && mLastFrameNumber != 0) || mLastFrameNumber == nv->getTraversalNumber()) + unsigned int traversalNumber = nv->getTraversalNumber(); + if (mLastFrameNumber == traversalNumber || (mLastFrameNumber != 0 && !mSkeleton->getActive())) { osg::Geometry& geom = *getGeometry(mLastFrameNumber); nv->pushOntoNodePath(&geom); @@ -183,10 +187,10 @@ void RigGeometry::cull(osg::NodeVisitor* nv) nv->popFromNodePath(); return; } - mLastFrameNumber = nv->getTraversalNumber(); + mLastFrameNumber = traversalNumber; osg::Geometry& geom = *getGeometry(mLastFrameNumber); - mSkeleton->updateBoneMatrices(nv->getTraversalNumber()); + mSkeleton->updateBoneMatrices(traversalNumber); // skinning const osg::Vec3Array* positionSrc = static_cast(mSourceGeometry->getVertexArray()); @@ -197,34 +201,31 @@ void RigGeometry::cull(osg::NodeVisitor* nv) osg::Vec3Array* normalDst = static_cast(geom.getNormalArray()); osg::Vec4Array* tangentDst = static_cast(geom.getTexCoordArray(7)); - for (Bone2VertexMap::const_iterator it = mBone2VertexMap.begin(); it != mBone2VertexMap.end(); ++it) + for (auto &pair : mBone2VertexMap) { - osg::Matrixf resultMat (0, 0, 0, 0, + osg::Matrixf resultMat (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); - for (std::vector::const_iterator weightIt = it->first.begin(); weightIt != it->first.end(); ++weightIt) + for (auto &weight : pair.first) { - Bone* bone = weightIt->first.first; - const osg::Matrix& invBindMatrix = weightIt->first.second; - float weight = weightIt->second; - const osg::Matrixf& boneMatrix = bone->mMatrixInSkeletonSpace; - accumulateMatrix(invBindMatrix, boneMatrix, weight, resultMat); + accumulateMatrix(weight.first.second, weight.first.first->mMatrixInSkeletonSpace, weight.second, resultMat); } + if (mGeomToSkelMatrix) resultMat *= (*mGeomToSkelMatrix); - for (std::vector::const_iterator vertexIt = it->second.begin(); vertexIt != it->second.end(); ++vertexIt) + for (auto &vertex : pair.second) { - unsigned short vertex = *vertexIt; (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); if (normalDst) - (*normalDst)[vertex] = osg::Matrix::transform3x3((*normalSrc)[vertex], resultMat); + (*normalDst)[vertex] = osg::Matrixf::transform3x3((*normalSrc)[vertex], resultMat); + if (tangentDst) { - osg::Vec4f srcTangent = (*tangentSrc)[vertex]; - osg::Vec3f transformedTangent = osg::Matrix::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat); + const osg::Vec4f& srcTangent = (*tangentSrc)[vertex]; + osg::Vec3f transformedTangent = osg::Matrixf::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat); (*tangentDst)[vertex] = osg::Vec4f(transformedTangent, srcTangent.w()); } } @@ -305,7 +306,7 @@ void RigGeometry::updateGeomToSkelMatrix(const osg::NodePath& nodePath) { if (!geomToSkelMatrix) geomToSkelMatrix = new osg::RefMatrix; - trans->computeWorldToLocalMatrix(*geomToSkelMatrix, NULL); + trans->computeWorldToLocalMatrix(*geomToSkelMatrix, nullptr); } } } diff --git a/components/sceneutil/skeleton.cpp b/components/sceneutil/skeleton.cpp index 58ed9a27c..9be440d93 100644 --- a/components/sceneutil/skeleton.cpp +++ b/components/sceneutil/skeleton.cpp @@ -64,7 +64,7 @@ Bone* Skeleton::getBone(const std::string &name) BoneCache::iterator found = mBoneCache.find(Misc::StringUtils::lowerCase(name)); if (found == mBoneCache.end()) - return NULL; + return nullptr; // find or insert in the bone hierarchy @@ -81,7 +81,7 @@ Bone* Skeleton::getBone(const std::string &name) if (!matrixTransform) continue; - Bone* child = NULL; + Bone* child = nullptr; for (unsigned int i=0; imChildren.size(); ++i) { if (bone->mChildren[i]->mNode == *it) @@ -117,7 +117,7 @@ void Skeleton::updateBoneMatrices(unsigned int traversalNumber) if (mRootBone.get()) { for (unsigned int i=0; imChildren.size(); ++i) - mRootBone->mChildren[i]->update(NULL); + mRootBone->mChildren[i]->update(nullptr); } mNeedToUpdateBoneMatrices = false; @@ -167,7 +167,7 @@ void Skeleton::childRemoved(unsigned int, unsigned int) } Bone::Bone() - : mNode(NULL) + : mNode(nullptr) { } diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 5a5ecaded..121cdaca6 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -30,8 +30,8 @@ namespace SceneUtil void StateSetUpdater::reset() { - mStateSets[0] = NULL; - mStateSets[1] = NULL; + mStateSets[0] = nullptr; + mStateSets[1] = nullptr; } StateSetUpdater::StateSetUpdater() diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp index 42ea31133..21ba836c0 100644 --- a/components/sceneutil/unrefqueue.cpp +++ b/components/sceneutil/unrefqueue.cpp @@ -1,28 +1,15 @@ #include "unrefqueue.hpp" -#include - //#include //#include -#include namespace SceneUtil { - - class UnrefWorkItem : public SceneUtil::WorkItem + void UnrefWorkItem::doWork() { - public: - std::deque > mObjects; - - virtual void doWork() - { - //osg::Timer timer; - //size_t objcount = mObjects.size(); - mObjects.clear(); - //Log(Debug::Verbose) << "cleared " << objcount << " objects in " << timer.time_m(); - } - }; + mObjects.clear(); + } UnrefQueue::UnrefQueue() { diff --git a/components/sceneutil/unrefqueue.hpp b/components/sceneutil/unrefqueue.hpp index 4a2724927..7155e669c 100644 --- a/components/sceneutil/unrefqueue.hpp +++ b/components/sceneutil/unrefqueue.hpp @@ -1,13 +1,23 @@ #ifndef OPENMW_COMPONENTS_UNREFQUEUE_H #define OPENMW_COMPONENTS_UNREFQUEUE_H +#include + #include #include +#include + namespace SceneUtil { class WorkQueue; - class UnrefWorkItem; + + class UnrefWorkItem : public SceneUtil::WorkItem + { + public: + std::deque > mObjects; + virtual void doWork(); + }; /// @brief Handles unreferencing of objects through the WorkQueue. Typical use scenario /// would be the main thread pushing objects that are no longer needed, and the background thread deleting them. diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 2f6123e34..3c3559a08 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,9 +1,12 @@ #include "visitor.hpp" +#include #include #include +#include + #include namespace SceneUtil @@ -23,7 +26,7 @@ namespace SceneUtil { if (Misc::StringUtils::ciEqual(node.className(), mNameToFind)) mFoundNodes.push_back(&node); - + traverse(node); } @@ -54,4 +57,103 @@ namespace SceneUtil partsys->setFreezeOnCull(false); } + void NodeMapVisitor::apply(osg::MatrixTransform& trans) + { + // Take transformation for first found node in file + const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName()); + if (mMap.find(nodeName) == mMap.end()) + { + mMap[nodeName] = &trans; + } + + traverse(trans); + } + + void RemoveVisitor::remove() + { + for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) + { + if (!it->second->removeChild(it->first)) + Log(Debug::Error) << "error removing " << it->first->getName(); + } + } + + void CleanObjectRootVisitor::apply(osg::Drawable& drw) + { + applyDrawable(drw); + } + + void CleanObjectRootVisitor::apply(osg::Group& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::apply(osg::MatrixTransform& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::apply(osg::Node& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::applyNode(osg::Node& node) + { + if (node.getStateSet()) + node.setStateSet(nullptr); + + if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) + mToRemove.push_back(std::make_pair(&node, node.getParent(0))); + else + traverse(node); + } + + void CleanObjectRootVisitor::applyDrawable(osg::Node& node) + { + osg::NodePath::iterator parent = getNodePath().end()-2; + // We know that the parent is a Group because only Groups can have children. + osg::Group* parentGroup = static_cast(*parent); + + // Try to prune nodes that would be empty after the removal + if (parent != getNodePath().begin()) + { + // This could be extended to remove the parent's parent, and so on if they are empty as well. + // But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance. + osg::Group* parentParent = static_cast(*(parent - 1)); + if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC) + { + mToRemove.push_back(std::make_pair(parentGroup, parentParent)); + return; + } + } + + mToRemove.push_back(std::make_pair(&node, parentGroup)); + } + + void RemoveTriBipVisitor::apply(osg::Drawable& drw) + { + applyImpl(drw); + } + + void RemoveTriBipVisitor::apply(osg::Group& node) + { + traverse(node); + } + + void RemoveTriBipVisitor::apply(osg::MatrixTransform& node) + { + traverse(node); + } + + void RemoveTriBipVisitor::applyImpl(osg::Node& node) + { + const std::string toFind = "tri bip"; + if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0) + { + osg::Group* parent = static_cast(*(getNodePath().end()-2)); + // Not safe to remove in apply(), since the visitor is still iterating the child list + mToRemove.push_back(std::make_pair(&node, parent)); + } + } } diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index 265fd6d02..02a351da7 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H #define OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H +#include #include // Commonly used scene graph visitors @@ -8,14 +9,14 @@ namespace SceneUtil { // Find a Group by name, case-insensitive - // If not found, mFoundNode will be NULL + // If not found, mFoundNode will be nullptr class FindByNameVisitor : public osg::NodeVisitor { public: FindByNameVisitor(const std::string& nameToFind) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mNameToFind(nameToFind) - , mFoundNode(NULL) + , mFoundNode(nullptr) { } @@ -58,6 +59,65 @@ namespace SceneUtil virtual void apply(osg::Drawable& drw); }; + /// Maps names to nodes + class NodeMapVisitor : public osg::NodeVisitor + { + public: + typedef std::map > NodeMap; + + NodeMapVisitor(NodeMap& map) + : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + , mMap(map) + { + } + + void apply(osg::MatrixTransform& trans); + + private: + NodeMap& mMap; + }; + + /// @brief Base class for visitors that remove nodes from a scene graph. + /// Subclasses need to fill the mToRemove vector. + /// To use, node->accept(removeVisitor); removeVisitor.remove(); + class RemoveVisitor : public osg::NodeVisitor + { + public: + RemoveVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { + } + + void remove(); + + protected: + // + typedef std::vector > RemoveVec; + std::vector > mToRemove; + }; + + // Removes all drawables from a graph. + class CleanObjectRootVisitor : public RemoveVisitor + { + public: + virtual void apply(osg::Drawable& drw); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + virtual void apply(osg::Node& node); + + void applyNode(osg::Node& node); + void applyDrawable(osg::Node& node); + }; + + class RemoveTriBipVisitor : public RemoveVisitor + { + public: + virtual void apply(osg::Drawable& drw); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + + void applyImpl(osg::Node& node); + }; } #endif diff --git a/components/sceneutil/workqueue.cpp b/components/sceneutil/workqueue.cpp index 37e8563e1..0ca5e4d3e 100644 --- a/components/sceneutil/workqueue.cpp +++ b/components/sceneutil/workqueue.cpp @@ -97,7 +97,7 @@ osg::ref_ptr WorkQueue::removeWorkItem() return item; } else - return NULL; + return nullptr; } unsigned int WorkQueue::getNumItems() const diff --git a/components/sceneutil/workqueue.hpp b/components/sceneutil/workqueue.hpp index 7de6a5af5..2084df328 100644 --- a/components/sceneutil/workqueue.hpp +++ b/components/sceneutil/workqueue.hpp @@ -57,7 +57,7 @@ namespace SceneUtil void addWorkItem(osg::ref_ptr item, bool front=false); /// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added. - /// If the workqueue is in the process of being destroyed, may return NULL. + /// If the workqueue is in the process of being destroyed, may return nullptr. /// @par Used internally by the WorkThread. osg::ref_ptr removeWorkItem(); diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index 1560b74b3..6d2117575 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -190,7 +190,7 @@ namespace CursorDecompression SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); SDL_Texture *cursorTexture = SDL_CreateTextureFromSurface(renderer, cursorSurface); - SDL_RenderCopyEx(renderer, cursorTexture, NULL, NULL, -rotDegrees, NULL, SDL_FLIP_VERTICAL); + SDL_RenderCopyEx(renderer, cursorTexture, nullptr, nullptr, -rotDegrees, nullptr, SDL_FLIP_VERTICAL); SDL_DestroyTexture(cursorTexture); SDL_FreeSurface(cursorSurface); diff --git a/components/sdlutil/sdlgraphicswindow.cpp b/components/sdlutil/sdlgraphicswindow.cpp index f9767238f..dc6129e43 100644 --- a/components/sdlutil/sdlgraphicswindow.cpp +++ b/components/sdlutil/sdlgraphicswindow.cpp @@ -77,7 +77,7 @@ void GraphicsWindowSDL2::init() return; WindowData *inheritedWindowData = dynamic_cast(_traits->inheritedWindowData.get()); - mWindow = inheritedWindowData ? inheritedWindowData->mWindow : NULL; + mWindow = inheritedWindowData ? inheritedWindowData->mWindow : nullptr; mOwnsWindow = (mWindow == 0); if(mOwnsWindow) @@ -162,7 +162,7 @@ bool GraphicsWindowSDL2::releaseContextImplementation() return false; } - return SDL_GL_MakeCurrent(NULL, NULL)==0; + return SDL_GL_MakeCurrent(nullptr, nullptr)==0; } @@ -170,11 +170,11 @@ void GraphicsWindowSDL2::closeImplementation() { if(mContext) SDL_GL_DeleteContext(mContext); - mContext = NULL; + mContext = nullptr; if(mWindow && mOwnsWindow) SDL_DestroyWindow(mWindow); - mWindow = NULL; + mWindow = nullptr; mValid = false; mRealized = false; diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 5fc828fb5..f7111fdf8 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -10,10 +10,10 @@ namespace SDLUtil InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr viewer, bool grab) : mSDLWindow(window), mViewer(viewer), - mMouseListener(NULL), - mKeyboardListener(NULL), - mWindowListener(NULL), - mConListener(NULL), + mMouseListener(nullptr), + mKeyboardListener(nullptr), + mWindowListener(nullptr), + mConListener(nullptr), mWarpX(0), mWarpY(0), mWarpCompensate(false), @@ -234,7 +234,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v bool InputWrapper::isKeyDown(SDL_Scancode key) { - return (SDL_GetKeyboardState(NULL)[key]) != 0; + return (SDL_GetKeyboardState(nullptr)[key]) != 0; } /// \brief Moves the mouse to the specified point within the viewport diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 2ede0abc2..0f4dc2e73 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -285,7 +285,7 @@ namespace Shader if (stream.fail()) { Log(Debug::Error) << "Failed to open " << p.string(); - return NULL; + return nullptr; } std::stringstream buffer; buffer << stream.rdbuf(); @@ -293,7 +293,7 @@ namespace Shader // parse includes std::string source = buffer.str(); if (!addLineDirectivesAfterConditionalBlocks(source) || !parseIncludes(boost::filesystem::path(mPath), source)) - return NULL; + return nullptr; templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, source)).first; } @@ -306,7 +306,7 @@ namespace Shader { // Add to the cache anyway to avoid logging the same error over and over. mShaders.insert(std::make_pair(std::make_pair(shaderTemplate, defines), nullptr)); - return NULL; + return nullptr; } osg::ref_ptr shader (new osg::Shader(shaderType)); diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index 02f35975d..05775edb6 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -28,7 +28,7 @@ namespace Shader /// @param shaderTemplate The filename of the shader template. /// @param defines Define values that can be retrieved by the shader template. /// @param shaderType The type of shader (usually vertex or fragment shader). - /// @note May return NULL on failure. + /// @note May return nullptr on failure. /// @note Thread safe. osg::ref_ptr getShader(const std::string& shaderTemplate, const DefineMap& defines, osg::Shader::Type shaderType); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index cbd950ea3..a0a6832d0 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -28,7 +28,7 @@ namespace Shader , mMaterialOverridden(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) - , mNode(NULL) + , mNode(nullptr) { } @@ -102,15 +102,15 @@ namespace Shader void ShaderVisitor::applyStateSet(osg::ref_ptr stateset, osg::Node& node) { - osg::StateSet* writableStateSet = NULL; + osg::StateSet* writableStateSet = nullptr; if (mAllowedToModifyStateSets) writableStateSet = node.getStateSet(); const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); if (!texAttributes.empty()) { - const osg::Texture* diffuseMap = NULL; - const osg::Texture* normalMap = NULL; - const osg::Texture* specularMap = NULL; + const osg::Texture* diffuseMap = nullptr; + const osg::Texture* normalMap = nullptr; + const osg::Texture* specularMap = nullptr; for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); @@ -153,7 +153,7 @@ namespace Shader } } - if (mAutoUseNormalMaps && diffuseMap != NULL && normalMap == NULL && diffuseMap->getImage(0)) + if (mAutoUseNormalMaps && diffuseMap != nullptr && normalMap == nullptr && diffuseMap->getImage(0)) { std::string normalMapFileName = diffuseMap->getImage(0)->getFileName(); @@ -195,7 +195,7 @@ namespace Shader mRequirements.back().mNormalHeight = normalHeight; } } - if (mAutoUseSpecularMaps && diffuseMap != NULL && specularMap == NULL && diffuseMap->getImage(0)) + if (mAutoUseSpecularMaps && diffuseMap != nullptr && specularMap == nullptr && diffuseMap->getImage(0)) { std::string specularMapFileName = diffuseMap->getImage(0)->getFileName(); boost::replace_last(specularMapFileName, ".", mSpecularMapPattern + "."); @@ -254,7 +254,7 @@ namespace Shader return; osg::Node& node = *reqs.mNode; - osg::StateSet* writableStateSet = NULL; + osg::StateSet* writableStateSet = nullptr; if (mAllowedToModifyStateSets) writableStateSet = node.getOrCreateStateSet(); else @@ -321,7 +321,7 @@ namespace Shader // make sure that all UV sets are there for (std::map::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it) { - if (sourceGeometry.getTexCoordArray(it->first) == NULL) + if (sourceGeometry.getTexCoordArray(it->first) == nullptr) { sourceGeometry.setTexCoordArray(it->first, sourceGeometry.getTexCoordArray(0)); changed = true; @@ -342,7 +342,7 @@ namespace Shader void ShaderVisitor::apply(osg::Geometry& geometry) { - bool needPop = (geometry.getStateSet() != NULL); + bool needPop = (geometry.getStateSet() != nullptr); if (geometry.getStateSet()) // TODO: check if stateset affects shader permutation before pushing it { pushRequirements(geometry); @@ -365,7 +365,7 @@ namespace Shader void ShaderVisitor::apply(osg::Drawable& drawable) { // non-Geometry drawable (e.g. particle system) - bool needPop = (drawable.getStateSet() != NULL); + bool needPop = (drawable.getStateSet() != nullptr); if (drawable.getStateSet()) { diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 7575113ef..b23b0b76c 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -22,7 +22,7 @@ namespace Terrain { ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer) - : ResourceManager(NULL) + : ResourceManager(nullptr) , mStorage(storage) , mSceneManager(sceneMgr) , mTextureManager(textureManager) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index d3fdfa1f8..3dc0aa41c 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -45,7 +45,7 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const CompositeMap* node = *mImmediateCompileSet.begin(); mCompiled.insert(node); - compile(*node, renderInfo, NULL); + compile(*node, renderInfo, nullptr); mImmediateCompileSet.erase(mImmediateCompileSet.begin()); } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index f4fc8df89..2222cbb84 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -41,7 +41,7 @@ bool adjacent(ChildDirection dir, Direction dir2) QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir) { if (currentNode->getDirection() == Root) - return NULL; // Arrived at root node, the root node does not have neighbours + return nullptr; // Arrived at root node, the root node does not have neighbours QuadTreeNode* nextNode; if (adjacent(currentNode->getDirection(), dir)) @@ -52,7 +52,7 @@ QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir) if (nextNode && nextNode->getNumChildren()) return nextNode->getChild(reflect(currentNode->getDirection(), dir)); else - return NULL; + return nullptr; } QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float size, const osg::Vec2f& center) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index c4062d51d..0d5b25697 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -4,6 +4,7 @@ #include +#include #include #include "quadtreenode.hpp" @@ -83,7 +84,7 @@ public: { float dist = distanceToBox(node->getBoundingBox(), eyePoint); int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); - int lodLevel = Log2(static_cast(dist/(8192*mMinSize))); + int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize))); return nativeLodLevel <= lodLevel; } @@ -96,8 +97,8 @@ class RootNode : public QuadTreeNode { public: RootNode(float size, const osg::Vec2f& center) - : QuadTreeNode(NULL, Root, size, center) - , mWorld(NULL) + : QuadTreeNode(nullptr, Root, size, center) + , mWorld(nullptr) { } @@ -317,7 +318,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunk unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); if (lodFlags != entry.mLodFlags) { - entry.mRenderingNode = NULL; + entry.mRenderingNode = nullptr; entry.mLodFlags = lodFlags; } } @@ -379,7 +380,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (udc && udc->getUserData()) { mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); - udc->setUserData(NULL); + udc->setUserData(nullptr); } entry.mRenderingNode->accept(nv); } @@ -442,7 +443,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, NULL, mRootNode->getLodCallback(), eyePoint, false); + traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false); for (unsigned int i=0; igetNumEntries(); ++i) { diff --git a/components/terrain/terraindrawable.hpp b/components/terrain/terraindrawable.hpp index 79a28deeb..6bef60bc7 100644 --- a/components/terrain/terraindrawable.hpp +++ b/components/terrain/terraindrawable.hpp @@ -24,7 +24,7 @@ namespace Terrain public: virtual osg::Object* cloneType() const { return new TerrainDrawable (); } virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new TerrainDrawable (*this,copyop); } - virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=nullptr; } virtual const char* className() const { return "TerrainDrawable"; } virtual const char* libraryName() const { return "Terrain"; } diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 74f683774..dbc1429da 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -34,7 +34,7 @@ TerrainGrid::~TerrainGrid() void TerrainGrid::cacheCell(View* view, int x, int y) { osg::Vec2f center(x+0.5f, y+0.5f); - static_cast(view)->mLoaded = buildTerrain(NULL, 1.f, center); + static_cast(view)->mLoaded = buildTerrain(nullptr, 1.f, center); } osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter) @@ -57,7 +57,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu { osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0); if (!node) - return NULL; + return nullptr; if (parent) parent->addChild(node); @@ -71,7 +71,7 @@ void TerrainGrid::loadCell(int x, int y) return; // already loaded osg::Vec2f center(x+0.5f, y+0.5f); - osg::ref_ptr terrainNode = buildTerrain(NULL, 1.f, center); + osg::ref_ptr terrainNode = buildTerrain(nullptr, 1.f, center); if (!terrainNode) return; // no terrain defined diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 5c70f65f2..2f9cb6641 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -64,7 +64,7 @@ void ViewData::reset(unsigned int frame) { // clear any unused entries for (unsigned int i=mNumEntries; isecond; if (vd->getFrameLastUsed() + 2 < frame) { - vd->setViewer(NULL); + vd->setViewer(nullptr); vd->clear(); mUnusedViews.push_back(vd); mViews.erase(it++); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index ae71693bd..da1004783 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -90,7 +90,7 @@ namespace Terrain /// Create a View to use with preload feature. The caller is responsible for deleting the view. /// @note Thread safe. - virtual View* createView() { return NULL; } + virtual View* createView() { return nullptr; } /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. virtual void preload(View* view, const osg::Vec3f& eyePoint) {} diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index 14a32f178..ef0f43207 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -5,7 +5,7 @@ namespace Translation { Storage::Storage() - : mEncoder(NULL) + : mEncoder(nullptr) { } diff --git a/components/widgets/box.cpp b/components/widgets/box.cpp index bf18cef5b..d38410a2c 100644 --- a/components/widgets/box.cpp +++ b/components/widgets/box.cpp @@ -48,7 +48,7 @@ namespace Gui } else { - TextBox::setPropertyOverride (_key, _value); + Gui::TextBox::setPropertyOverride (_key, _value); } } @@ -81,11 +81,10 @@ namespace Gui } else { - EditBox::setPropertyOverride (_key, _value); + Gui::EditBox::setPropertyOverride (_key, _value); } } - MyGUI::IntSize AutoSizedButton::getRequestedSize() { MyGUI::IntSize padding(24, 8); @@ -111,16 +110,14 @@ namespace Gui } else { - Button::setPropertyOverride (_key, _value); + Gui::Button::setPropertyOverride (_key, _value); } } - Box::Box() : mSpacing(4) , mPadding(0) , mAutoResize(false) { - } void Box::notifyChildrenSizeChanged () diff --git a/components/widgets/box.hpp b/components/widgets/box.hpp index 66be00719..9bab575c4 100644 --- a/components/widgets/box.hpp +++ b/components/widgets/box.hpp @@ -4,10 +4,27 @@ #include #include #include +#include #include +#include "fontwrapper.hpp" + namespace Gui { + class Button : public FontWrapper + { + MYGUI_RTTI_DERIVED( Button ) + }; + + class TextBox : public FontWrapper + { + MYGUI_RTTI_DERIVED( TextBox ) + }; + + class EditBox : public FontWrapper + { + MYGUI_RTTI_DERIVED( EditBox ) + }; class AutoSizedWidget { @@ -22,7 +39,7 @@ namespace Gui MyGUI::Align mExpandDirection; }; - class AutoSizedTextBox : public AutoSizedWidget, public MyGUI::TextBox + class AutoSizedTextBox : public AutoSizedWidget, public TextBox { MYGUI_RTTI_DERIVED( AutoSizedTextBox ) @@ -32,9 +49,10 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; - class AutoSizedEditBox : public AutoSizedWidget, public MyGUI::EditBox + class AutoSizedEditBox : public AutoSizedWidget, public EditBox { MYGUI_RTTI_DERIVED( AutoSizedEditBox ) @@ -47,9 +65,10 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; - class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button + class AutoSizedButton : public AutoSizedWidget, public Button { MYGUI_RTTI_DERIVED( AutoSizedButton ) @@ -59,6 +78,7 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; /** diff --git a/components/widgets/fontwrapper.hpp b/components/widgets/fontwrapper.hpp new file mode 100644 index 000000000..2424b22a7 --- /dev/null +++ b/components/widgets/fontwrapper.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_WIDGETS_WRAPPER_H +#define OPENMW_WIDGETS_WRAPPER_H + +#include "widgets.hpp" + +#include + +namespace Gui +{ + template + class FontWrapper : public T + { + public: + virtual void setFontName(const std::string& name) + { + T::setFontName(name); + T::setPropertyOverride ("FontHeight", mFontSize); + } + + protected: + FontWrapper() + { + // Note: we can not use the WindowManager here, so there is a code duplication a bit. + int fontSize = Settings::Manager::getInt("font size", "GUI"); + fontSize = std::min(std::max(12, fontSize), 20); + mFontSize = std::to_string(fontSize); + } + + virtual void setPropertyOverride(const std::string& _key, const std::string& _value) + { + T::setPropertyOverride (_key, _value); + + // There is a bug in MyGUI: when it initializes the FontName property, it reset the font height. + // We should restore it. + if (_key == "FontName") + { + T::setPropertyOverride ("FontHeight", mFontSize); + } + } + + std::string mFontSize; + }; +} + +#endif diff --git a/components/widgets/numericeditbox.cpp b/components/widgets/numericeditbox.cpp index efe480653..e8ba226f7 100644 --- a/components/widgets/numericeditbox.cpp +++ b/components/widgets/numericeditbox.cpp @@ -59,6 +59,11 @@ namespace Gui } } + int NumericEditBox::getValue() + { + return mValue; + } + void NumericEditBox::setMinValue(int minValue) { mMinValue = minValue; diff --git a/components/widgets/numericeditbox.hpp b/components/widgets/numericeditbox.hpp index ca7674f22..137583d37 100644 --- a/components/widgets/numericeditbox.hpp +++ b/components/widgets/numericeditbox.hpp @@ -3,13 +3,15 @@ #include +#include "fontwrapper.hpp" + namespace Gui { /** * @brief A variant of the EditBox that only allows integer inputs */ - class NumericEditBox : public MyGUI::EditBox + class NumericEditBox : public FontWrapper { MYGUI_RTTI_DERIVED(NumericEditBox) @@ -17,7 +19,8 @@ namespace Gui NumericEditBox() : mValue(0), mMinValue(std::numeric_limits::min()), mMaxValue(std::numeric_limits::max()) - {} + { + } void initialiseOverride(); void shutdownOverride(); @@ -27,6 +30,7 @@ namespace Gui /// @note Does not trigger eventValueChanged void setValue (int value); + int getValue(); void setMinValue(int minValue); void setMaxValue(int maxValue); diff --git a/components/widgets/sharedstatebutton.cpp b/components/widgets/sharedstatebutton.cpp index 6859a3065..f4456275b 100644 --- a/components/widgets/sharedstatebutton.cpp +++ b/components/widgets/sharedstatebutton.cpp @@ -7,7 +7,6 @@ namespace Gui : mIsMousePressed(false) , mIsMouseFocus(false) { - } void SharedStateButton::shutdownOverride() diff --git a/components/widgets/sharedstatebutton.hpp b/components/widgets/sharedstatebutton.hpp index 3d7fbc84e..414349396 100644 --- a/components/widgets/sharedstatebutton.hpp +++ b/components/widgets/sharedstatebutton.hpp @@ -3,6 +3,8 @@ #include +#include "fontwrapper.hpp" + namespace Gui { @@ -11,7 +13,7 @@ namespace Gui typedef std::vector ButtonGroup; /// @brief A button that applies its own state changes to other widgets, to do this you define it as part of a ButtonGroup. - class SharedStateButton : public MyGUI::Button + class SharedStateButton : public FontWrapper { MYGUI_RTTI_DERIVED(SharedStateButton) diff --git a/components/widgets/widgets.cpp b/components/widgets/widgets.cpp index 92f2084df..c1a9a5053 100644 --- a/components/widgets/widgets.cpp +++ b/components/widgets/widgets.cpp @@ -18,9 +18,12 @@ namespace Gui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); diff --git a/components/widgets/widgets.hpp b/components/widgets/widgets.hpp index d17132135..a5ba84b11 100644 --- a/components/widgets/widgets.hpp +++ b/components/widgets/widgets.hpp @@ -1,9 +1,10 @@ #ifndef OPENMW_COMPONENTS_WIDGETS_H #define OPENMW_COMPONENTS_WIDGETS_H +extern int GuiFontHeight; + namespace Gui { - /// Register all widgets from this component with MyGUI's factory manager. void registerAllWidgets(); diff --git a/components/widgets/windowcaption.cpp b/components/widgets/windowcaption.cpp index 1c8fb5608..0fed4bead 100644 --- a/components/widgets/windowcaption.cpp +++ b/components/widgets/windowcaption.cpp @@ -6,9 +6,9 @@ namespace Gui { WindowCaption::WindowCaption() - : mLeft(NULL) - , mRight(NULL) - , mClient(NULL) + : mLeft(nullptr) + , mRight(nullptr) + , mClient(nullptr) { } diff --git a/docs/openmw-stage1.md b/docs/openmw-stage1.md index 4707a4435..4290080b8 100644 --- a/docs/openmw-stage1.md +++ b/docs/openmw-stage1.md @@ -1383,9 +1383,9 @@ We add several new script instructions: * PlayBackgroundMusic (t) * PlaylistBackgroundMusic (pl) * StopBackgroundMusic -* PlayEventMusic (t, loop=0, force=0) -* PlaylistEventMusic (pl, loop=0, force=0) -* StopEventMusic +* PlayEventMusic (t, priority, loop=0, force=0) +* PlaylistEventMusic (pl, priority, loop=0, force=0) +* StopEventMusic (priority) Arguments: @@ -1393,8 +1393,14 @@ Arguments: * pl (string): a playlist (specified by its ID) * loop (integer): looping or not * force (integer): Terminate any other running event music first. If this flag is 0 and there is already event music playing the new event music is ignored. Event music provided by content developers will usually not set this flag. +* priority (integer): Priority for event music. If there are multiple instances of active event music only the one with the highest priority will play. There can only ever be one instance of event music for any priority level. If event music of higher priority stops and there is event music of lower priority the event music of lower priority starts to play. -Note: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart. +Note: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart. Changes in looping behaviour are considered though. + +New games starting with a new format omwgame file can use priorities as the developer sees fit. For old games where automatic injection of scripts and music records take place we define the following priorities: + +* 10: Combat music +* 0: Basic event music ### Transition of Existing System diff --git a/docs/source/manuals/installation/common-problems.rst b/docs/source/manuals/installation/common-problems.rst index 543b8ec9b..0967ea9ef 100644 --- a/docs/source/manuals/installation/common-problems.rst +++ b/docs/source/manuals/installation/common-problems.rst @@ -47,4 +47,33 @@ Installing game files via Steam on macOS: DISK WRITE ERROR [other entries] } - Restart the Steam client. The download should now proceed. \ No newline at end of file + Restart the Steam client. The download should now proceed. + +In-game textures show up as pink +################################ + +:Symptoms: + Some textures don't show up and are replaced by pink "filler" textures. + +:Cause: + Textures shipped with Morrowind are compressed with S3TC, a texture compression algorithm that was patented in + the United States until October 2017. Software drivers and operating system distributions released before that date + may not include support for this algorithm. + +:Fix: + Upgrade your graphics drivers and/or operating system to the latest version. + +Music plays, but only a black screen is shown +############################################# + +:Symptoms: + When launching OpenMW, audio is heard but there is only a black screen. + +:Cause: + This can occur when you did not import the Morrowind.ini file when the launcher asked for it. + +:Fix: + To fix it, you need to delete the `launcher.cfg` file from your configuration path + ([Path description on Wiki](https://wiki.openmw.org/index.php?title=Paths)), then start the launcher, select your + Morrowind installation, and when the launcher asks whether the `Morrowind.ini` file should be imported, make sure + to select "Yes". \ No newline at end of file diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 3040fc48b..c8f4e16f8 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -12,6 +12,27 @@ This setting scales the GUI interface windows. A value of 1.0 results in the normal scale. Larger values are useful to increase the scale of the GUI for high resolution displays. This setting can only be configured by editing the settings configuration file. +font size +--------- + +:Type: integer +:Range: 12 to 20 +:Default: 16 + +Allows to specify glyph size for in-game fonts. +Note: default bitmap fonts are supposed to work with 16px size, otherwise glyphs will be blurry. +TrueType fonts do not have this issue. + +ttf resolution +-------------- + +:Type: integer +:Range: 48 to 960 +:Default: 96 + +Allows to specify resolution for in-game TrueType fonts. +Note: actual resolution depends on "scaling factor" setting value, this value is for 1.0 scaling factor. + menu transparency ----------------- diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index b733cc7c4..4c3f4579f 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -16,7 +16,7 @@ If the setting is 2, the crosshair is the color of the color crosshair owned set If the setting is 3, both the tool tip background and the crosshair are colored. The crosshair is not visible if crosshair is false. -This setting can only be configured by editing the settings configuration file. +This setting can be configured in Advanced tab of the launcher. show projectile damage ---------------------- @@ -27,7 +27,7 @@ show projectile damage If this setting is true, the damage bonus of arrows and bolts will show on their tooltip. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show melee info --------------- @@ -36,9 +36,9 @@ show melee info :Range: True/False :Default: False -If this setting is true, the reach and speed of melee weapons will show on their tooltip. +If this setting is true, the reach and speed of weapons will show on their tooltip. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show enchant chance ------------------- @@ -49,7 +49,7 @@ show enchant chance Whether or not the chance of success will be displayed in the enchanting menu. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. best attack ----------- @@ -73,13 +73,13 @@ can loot during death animation If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. However disposing corpses during death animation is not recommended - death counter may not be incremented, and this behaviour can break quests. -This is how original Morrowind behaves. +This is how Morrowind behaves. If this setting is false, player has to wait until end of death animation in all cases. This case is more safe, but makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. difficulty ---------- @@ -97,19 +97,19 @@ and values above 500 will result in the player inflicting no damage. This setting can be controlled in game with the Difficulty slider in the Prefs panel of the Options menu. -classic reflect absorb attribute behavior +classic reflected absorb spells behavior ----------------------------------------- :Type: boolean :Range: True/False :Default: True -If this setting is true, "Absorb Attribute" spells which were reflected by the target are not "mirrored", -and the caster will absorb their own attribute resulting in no effect on both the caster and the target. +If this setting is true, effects of Absorb spells which were reflected by the target are not mirrored, +and the caster will absorb their own stat resulting in no effect on either the caster and the target. This makes the gameplay as a mage easier, but these spells become imbalanced. -This is how the original Morrowind behaves. +This is how Morrowind behaves. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show effect duration -------------------- @@ -121,7 +121,7 @@ show effect duration Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. enchanted weapons are magical ----------------------------- @@ -131,9 +131,9 @@ enchanted weapons are magical :Default: True Make enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have. -This is how original Morrowind behaves. +This is how Morrowind behaves. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. prevent merchant equipping -------------------------- @@ -144,7 +144,7 @@ prevent merchant equipping Prevent merchants from equipping items that are sold to them. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. followers attack on sight ------------------------- @@ -157,7 +157,7 @@ Make player followers and escorters start combat with enemies who have started c Otherwise they wait for the enemies or the player to do an attack first. Please note this setting has not been extensively tested and could have side effects with certain quests. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. use additional anim sources --------------------------- @@ -182,4 +182,4 @@ barter disposition change is permanent If this setting is true, disposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them. This imitates the option Morrowind Code Patch offers. -This setting can be toggled with a checkbox in Advanced tab of the launcher. +This setting can be toggled in Advanced tab of the launcher. diff --git a/files/mygui/core.skin b/files/mygui/core.skin index 9aa566451..ee9135554 100644 --- a/files/mygui/core.skin +++ b/files/mygui/core.skin @@ -2,7 +2,6 @@ - diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index a8e5431fb..714872fc3 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -74,7 +74,20 @@ - + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index c83c4982b..e6f0f858c 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -32,11 +32,13 @@ + + diff --git a/files/mygui/openmw_chargen_birth.layout b/files/mygui/openmw_chargen_birth.layout index a137d83d1..18d412340 100644 --- a/files/mygui/openmw_chargen_birth.layout +++ b/files/mygui/openmw_chargen_birth.layout @@ -16,7 +16,7 @@ > - + diff --git a/files/mygui/openmw_chargen_class.layout b/files/mygui/openmw_chargen_class.layout index 98c8e4fde..1402d9b5c 100644 --- a/files/mygui/openmw_chargen_class.layout +++ b/files/mygui/openmw_chargen_class.layout @@ -27,11 +27,11 @@ - + - + @@ -73,7 +73,7 @@ - + diff --git a/files/mygui/openmw_chargen_create_class.layout b/files/mygui/openmw_chargen_create_class.layout index c04cfe064..fe491eb6d 100644 --- a/files/mygui/openmw_chargen_create_class.layout +++ b/files/mygui/openmw_chargen_create_class.layout @@ -11,7 +11,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -74,7 +74,7 @@ - + diff --git a/files/mygui/openmw_chargen_generate_class_result.layout b/files/mygui/openmw_chargen_generate_class_result.layout index d49f57dde..48a203b18 100644 --- a/files/mygui/openmw_chargen_generate_class_result.layout +++ b/files/mygui/openmw_chargen_generate_class_result.layout @@ -24,7 +24,7 @@ - + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index 31f0436d5..ea7ec6179 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -1,7 +1,7 @@ - + @@ -54,28 +54,28 @@ - + - + - + - + - + - + - + diff --git a/files/mygui/openmw_chargen_select_attribute.layout b/files/mygui/openmw_chargen_select_attribute.layout index 0821d4a57..57bd4ebc6 100644 --- a/files/mygui/openmw_chargen_select_attribute.layout +++ b/files/mygui/openmw_chargen_select_attribute.layout @@ -1,28 +1,31 @@ - - + + - + - - - - - - - - + + + + + + + + - - - + + + + + + diff --git a/files/mygui/openmw_chargen_select_skill.layout b/files/mygui/openmw_chargen_select_skill.layout index d1061882d..3737ea904 100644 --- a/files/mygui/openmw_chargen_select_skill.layout +++ b/files/mygui/openmw_chargen_select_skill.layout @@ -1,10 +1,10 @@ - - + + - + @@ -44,20 +44,23 @@ - - - - - - - - - + + + + + + + + + - - - + + + + + + diff --git a/files/mygui/openmw_chargen_select_specialization.layout b/files/mygui/openmw_chargen_select_specialization.layout index c4fe6c631..47dc32f62 100644 --- a/files/mygui/openmw_chargen_select_specialization.layout +++ b/files/mygui/openmw_chargen_select_specialization.layout @@ -2,7 +2,7 @@ - + @@ -22,9 +22,12 @@ - - - + + + + + + diff --git a/files/mygui/openmw_confirmation_dialog.layout b/files/mygui/openmw_confirmation_dialog.layout index c5eb573a7..246c8aa8f 100644 --- a/files/mygui/openmw_confirmation_dialog.layout +++ b/files/mygui/openmw_confirmation_dialog.layout @@ -13,7 +13,7 @@ - + diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 6bb585e50..5783779db 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -9,7 +9,7 @@ - + diff --git a/files/mygui/openmw_count_window.layout b/files/mygui/openmw_count_window.layout index 520d49fa3..2e083dcea 100644 --- a/files/mygui/openmw_count_window.layout +++ b/files/mygui/openmw_count_window.layout @@ -16,12 +16,12 @@ - + - + - + diff --git a/files/mygui/openmw_enchanting_dialog.layout b/files/mygui/openmw_enchanting_dialog.layout index ed3cfb03e..4738cdc13 100644 --- a/files/mygui/openmw_enchanting_dialog.layout +++ b/files/mygui/openmw_enchanting_dialog.layout @@ -50,7 +50,7 @@ - + diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index e4037561d..6ffe3017e 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -2,8 +2,6 @@ - - diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 6221799b5..e935e2f5c 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -29,7 +29,7 @@ - + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index 964b5ea95..5d6e29066 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -25,10 +25,12 @@ + + @@ -59,8 +61,9 @@ - - + + + diff --git a/files/mygui/openmw_journal.skin.xml b/files/mygui/openmw_journal.skin.xml index 42be2bb62..0702d8c5e 100644 --- a/files/mygui/openmw_journal.skin.xml +++ b/files/mygui/openmw_journal.skin.xml @@ -2,7 +2,7 @@ - + @@ -13,15 +13,13 @@ + - - - - + - + diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 5b6f14dd5..9af0e7966 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -127,7 +127,6 @@ - @@ -140,8 +139,6 @@ - - @@ -153,7 +150,6 @@ - diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index 0af9d59fe..30e22302d 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -48,7 +48,7 @@ - + @@ -62,7 +62,7 @@ - + diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index a272ae84a..ef618b316 100644 --- a/files/mygui/openmw_windows.skin.xml +++ b/files/mygui/openmw_windows.skin.xml @@ -156,10 +156,10 @@ - + - + @@ -443,7 +443,6 @@ - @@ -458,7 +457,6 @@ ------------------------------------------------------ --> - @@ -593,7 +591,6 @@ - @@ -730,7 +727,6 @@ - diff --git a/files/opencs/cell.png b/files/opencs/cell.png index 9127dd5e5..4c3fbe251 100644 Binary files a/files/opencs/cell.png and b/files/opencs/cell.png differ diff --git a/files/opencs/debug-profile.png b/files/opencs/debug-profile.png new file mode 100644 index 000000000..ddb01da43 Binary files /dev/null and b/files/opencs/debug-profile.png differ diff --git a/files/opencs/edit-undo.png b/files/opencs/edit-undo.png deleted file mode 100644 index 8b0fef9a8..000000000 Binary files a/files/opencs/edit-undo.png and /dev/null differ diff --git a/files/opencs/error-log.png b/files/opencs/error-log.png new file mode 100644 index 000000000..df698d145 Binary files /dev/null and b/files/opencs/error-log.png differ diff --git a/files/opencs/Info.png b/files/opencs/info.png similarity index 100% rename from files/opencs/Info.png rename to files/opencs/info.png diff --git a/files/opencs/instance.png b/files/opencs/instance.png new file mode 100644 index 000000000..ce63e64ed Binary files /dev/null and b/files/opencs/instance.png differ diff --git a/files/opencs/leveled-creature.png b/files/opencs/levelled-creature.png similarity index 100% rename from files/opencs/leveled-creature.png rename to files/opencs/levelled-creature.png diff --git a/files/opencs/leveled-item.png b/files/opencs/levelled-item.png similarity index 100% rename from files/opencs/leveled-item.png rename to files/opencs/levelled-item.png diff --git a/files/opencs/magic-effect.png b/files/opencs/magic-effect.png index 4901724c5..44b682bf4 100644 Binary files a/files/opencs/magic-effect.png and b/files/opencs/magic-effect.png differ diff --git a/files/opencs/menu-close.png b/files/opencs/menu-close.png new file mode 100644 index 000000000..81bc98677 Binary files /dev/null and b/files/opencs/menu-close.png differ diff --git a/files/opencs/menu-exit.png b/files/opencs/menu-exit.png new file mode 100644 index 000000000..f583536fb Binary files /dev/null and b/files/opencs/menu-exit.png differ diff --git a/files/opencs/menu-merge.png b/files/opencs/menu-merge.png new file mode 100644 index 000000000..c76288ae1 Binary files /dev/null and b/files/opencs/menu-merge.png differ diff --git a/files/opencs/menu-new-addon.png b/files/opencs/menu-new-addon.png new file mode 100644 index 000000000..df137b2b2 Binary files /dev/null and b/files/opencs/menu-new-addon.png differ diff --git a/files/opencs/menu-new-game.png b/files/opencs/menu-new-game.png new file mode 100644 index 000000000..701daf34b Binary files /dev/null and b/files/opencs/menu-new-game.png differ diff --git a/files/opencs/menu-new-window.png b/files/opencs/menu-new-window.png new file mode 100644 index 000000000..4a42da0d1 Binary files /dev/null and b/files/opencs/menu-new-window.png differ diff --git a/files/opencs/menu-open.png b/files/opencs/menu-open.png new file mode 100644 index 000000000..3766e1754 Binary files /dev/null and b/files/opencs/menu-open.png differ diff --git a/files/opencs/menu-preferences.png b/files/opencs/menu-preferences.png new file mode 100644 index 000000000..4644297ad Binary files /dev/null and b/files/opencs/menu-preferences.png differ diff --git a/files/opencs/menu-redo.png b/files/opencs/menu-redo.png new file mode 100644 index 000000000..0cd0eedca Binary files /dev/null and b/files/opencs/menu-redo.png differ diff --git a/files/opencs/menu-reload.png b/files/opencs/menu-reload.png new file mode 100644 index 000000000..2b780598c Binary files /dev/null and b/files/opencs/menu-reload.png differ diff --git a/files/opencs/menu-save.png b/files/opencs/menu-save.png new file mode 100644 index 000000000..4be88765b Binary files /dev/null and b/files/opencs/menu-save.png differ diff --git a/files/opencs/menu-search.png b/files/opencs/menu-search.png new file mode 100644 index 000000000..1ec63f0e8 Binary files /dev/null and b/files/opencs/menu-search.png differ diff --git a/files/opencs/menu-status-bar.png b/files/opencs/menu-status-bar.png new file mode 100644 index 000000000..dfb72b1e1 Binary files /dev/null and b/files/opencs/menu-status-bar.png differ diff --git a/files/opencs/menu-undo.png b/files/opencs/menu-undo.png new file mode 100644 index 000000000..bd177ce65 Binary files /dev/null and b/files/opencs/menu-undo.png differ diff --git a/files/opencs/menu-verify.png b/files/opencs/menu-verify.png new file mode 100644 index 000000000..a7878ebb3 Binary files /dev/null and b/files/opencs/menu-verify.png differ diff --git a/files/opencs/metadata.png b/files/opencs/metadata.png new file mode 100644 index 000000000..34c78ffd6 Binary files /dev/null and b/files/opencs/metadata.png differ diff --git a/files/opencs/object.png b/files/opencs/object.png new file mode 100644 index 000000000..4f552151d Binary files /dev/null and b/files/opencs/object.png differ diff --git a/files/opencs/placeholder.png b/files/opencs/placeholder.png index 1d3df3c47..b9db9f188 100644 Binary files a/files/opencs/placeholder.png and b/files/opencs/placeholder.png differ diff --git a/files/opencs/region-map.png b/files/opencs/region-map.png new file mode 100644 index 000000000..7631847be Binary files /dev/null and b/files/opencs/region-map.png differ diff --git a/files/opencs/region.png b/files/opencs/region.png index a10089243..2ebaeb028 100644 Binary files a/files/opencs/region.png and b/files/opencs/region.png differ diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index 0afd855f9..93b47dbdd 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -15,6 +15,7 @@ clothing.png container.png creature.png + debug-profile.png dialogue-info.png dialogue-journal.png dialogue-regular.png @@ -27,34 +28,60 @@ journal-topics.png door.png enchantment.png + error-log.png faction.png filter.png global-variable.png gmst.png - Info.png + info.png ingredient.png + instance.png land-heightmap.png land-texture.png - leveled-creature.png - leveled-item.png + levelled-creature.png + levelled-item.png light.png lockpick.png magic-effect.png magicrabbit.png map.png + probe.png + menu-close.png + menu-exit.png + menu-new-addon.png + menu-new-game.png + menu-new-window.png + menu-merge.png + menu-open.png + menu-preferences.png + menu-reload.png + menu-redo.png + menu-save.png + menu-search.png + menu-status-bar.png + menu-undo.png + menu-verify.png + metadata.png miscellaneous.png list-modified.png npc.png + object.png pathgrid.png potion.png - probe.png race.png random-item.png random.png list-removed.png + region.png + region-map.png repair.png + run-log.png + run-openmw.png + scene.png script.png skill.png + start-script.png + stop-openmw.png sound-generator.png sound.png spell.png @@ -64,7 +91,6 @@ record-next.png record-previous.png record-delete.png - record-revert.png record-preview.png record-clone.png record-add.png diff --git a/files/opencs/run-log.png b/files/opencs/run-log.png new file mode 100644 index 000000000..463ead176 Binary files /dev/null and b/files/opencs/run-log.png differ diff --git a/files/opencs/run-openmw.png b/files/opencs/run-openmw.png new file mode 100644 index 000000000..1033d62ba Binary files /dev/null and b/files/opencs/run-openmw.png differ diff --git a/files/opencs/scene.png b/files/opencs/scene.png new file mode 100644 index 000000000..a9fc03f76 Binary files /dev/null and b/files/opencs/scene.png differ diff --git a/files/opencs/start-script.png b/files/opencs/start-script.png new file mode 100644 index 000000000..73ed157b9 Binary files /dev/null and b/files/opencs/start-script.png differ diff --git a/files/opencs/stop-openmw.png b/files/opencs/stop-openmw.png new file mode 100644 index 000000000..d8f809672 Binary files /dev/null and b/files/opencs/stop-openmw.png differ diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b9514cfcf..bd4898cde 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -142,6 +142,12 @@ global = false # Scales GUI window and widget size. (<1.0 is smaller, >1.0 is larger). scaling factor = 1.0 +# Size of in-game fonts +font size = 16 + +# Resolution of TrueType fonts glyphs +ttf resolution = 96 + # Transparency of GUI windows (0.0 to 1.0, transparent to opaque). menu transparency = 0.84 @@ -182,7 +188,7 @@ show owned = 0 # Show damage bonus of arrow and bolts. show projectile damage = false -# Show additional melee weapon info: reach and attack speed +# Show additional weapon info: reach and attack speed show melee info = false # Show success probability in self-enchant dialog @@ -194,8 +200,8 @@ best attack = false # Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100). difficulty = 0 -# Replicate how reflected "absorb attribute" spells do not have any effect in Morrowind engine. The caster absorbs the attribute from themselves. -classic reflect absorb attribute behavior = true +# Make reflected Absorb spells have no practical effect, like in Morrowind. +classic reflected absorb spells behavior = true # Show duration of magic effect and lights in the spells window. show effect duration = false diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index c1d100b7a..9e6d5573f 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -288,7 +288,7 @@ void main(void) #if REFRACTION gl_FragData[0].w = 1.0; #else - gl_FragData[0].w = clamp(fresnel*6.0 + specular, 0.0, 1.0); //clamp(fresnel*2.0 + specular, 0.0, 1.0); + gl_FragData[0].w = clamp(fresnel*6.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); #endif applyShadowDebugOverlay(); diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 8395d028f..0793345c8 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -73,7 +73,7 @@ Enchanted weapons are magical - + @@ -86,6 +86,16 @@ + + + + <html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html> + + + Classic reflected Absorb spells behavior + + + diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 5a6b7265a..5c26e1044 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -62,7 +62,7 @@ - Select a profiile + Select a content list