1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-23 03:23:54 +00:00

Merge branch 'master' into coverity_scan

This commit is contained in:
Marc Zinnschlag 2018-10-19 09:51:15 +02:00
commit 85f9b0d004
433 changed files with 5135 additions and 2537 deletions

View file

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

View file

@ -1,6 +1,7 @@
0.45.0 0.45.0
------ ------
Bug #1875: Actors in inactive cells don't heal from resting
Bug #1990: Sunrise/sunset not set correct Bug #1990: Sunrise/sunset not set correct
Bug #2131: Lustidrike's spell misses the player every time Bug #2131: Lustidrike's spell misses the player every time
Bug #2222: Fatigue's effect on selling price is backwards 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 #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 #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 #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 #3249: Fixed revert function not updating views properly
Bug #3288: TrueType fonts are handled incorrectly
Bug #3374: Touch spells not hitting kwama foragers Bug #3374: Touch spells not hitting kwama foragers
Bug #3486: [Mod] NPC Commands does not work Bug #3486: [Mod] NPC Commands does not work
Bug #3533: GetSpellEffects should detect effects with zero duration Bug #3533: GetSpellEffects should detect effects with zero duration
Bug #3591: Angled hit distance too low Bug #3591: Angled hit distance too low
Bug #3629: DB assassin attack never triggers creature spawning Bug #3629: DB assassin attack never triggers creature spawning
Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers 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 #3788: GetPCInJail and GetPCTraveling do not work as in vanilla
Bug #3836: Script fails to compile when command argument contains "\n" Bug #3836: Script fails to compile when command argument contains "\n"
Bug #3876: Landscape texture painting is misaligned Bug #3876: Landscape texture painting is misaligned
@ -45,6 +49,7 @@
Bug #4230: AiTravel package issues break some Tribunal quests 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 #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 #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 #4271: Scamp flickers when attacking
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
Bug #4286: Scripted animations can be interrupted Bug #4286: Scripted animations can be interrupted
@ -54,6 +59,7 @@
Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4307: World cleanup should remove dead bodies only if death animation is finished
Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4311: OpenMW does not handle RootCollisionNode correctly
Bug #4327: Missing animations during spell/weapon stance switching Bug #4327: Missing animations during spell/weapon stance switching
Bug #4333: Keyboard navigation in containers is not intuitive
Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4358: Running animation is interrupted when magic mode is toggled
Bug #4368: Settings window ok button doesn't have key focus by default Bug #4368: Settings window ok button doesn't have key focus by default
Bug #4378: On-self absorb spells restore stats Bug #4378: On-self absorb spells restore stats
@ -75,6 +81,7 @@
Bug #4460: Script function "Equip" doesn't bypass beast restrictions Bug #4460: Script function "Equip" doesn't bypass beast restrictions
Bug #4461: "Open" spell from non-player caster isn't a crime 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 #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 #4469: Abot Silt Striders Model turn 90 degrees on horizontal
Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour
Bug #4474: No fallback when getVampireHead fails Bug #4474: No fallback when getVampireHead fails
@ -90,8 +97,11 @@
Bug #4503: Cast and ExplodeSpell commands increase alteration skill Bug #4503: Cast and ExplodeSpell commands increase alteration skill
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4519: Knockdown does not discard movement in the 1st-person mode
Bug #4527: Sun renders on water shader in some situations where it shouldn't
Bug #4531: Movement does not reset idle animations 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 #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 #4545: Creatures flee from werewolves
Bug #4551: Replace 0 sound range with default range separately Bug #4551: Replace 0 sound range with default range separately
Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #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 #4575: Weird result of attack animation blending with movement animations
Bug #4576: Reset of idle animations when attack can not be started 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 #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 #4597: <> operator causes a compile error
Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4604: Picking up gold from the ground only makes 1 grabbed
Bug #4607: Scaling for animated collision shapes is applied twice Bug #4607: Scaling for animated collision shapes is applied twice
Bug #4608: Falling damage 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 #4614: Crash due to division by zero when FlipController has no textures
Bug #4615: Flicker effects for light sources are handled incorrectly 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 #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 #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 #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 #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 #1645: Casting effects from objects
Feature #2606: Editor: Implemented (optional) case sensitive global search 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 #3083: Play animation when NPC is casting spell via script
Feature #3103: Provide option for disposition to get increased by successful trade Feature #3103: Provide option for disposition to get increased by successful trade
Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results Feature #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 #4012: Editor: Write a log file if OpenCS crashes
Feature #4222: 360° screenshots Feature #4222: 360° screenshots
Feature #4256: Implement ToggleBorders (TB) console command Feature #4256: Implement ToggleBorders (TB) console command
Feature #4285: Support soundgen calls for activators
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
Feature #4345: Add equivalents for the command line commands to Launcher Feature #4345: Add equivalents for the command line commands to Launcher
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically 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 #4625: Weapon priority: use weighted mean for melee damage rating
Feature #4626: Weapon priority: account for weapon speed Feature #4626: Weapon priority: account for weapon speed
Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating 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 #2490: Don't open command prompt window on Release-mode builds automatically
Task #4545: Enable is_pod string test Task #4545: Enable is_pod string test
Task #4605: Optimize skinning Task #4605: Optimize skinning
Task #4606: Support Rapture3D's OpenAL driver Task #4606: Support Rapture3D's OpenAL driver
Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9
Task #4621: Optimize combat AI 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 0.44.0
------ ------

View file

@ -346,7 +346,7 @@ if [ -z $SKIP_DOWNLOAD ]; then
# OpenAL # OpenAL
download "OpenAL-Soft 1.17.2" \ 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" "OpenAL-Soft-1.17.2.zip"
# OSG # OSG

View file

@ -1,6 +1,6 @@
#!/bin/sh -e #!/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 cd googletest
mkdir build mkdir build
cd build cd build

View file

@ -8,6 +8,8 @@
#include <components/esm/creaturestate.hpp> #include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp> #include <components/esm/containerstate.hpp>
#include <components/misc/constants.hpp>
#include "convertcrec.hpp" #include "convertcrec.hpp"
#include "convertcntc.hpp" #include "convertcntc.hpp"
#include "convertscri.hpp" #include "convertscri.hpp"
@ -288,12 +290,12 @@ namespace ESSImport
notepos[1] += 31.f; notepos[1] += 31.f;
notepos[0] += 0.5; notepos[0] += 0.5;
notepos[1] += 0.5; notepos[1] += 0.5;
notepos[0] = 8192 * notepos[0] / 32.f; notepos[0] = Constants::CellSizeInUnits * notepos[0] / 32.f;
notepos[1] = 8192 * notepos[1] / 32.f; notepos[1] = Constants::CellSizeInUnits * notepos[1] / 32.f;
if (cell.isExterior()) if (cell.isExterior())
{ {
notepos[0] += 8192 * cell.mData.mX; notepos[0] += Constants::CellSizeInUnits * cell.mData.mX;
notepos[1] += 8192 * cell.mData.mY; notepos[1] += Constants::CellSizeInUnits * cell.mData.mY;
} }
// TODO: what encoding is this in? // TODO: what encoding is this in?
std::string note = esm.getHNString("MPNT"); std::string note = esm.getHNString("MPNT");

View file

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

View file

@ -1,5 +1,6 @@
#include "convertplayer.hpp" #include "convertplayer.hpp"
#include <components/misc/constants.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
namespace ESSImport namespace ESSImport
@ -78,9 +79,8 @@ namespace ESSImport
if (pcdt.mHasENAM) if (pcdt.mHasENAM)
{ {
const int cellSize = 8192; out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * Constants::CellSizeInUnits;
out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize; out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * Constants::CellSizeInUnits;
out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize;
out.mLastKnownExteriorPosition[2] = 0.0f; out.mLastKnownExteriorPosition[2] = 0.0f;
} }
} }

View file

@ -25,6 +25,8 @@
#include <components/esm/loadlevlist.hpp> #include <components/esm/loadlevlist.hpp>
#include <components/esm/loadglob.hpp> #include <components/esm/loadglob.hpp>
#include <components/misc/constants.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "importercontext.hpp" #include "importercontext.hpp"
@ -413,9 +415,8 @@ namespace ESSImport
if (context.mPlayer.mCellId.mPaged) if (context.mPlayer.mCellId.mPaged)
{ {
// exterior cell -> determine cell coordinates based on position // 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] / Constants::CellSizeInUnits));
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] / Constants::CellSizeInUnits));
int cellY = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize));
context.mPlayer.mCellId.mIndex.mX = cellX; context.mPlayer.mCellId.mIndex.mX = cellX;
context.mPlayer.mCellId.mIndex.mY = cellY; context.mPlayer.mCellId.mIndex.mY = cellY;
} }

View file

@ -14,13 +14,13 @@ namespace ESSImport
{ {
struct GMDT struct GMDT
{ {
char mCellName[64]; char mCellName[64] {};
int mFogColour; int mFogColour {0};
float mFogDensity; float mFogDensity {0.f};
int mCurrentWeather, mNextWeather; int mCurrentWeather {0}, mNextWeather {0};
int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage int mWeatherTransition {0}; // 0-100 transition between weathers, top 3 bytes may be garbage
float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition float mTimeOfNextTransition {0.f}; // weather changes when gamehour == timeOfNextTransition
int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage int mMasserPhase {0}, mSecundaPhase {0}; // top 3 bytes may be garbage
}; };
GMDT mGMDT; GMDT mGMDT;

View file

@ -72,6 +72,7 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
@ -131,6 +132,7 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");

View file

@ -36,6 +36,8 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
ui.setupUi (this); ui.setupUi (this);
setObjectName ("DataFilesPage"); setObjectName ("DataFilesPage");
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); 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); 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)); QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles));
std::sort(cellNamesList.begin(), cellNamesList.end()); std::sort(cellNamesList.begin(), cellNamesList.end());
emit signalLoadedCellsChanged(cellNamesList); emit signalLoadedCellsChanged(cellNamesList);
} }

View file

@ -19,6 +19,7 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world opencs_units (model/world
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel 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 mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages gmstcheck topicinfocheck journalcheck mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck
) )
opencs_hdrs_noqt (model/tools opencs_hdrs_noqt (model/tools
@ -88,7 +89,7 @@ opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode instancemovemode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater terraintexturemode cellwater terraintexturemode actor
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render

View file

@ -22,7 +22,7 @@ CS::Editor::Editor (int argc, char **argv)
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), : mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""), mViewManager (mDocumentManager), mPid(""),
mLock(), mMerge (mDocumentManager), 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(); 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); mCfgMgr.readConfiguration(variables, desc, quiet);
mDocumentManager.setEncoding ( const std::string encoding = variables["encoding"].as<Files::EscapeHashString>().toStdString();
ToUTF8::calculateEncoding (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()); mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::EscapeHashString>().toStdString());
@ -338,7 +339,7 @@ bool CS::Editor::makeIPCServer()
} }
mServer->close(); mServer->close();
mServer = NULL; mServer = nullptr;
return false; return false;
} }

View file

@ -35,11 +35,6 @@ void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string&
mMessages.push_back (Message (id, message, hint, severity)); 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 CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const
{ {
return mMessages.begin(); return mMessages.begin();

View file

@ -56,9 +56,6 @@ namespace CSMDoc
const std::string& hint = "", const std::string& hint = "",
Message::Severity severity = Message::Severity_Default); 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 begin() const;
Iterator end() const; Iterator end() const;

View file

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

View file

@ -305,6 +305,7 @@ void CSMPrefs::State::declare()
declareShortcut ("document-assets-videos", "Open Video Asset List", QKeySequence()); declareShortcut ("document-assets-videos", "Open Video Asset List", QKeySequence());
declareShortcut ("document-debug-run", "Run Debug", QKeySequence()); declareShortcut ("document-debug-run", "Run Debug", QKeySequence());
declareShortcut ("document-debug-shutdown", "Stop Debug", QKeySequence()); declareShortcut ("document-debug-shutdown", "Stop Debug", QKeySequence());
declareShortcut ("document-debug-profiles", "Debug Profiles", QKeySequence());
declareShortcut ("document-debug-runlog", "Open Run Log", QKeySequence()); declareShortcut ("document-debug-runlog", "Open Run Log", QKeySequence());
declareSubcategory ("Table"); declareSubcategory ("Table");

View file

@ -1,16 +1,15 @@
#include "birthsigncheck.hpp" #include "birthsigncheck.hpp"
#include <sstream> #include <components/misc/resourcehelpers.hpp>
#include <map>
#include <components/esm/loadbsgn.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns) CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns,
: mBirthsigns (birthsigns) const CSMWorld::Resources &textures)
: mBirthsigns(birthsigns),
mTextures(textures)
{ {
mIgnoreBaseRecords = false; mIgnoreBaseRecords = false;
} }
@ -34,17 +33,20 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId);
// test for empty name, description and texture
if (birthsign.mName.empty()) 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()) 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()) if (birthsign.mTexture.empty())
messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture")); messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error);
else if (mTextures.searchId(birthsign.mTexture) == -1)
/// \todo test if the texture exists {
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 /// \todo check data members that can't be edited in the table view
} }

View file

@ -4,6 +4,7 @@
#include <components/esm/loadbsgn.hpp> #include <components/esm/loadbsgn.hpp>
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "../world/resources.hpp"
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
@ -12,12 +13,14 @@ namespace CSMTools
/// \brief VerifyStage: make sure that birthsign records are internally consistent /// \brief VerifyStage: make sure that birthsign records are internally consistent
class BirthsignCheckStage : public CSMDoc::Stage class BirthsignCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns; const CSMWorld::IdCollection<ESM::BirthSign> &mBirthsigns;
const CSMWorld::Resources &mTextures;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
public: public:
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns); BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign> &birthsigns,
const CSMWorld::Resources &textures);
virtual int setup(); virtual int setup();
///< \return number of steps ///< \return number of steps

View file

@ -34,25 +34,23 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
// Check BYDT // Check BYDT
if (bodyPart.mData.mPart > 14 ) 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 ) 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 ) 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 // Check MODL
if ( bodyPart.mModel.empty() ) 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 ) 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 // Check FNAM
if ( bodyPart.mRace.empty() ) 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 ) 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);
} }

View file

@ -1,6 +1,5 @@
#include "classcheck.hpp" #include "classcheck.hpp"
#include <sstream>
#include <map> #include <map>
#include <components/esm/loadclas.hpp> #include <components/esm/loadclas.hpp>
@ -37,26 +36,22 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
// A class should have a name // A class should have a name
if (class_.mName.empty()) 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 // A playable class should have a description
if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) 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 // test for invalid attributes
for (int i=0; i<2; ++i) for (int i=0; i<2; ++i)
if (class_.mData.mAttribute[i]==-1) if (class_.mData.mAttribute[i]==-1)
{ {
std::ostringstream stream; messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error);
stream << "Attribute #" << i << " of " << class_.mId << " is not set";
messages.push_back (std::make_pair (id, stream.str()));
} }
if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) 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 // 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) for (int i2=0; i2<2; ++i2)
++skills[class_.mData.mSkills[i][i2]]; ++skills[class_.mData.mSkills[i][i2]];
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) for (auto &skill : skills)
if (iter->second>1) if (skill.second>1)
{ {
messages.push_back (std::make_pair (id, messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error);
ESM::Skill::indexToId (iter->first) + " is listed more than once"));
} }
} }

View file

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

View file

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

View file

@ -1,9 +1,7 @@
#include "factioncheck.hpp" #include "factioncheck.hpp"
#include <sstream>
#include <map> #include <map>
#include <components/esm/loadfact.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
@ -37,12 +35,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages
// test for empty name // test for empty name
if (faction.mName.empty()) 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 // test for invalid attributes
if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) 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 // test for non-unique skill
@ -52,11 +50,10 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages
if (faction.mData.mSkills[i]!=-1) if (faction.mData.mSkills[i]!=-1)
++skills[faction.mData.mSkills[i]]; ++skills[faction.mData.mSkills[i]];
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) for (auto &skill : skills)
if (iter->second>1) if (skill.second>1)
{ {
messages.push_back (std::make_pair (id, messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error);
ESM::Skill::indexToId (iter->first) + " is listed more than once"));
} }
/// \todo check data members that can't be edited in the table view /// \todo check data members that can't be edited in the table view

View file

@ -1,7 +1,6 @@
#include "journalcheck.hpp" #include "journalcheck.hpp"
#include <set> #include <set>
#include <sstream>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
@ -57,34 +56,27 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
if (journalInfo.mResponse.empty()) if (journalInfo.mResponse.empty())
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning);
messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning);
} }
std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex); std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);
// Duplicate index // Duplicate index
if (result.second == false) if (!result.second)
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
messages.add(id, "Duplicated quest index " + std::to_string(journalInfo.mData.mJournalIndex), "", CSMDoc::Message::Severity_Error);
std::ostringstream stream;
stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex;
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
} }
} }
if (totalInfoCount == 0) if (totalInfoCount == 0)
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
messages.add(id, "No related journal entry", "", CSMDoc::Message::Severity_Warning);
messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning);
} }
else if (statusNamedCount > 1) else if (statusNamedCount > 1)
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
messages.add(id, "Multiple entries with quest status 'Named'", "", CSMDoc::Message::Severity_Error);
messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error);
} }
} }

View file

@ -4,79 +4,26 @@
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/resources.hpp" std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id,
#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,
const CSMWorld::UniversalId &type, const CSMWorld::UniversalId &type,
const std::string &column) const const std::string &column) const
{ {
std::string error; CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id);
if (!id.empty()) if (index.first == -1)
{ return (column + " '" + id + "' does not exist");
CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id); else if (index.second != type.getType())
if (index.first == -1) return (column + " '" + id + "' does not have " + type.getTypeName() + " type");
{ return std::string();
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;
} }
CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects, CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds, const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables, const CSMWorld::RefIdCollection &objects,
const CSMWorld::Resources &icons, const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures) const CSMWorld::Resources &textures)
: mMagicEffects(effects), : mMagicEffects(effects),
mSounds(sounds), mSounds(sounds),
mReferenceables(referenceables), mObjects(objects),
mIcons(icons), mIcons(icons),
mTextures(textures) mTextures(textures)
{ {
@ -100,46 +47,75 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa
ESM::MagicEffect effect = record.get(); ESM::MagicEffect effect = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId); 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) 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()) 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, if (!effect.mCasting.empty())
id,
checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound"));
if (effect.mDescription.empty())
{ {
messages.push_back(std::make_pair(id, "Description is empty")); const std::string error = checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
} }
if (!effect.mHit.empty())
{
const std::string error = checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mArea.empty())
{
const std::string error = checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mBolt.empty())
{
const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mCastSound.empty() && mSounds.searchId(effect.mCastSound) == -1)
messages.add(id, "Casting sound '" + effect.mCastSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mHitSound.empty() && mSounds.searchId(effect.mHitSound) == -1)
messages.add(id, "Hit sound '" + effect.mHitSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mAreaSound.empty() && mSounds.searchId(effect.mAreaSound) == -1)
messages.add(id, "Area sound '" + effect.mAreaSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mBoltSound.empty() && mSounds.searchId(effect.mBoltSound) == -1)
messages.add(id, "Bolt sound '" + effect.mBoltSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
} }

View file

@ -6,14 +6,10 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "../world/refidcollection.hpp" #include "../world/refidcollection.hpp"
#include "../world/resources.hpp"
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
namespace CSMWorld
{
class Resources;
}
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that magic effect records are internally consistent /// \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::MagicEffect> &mMagicEffects;
const CSMWorld::IdCollection<ESM::Sound> &mSounds; const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables; const CSMWorld::RefIdCollection &mObjects;
const CSMWorld::Resources &mIcons; const CSMWorld::Resources &mIcons;
const CSMWorld::Resources &mTextures; const CSMWorld::Resources &mTextures;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
private: private:
bool isTextureExists(const std::string &texture, bool isIcon) const; std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) 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;
public: public:
MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects, MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds, const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables, const CSMWorld::RefIdCollection &objects,
const CSMWorld::Resources &icons, const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures); const CSMWorld::Resources &textures);

View file

@ -37,9 +37,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
// check the number of pathgrid points // check the number of pathgrid points
if (pathgrid.mData.mS2 < static_cast<int>(pathgrid.mPoints.size())) 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())) 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<CSMTools::Point> pointList(pathgrid.mPoints.size());
std::vector<int> duplList; 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) if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1)
{ {
std::ostringstream ss; std::ostringstream ss;
ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0 ss << "Duplicate edge between points #" << pathgrid.mEdges[i].mV0 << " and #" << pathgrid.mEdges[i].mV1;
<< " and " << pathgrid.mEdges[i].mV1; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
break; break;
} }
} }
@ -70,8 +69,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
else else
{ {
std::ostringstream ss; std::ostringstream ss;
ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0; ss << "An edge is connected to a non-existent point #" << pathgrid.mEdges[i].mV0;
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
} }
} }
@ -93,18 +92,15 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
if (!foundReverse) if (!foundReverse)
{ {
std::ostringstream ss; std::ostringstream ss;
ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; ss << "Missing edge between points #" << i << " and #" << pointList[i].mOtherIndex[j];
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
} }
} }
// check duplicate points // check duplicate points
// FIXME: how to do this efficiently? // 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 && if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX &&
pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY &&
pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) 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()) if (it == duplList.end())
{ {
std::ostringstream ss; std::ostringstream ss;
ss << " has a duplicated point (" << i ss << "Point #" << i << " duplicates point #" << j
<< ") x=" << pathgrid.mPoints[i].mX << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")";
<< ", y=" << pathgrid.mPoints[i].mY messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning);
<< ", z=" << pathgrid.mPoints[i].mZ;
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning);
duplList.push_back(i); duplList.push_back(i);
break; break;
@ -132,11 +126,11 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
if (pointList[i].mConnectionNum == 0) if (pointList[i].mConnectionNum == 0)
{ {
std::ostringstream ss; std::ostringstream ss;
ss << " has an orphaned point (" << i ss << "Point #" << i << " ("
<< ") x=" << pathgrid.mPoints[i].mX << pathgrid.mPoints[i].mX << ", "
<< ", y=" << pathgrid.mPoints[i].mY << pathgrid.mPoints[i].mY << ", "
<< ", z=" << pathgrid.mPoints[i].mZ; << pathgrid.mPoints[i].mZ << ") is disconnected from other points";
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning);
} }
} }

View file

@ -1,9 +1,5 @@
#include "racecheck.hpp" #include "racecheck.hpp"
#include <sstream>
#include <components/esm/loadrace.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -29,24 +25,24 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me
// test for empty name and description // test for empty name and description
if (race.mName.empty()) 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()) 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 // test for positive height
if (race.mData.mHeight.mMale<=0) 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) 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 // test for non-negative weight
if (race.mData.mWeight.mMale<0) 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) 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 /// \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); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);
if (!mPlayable) 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) CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)

View file

@ -1,6 +1,7 @@
#include "referenceablecheck.hpp" #include "referenceablecheck.hpp"
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
@ -11,13 +12,18 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races, const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,
const CSMWorld::IdCollection<ESM::Class>& classes, const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& faction, const CSMWorld::IdCollection<ESM::Faction>& faction,
const CSMWorld::IdCollection<ESM::Script>& scripts) const CSMWorld::IdCollection<ESM::Script>& scripts,
: const CSMWorld::Resources& models,
mReferencables(referenceable), const CSMWorld::Resources& icons,
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts)
:mReferencables(referenceable),
mRaces(races), mRaces(races),
mClasses(classes), mClasses(classes),
mFactions(faction), mFactions(faction),
mScripts(scripts), mScripts(scripts),
mModels(models),
mIcons(icons),
mBodyParts(bodyparts),
mPlayerPresent(false) mPlayerPresent(false)
{ {
mIgnoreBaseRecords = false; mIgnoreBaseRecords = false;
@ -270,9 +276,10 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get(); const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId);
//Checking for model, IIRC all activators should have a model
if (activator.mModel.empty()) 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 // Check that mentioned scripts exist
scriptCheck<ESM::Activator>(activator, messages, id.toString()); scriptCheck<ESM::Activator>(activator, messages, id.toString());
@ -293,7 +300,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId);
inventoryItemCheck<ESM::Potion>(potion, messages, id.toString()); 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 // Check that mentioned scripts exist
scriptCheck<ESM::Potion>(potion, messages, id.toString()); scriptCheck<ESM::Potion>(potion, messages, id.toString());
@ -338,13 +345,13 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
inventoryItemCheck<ESM::Armor>(armor, messages, id.toString(), true); 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) 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) 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 // Check that mentioned scripts exist
scriptCheck<ESM::Armor>(armor, messages, id.toString()); 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(); const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); 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()) 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) //Checking for capacity (weight)
if (container.mWeight < 0) //0 is allowed if (container.mWeight < 0) //0 is allowed
messages.push_back (std::make_pair (id, messages.add(id, "Capacity is negative", "", CSMDoc::Message::Severity_Error);
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"));
//checking contained items //checking contained items
inventoryListCheck(container.mInventory.mList, messages, id.toString()); 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(); const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); 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()) 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 //stats checks
if (creature.mData.mLevel < 1) if (creature.mData.mLevel <= 0)
messages.push_back (std::make_pair (id, creature.mId + " has non-positive level")); messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mStrength < 0) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) for (int i = 0; i < 6; ++i)
{ {
if (creature.mData.mAttack[i] < 0) if (creature.mData.mAttack[i] < 0)
{ messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error);
messages.push_back (std::make_pair (id, if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1])
creature.mId + " has negative attack strength")); messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Error);
break;
}
} }
//TODO, find meaning of other values if (creature.mData.mGold < 0)
if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error);
messages.push_back (std::make_pair (id, creature.mId + " has negative gold "));
if (creature.mScale == 0) 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 // Check inventory
inventoryListCheck(creature.mInventory.mList, messages, id.toString()); inventoryListCheck(creature.mInventory.mList, messages, id.toString());
// Check that mentioned scripts exist // Check that mentioned scripts exist
scriptCheck<ESM::Creature>(creature, messages, id.toString()); scriptCheck<ESM::Creature>(creature, messages, id.toString());
/// \todo Check spells, teleport table, AI data and AI packages for validity
} }
void CSMTools::ReferenceableCheckStage::doorCheck( void CSMTools::ReferenceableCheckStage::doorCheck(
@ -495,10 +516,12 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
//usual, name or model //usual, name or model
if (door.mName.empty()) 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()) 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 // Check that mentioned scripts exist
scriptCheck<ESM::Door>(door, messages, id.toString()); scriptCheck<ESM::Door>(door, messages, id.toString());
@ -572,7 +595,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId);
if (light.mData.mRadius < 0) 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) if (light.mData.mFlags & ESM::Light::Carry)
inventoryItemCheck<ESM::Light>(light, messages, id.toString()); inventoryItemCheck<ESM::Light>(light, messages, id.toString());
@ -644,96 +667,75 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
return; return;
short level(npc.mNpdt.mLevel); 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); int gold(npc.mNpdt.mGold);
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated
{ {
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag 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; return;
} }
level = npc.mNpdt.mLevel;
disposition = npc.mNpdt.mDisposition;
reputation = npc.mNpdt.mReputation;
rank = npc.mNpdt.mRank;
gold = npc.mNpdt.mGold;
} }
else else
{ {
if (npc.mNpdt.mAgility == 0)
messages.push_back (std::make_pair (id, npc.mId + " agility has zero value"));
if (npc.mNpdt.mEndurance == 0)
messages.push_back (std::make_pair (id, npc.mId + " endurance has zero value"));
if (npc.mNpdt.mIntelligence == 0)
messages.push_back (std::make_pair (id, npc.mId + " intelligence has zero value"));
if (npc.mNpdt.mLuck == 0)
messages.push_back (std::make_pair (id, npc.mId + " luck has zero value"));
if (npc.mNpdt.mPersonality == 0)
messages.push_back (std::make_pair (id, npc.mId + " personality has zero value"));
if (npc.mNpdt.mStrength == 0) if (npc.mNpdt.mStrength == 0)
messages.push_back (std::make_pair (id, npc.mId + " strength has zero value")); messages.add(id, "Strength is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mIntelligence == 0)
if (npc.mNpdt.mSpeed == 0) messages.add(id, "Intelligence is equal to zero", "", CSMDoc::Message::Severity_Warning);
messages.push_back (std::make_pair (id, npc.mId + " speed has zero value"));
if (npc.mNpdt.mWillpower == 0) if (npc.mNpdt.mWillpower == 0)
messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value")); messages.add(id, "Willpower is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mAgility == 0)
messages.add(id, "Agility is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mSpeed == 0)
messages.add(id, "Speed is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mEndurance == 0)
messages.add(id, "Endurance is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mPersonality == 0)
messages.add(id, "Personality is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mLuck == 0)
messages.add(id, "Luck is equal to zero", "", CSMDoc::Message::Severity_Warning);
} }
if (level < 1) if (level <= 0)
messages.push_back (std::make_pair (id, npc.mId + " level is non positive")); messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
if (gold < 0) 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()) 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()) 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) 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()) 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) 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) if (!npc.mFaction.empty() && mFactions.searchId(npc.mFaction) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has negative disposition")); 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
{
messages.push_back (std::make_pair (id, npc.mId + " has negative reputation"));
}
if (!npc.mFaction.empty())
{
if (rank < 0)
messages.push_back (std::make_pair (id, npc.mId + " has negative rank"));
if (mFactions.searchId(npc.mFaction) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has invalid faction"));
}
if (npc.mHead.empty()) if (npc.mHead.empty())
messages.push_back (std::make_pair (id, npc.mId + " has no head")); messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error);
else
{
if (mBodyParts.searchId(npc.mHead) == -1)
messages.add(id, "Head body part '" + npc.mHead + "' does not exist", "", CSMDoc::Message::Severity_Error);
/// \todo Check gender, race and other body parts stuff validity for the specific NPC
}
if (npc.mHair.empty()) if (npc.mHair.empty())
messages.push_back (std::make_pair (id, npc.mId + " has no hair")); messages.add(id, "Hair is missing", "", CSMDoc::Message::Severity_Error);
else
//TODO: reputation, Disposition, rank, everything else {
if (mBodyParts.searchId(npc.mHair) == -1)
messages.add(id, "Hair body part '" + npc.mHair + "' does not exist", "", CSMDoc::Message::Severity_Error);
/// \todo Check gender, race and other body part stuff validity for the specific NPC
}
// Check inventory // Check inventory
inventoryListCheck(npc.mInventory.mList, messages, id.toString()); inventoryListCheck(npc.mInventory.mList, messages, id.toString());
@ -793,28 +795,25 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
weapon.mData.mType == ESM::Weapon::Bolt)) weapon.mData.mType == ESM::Weapon::Bolt))
{ {
if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1])
messages.push_back (std::make_pair (id, messages.add(id, "Minimum slash damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
weapon.mId + " has minimum slash damage higher than maximum"));
if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1])
messages.push_back (std::make_pair (id, messages.add(id, "Minimum thrust damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
weapon.mId + " has minimum thrust damage higher than maximum"));
} }
if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) if (weapon.mData.mChop[0] > weapon.mData.mChop[1])
messages.push_back (std::make_pair (id, messages.add(id, "Minimum chop damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
weapon.mId + " has minimum chop damage higher than maximum"));
if (!(weapon.mData.mType == ESM::Weapon::Arrow || if (!(weapon.mData.mType == ESM::Weapon::Arrow ||
weapon.mData.mType == ESM::Weapon::Bolt || weapon.mData.mType == ESM::Weapon::Bolt ||
weapon.mData.mType == ESM::Weapon::MarksmanThrown)) weapon.mData.mType == ESM::Weapon::MarksmanThrown))
{ {
//checking of health //checking of health
if (weapon.mData.mHealth <= 0) if (weapon.mData.mHealth == 0)
messages.push_back (std::make_pair (id, weapon.mId + " has non-positive health")); messages.add(id, "Durability is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (weapon.mData.mReach < 0) 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); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId);
if (staticElement.mModel.empty()) 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 //final check
@ -885,8 +886,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck (
void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages) void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages)
{ {
if (!mPlayerPresent) if (!mPlayerPresent)
messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables, messages.add(CSMWorld::UniversalId::Type_Referenceables, "Player record is missing", "", CSMDoc::Message::Severity_SeriousError);
"There is no player record"));
} }
void CSMTools::ReferenceableCheckStage::inventoryListCheck( void CSMTools::ReferenceableCheckStage::inventoryListCheck(
@ -900,8 +900,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName);
if (localIndex.first == -1) if (localIndex.first == -1)
messages.push_back (std::make_pair (id, messages.add(id, "Item '" + itemName + "' does not exist", "", CSMDoc::Message::Severity_Error);
id + " contains non-existing item (" + itemName + ")"));
else else
{ {
// Needs to accommodate containers, creatures, and NPCs // Needs to accommodate containers, creatures, and NPCs
@ -922,8 +921,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
case CSMWorld::UniversalId::Type_ItemLevelledList: case CSMWorld::UniversalId::Type_ItemLevelledList:
break; break;
default: default:
messages.push_back (std::make_pair(id, messages.add(id, "'" + itemName + "' is not an item", "", CSMDoc::Message::Severity_Error);
id + " contains item of invalid type (" + itemName + ")"));
} }
} }
} }
@ -935,67 +933,82 @@ template<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemChe
const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable) const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable)
{ {
if (someItem.mName.empty()) 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 //Checking for weight
if (someItem.mData.mWeight < 0) 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 //Checking for value
if (someItem.mData.mValue < 0) 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 //checking for model
if (someItem.mModel.empty()) 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 //checking for icon
if (someItem.mIcon.empty()) 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) 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 ( template<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemCheck (
const Item& someItem, CSMDoc::Messages& messages, const std::string& someID) const Item& someItem, CSMDoc::Messages& messages, const std::string& someID)
{ {
if (someItem.mName.empty()) 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 //Checking for weight
if (someItem.mData.mWeight < 0) 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 //Checking for value
if (someItem.mData.mValue < 0) 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 //checking for model
if (someItem.mModel.empty()) 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 //checking for icon
if (someItem.mIcon.empty()) 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 ( template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck (
const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken) const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken)
{ {
if (someTool.mData.mQuality <= 0) 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) if (canBeBroken && someTool.mData.mUses<=0)
messages.push_back (std::make_pair (someID, messages.add(someID, "Number of uses is non-positive", "", CSMDoc::Message::Severity_Error);
someTool.mId + " has non-positive uses count"));
} }
template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck ( template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck (
const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID)
{ {
if (someTool.mData.mQuality <= 0) 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 ( 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) for (unsigned i = 0; i < someList.mList.size(); ++i)
{ {
if (mReferencables.searchId(someList.mList[i].mId).first == -1) if (mReferencables.searchId(someList.mList[i].mId).first == -1)
messages.push_back (std::make_pair (someID, messages.add(someID, "Object '" + someList.mList[i].mId + "' does not exist", "", CSMDoc::Message::Severity_Error);
someList.mId + " contains item without referencable"));
if (someList.mList[i].mLevel < 1) if (someList.mList[i].mLevel < 1)
messages.push_back (std::make_pair (someID, messages.add(someID, "Level of item '" + someList.mList[i].mId + "' is non-positive", "", CSMDoc::Message::Severity_Error);
someList.mId + " contains item with non-positive level"));
} }
} }
@ -1019,6 +1030,6 @@ template<typename Tool> void CSMTools::ReferenceableCheckStage::scriptCheck (
if (!someTool.mScript.empty()) if (!someTool.mScript.empty())
{ {
if (mScripts.searchId(someTool.mScript) == -1) 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);
} }
} }

View file

@ -5,6 +5,7 @@
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
#include "../world/data.hpp" #include "../world/data.hpp"
#include "../world/refiddata.hpp" #include "../world/refiddata.hpp"
#include "../world/resources.hpp"
namespace CSMTools namespace CSMTools
{ {
@ -16,7 +17,10 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Race>& races, const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Class>& classes, const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions, 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 void perform(int stage, CSMDoc::Messages& messages);
virtual int setup(); virtual int setup();
@ -81,6 +85,9 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Class>& mClasses; const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::Script>& mScripts; const CSMWorld::IdCollection<ESM::Script>& mScripts;
const CSMWorld::Resources& mModels;
const CSMWorld::Resources& mIcons;
const CSMWorld::IdCollection<ESM::BodyPart>& mBodyParts;
bool mPlayerPresent; bool mPlayerPresent;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
}; };

View file

@ -9,7 +9,7 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage(
const CSMWorld::IdCollection<ESM::Faction>& factions) const CSMWorld::IdCollection<ESM::Faction>& factions)
: :
mReferences(references), mReferences(references),
mReferencables(referencables), mObjects(referencables),
mDataSet(referencables.getDataSet()), mDataSet(referencables.getDataSet()),
mCells(cells), mCells(cells),
mFactions(factions) mFactions(factions)
@ -28,78 +28,59 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message
const CSMWorld::CellRef& cellRef = record.get(); const CSMWorld::CellRef& cellRef = record.get();
const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId); const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId);
// Check for empty reference id // Check reference id
if (cellRef.mRefID.empty()) { if (cellRef.mRefID.empty())
messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)")); messages.add(id, "Instance is not based on an object", "", CSMDoc::Message::Severity_Error);
} else { else
{
// Check for non existing referenced object // Check for non existing referenced object
if (mReferencables.searchId(cellRef.mRefID) == -1) { if (mObjects.searchId(cellRef.mRefID) == -1)
messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID)); messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID + "'", "", CSMDoc::Message::Severity_Error);
} else { else
{
// Check if reference charge is valid for it's proper referenced type // Check if reference charge is valid for it's proper referenced type
CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID); CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID);
bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light; bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light;
if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) { if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1))
std::string str = " has invalid charge "; messages.add(id, "Invalid charge", "", CSMDoc::Message::Severity_Error);
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 object have owner, check if that owner reference is valid // If object have owner, check if that owner reference is valid
if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1) if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1)
messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner)); 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 object have creature soul trapped, check if that creature reference is valid
if (!cellRef.mSoul.empty()) if (!cellRef.mSoul.empty())
if (mReferencables.searchId(cellRef.mSoul) == -1) if (mObjects.searchId(cellRef.mSoul) == -1)
messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul)); messages.add(id, "Trapped soul object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error);
bool hasFaction = !cellRef.mFaction.empty(); if (cellRef.mFaction.empty())
// If object have faction, check if that faction reference is valid
if (hasFaction)
if (mFactions.searchId(cellRef.mFaction) == -1)
messages.push_back(std::make_pair(id, " has non existing faction " + cellRef.mFaction));
// Check item's faction rank
if (hasFaction && cellRef.mFactionRank < -1)
messages.push_back(std::make_pair(id, " has faction set but has invalid faction rank " + std::to_string(cellRef.mFactionRank)));
else if (!hasFaction && cellRef.mFactionRank != -2)
messages.push_back(std::make_pair(id, " has invalid faction rank " + std::to_string(cellRef.mFactionRank)));
// If door have destination cell, check if that reference is valid
if (!cellRef.mDestCell.empty())
if (mCells.searchId(cellRef.mDestCell) == -1)
messages.push_back(std::make_pair(id, " has non existing destination cell " + cellRef.mDestCell));
// Check if scale isn't negative
if (cellRef.mScale < 0)
{ {
std::string str = " has negative scale "; if (cellRef.mFactionRank != -2)
str += std::to_string(cellRef.mScale); messages.add(id, "Reference without a faction has a faction rank", "", CSMDoc::Message::Severity_Error);
messages.push_back(std::make_pair(id, id.getId() + str));
} }
else
{
if (mFactions.searchId(cellRef.mFaction) == -1)
messages.add(id, "Faction '" + cellRef.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error);
else if (cellRef.mFactionRank < -1)
messages.add(id, "Invalid faction rank", "", CSMDoc::Message::Severity_Error);
}
if (!cellRef.mDestCell.empty() && mCells.searchId(cellRef.mDestCell) == -1)
messages.add(id, "Destination cell '" + cellRef.mDestCell + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (cellRef.mScale < 0)
messages.add(id, "Negative scale", "", CSMDoc::Message::Severity_Error);
// Check if enchantement points aren't negative or are at full (-1) // Check if enchantement points aren't negative or are at full (-1)
if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1) if (cellRef.mEnchantmentCharge < -1)
{ messages.add(id, "Negative number of enchantment points", "", CSMDoc::Message::Severity_Error);
std::string str = " has negative enchantment points ";
str += std::to_string(cellRef.mEnchantmentCharge);
messages.push_back(std::make_pair(id, id.getId() + str));
}
// Check if gold value isn't negative // Check if gold value isn't negative
if (cellRef.mGoldValue < 0) if (cellRef.mGoldValue < 0)
{ messages.add(id, "Negative gold value", "", CSMDoc::Message::Severity_Error);
std::string str = " has negative gold value ";
str += cellRef.mGoldValue;
messages.push_back(std::make_pair(id, id.getId() + str));
}
} }
int CSMTools::ReferenceCheckStage::setup() int CSMTools::ReferenceCheckStage::setup()

View file

@ -19,7 +19,7 @@ namespace CSMTools
private: private:
const CSMWorld::RefCollection& mReferences; const CSMWorld::RefCollection& mReferences;
const CSMWorld::RefIdCollection& mReferencables; const CSMWorld::RefIdCollection& mObjects;
const CSMWorld::RefIdData& mDataSet; const CSMWorld::RefIdData& mDataSet;
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells; const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;

View file

@ -1,10 +1,5 @@
#include "regioncheck.hpp" #include "regioncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadregn.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -36,7 +31,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
// test for empty name // test for empty name
if (region.mName.empty()) 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 /// \todo test that the ID in mSleeplist exists

View file

@ -30,10 +30,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
stream stream << "line " << loc.mLine << ", column " << loc.mColumn << ": " << message << " (" << loc.mLiteral << ")";
<< "script " << mFile
<< ", line " << loc.mLine << ", column " << loc.mColumn
<< " (" << loc.mLiteral << "): " << message;
std::ostringstream hintStream; 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); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
std::ostringstream stream; std::ostringstream stream;
stream << "script " << mFile << ": " << message; stream << message;
mMessages->add (id, stream.str(), "", getSeverity (type)); 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); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
std::ostringstream stream; std::ostringstream stream;
stream << "script " << mFile << ": " << error.what(); stream << error.what();
messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError); messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError);
} }

View file

@ -1,9 +1,5 @@
#include "skillcheck.hpp" #include "skillcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.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); 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) for (int i=0; i<4; ++i)
if (skill.mData.mUseValue[i]<0) if (skill.mData.mUseValue[i]<0)
{ {
std::ostringstream stream; messages.add(id, "Use value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error);
stream << "Use value #" << i << " of " << skill.mId << " is negative";
messages.push_back (std::make_pair (id, stream.str()));
} }
if (skill.mDescription.empty())
messages.push_back (std::make_pair (id, skill.mId + " has an empty description"));
} }

View file

@ -1,15 +1,13 @@
#include "soundcheck.hpp" #include "soundcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds) CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound> &sounds,
: mSounds (sounds) const CSMWorld::Resources &soundfiles)
: mSounds (sounds),
mSoundFiles (soundfiles)
{ {
mIgnoreBaseRecords = false; mIgnoreBaseRecords = false;
} }
@ -34,7 +32,16 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);
if (sound.mData.mMinRange>sound.mData.mMaxRange) if (sound.mData.mMinRange>sound.mData.mMaxRange)
messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); {
messages.add(id, "Minimum range is larger than maximum range", "", CSMDoc::Message::Severity_Warning);
}
/// \todo check, if the sound file exists if (sound.mSound.empty())
{
messages.add(id, "Sound file is missing", "", CSMDoc::Message::Severity_Error);
}
else if (mSoundFiles.searchId(sound.mSound) == -1)
{
messages.add(id, "Sound file '" + sound.mSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
} }

View file

@ -3,6 +3,7 @@
#include <components/esm/loadsoun.hpp> #include <components/esm/loadsoun.hpp>
#include "../world/resources.hpp"
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
@ -13,11 +14,13 @@ namespace CSMTools
class SoundCheckStage : public CSMDoc::Stage class SoundCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Sound>& mSounds; const CSMWorld::IdCollection<ESM::Sound>& mSounds;
const CSMWorld::Resources &mSoundFiles;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
public: public:
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds); SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds,
const CSMWorld::Resources &soundfiles);
virtual int setup(); virtual int setup();
///< \return number of steps ///< \return number of steps

View file

@ -1,7 +1,5 @@
#include "soundgencheck.hpp" #include "soundgencheck.hpp"
#include <sstream>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/refiddata.hpp" #include "../world/refiddata.hpp"
@ -9,10 +7,10 @@
CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens, CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
const CSMWorld::IdCollection<ESM::Sound> &sounds, const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables) const CSMWorld::RefIdCollection &objects)
: mSoundGens(soundGens), : mSoundGens(soundGens),
mSounds(sounds), mSounds(sounds),
mReferenceables(referenceables) mObjects(objects)
{ {
mIgnoreBaseRecords = false; mIgnoreBaseRecords = false;
} }
@ -37,23 +35,23 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages
if (!soundGen.mCreature.empty()) 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) 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) 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()) 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) 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);
} }
} }

View file

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

View file

@ -34,13 +34,13 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);
// test for empty name and description // test for empty name
if (spell.mName.empty()) 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 // test for invalid cost values
if (spell.mData.mCost<0) 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 /// \todo check data members that can't be edited in the table view
} }

View file

@ -25,8 +25,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId);
if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1) if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1)
messages.push_back ( messages.add(id, "Start script " + scriptId + " does not exist", "", CSMDoc::Message::Severity_Error);
std::make_pair (id, "Start script " + scriptId + " does not exist"));
} }
int CSMTools::StartScriptCheckStage::setup() int CSMTools::StartScriptCheckStage::setup()

View file

@ -32,6 +32,7 @@
#include "gmstcheck.hpp" #include "gmstcheck.hpp"
#include "topicinfocheck.hpp" #include "topicinfocheck.hpp"
#include "journalcheck.hpp" #include "journalcheck.hpp"
#include "enchantmentcheck.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type) 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 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 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 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())); 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 JournalCheckStage(mData.getJournals(), mData.getJournalInfos()));
mVerifierOperation->appendStage (new EnchantmentCheckStage(mData.getEnchantments()));
mVerifier.setOperation (mVerifierOperation); mVerifier.setOperation (mVerifierOperation);
} }

View file

@ -133,8 +133,7 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1) if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)
{ {
std::ostringstream stream; messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error);
messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error);
} }
if (!topicInfo.mRace.empty()) 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, bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages) CSMDoc::Messages& messages)
{ {
const std::string specifier = "Actor";
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor); CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);
if (index.first == -1) if (index.first == -1)
{ {
writeMissingIdError(specifier, actor, id, messages); messages.add(id, "Actor '" + actor + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (mReferencables.getRecord(index).isDeleted()) 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; return false;
} }
else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature) 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; 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, bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages) CSMDoc::Messages& messages)
{ {
const std::string specifier = "Cell";
if (mCellNames.find(cell) == mCellNames.end()) 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; return false;
} }
@ -209,9 +207,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
if (rank < -1) if (rank < -1)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required"; 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);
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
@ -229,8 +226,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
if (rank >= limit) if (rank >= limit)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1 stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1
<< " for the " << factionName << " faction"; << " for the '" << factionName << "' faction";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false; 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, bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages) CSMDoc::Messages& messages)
{ {
const std::string specifier = "Item";
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item); CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);
if (index.first == -1) if (index.first == -1)
{ {
writeMissingIdError(specifier, item, id, messages); messages.add(id, ("Item '" + item + "' does not exist"), "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (mReferencables.getRecord(index).isDeleted()) 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; return false;
} }
else else
@ -276,8 +271,13 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS
break; break;
default: 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; return false;
}
} }
} }
@ -291,13 +291,13 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None) 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; return false;
} }
else if (!infoCondition.variantTypeIsValid()) else if (!infoCondition.variantTypeIsValid())
{ {
std::ostringstream stream; 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()) 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_Long: stream << "Long"; break;
case ESM::VT_Float: stream << "Float"; break; case ESM::VT_Float: stream << "Float"; break;
case ESM::VT_String: stream << "String"; 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); messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (infoCondition.conditionIsAlwaysTrue()) else if (infoCondition.conditionIsAlwaysTrue())
{ {
std::ostringstream stream; messages.add(id, "Condition '" + infoCondition.toString() + "' is always true", "", CSMDoc::Message::Severity_Warning);
stream << "Info Condition: " << infoCondition.toString() << " is always true";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
return false; return false;
} }
else if (infoCondition.conditionIsNeverTrue()) else if (infoCondition.conditionIsNeverTrue())
{ {
std::ostringstream stream; messages.add(id, "Condition '" + infoCondition.toString() + "' is never true", "", CSMDoc::Message::Severity_Warning);
stream << "Info Condition: " << infoCondition.toString() << " is never true";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
return false; 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, bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages) CSMDoc::Messages& messages)
{ {
const std::string specifier = "Sound File";
if (mSoundFiles.searchId(sound) == -1) 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; return false;
} }
@ -402,47 +395,14 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW
if (index == -1) if (index == -1)
{ {
writeMissingIdError(T::getRecordType(), name, id, messages); messages.add(id, T::getRecordType() + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (collection.getRecord(index).isDeleted()) 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 false;
} }
return true; 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);
}

View file

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

View file

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

View file

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

View file

@ -5,6 +5,8 @@
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
#include <components/misc/constants.hpp>
CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} 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) std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y)
{ {
const int cellSize = 8192; return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits));
return std::make_pair (std::floor (x/cellSize), std::floor (y/cellSize));
} }
bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)

View file

@ -318,7 +318,7 @@ namespace CSMWorld
QVariant BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const 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()); return QString::fromUtf8(record.get().mRace.c_str());
} }

View file

@ -244,7 +244,7 @@ void CSMWorld::CreateCommand::applyModifications()
if (!mNestedValues.empty()) if (!mNestedValues.empty())
{ {
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel); 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"); 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); int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell);
mIndex = mModel.index (mRow, cellColumn); mIndex = mModel.index (mRow, cellColumn);
const int cellSize = 8192;
QModelIndex xIndex = mModel.index ( QModelIndex xIndex = mModel.index (
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos)); mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos));
QModelIndex yIndex = mModel.index ( QModelIndex yIndex = mModel.index (
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos)); mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos));
int x = std::floor (mModel.data (xIndex).toFloat() / cellSize); int x = std::floor (mModel.data (xIndex).toFloat() / Constants::CellSizeInUnits);
int y = std::floor (mModel.data (yIndex).toFloat() / cellSize); int y = std::floor (mModel.data (yIndex).toFloat() / Constants::CellSizeInUnits);
std::ostringstream stream; std::ostringstream stream;

View file

@ -572,6 +572,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
UniversalId::Type_Video); UniversalId::Type_Video);
addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData); 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 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; 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() void CSMWorld::Data::merge()
{ {
mGlobals.merge(); mGlobals.merge();

View file

@ -36,6 +36,7 @@
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
#include "actoradapter.hpp"
#include "idcollection.hpp" #include "idcollection.hpp"
#include "nestedidcollection.hpp" #include "nestedidcollection.hpp"
#include "universalid.hpp" #include "universalid.hpp"
@ -110,6 +111,7 @@ namespace CSMWorld
RefCollection mRefs; RefCollection mRefs;
IdCollection<ESM::Filter> mFilters; IdCollection<ESM::Filter> mFilters;
Collection<MetaData> mMetaData; Collection<MetaData> mMetaData;
std::unique_ptr<ActorAdapter> mActorAdapter;
const Fallback::Map* mFallbackMap; const Fallback::Map* mFallbackMap;
std::vector<QAbstractItemModel *> mModels; std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex; 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 /// \note The returned table may either be the model for the ID itself or the model that
/// contains the record specified by the ID. /// contains the record specified by the ID.
const ActorAdapter* getActorAdapter() const;
ActorAdapter* getActorAdapter();
void merge(); void merge();
///< Merge modified into base. ///< Merge modified into base.

View file

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

View file

@ -18,7 +18,7 @@ namespace
void CSMWorld::IdTableProxyModel::updateColumnMap() void CSMWorld::IdTableProxyModel::updateColumnMap()
{ {
Q_ASSERT(mSourceModel != NULL); Q_ASSERT(mSourceModel != nullptr);
mColumnMap.clear(); mColumnMap.clear();
if (mFilter) if (mFilter)
@ -33,7 +33,7 @@ void CSMWorld::IdTableProxyModel::updateColumnMap()
bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent)
const const
{ {
Q_ASSERT(mSourceModel != NULL); Q_ASSERT(mSourceModel != nullptr);
// It is not possible to use filterAcceptsColumn() and check for // It is not possible to use filterAcceptsColumn() and check for
// sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags) // 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) CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
: QSortFilterProxyModel (parent), : QSortFilterProxyModel (parent),
mSourceModel(NULL) mSourceModel(nullptr)
{ {
setSortCaseSensitivity (Qt::CaseInsensitive); setSortCaseSensitivity (Qt::CaseInsensitive);
} }
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const 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)); 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 QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const
{ {
Q_ASSERT(mSourceModel != NULL); Q_ASSERT(mSourceModel != nullptr);
int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id); int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id);
return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString(); return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString();

View file

@ -28,7 +28,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod
{ {
IdTableProxyModel::setSourceModel(sourceModel); IdTableProxyModel::setSourceModel(sourceModel);
if (mSourceModel != NULL) if (mSourceModel != nullptr)
{ {
mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId); mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId);
mFirstRowCache.clear(); mFirstRowCache.clear();
@ -37,7 +37,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod
bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const 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 first = mSourceModel->index(getFirstInfoRow(left.row()), left.column());
QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.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 int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const
{ {
Q_ASSERT(mSourceModel != NULL); Q_ASSERT(mSourceModel != nullptr);
int row = currentRow; int row = currentRow;
int column = mInfoColumnIndex; int column = mInfoColumnIndex;

View file

@ -14,8 +14,8 @@ void CSMWorld::MetaData::blank()
void CSMWorld::MetaData::load (ESM::ESMReader& esm) void CSMWorld::MetaData::load (ESM::ESMReader& esm)
{ {
mFormat = esm.getHeader().mFormat; mFormat = esm.getHeader().mFormat;
mAuthor = esm.getHeader().mData.author.toString(); mAuthor = esm.getHeader().mData.author;
mDescription = esm.getHeader().mData.desc.toString(); mDescription = esm.getHeader().mData.desc;
} }
void CSMWorld::MetaData::save (ESM::ESMWriter& esm) const void CSMWorld::MetaData::save (ESM::ESMWriter& esm) const

View file

@ -453,13 +453,13 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD
CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns)
: ActorColumns (actorColumns), : ActorColumns (actorColumns),
mType(NULL), mType(nullptr),
mScale(NULL), mScale(nullptr),
mOriginal(NULL), mOriginal(nullptr),
mAttributes(NULL), mAttributes(nullptr),
mAttacks(NULL), mAttacks(nullptr),
mMisc(NULL), mMisc(nullptr),
mBloodType(NULL) mBloodType(nullptr)
{} {}
CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns)
@ -748,16 +748,16 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData&
CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns)
: ActorColumns (actorColumns), : ActorColumns (actorColumns),
mRace(NULL), mRace(nullptr),
mClass(NULL), mClass(nullptr),
mFaction(NULL), mFaction(nullptr),
mHair(NULL), mHair(nullptr),
mHead(NULL), mHead(nullptr),
mAttributes(NULL), mAttributes(nullptr),
mSkills(NULL), mSkills(nullptr),
mMisc(NULL), mMisc(nullptr),
mBloodType(NULL), mBloodType(nullptr),
mGender(NULL) mGender(nullptr)
{} {}
CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)

View file

@ -437,7 +437,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer)); mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer));
lightColumns.mRadius = &mColumns.back(); 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(); lightColumns.mColor = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound)); mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound));

View file

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

View file

@ -18,47 +18,43 @@ namespace
static const TypeData sNoArg[] = static const TypeData sNoArg[] =
{ {
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 }, { 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_Globals, "Global Variables", ":./global-variable.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", ":./skill.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", ":./class.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", ":./faction.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", ":./race.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", ":./sound.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", ":./script.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", ":./region.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", ":./birthsign.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", ":./spell.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", ":./dialogue-topics.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", ":./journal-topics.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", ":./dialogue-topic-infos.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", ":./journal-topic-infos.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", ":./cell.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", ":./enchantment.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", ":./body-part.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects", ":./object.png" },
"Objects", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances", ":./instance.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", ":./region-map.png" },
"Instances", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", ":./filter.png" },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", ":./resources-mesh" },
"Region Map", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", ":./resources-icon" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", ":./resources-music" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", ":resources-sound" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", ":./resources-texture" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":./resources-video" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", ":./debug-profile.png" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", ":./sound-generator.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", ":./magic-effect.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":./land-heightmap.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "Land Textures", ":./land-texture.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":./pathgrid.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", ":./start-script.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":./metadata.png" },
{ 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_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker { 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_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, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "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_Activator, "Activator", ":./activator.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.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_Door, "Door", ":./door.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, { 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, { 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_Light, "Light", ":./light.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, { 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_Repair, "Repair", ":./repair.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.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_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_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_Scene, "Scene", ":./scene.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, { 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_Enchantment, "Enchantment", ":./enchantment.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":./body-part.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_Mesh, "Mesh", ":./resources-mesh"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":resources-icon"}, { 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_Music, "Music", ":./resources-music" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":resources-sound" }, { 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_Texture, "Texture", ":./resources-texture" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":resources-video"}, { 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_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_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_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_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_Pathgrid, "Pathgrid", ":./pathgrid.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", ":./start-script.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
}; };
static const TypeData sIndexArg[] = static const TypeData sIndexArg[] =
{ {
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 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", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", ":./error-log.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", 0 }, { 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 { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
}; };
} }

View file

@ -33,6 +33,11 @@ void CSVDoc::FileDialog::addFiles(const QString &path)
mSelector->addFiles(path); mSelector->addFiles(path);
} }
void CSVDoc::FileDialog::setEncoding(const QString &encoding)
{
mSelector->setEncoding(encoding);
}
void CSVDoc::FileDialog::clearFiles() void CSVDoc::FileDialog::clearFiles()
{ {
mSelector->clearFiles(); mSelector->clearFiles();
@ -184,7 +189,7 @@ void CSVDoc::FileDialog::slotRejected()
if(mFileWidget) if(mFileWidget)
{ {
delete mFileWidget; delete mFileWidget;
mFileWidget = NULL; mFileWidget = nullptr;
} }
close(); close();
} }
@ -195,7 +200,7 @@ void CSVDoc::FileDialog::slotNewFile()
if(mFileWidget) if(mFileWidget)
{ {
delete mFileWidget; delete mFileWidget;
mFileWidget = NULL; mFileWidget = nullptr;
} }
disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile()));
close(); close();

View file

@ -42,6 +42,7 @@ namespace CSVDoc
void showDialog (ContentAction action); void showDialog (ContentAction action);
void addFiles (const QString &path); void addFiles (const QString &path);
void setEncoding (const QString &encoding);
void clearFiles (); void clearFiles ();
QString filename() const; QString filename() const;

View file

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

View file

@ -47,58 +47,40 @@ void CSVDoc::View::setupFileMenu()
{ {
QMenu *file = menuBar()->addMenu (tr ("File")); 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())); connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest()));
setupShortcut("document-file-newgame", newGame);
file->addAction (newGame);
QAction* newAddon = createMenuEntry("New Addon", ":./menu-new-addon.png", file, "document-file-newaddon");
QAction *newAddon = new QAction (tr ("New Addon"), this);
connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); 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())); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest()));
setupShortcut("document-file-open", open);
file->addAction (open);
mSave = new QAction (tr ("Save"), this); QAction* save = createMenuEntry("Save", ":./menu-save.png", file, "document-file-save");
connect (mSave, SIGNAL (triggered()), this, SLOT (save())); connect (save, SIGNAL (triggered()), this, SLOT (save()));
setupShortcut("document-file-save", mSave); mSave = save;
file->addAction (mSave);
mVerify = new QAction (tr ("Verify"), this); QAction* verify = createMenuEntry("Verify", ":./menu-verify.png", file, "document-file-verify");
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); connect (verify, SIGNAL (triggered()), this, SLOT (verify()));
setupShortcut("document-file-verify", mVerify); mVerify = verify;
file->addAction (mVerify);
mMerge = new QAction (tr ("Merge"), this); QAction* merge = createMenuEntry("Merge", ":./menu-merge.png", file, "document-file-merge");
connect (mMerge, SIGNAL (triggered()), this, SLOT (merge())); connect (merge, SIGNAL (triggered()), this, SLOT (merge()));
setupShortcut("document-file-merge", mMerge); mMerge = merge;
file->addAction (mMerge);
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())); 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())); 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())); 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 (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 namespace
@ -130,251 +112,174 @@ void CSVDoc::View::setupEditMenu()
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo")); mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
setupShortcut("document-edit-undo", mUndo); setupShortcut("document-edit-undo", mUndo);
connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ())); connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));
mUndo->setIcon(QIcon(QString::fromStdString(":./menu-undo.png")));
edit->addAction (mUndo); edit->addAction (mUndo);
mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo")); mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo"));
connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ())); connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ()));
setupShortcut("document-edit-redo", mRedo); setupShortcut("document-edit-redo", mRedo);
mRedo->setIcon(QIcon(QString::fromStdString(":./menu-redo.png")));
edit->addAction (mRedo); 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())); 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())); connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView()));
setupShortcut("document-edit-search", search);
edit->addAction (search);
} }
void CSVDoc::View::setupViewMenu() void CSVDoc::View::setupViewMenu()
{ {
QMenu *view = menuBar()->addMenu (tr ("View")); 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())); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
setupShortcut("document-view-newview", newWindow);
view->addAction (newWindow);
mShowStatusBar = new QAction (tr ("Toggle Status Bar"), this); mShowStatusBar = createMenuEntry("Toggle Status Bar", ":./menu-status-bar.png", view, "document-view-statusbar");
mShowStatusBar->setCheckable (true);
connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); 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()); mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue());
view->addAction (mShowStatusBar); 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())); connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView()));
setupShortcut("document-view-filters", filters);
view->addAction (filters);
} }
void CSVDoc::View::setupWorldMenu() void CSVDoc::View::setupWorldMenu()
{ {
QMenu *world = menuBar()->addMenu (tr ("World")); 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())); 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())); 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())); 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())); 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())); 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())); 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())); 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 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())); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
setupShortcut("document-world-regionmap", regionMap);
world->addAction (regionMap);
} }
void CSVDoc::View::setupMechanicsMenu() void CSVDoc::View::setupMechanicsMenu()
{ {
QMenu *mechanics = menuBar()->addMenu (tr ("Mechanics")); 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())); 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())); 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())); 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())); 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())); connect (enchantments, SIGNAL (triggered()), this, SLOT (addEnchantmentsSubView()));
setupShortcut("document-mechanics-enchantments", enchantments);
mechanics->addAction (enchantments);
QAction *effects = new QAction (tr ("Magic Effects"), this); QAction* magicEffects = createMenuEntry(CSMWorld::UniversalId::Type_MagicEffects, mechanics, "document-mechanics-magiceffects");
connect (effects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView())); connect (magicEffects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView()));
setupShortcut("document-mechanics-magiceffects", effects);
mechanics->addAction (effects);
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())); connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView()));
setupShortcut("document-mechanics-startscripts", startScripts);
mechanics->addAction (startScripts);
} }
void CSVDoc::View::setupCharacterMenu() void CSVDoc::View::setupCharacterMenu()
{ {
QMenu *characters = menuBar()->addMenu (tr ("Characters")); 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())); 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())); 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())); 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())); 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())); 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())); 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())); 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())); 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())); 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())); connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView()));
setupShortcut("document-character-bodyparts", bodyParts);
characters->addAction (bodyParts);
} }
void CSVDoc::View::setupAssetsMenu() void CSVDoc::View::setupAssetsMenu()
{ {
QMenu *assets = menuBar()->addMenu (tr ("Assets")); 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())); connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged()));
setupShortcut("document-assets-reload", reload);
assets->addAction (reload);
assets->addSeparator(); 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())); 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())); connect (soundGens, SIGNAL (triggered()), this, SLOT (addSoundGensSubView()));
setupShortcut("document-assets-soundgens", soundGens);
assets->addAction (soundGens);
assets->addSeparator(); // resources follow here 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())); 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())); 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())); connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView()));
setupShortcut("document-assets-music", musics);
assets->addAction (musics);
QAction *soundsRes = new QAction (tr ("Sound Files"), this); QAction* soundFiles = createMenuEntry(CSMWorld::UniversalId::Type_SoundsRes, assets, "document-assets-soundres");
connect (soundsRes, SIGNAL (triggered()), this, SLOT (addSoundsResSubView())); connect (soundFiles, SIGNAL (triggered()), this, SLOT (addSoundsResSubView()));
setupShortcut("document-assets-soundres", soundsRes);
assets->addAction (soundsRes);
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())); 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())); connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView()));
setupShortcut("document-assets-videos", videos);
assets->addAction (videos);
} }
void CSVDoc::View::setupDebugMenu() void CSVDoc::View::setupDebugMenu()
{ {
QMenu *debug = menuBar()->addMenu (tr ("Debug")); 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())); connect (profiles, SIGNAL (triggered()), this, SLOT (addDebugProfilesSubView()));
debug->addAction (profiles);
debug->addSeparator(); debug->addSeparator();
@ -387,18 +292,41 @@ void CSVDoc::View::setupDebugMenu()
QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu); QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu);
runDebug->setText (tr ("Run OpenMW")); runDebug->setText (tr ("Run OpenMW"));
setupShortcut("document-debug-run", runDebug); setupShortcut("document-debug-run", runDebug);
runDebug->setIcon(QIcon(QString::fromStdString(":./run-openmw.png")));
mStopDebug = new QAction (tr ("Shutdown OpenMW"), this); QAction* stopDebug = createMenuEntry("Stop OpenMW", ":./stop-openmw.png", debug, "document-debug-shutdown");
connect (mStopDebug, SIGNAL (triggered()), this, SLOT (stop())); connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop()));
setupShortcut("document-debug-shutdown", mStopDebug); mStopDebug = stopDebug;
debug->addAction (mStopDebug);
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())); 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() void CSVDoc::View::setupUi()
@ -472,7 +400,7 @@ void CSVDoc::View::updateSubViewIndices(SubView *view)
else else
{ {
delete subView->titleBarWidget(); 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) CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), : 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"]; CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
@ -635,7 +563,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
return; return;
} }
SubView *view = NULL; SubView *view = nullptr;
if(isReferenceable) if(isReferenceable)
{ {
view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument); 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) void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
{ {
if (*setting=="Windows/hide-subview") if (*setting=="Windows/hide-subview")
updateSubViewIndices (NULL); updateSubViewIndices (nullptr);
else if (*setting=="Windows/mainwindow-scrollbar") else if (*setting=="Windows/mainwindow-scrollbar")
{ {
if (setting->toString()!="Grow Only") if (setting->toString()!="Grow Only")
@ -731,7 +659,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
mScroll->takeWidget(); mScroll->takeWidget();
setCentralWidget (&mSubViewWindow); setCentralWidget (&mSubViewWindow);
mScroll->deleteLater(); mScroll->deleteLater();
mScroll = NULL; mScroll = nullptr;
} }
} }
} }

View file

@ -66,6 +66,9 @@ namespace CSVDoc
void closeEvent (QCloseEvent *event); 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 setupFileMenu();
void setupEditMenu(); void setupEditMenu();
@ -146,7 +149,7 @@ namespace CSVDoc
void updateTitle(); void updateTitle();
// called when subviews are added or removed // called when subviews are added or removed
void updateSubViewIndices (SubView *view = NULL); void updateSubViewIndices (SubView *view = nullptr);
private slots: private slots:

View file

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

View file

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

View file

@ -38,7 +38,7 @@ namespace CSVRender
, mCameraSensitivity(1/650.f) , mCameraSensitivity(1/650.f)
, mSecondaryMoveMult(50) , mSecondaryMoveMult(50)
, mWheelMoveMult(8) , mWheelMoveMult(8)
, mCamera(NULL) , mCamera(nullptr)
{ {
} }
@ -81,7 +81,7 @@ namespace CSVRender
bool wasActive = mActive; bool wasActive = mActive;
mCamera = camera; mCamera = camera;
mActive = (mCamera != NULL); mActive = (mCamera != nullptr);
if (mActive != wasActive) if (mActive != wasActive)
{ {

View file

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

View file

@ -10,6 +10,8 @@
#include "../../model/prefs/state.hpp" #include "../../model/prefs/state.hpp"
#include "../../model/prefs/shortcutmanager.hpp" #include "../../model/prefs/shortcutmanager.hpp"
#include <components/misc/constants.hpp>
#include "mask.hpp" #include "mask.hpp"
CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow) CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow)
@ -57,7 +59,7 @@ QString CSVRender::CellArrowTag::getToolTip (bool hideBasics) const
void CSVRender::CellArrow::adjustTransform() void CSVRender::CellArrow::adjustTransform()
{ {
// position // position
const int cellSize = 8192; const int cellSize = Constants::CellSizeInUnits;
const int offset = cellSize / 2 + 800; const int offset = cellSize / 2 + 800;
int x = mCoordinates.getX()*cellSize + cellSize/2; int x = mCoordinates.getX()*cellSize + cellSize/2;

View file

@ -5,6 +5,8 @@
#include <osg/Geode> #include <osg/Geode>
#include <osgText/Text> #include <osgText/Text>
#include <components/misc/constants.hpp>
CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker) CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker)
: TagBase(Mask_CellMarker), mMarker(marker) : TagBase(Mask_CellMarker), mMarker(marker)
{} {}
@ -49,7 +51,7 @@ void CSVRender::CellMarker::buildMarker()
void CSVRender::CellMarker::positionMarker() void CSVRender::CellMarker::positionMarker()
{ {
const int cellSize = 8192; const int cellSize = Constants::CellSizeInUnits;
const int markerHeight = 0; const int markerHeight = 0;
// Move marker to center of cell. // Move marker to center of cell.

View file

@ -29,15 +29,13 @@ int CSVRender::InstanceMode::getSubModeFromId (const std::string& id) const
osg::Vec3f CSVRender::InstanceMode::quatToEuler(const osg::Quat& rot) const osg::Vec3f CSVRender::InstanceMode::quatToEuler(const osg::Quat& rot) const
{ {
const float Pi = 3.14159265f;
float x, y, z; float x, y, z;
float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z()); float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z());
if (std::abs(test) >= 1.f) if (std::abs(test) >= 1.f)
{ {
x = atan2(rot.x(), rot.w()); x = atan2(rot.x(), rot.w());
y = (test > 0) ? (Pi / 2) : (-Pi / 2); y = (test > 0) ? (osg::PI / 2) : (-osg::PI / 2);
z = 0; z = 0;
} }
else else

View file

@ -1,6 +1,8 @@
#include "object.hpp" #include "object.hpp"
#include <stdexcept> #include <stdexcept>
#include <string>
#include <iostream>
#include <osg/Depth> #include <osg/Depth>
#include <osg/Group> #include <osg/Group>
@ -29,6 +31,7 @@
#include <components/sceneutil/lightmanager.hpp> #include <components/sceneutil/lightmanager.hpp>
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
#include "actor.hpp"
#include "mask.hpp" #include "mask.hpp"
@ -78,64 +81,62 @@ void CSVRender::Object::update()
{ {
clear(); 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 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); 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)
model = referenceables.getData (index,
referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)).
toString().toUtf8().constData();
int recordType =
referenceables.getData (index,
referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt();
if (recordType == CSMWorld::UniversalId::Type_Light)
{
light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();
if (model.empty())
model = "marker_light.nif";
}
if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)
{
if (model.empty())
model = "marker_creature.nif";
}
if (model.empty())
error = 2;
}
mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
if (error) if (index == -1)
{ {
mBaseNode->addChild(createErrorCube()); mBaseNode->addChild(createErrorCube());
return;
} }
else
/// \todo check for Deleted state (error 1)
int recordType = referenceables.getData(index, TypeIndex).toInt();
std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData();
if (recordType == CSMWorld::UniversalId::Type_Light)
{ {
try light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();
if (model.empty())
model = "marker_light.nif";
}
if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)
{
if (model.empty())
model = "marker_creature.nif";
}
try
{
if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature)
{
if (!mActor) mActor.reset(new Actor(mReferenceableId, mData));
mActor->update();
mBaseNode->addChild(mActor->getBaseNode());
}
else if (!model.empty())
{ {
std::string path = "meshes\\" + model; std::string path = "meshes\\" + model;
mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); mResourceSystem->getSceneManager()->getInstance(path, mBaseNode);
} }
catch (std::exception& e) else
{ {
// TODO: use error marker mesh throw std::runtime_error(mReferenceableId + " has no model");
Log(Debug::Error) << e.what();
} }
} }
catch (std::exception& e)
{
// TODO: use error marker mesh
Log(Debug::Error) << e.what();
}
if (light) 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) 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 InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius());
const float OuterRadius = InnerRadius + MarkerShaftWidth; const float OuterRadius = InnerRadius + MarkerShaftWidth;
const float SegmentDistance = 100.f; 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 VerticesPerSegment = 4;
const size_t IndicesPerSegment = 24; const size_t IndicesPerSegment = 24;
const size_t VertexCount = SegmentCount * VerticesPerSegment; const size_t VertexCount = SegmentCount * VerticesPerSegment;
const size_t IndexCount = SegmentCount * IndicesPerSegment; const size_t IndexCount = SegmentCount * IndicesPerSegment;
const float Angle = 2 * Pi / SegmentCount; const float Angle = 2 * osg::PI / SegmentCount;
const unsigned short IndexPattern[IndicesPerSegment] = const unsigned short IndexPattern[IndicesPerSegment] =
{ {

View file

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

View file

@ -8,6 +8,8 @@
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include <components/misc/constants.hpp>
#include "../../model/prefs/shortcut.hpp" #include "../../model/prefs/shortcut.hpp"
#include "../../model/world/tablemimedata.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) void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, int offsetY)
{ {
const int CellSize = 8192;
osg::Vec3f eye, center, up; osg::Vec3f eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up); getCamera()->getViewMatrixAsLookAt(eye, center, up);
int cellX = (int)std::floor(center.x() / CellSize) + offsetX; int cellX = (int)std::floor(center.x() / Constants::CellSizeInUnits) + offsetX;
int cellY = (int)std::floor(center.y() / CellSize) + offsetY; int cellY = (int)std::floor(center.y() / Constants::CellSizeInUnits) + offsetY;
CSMWorld::CellCoordinates cellCoordinates(cellX, cellY); CSMWorld::CellCoordinates cellCoordinates(cellX, cellY);
@ -527,7 +527,7 @@ void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, in
CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document) CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document)
: WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"), : WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"),
mControlElements(NULL), mDisplayCellCoord(true) mControlElements(nullptr), mDisplayCellCoord(true)
{ {
QAbstractItemModel *cells = QAbstractItemModel *cells =
document.getData().getTableModel (CSMWorld::UniversalId::Type_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 std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const
{ {
const int cellSize = 8192;
CSMWorld::CellCoordinates cellCoordinates ( CSMWorld::CellCoordinates cellCoordinates (
static_cast<int> (std::floor (point.x()/cellSize)), static_cast<int> (std::floor (point.x() / Constants::CellSizeInUnits)),
static_cast<int> (std::floor (point.y()/cellSize))); static_cast<int> (std::floor (point.y() / Constants::CellSizeInUnits)));
return cellCoordinates.getId (mWorldspace); return cellCoordinates.getId (mWorldspace);
} }
CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const
{ {
const int cellSize = 8192;
CSMWorld::CellCoordinates coords( CSMWorld::CellCoordinates coords(
static_cast<int> (std::floor (point.x()/cellSize)), static_cast<int> (std::floor (point.x() / Constants::CellSizeInUnits)),
static_cast<int> (std::floor (point.y()/cellSize))); static_cast<int> (std::floor (point.y() / Constants::CellSizeInUnits)));
std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator searchResult = mCells.find(coords); std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator searchResult = mCells.find(coords);
if (searchResult != mCells.end()) if (searchResult != mCells.end())

View file

@ -185,7 +185,7 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
bool retrieveInput) bool retrieveInput)
: RenderWidget(parent, f) : RenderWidget(parent, f)
, mResourceSystem(resourceSystem) , mResourceSystem(resourceSystem)
, mLighting(NULL) , mLighting(nullptr)
, mHasDefaultAmbient(false) , mHasDefaultAmbient(false)
, mPrevMouseX(0) , mPrevMouseX(0)
, mPrevMouseY(0) , mPrevMouseY(0)
@ -425,21 +425,21 @@ void SceneWidget::selectNavigationMode (const std::string& mode)
{ {
if (mode=="1st") if (mode=="1st")
{ {
mCurrentCamControl->setCamera(NULL); mCurrentCamControl->setCamera(nullptr);
mCurrentCamControl = mFreeCamControl; mCurrentCamControl = mFreeCamControl;
mFreeCamControl->setCamera(getCamera()); mFreeCamControl->setCamera(getCamera());
mFreeCamControl->fixUpAxis(CameraController::WorldUp); mFreeCamControl->fixUpAxis(CameraController::WorldUp);
} }
else if (mode=="free") else if (mode=="free")
{ {
mCurrentCamControl->setCamera(NULL); mCurrentCamControl->setCamera(nullptr);
mCurrentCamControl = mFreeCamControl; mCurrentCamControl = mFreeCamControl;
mFreeCamControl->setCamera(getCamera()); mFreeCamControl->setCamera(getCamera());
mFreeCamControl->unfixUpAxis(); mFreeCamControl->unfixUpAxis();
} }
else if (mode=="orbit") else if (mode=="orbit")
{ {
mCurrentCamControl->setCamera(NULL); mCurrentCamControl->setCamera(nullptr);
mCurrentCamControl = mOrbitCamControl; mCurrentCamControl = mOrbitCamControl;
mOrbitCamControl->setCamera(getCamera()); mOrbitCamControl->setCamera(getCamera());
mOrbitCamControl->reset(); mOrbitCamControl->reset();

View file

@ -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 // 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)); int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY));
if (index == -1) if (index == -1)
return NULL; return nullptr;
const ESM::Land& land = mData.getLand().getRecord(index).get(); 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); return new ESMTerrain::LandObject(&land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX);

View file

@ -648,11 +648,6 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
mDragX = event->posF().x(); mDragX = event->posF().x();
mDragY = height() - event->posF().y(); mDragY = height() - event->posF().y();
#endif #endif
if (mDragMode == InteractionType_PrimaryEdit)
{
editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos
}
} }
} }
else else

View file

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

View file

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

View file

@ -25,7 +25,7 @@ std::string CSVWorld::CellCreator::getId() const
void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const
{ {
CSMWorld::IdTree *model = dynamic_cast<CSMWorld::IdTree *>(getData().getTableModel(getCollectionId())); 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 parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell);
int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior); int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior);
command.addNestedValue(parentIndex, index, mType->currentIndex() == 0); command.addNestedValue(parentIndex, index, mType->currentIndex() == 0);

View file

@ -158,7 +158,7 @@ mNotEditableDelegate(table, parent)
CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display) 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)); std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));
if (delegateIt == mDelegates.end()) if (delegateIt == mDelegates.end())
{ {
@ -251,11 +251,11 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::
variant = index.data(Qt::DisplayRole); variant = index.data(Qt::DisplayRole);
if (!variant.isValid()) if (!variant.isValid())
{ {
return NULL; return nullptr;
} }
} }
QWidget* editor = NULL; QWidget* editor = nullptr;
if (! (mTable->flags (index) & Qt::ItemIsEditable)) if (! (mTable->flags (index) & Qt::ItemIsEditable))
{ {
return mNotEditableDelegate.createEditor(qobject_cast<QWidget*>(mParent), return mNotEditableDelegate.createEditor(qobject_cast<QWidget*>(mParent),
@ -325,7 +325,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di
mWidget(widget), mWidget(widget),
mIdType(CSMWorld::TableMimeData::convertEnums(display)) mIdType(CSMWorld::TableMimeData::convertEnums(display))
{ {
Q_ASSERT(mWidget != NULL); Q_ASSERT(mWidget != nullptr);
Q_ASSERT(CSMWorld::ColumnBase::isId(display)); Q_ASSERT(CSMWorld::ColumnBase::isId(display));
Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None); 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())); connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest()));
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget); QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget);
if (lineEdit != NULL) if (lineEdit != nullptr)
{ {
mContextMenu = lineEdit->createStandardContextMenu(); mContextMenu = lineEdit->createStandardContextMenu();
} }
@ -360,11 +360,11 @@ QString CSVWorld::IdContextMenu::getWidgetValue() const
QLabel *label = qobject_cast<QLabel *>(mWidget); QLabel *label = qobject_cast<QLabel *>(mWidget);
QString value = ""; QString value = "";
if (lineEdit != NULL) if (lineEdit != nullptr)
{ {
value = lineEdit->text(); value = lineEdit->text();
} }
else if (label != NULL) else if (label != nullptr)
{ {
value = label->text(); value = label->text();
} }
@ -436,7 +436,7 @@ void CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor,
CSMWorld::ColumnBase::Display display, CSMWorld::ColumnBase::Display display,
int currentRow) const int currentRow) const
{ {
Q_ASSERT(editor != NULL); Q_ASSERT(editor != nullptr);
if (CSMWorld::ColumnBase::isId(display) && if (CSMWorld::ColumnBase::isId(display) &&
CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None) 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, int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher,
CSMDoc::Document& document, bool createAndDelete) : CSMDoc::Document& document, bool createAndDelete) :
QScrollArea(parent), QScrollArea(parent),
mWidgetMapper(NULL), mWidgetMapper(nullptr),
mNestedTableMapper(NULL), mNestedTableMapper(nullptr),
mDispatcher(NULL), mDispatcher(nullptr),
mNestedTableDispatcher(NULL), mNestedTableDispatcher(nullptr),
mMainWidget(NULL), mMainWidget(nullptr),
mTable(table), mTable(table),
mCommandDispatcher (commandDispatcher), mCommandDispatcher (commandDispatcher),
mDocument (document) mDocument (document)
@ -733,7 +733,7 @@ bool CSVWorld::SimpleDialogueSubView::isLocked() const
CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) :
SubView (id), SubView (id),
mEditWidget(0), mEditWidget(0),
mMainLayout(NULL), mMainLayout(nullptr),
mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))), mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))),
mLocked(false), mLocked(false),
mDocument(document), mDocument(document),

View file

@ -12,7 +12,7 @@ const CSMWorld::TableMimeData *CSVWorld::DragDropUtils::getTableMimeData(const Q
bool CSVWorld::DragDropUtils::canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type) bool CSVWorld::DragDropUtils::canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type)
{ {
const CSMWorld::TableMimeData *data = getTableMimeData(event); 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, CSMWorld::UniversalId CSVWorld::DragDropUtils::getAcceptedData(const QDropEvent &event,

View file

@ -83,7 +83,7 @@ void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event)
CSMWorld::ColumnBase::Display CSVWorld::DragRecordTable::getIndexDisplayType(const QModelIndex &index) const CSMWorld::ColumnBase::Display CSVWorld::DragRecordTable::getIndexDisplayType(const QModelIndex &index) const
{ {
Q_ASSERT(model() != NULL); Q_ASSERT(model() != nullptr);
if (index.isValid()) if (index.isValid())
{ {

View file

@ -28,7 +28,7 @@ namespace CSVWorld
bool mEditLock; bool mEditLock;
public: public:
DragRecordTable(CSMDoc::Document& document, QWidget* parent = NULL); DragRecordTable(CSMDoc::Document& document, QWidget* parent = nullptr);
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const = 0; virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const = 0;

View file

@ -95,7 +95,7 @@ void CSVWorld::ExtendedCommandConfigurator::setupGroupLayout()
int divider = 1; int divider = 1;
do do
{ {
while (layout->itemAt(0) != NULL) while (layout->itemAt(0) != nullptr)
{ {
layout->removeItem(layout->itemAt(0)); layout->removeItem(layout->itemAt(0));
} }

View file

@ -25,7 +25,7 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent,
{ {
if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid()) 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 // The completer for InfoCondVar needs to return a completer based on the first column

View file

@ -23,9 +23,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
bool editable, bool editable,
bool fixedRows) bool fixedRows)
: DragRecordTable(document, parent), : DragRecordTable(document, parent),
mAddNewRowAction(NULL), mAddNewRowAction(nullptr),
mRemoveRowAction(NULL), mRemoveRowAction(nullptr),
mEditIdAction(NULL), mEditIdAction(nullptr),
mModel(model) mModel(model)
{ {
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);

View file

@ -38,7 +38,7 @@ namespace CSVWorld
NestedTable(CSMDoc::Document& document, NestedTable(CSMDoc::Document& document,
CSMWorld::UniversalId id, CSMWorld::UniversalId id,
CSMWorld::NestedTableProxyModel* model, CSMWorld::NestedTableProxyModel* model,
QWidget* parent = NULL, QWidget* parent = nullptr,
bool editable = true, bool editable = true,
bool fixedRows = false); bool fixedRows = false);

View file

@ -27,7 +27,7 @@
#include "creator.hpp" #include "creator.hpp"
CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) 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; QVBoxLayout *layout = new QVBoxLayout;
@ -35,7 +35,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D
mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
CSVRender::WorldspaceWidget* worldspaceWidget = NULL; CSVRender::WorldspaceWidget* worldspaceWidget = nullptr;
widgetType whatWidget; widgetType whatWidget;
if (id.getId()==ESM::CellId::sDefaultWorldspace) 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) void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& universalIdData)
{ {
CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; CSVRender::PagedWorldspaceWidget* pagedNewWidget = nullptr;
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = nullptr;
CSVWidget::SceneToolbar* toolbar = NULL; CSVWidget::SceneToolbar* toolbar = nullptr;
CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (universalIdData); CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (universalIdData);

View file

@ -1,8 +1,6 @@
#include "util.hpp" #include "util.hpp"
#include <stdexcept> #include <stdexcept>
#include <climits>
#include <cfloat>
#include <QUndoStack> #include <QUndoStack>
#include <QMetaProperty> #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. // Color columns use a custom editor, so we need to fetch selected color from it.
CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor); CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor);
if (colorEditor != NULL) if (colorEditor != nullptr)
{ {
variant = colorEditor->colorInt(); variant = colorEditor->colorInt();
} }
@ -209,7 +207,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO
case CSMWorld::ColumnBase::Display_Integer: case CSMWorld::ColumnBase::Display_Integer:
{ {
DialogueSpinBox *sb = new DialogueSpinBox(parent); 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; 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 // Color columns use a custom editor, so we need explicitly set a data for it
CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor); CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor);
if (colorEditor != NULL) if (colorEditor != nullptr)
{ {
colorEditor->setColor(variant.toInt()); colorEditor->setColor(variant.toInt());
return; return;

View file

@ -20,14 +20,14 @@ void releaseArgv();
int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) { int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) {
int ret = 0; int ret = 0;
SDL_GetMouseState(&ret, NULL); SDL_GetMouseState(&ret, nullptr);
return ret; return ret;
} }
int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) { int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) {
int ret = 0; int ret = 0;
SDL_GetMouseState(NULL, &ret); SDL_GetMouseState(nullptr, &ret);
return ret; return ret;
} }

View file

@ -197,9 +197,9 @@ bool OMW::Engine::frame(float frametime)
} }
OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mWindow(NULL) : mWindow(nullptr)
, mEncoding(ToUTF8::WINDOWS_1252) , mEncoding(ToUTF8::WINDOWS_1252)
, mEncoder(NULL) , mEncoder(nullptr)
, mScreenCaptureOperation(nullptr) , mScreenCaptureOperation(nullptr)
, mSkipMenu (false) , mSkipMenu (false)
, mUseSound (true) , mUseSound (true)
@ -237,18 +237,18 @@ OMW::Engine::~Engine()
mEnvironment.cleanup(); mEnvironment.cleanup();
delete mScriptContext; delete mScriptContext;
mScriptContext = NULL; mScriptContext = nullptr;
mWorkQueue = NULL; mWorkQueue = nullptr;
mResourceSystem.reset(); mResourceSystem.reset();
mViewer = NULL; mViewer = nullptr;
if (mWindow) if (mWindow)
{ {
SDL_DestroyWindow(mWindow); SDL_DestroyWindow(mWindow);
mWindow = NULL; mWindow = nullptr;
} }
SDL_Quit(); 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(), MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap, mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap,
Version::getOpenmwVersionDescription(mResDir.string())); Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
mEnvironment.setWindowManager (window); mEnvironment.setWindowManager (window);
// Create sound system // Create sound system

View file

@ -90,6 +90,8 @@ namespace MWBase
virtual void setPlayerClass (const ESM::Class& class_) = 0; virtual void setPlayerClass (const ESM::Class& class_) = 0;
///< Set player class to custom class. ///< Set player class to custom class.
virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) = 0;
virtual void rest(bool sleep) = 0; virtual void rest(bool sleep) = 0;
///< If the player is sleeping or waiting, this should be called every hour. ///< If the player is sleeping or waiting, this should be called every hour.
/// @param sleep is the player sleeping or waiting? /// @param sleep is the player sleeping or waiting?

View file

@ -219,6 +219,7 @@ namespace MWBase
virtual const MWWorld::Ptr& getSelectedEnchantItem() const = 0; virtual const MWWorld::Ptr& getSelectedEnchantItem() const = 0;
virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0;
virtual const MWWorld::Ptr& getSelectedWeapon() const = 0; virtual const MWWorld::Ptr& getSelectedWeapon() const = 0;
virtual int getFontHeight() const = 0;
virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedSpell() = 0;
virtual void unsetSelectedWeapon() = 0; virtual void unsetSelectedWeapon() = 0;
@ -289,6 +290,8 @@ namespace MWBase
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;
virtual void loadUserFonts() = 0;
virtual Loading::Listener* getLoadingScreen() = 0; virtual Loading::Listener* getLoadingScreen() = 0;
/// Should the cursor be visible? /// Should the cursor be visible?
@ -350,7 +353,8 @@ namespace MWBase
virtual const MWGui::TextColours& getTextColours() = 0; 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;
}; };
} }

View file

@ -297,9 +297,11 @@ namespace MWBase
///< Queues movement for \a ptr (in local space), to be applied in the next call to ///< Queues movement for \a ptr (in local space), to be applied in the next call to
/// doPhysics. /// 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. ///< 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; virtual bool toggleCollisionMode() = 0;
///< Toggle collision mode for player. If disabled player object should ignore ///< Toggle collision mode for player. If disabled player object should ignore
/// collisions and gravity. /// collisions and gravity.
@ -438,12 +440,16 @@ namespace MWBase
virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0;
virtual int canRest() = 0; enum RestPermitted
///< check if the player is allowed to rest \n {
/// 0 - yes \n Rest_Allowed = 0,
/// 1 - only waiting \n Rest_OnlyWaiting = 1,
/// 2 - player is underwater \n Rest_PlayerIsUnderwater = 2,
/// 3 - enemies are nearby (not implemented) Rest_EnemiesAreNearby = 3
};
/// check if the player is allowed to rest
virtual RestPermitted canRest() const = 0;
/// \todo Probably shouldn't be here /// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0;
@ -567,6 +573,8 @@ namespace MWBase
virtual bool isPlayerInJail() const = 0; virtual bool isPlayerInJail() const = 0;
virtual void rest() = 0;
virtual void setPlayerTraveling(bool traveling) = 0; virtual void setPlayerTraveling(bool traveling) = 0;
virtual bool isPlayerTraveling() const = 0; virtual bool isPlayerTraveling() const = 0;

View file

@ -1,6 +1,7 @@
#include "activator.hpp" #include "activator.hpp"
#include <components/esm/loadacti.hpp> #include <components/esm/loadacti.hpp>
#include <components/misc/rng.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -134,4 +135,60 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell); 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);
}
} }

View file

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

View file

@ -74,7 +74,7 @@ namespace MWClass
if (ref->mBase->mFlags & ESM::Container::Respawn) if (ref->mBase->mFlags & ESM::Container::Respawn)
{ {
MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr);
ptr.getRefData().setCustomData(NULL); ptr.getRefData().setCustomData(nullptr);
} }
} }

View file

@ -298,7 +298,7 @@ namespace MWClass
bool healthdmg = true; bool healthdmg = true;
if (!weapon.isEmpty()) if (!weapon.isEmpty())
{ {
const unsigned char *attack = NULL; const unsigned char *attack = nullptr;
if(type == ESM::Weapon::AT_Chop) if(type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop; attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
else if(type == ESM::Weapon::AT_Slash) else if(type == ESM::Weapon::AT_Slash)
@ -688,9 +688,9 @@ namespace MWClass
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return 2; return ESM::SoundGenerator::SwimLeft;
if(world->isOnGround(ptr)) if(world->isOnGround(ptr))
return 0; return ESM::SoundGenerator::LeftFoot;
return -1; return -1;
} }
if(name == "right") if(name == "right")
@ -698,23 +698,23 @@ namespace MWClass
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return 3; return ESM::SoundGenerator::SwimRight;
if(world->isOnGround(ptr)) if(world->isOnGround(ptr))
return 1; return ESM::SoundGenerator::RightFoot;
return -1; return -1;
} }
if(name == "swimleft") if(name == "swimleft")
return 2; return ESM::SoundGenerator::SwimLeft;
if(name == "swimright") if(name == "swimright")
return 3; return ESM::SoundGenerator::SwimRight;
if(name == "moan") if(name == "moan")
return 4; return ESM::SoundGenerator::Moan;
if(name == "roar") if(name == "roar")
return 5; return ESM::SoundGenerator::Roar;
if(name == "scream") if(name == "scream")
return 6; return ESM::SoundGenerator::Scream;
if(name == "land") if(name == "land")
return 7; return ESM::SoundGenerator::Land;
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
} }
@ -828,7 +828,7 @@ namespace MWClass
ptr.getRefData().setCount(1); ptr.getRefData().setCount(1);
MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr);
ptr.getRefData().setCustomData(NULL); ptr.getRefData().setCustomData(nullptr);
// Reset to original position // Reset to original position
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], 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