Merge branch 'master' into coverity_scan

pull/541/head
Marc Zinnschlag 6 years ago
commit 85f9b0d004

@ -148,6 +148,7 @@ Programmers
Scott Howard
scrawl
Sebastian Wick (swick)
Sergey Fukanchik
Sergey Shambir
ShadowRadiance
Siimacore

@ -1,6 +1,7 @@
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
@ -19,13 +20,16 @@
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 <item> that has a script containing Equip <item>
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
@ -45,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
@ -54,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
@ -75,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
@ -90,8 +97,11 @@
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
@ -105,18 +115,41 @@
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 #4646: Weapon force-equipment messes up ongoing attack animations
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 #4656: Combat AI: back up behaviour is incorrect
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
Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one
Bug #4678: Crash in ESP parser when SCVR has no variable names
Bug #4685: Missing sound causes an exception inside Say command
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
@ -125,6 +158,7 @@
Feature #4012: Editor: Write a log file if OpenCS crashes
Feature #4222: 360° screenshots
Feature #4256: Implement ToggleBorders (TB) console command
Feature #4285: Support soundgen calls for activators
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
Feature #4345: Add equivalents for the command line commands to Launcher
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
@ -142,12 +176,18 @@
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
Feature #4682: Use the collision box from basic creature mesh if the X one have no collisions
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
------

@ -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

@ -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

@ -8,6 +8,8 @@
#include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp>
#include <components/misc/constants.hpp>
#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");

@ -526,7 +526,10 @@ public:
class ConvertGAME : public Converter
{
public:
ConvertGAME() : mHasGame(false) {}
ConvertGAME()
: mHasGame(false)
{
}
virtual void read(ESM::ESMReader &esm)
{

@ -1,5 +1,6 @@
#include "convertplayer.hpp"
#include <components/misc/constants.hpp>
#include <components/misc/stringops.hpp>
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;
}
}

@ -25,6 +25,8 @@
#include <components/esm/loadlevlist.hpp>
#include <components/esm/loadglob.hpp>
#include <components/misc/constants.hpp>
#include <components/to_utf8/to_utf8.hpp>
#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<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize));
int cellY = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize));
int cellX = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0] / Constants::CellSizeInUnits));
int cellY = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / Constants::CellSizeInUnits));
context.mPlayer.mCellId.mIndex.mX = cellX;
context.mPlayer.mCellId.mIndex.mY = cellY;
}

@ -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;

@ -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");

@ -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);
}
}

@ -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

@ -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<Files::PathContainer, std::vector<std::string> > config = readConfig();
@ -108,8 +108,9 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mCfgMgr.readConfiguration(variables, desc, quiet);
mDocumentManager.setEncoding (
ToUTF8::calculateEncoding (variables["encoding"].as<Files::EscapeHashString>().toStdString()));
const std::string encoding = variables["encoding"].as<Files::EscapeHashString>().toStdString();
mDocumentManager.setEncoding (ToUTF8::calculateEncoding (encoding));
mFileDialog.setEncoding (QString::fromUtf8(encoding.c_str()));
mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::EscapeHashString>().toStdString());
@ -338,7 +339,7 @@ bool CS::Editor::makeIPCServer()
}
mServer->close();
mServer = NULL;
mServer = nullptr;
return false;
}

@ -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<CSMWorld::UniversalId, std::string>& data)
{
add (data.first, data.second);
}
CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const
{
return mMessages.begin();

@ -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<CSMWorld::UniversalId, std::string>& data);
Iterator begin() const;
Iterator end() const;

@ -3,7 +3,7 @@
#include "operation.hpp"
CSMDoc::OperationHolder::OperationHolder (Operation *operation)
: mOperation(NULL)
: mOperation(nullptr)
, mRunning (false)
{
if (operation)

@ -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");

@ -1,16 +1,15 @@
#include "birthsigncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadbsgn.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns)
: mBirthsigns (birthsigns)
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& 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
}

@ -4,6 +4,7 @@
#include <components/esm/loadbsgn.hpp>
#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<ESM::BirthSign>& mBirthsigns;
const CSMWorld::IdCollection<ESM::BirthSign> &mBirthsigns;
const CSMWorld::Resources &mTextures;
bool mIgnoreBaseRecords;
public:
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns);
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign> &birthsigns,
const CSMWorld::Resources &textures);
virtual int setup();
///< \return number of steps

@ -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);
}

@ -1,6 +1,5 @@
#include "classcheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadclas.hpp>
@ -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<int, int>::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);
}
}

@ -0,0 +1,82 @@
#include "enchantmentcheck.hpp"
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::EnchantmentCheckStage::EnchantmentCheckStage (const CSMWorld::IdCollection<ESM::Enchantment>& 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<ESM::Enchantment>& 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<ESM::ENAMstruct>::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;
}
}
}

@ -0,0 +1,31 @@
#ifndef CSM_TOOLS_ENCHANTMENTCHECK_H
#define CSM_TOOLS_ENCHANTMENTCHECK_H
#include <components/esm/loadench.hpp>
#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<ESM::Enchantment>& mEnchantments;
bool mIgnoreBaseRecords;
public:
EnchantmentCheckStage (const CSMWorld::IdCollection<ESM::Enchantment>& 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

@ -1,9 +1,7 @@
#include "factioncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadfact.hpp>
#include <components/esm/loadskil.hpp>
#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<int, int>::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

@ -1,7 +1,6 @@
#include "journalcheck.hpp"
#include <set>
#include <sstream>
#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<std::set<int>::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);
}
}

@ -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<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &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.mCasting.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.mDescription.empty())
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())
{
messages.push_back(std::make_pair(id, "Description is 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);
}

@ -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<ESM::MagicEffect> &mMagicEffects;
const CSMWorld::IdCollection<ESM::Sound> &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<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables,
const CSMWorld::RefIdCollection &objects,
const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures);

@ -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<int>(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<int>(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<CSMTools::Point> pointList(pathgrid.mPoints.size());
std::vector<int> 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,18 +92,15 @@ 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)
@ -113,11 +109,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
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);
}
}

@ -1,9 +1,5 @@
#include "racecheck.hpp"
#include <sstream>
#include <components/esm/loadrace.hpp>
#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<ESM::Race>& races)

@ -1,6 +1,7 @@
#include "referenceablecheck.hpp"
#include <components/misc/stringops.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../prefs/state.hpp"
@ -11,13 +12,18 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,
const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& faction,
const CSMWorld::IdCollection<ESM::Script>& scripts)
:
mReferencables(referenceable),
const CSMWorld::IdCollection<ESM::Script>& scripts,
const CSMWorld::Resources& models,
const CSMWorld::Resources& icons,
const CSMWorld::IdCollection<ESM::BodyPart>& 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<const CSMWorld::Record<ESM::Activator>& >(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<ESM::Activator>(activator, messages, id.toString());
@ -293,7 +300,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId);
inventoryItemCheck<ESM::Potion>(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<ESM::Potion>(potion, messages, id.toString());
@ -338,13 +345,13 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
inventoryItemCheck<ESM::Armor>(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<ESM::Armor>(armor, messages, id.toString());
@ -383,18 +390,19 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(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<const CSMWorld::Record<ESM::Creature>&>(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<ESM::Creature>(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<ESM::Door>(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<ESM::Light>(light, messages, id.toString());
@ -644,97 +667,76 @@ 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.mStrength == 0)
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.add(id, "Willpower is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mAgility == 0)
messages.push_back (std::make_pair (id, npc.mId + " agility has zero value"));
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.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"));
messages.add(id, "Endurance is equal to zero", "", CSMDoc::Message::Severity_Warning);
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"));
if (npc.mNpdt.mWillpower == 0)
messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value"));
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 (!npc.mFaction.empty() && mFactions.searchId(npc.mFaction) == -1)
messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid
if (npc.mHead.empty())
messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error);
else
{
messages.push_back (std::make_pair (id, npc.mId + " has negative reputation"));
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.mFaction.empty())
if (npc.mHair.empty())
messages.add(id, "Hair is missing", "", CSMDoc::Message::Severity_Error);
else
{
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 (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
}
if (npc.mHead.empty())
messages.push_back (std::make_pair (id, npc.mId + " has no head"));
if (npc.mHair.empty())
messages.push_back (std::make_pair (id, npc.mId + " has no hair"));
//TODO: reputation, Disposition, rank, everything else
// 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<typename Item> 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<typename Item> 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<typename Tool> 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<typename Tool> 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<typename List> void CSMTools::ReferenceableCheckStage::listCheck (
@ -1004,12 +1017,10 @@ template<typename List> 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<typename Tool> 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);
}
}

@ -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<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions,
const CSMWorld::IdCollection<ESM::Script>& scripts);
const CSMWorld::IdCollection<ESM::Script>& scripts,
const CSMWorld::Resources& models,
const CSMWorld::Resources& icons,
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts);
virtual void perform(int stage, CSMDoc::Messages& messages);
virtual int setup();
@ -81,6 +85,9 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::Script>& mScripts;
const CSMWorld::Resources& mModels;
const CSMWorld::Resources& mIcons;
const CSMWorld::IdCollection<ESM::BodyPart>& mBodyParts;
bool mPlayerPresent;
bool mIgnoreBaseRecords;
};

@ -9,7 +9,7 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage(
const CSMWorld::IdCollection<ESM::Faction>& 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));
bool hasFaction = !cellRef.mFaction.empty();
if (mObjects.searchId(cellRef.mSoul) == -1)
messages.add(id, "Trapped soul object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error);
// If object have faction, check if that faction reference is valid
if (hasFaction)
if (cellRef.mFaction.empty())
{
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.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)));
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 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));
if (!cellRef.mDestCell.empty() && mCells.searchId(cellRef.mDestCell) == -1)
messages.add(id, "Destination cell '" + cellRef.mDestCell + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check if scale isn't negative
if (cellRef.mScale < 0)
{
std::string str = " has negative scale ";
str += std::to_string(cellRef.mScale);
messages.push_back(std::make_pair(id, id.getId() + str));
}
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()

@ -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<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;

@ -1,10 +1,5 @@
#include "regioncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadregn.hpp>
#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

@ -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);
}

@ -1,9 +1,5 @@
#include "skillcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#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"));
}

@ -1,15 +1,13 @@
#include "soundcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds)
: mSounds (sounds)
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound> &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"));
/// \todo check, if the sound file exists
{
messages.add(id, "Minimum range is larger than maximum range", "", CSMDoc::Message::Severity_Warning);
}
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);
}
}

@ -3,6 +3,7 @@
#include <components/esm/loadsoun.hpp>
#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<ESM::Sound>& mSounds;
const CSMWorld::Resources &mSoundFiles;
bool mIgnoreBaseRecords;
public:
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds);
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds,
const CSMWorld::Resources &soundfiles);
virtual int setup();
///< \return number of steps

@ -1,7 +1,5 @@
#include "soundgencheck.hpp"
#include <sstream>
#include "../prefs/state.hpp"
#include "../world/refiddata.hpp"
@ -9,10 +7,10 @@
CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
const CSMWorld::IdCollection<ESM::Sound> &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);
}
}

@ -12,13 +12,13 @@ namespace CSMTools
{
const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens;
const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables;
const CSMWorld::RefIdCollection &mObjects;
bool mIgnoreBaseRecords;
public:
SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables);
const CSMWorld::RefIdCollection &objects);
virtual int setup();
///< \return number of steps

@ -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
}

@ -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()

@ -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);
}

@ -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);
}

@ -80,17 +80,6 @@ namespace CSMTools
template <typename T>
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& 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);
};
}

@ -0,0 +1,686 @@
#include "actoradapter.hpp"
#include <components/esm/loadarmo.hpp>
#include <components/esm/loadclot.hpp>
#include <components/esm/loadnpc.hpp>
#include <components/esm/loadrace.hpp>
#include <components/esm/mappings.hpp>
#include <components/sceneutil/actorutil.hpp>
#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<const Record<ESM::NPC>&>(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<ESM::PartReference>& 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<const Record<ESM::Armor>&>(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<const Record<ESM::Clothing>&>(record).get();
if (clothing.mData.mType == ESM::Clothing::Robe)
{
auto reservedList = std::vector<ESM::PartReference>();
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>();
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<const Record<ESM::Creature>&>(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();
}
}

@ -0,0 +1,174 @@
#ifndef CSM_WOLRD_ACTORADAPTER_H
#define CSM_WOLRD_ACTORADAPTER_H
#include <array>
#include <map>
#include <unordered_set>
#include <QObject>
#include <QModelIndex>
#include <components/esm/loadarmo.hpp>
#include <components/esm/loadbody.hpp>
#include <components/misc/weakcache.hpp>
#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<ESM::PartReferenceType, std::pair<std::string, int>>;
/// A list indexed by ESM::BodyPart::MeshPart
using RacePartList = std::array<std::string, ESM::BodyPart::MP_Count>;
/// Tracks unique strings
using StringSet = std::unordered_set<std::string>;
/// 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<RaceData>;
/// 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<ActorData>;
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<ESM::Race>& mRaces;
IdCollection<ESM::BodyPart>& mBodyParts;
Misc::WeakCache<std::string, ActorData> mCachedActors; // Key: referenceable id
Misc::WeakCache<std::string, RaceData> mCachedRaces; // Key: race id
StringSet mDirtyActors; // Actors that need updating
StringSet mDirtyRaces; // Races that need updating
};
}
#endif

@ -5,6 +5,8 @@
#include <ostream>
#include <sstream>
#include <components/misc/constants.hpp>
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, bool> CSMWorld::CellCoordinates::fromId (
std::pair<int, int> 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)

@ -318,7 +318,7 @@ namespace CSMWorld
QVariant BodyPartRaceColumn::get(const Record<ESM::BodyPart> &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());
}

@ -244,7 +244,7 @@ void CSMWorld::CreateCommand::applyModifications()
if (!mNestedValues.empty())
{
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&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;

@ -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();

@ -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<ESM::Filter> mFilters;
Collection<MetaData> mMetaData;
std::unique_ptr<ActorAdapter> mActorAdapter;
const Fallback::Map* mFallbackMap;
std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> 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.

@ -92,7 +92,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data)
{
QAbstractItemModel *model = data.getTableModel(current->second);
CSMWorld::IdTableBase *table = dynamic_cast<CSMWorld::IdTableBase *>(model);
if (table != NULL)
if (table != nullptr)
{
int idColumn = table->searchColumnIndex(CSMWorld::Columns::ColumnId_Id);
if (idColumn != -1)

@ -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();

@ -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;

@ -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

@ -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)

@ -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));

@ -3,7 +3,7 @@
#include <stdexcept>
CSMWorld::ResourcesManager::ResourcesManager()
: mVFS(NULL)
: mVFS(nullptr)
{
}

@ -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
};
}

@ -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();

@ -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;

@ -64,7 +64,7 @@ namespace CSVDoc
void updateTitle();
void updateSubViewIndices (SubView *view = NULL);
void updateSubViewIndices (SubView *view = nullptr);
void universalIdChanged (const CSMWorld::UniversalId& universalId);

@ -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;
}
}
}

@ -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:

@ -0,0 +1,128 @@
#include "actor.hpp"
#include <osg/Group>
#include <osg/Node>
#include <components/esm/mappings.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/attach.hpp>
#include <components/sceneutil/skeleton.hpp>
#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<osg::Node> temp = sceneMgr->getInstance(model);
mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(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 "";
}
}

@ -0,0 +1,71 @@
#ifndef OPENCS_VIEW_RENDER_ACTOR_H
#define OPENCS_VIEW_RENDER_ACTOR_H
#include <string>
#include <osg/ref_ptr>
#include <QObject>
#include <components/esm/loadarmo.hpp>
#include <components/sceneutil/visitor.hpp>
#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<osg::Group> mBaseNode;
SceneUtil::Skeleton* mSkeleton;
SceneUtil::NodeMapVisitor::NodeMap mNodeMap;
};
}
#endif

@ -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)
{

@ -47,6 +47,7 @@ namespace CSVRender
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
traverse(node, nv);
CellNodeContainer* container = static_cast<CellNodeContainer*>(node->getUserData());
container->getCell()->updateLand();
}

@ -10,6 +10,8 @@
#include "../../model/prefs/state.hpp"
#include "../../model/prefs/shortcutmanager.hpp"
#include <components/misc/constants.hpp>
#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;

@ -5,6 +5,8 @@
#include <osg/Geode>
#include <osgText/Text>
#include <components/misc/constants.hpp>
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.

@ -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

@ -1,6 +1,8 @@
#include "object.hpp"
#include <stdexcept>
#include <string>
#include <iostream>
#include <osg/Depth>
#include <osg/Group>
@ -29,6 +31,7 @@
#include <components/sceneutil/lightmanager.hpp>
#include <components/fallback/fallback.hpp>
#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;
const ESM::Light* light = nullptr;
if (index==-1)
error = 1;
else
{
/// \todo check for Deleted state (error 1)
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
model = referenceables.getData (index,
referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)).
toString().toUtf8().constData();
if (index == -1)
{
mBaseNode->addChild(createErrorCube());
return;
}
int recordType =
referenceables.getData (index,
referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt();
if (recordType == CSMWorld::UniversalId::Type_Light)
{
light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();
if (model.empty())
model = "marker_light.nif";
}
/// \todo check for Deleted state (error 1)
if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)
{
if (model.empty())
model = "marker_creature.nif";
}
int recordType = referenceables.getData(index, TypeIndex).toInt();
std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData();
if (recordType == CSMWorld::UniversalId::Type_Light)
{
light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();
if (model.empty())
error = 2;
model = "marker_light.nif";
}
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
if (error)
if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)
{
mBaseNode->addChild(createErrorCube());
if (model.empty())
model = "marker_creature.nif";
}
else
try
{
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<osg::Node> CSVRender::Object::makeMoveOrScaleMarker (int axis)
osg::ref_ptr<osg::Node> 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] =
{

@ -1,6 +1,7 @@
#ifndef OPENCS_VIEW_OBJECT_H
#define OPENCS_VIEW_OBJECT_H
#include <memory>
#include <string>
#include <osg/ref_ptr>
@ -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<osg::Node> mMarker[3];
int mSubMode;
float mMarkerTransparency;
std::unique_ptr<Actor> mActor;
/// Not implemented
Object (const Object&);

@ -8,6 +8,8 @@
#include <components/esm/loadland.hpp>
#include <components/misc/constants.hpp>
#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<int> (std::floor (point.x()/cellSize)),
static_cast<int> (std::floor (point.y()/cellSize)));
static_cast<int> (std::floor (point.x() / Constants::CellSizeInUnits)),
static_cast<int> (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<int> (std::floor (point.x()/cellSize)),
static_cast<int> (std::floor (point.y()/cellSize)));
static_cast<int> (std::floor (point.x() / Constants::CellSizeInUnits)),
static_cast<int> (std::floor (point.y() / Constants::CellSizeInUnits)));
std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator searchResult = mCells.find(coords);
if (searchResult != mCells.end())

@ -185,7 +185,7 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> 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();

@ -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);

@ -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

@ -45,7 +45,7 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo
void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event)
{
QPushButton *button = qobject_cast<QPushButton *>(parentWidget());
if (button != NULL)
if (button != nullptr)
{
QStyleOptionButton option;
option.init(button);

@ -11,7 +11,7 @@ CSVWidget::CompleterPopup::CompleterPopup(QWidget *parent)
int CSVWidget::CompleterPopup::sizeHintForRow(int row) const
{
if (model() == NULL)
if (model() == nullptr)
{
return -1;
}

@ -25,7 +25,7 @@ std::string CSVWorld::CellCreator::getId() const
void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const
{
CSMWorld::IdTree *model = dynamic_cast<CSMWorld::IdTree *>(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);

@ -158,7 +158,7 @@ mNotEditableDelegate(table, parent)
CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display)
{
CommandDelegate *delegate = NULL;
CommandDelegate *delegate = nullptr;
std::map<int, CommandDelegate*>::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<QWidget*>(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<QLineEdit *>(mWidget);
if (lineEdit != NULL)
if (lineEdit != nullptr)
{
mContextMenu = lineEdit->createStandardContextMenu();
}
@ -360,11 +360,11 @@ QString CSVWorld::IdContextMenu::getWidgetValue() const
QLabel *label = qobject_cast<QLabel *>(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<CSMWorld::IdTable*>(document.getData().getTableModel(id))),
mLocked(false),
mDocument(document),

@ -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,

@ -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())
{

@ -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<CSMWorld::UniversalId> getDraggedRecords() const = 0;

@ -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));
}

@ -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

@ -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);

@ -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);

@ -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);

@ -1,8 +1,6 @@
#include "util.hpp"
#include <stdexcept>
#include <climits>
#include <cfloat>
#include <QUndoStack>
#include <QMetaProperty>
@ -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<CSVWidget::ColorEditor *>(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<int>::min(), std::numeric_limits<int>::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<CSVWidget::ColorEditor *>(editor);
if (colorEditor != NULL)
if (colorEditor != nullptr)
{
colorEditor->setColor(variant.toInt());
return;

@ -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;
}

@ -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

@ -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?

@ -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;
};
}

@ -297,9 +297,11 @@ namespace MWBase
///< Queues movement for \a ptr (in local space), to be applied in the next call to
/// doPhysics.
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) = 0;
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) = 0;
///< cast a Ray and return true if there is an object in the ray path.
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
virtual bool toggleCollisionMode() = 0;
///< Toggle collision mode for player. If disabled player object should ignore
/// collisions and gravity.
@ -438,12 +440,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;
@ -567,6 +573,8 @@ namespace MWBase
virtual bool isPlayerInJail() const = 0;
virtual void rest() = 0;
virtual void setPlayerTraveling(bool traveling) = 0;
virtual bool isPlayerTraveling() const = 0;

@ -1,6 +1,7 @@
#include "activator.hpp"
#include <components/esm/loadacti.hpp>
#include <components/misc/rng.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
@ -134,4 +135,60 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell);
}
std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const
{
std::string model = getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
std::string creatureId;
for (const ESM::Creature &iter : store.get<ESM::Creature>())
{
if (!iter.mModel.empty() && Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel))
{
creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId;
break;
}
}
if (creatureId.empty())
return std::string();
int type = getSndGenTypeFromName(name);
std::vector<const ESM::SoundGenerator*> sounds;
for (auto sound = store.get<ESM::SoundGenerator>().begin(); sound != store.get<ESM::SoundGenerator>().end(); ++sound)
if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature)))
sounds.push_back(&*sound);
if (!sounds.empty())
return sounds[Misc::Rng::rollDice(sounds.size())]->mSound;
if (type == ESM::SoundGenerator::Land)
return "Body Fall Large";
return std::string();
}
int Activator::getSndGenTypeFromName(const std::string &name)
{
if (name == "left")
return ESM::SoundGenerator::LeftFoot;
if (name == "right")
return ESM::SoundGenerator::RightFoot;
if (name == "swimleft")
return ESM::SoundGenerator::SwimLeft;
if (name == "swimright")
return ESM::SoundGenerator::SwimRight;
if (name == "moan")
return ESM::SoundGenerator::Moan;
if (name == "roar")
return ESM::SoundGenerator::Roar;
if (name == "scream")
return ESM::SoundGenerator::Scream;
if (name == "land")
return ESM::SoundGenerator::Land;
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}
}

@ -10,6 +10,8 @@ namespace MWClass
virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const;
static int getSndGenTypeFromName(const std::string &name);
public:
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const;
@ -44,6 +46,8 @@ namespace MWClass
///< Whether or not to use animated variant of model (default false)
virtual bool isActivator() const;
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
};
}

@ -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);
}
}

@ -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<ESM::Weapon>()->mBase->mData.mChop;
else if(type == ESM::Weapon::AT_Slash)
@ -688,9 +688,9 @@ 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))
return 2;
return ESM::SoundGenerator::SwimLeft;
if(world->isOnGround(ptr))
return 0;
return ESM::SoundGenerator::LeftFoot;
return -1;
}
if(name == "right")
@ -698,23 +698,23 @@ 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))
return 3;
return ESM::SoundGenerator::SwimRight;
if(world->isOnGround(ptr))
return 1;
return ESM::SoundGenerator::RightFoot;
return -1;
}
if(name == "swimleft")
return 2;
return ESM::SoundGenerator::SwimLeft;
if(name == "swimright")
return 3;
return ESM::SoundGenerator::SwimRight;
if(name == "moan")
return 4;
return ESM::SoundGenerator::Moan;
if(name == "roar")
return 5;
return ESM::SoundGenerator::Roar;
if(name == "scream")
return 6;
return ESM::SoundGenerator::Scream;
if(name == "land")
return 7;
return ESM::SoundGenerator::Land;
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}
@ -828,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],

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save