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

Merge upstream/master

This commit is contained in:
AnyOldName3 2018-10-10 21:23:19 +01:00
commit 0124be5713
457 changed files with 5859 additions and 3173 deletions

View file

@ -148,11 +148,13 @@ Programmers
Scott Howard
scrawl
Sebastian Wick (swick)
Sergey Fukanchik
Sergey Shambir
ShadowRadiance
Siimacore
sir_herrbatka
smbas
Sophie Kirschner (pineapplemachine)
spycrab
Stefan Galowicz (bogglez)
Stanislav Bobrov (Jiub)

View file

@ -1,9 +1,12 @@
0.45.0
------
Bug #1875: Actors in inactive cells don't heal from resting
Bug #1990: Sunrise/sunset not set correct
Bug #2131: Lustidrike's spell misses the player every time
Bug #2222: Fatigue's effect on selling price is backwards
Bug #2256: Landing sound not playing when jumping immediately after landing
Bug #2274: Thin platform clips through player character instead of lifting
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
Bug #2455: Creatures attacks degrade armor
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
@ -15,17 +18,24 @@
Bug #2872: Tab completion in console doesn't work with explicit reference
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue
Bug #3059: Unable to hit with marksman weapons when too close to an enemy
Bug #3072: Fatal error on AddItem <item> that has a script containing Equip <item>
Bug #3219: NPC and creature initial position tracing down limit is too small
Bug #3249: Fixed revert function not updating views properly
Bug #3288: TrueType fonts are handled incorrectly
Bug #3374: Touch spells not hitting kwama foragers
Bug #3486: [Mod] NPC Commands does not work
Bug #3533: GetSpellEffects should detect effects with zero duration
Bug #3591: Angled hit distance too low
Bug #3629: DB assassin attack never triggers creature spawning
Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers
Bug #3762: AddSoulGem and RemoveSpell redundant count arguments break script execution
Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla
Bug #3836: Script fails to compile when command argument contains "\n"
Bug #3876: Landscape texture painting is misaligned
Bug #3897: Have Goodbye give all choices the effects of Goodbye
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
Bug #3920: RemoveSpellEffects doesn't remove constant effects
Bug #3948: AiCombat moving target aiming uses incorrect speed for magic projectiles
Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes
Bug #3993: Terrain texture blending map is not upscaled
@ -39,6 +49,7 @@
Bug #4230: AiTravel package issues break some Tribunal quests
Bug #4231: Infected rats from the "Crimson Plague" quest rendered unconscious by change in Drain Fatigue functionality
Bug #4251: Stationary NPCs do not return to their position after combat
Bug #4260: Keyboard navigation makes persuasion exploitable
Bug #4271: Scamp flickers when attacking
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
Bug #4286: Scripted animations can be interrupted
@ -48,6 +59,7 @@
Bug #4307: World cleanup should remove dead bodies only if death animation is finished
Bug #4311: OpenMW does not handle RootCollisionNode correctly
Bug #4327: Missing animations during spell/weapon stance switching
Bug #4333: Keyboard navigation in containers is not intuitive
Bug #4358: Running animation is interrupted when magic mode is toggled
Bug #4368: Settings window ok button doesn't have key focus by default
Bug #4378: On-self absorb spells restore stats
@ -69,6 +81,7 @@
Bug #4460: Script function "Equip" doesn't bypass beast restrictions
Bug #4461: "Open" spell from non-player caster isn't a crime
Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
Bug #4467: Content selector: cyrillic characters are decoded incorrectly in plugin descriptions
Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal
Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour
Bug #4474: No fallback when getVampireHead fails
@ -84,13 +97,17 @@
Bug #4503: Cast and ExplodeSpell commands increase alteration skill
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
Bug #4519: Knockdown does not discard movement in the 1st-person mode
Bug #4527: Sun renders on water shader in some situations where it shouldn't
Bug #4531: Movement does not reset idle animations
Bug #4532: Underwater sfx isn't tied to 3rd person camera
Bug #4539: Paper Doll is affected by GUI scaling
Bug #4543: Picking cursed items through inventory (menumode) makes it disappear
Bug #4545: Creatures flee from werewolves
Bug #4551: Replace 0 sound range with default range separately
Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed
Bug #4557: Topics with reserved names are handled differently from vanilla
Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive
Bug #4560: OpenMW does not update pinned windows properly
Bug #4563: Fast travel price logic checks destination cell instead of service actor cell
Bug #4565: Underwater view distance should be limited
Bug #4573: Player uses headtracking in the 1st-person mode
@ -98,8 +115,36 @@
Bug #4575: Weird result of attack animation blending with movement animations
Bug #4576: Reset of idle animations when attack can not be started
Bug #4591: Attack strength should be 0 if player did not hold the attack button
Bug #4593: Editor: Instance dragging is broken
Bug #4597: <> operator causes a compile error
Bug #4604: Picking up gold from the ground only makes 1 grabbed
Bug #4607: Scaling for animated collision shapes is applied twice
Bug #4608: Falling damage is applied twice
Bug #4611: Instant magic effects have 0 duration in custom spell cost calculations unlike vanilla
Bug #4614: Crash due to division by zero when FlipController has no textures
Bug #4615: Flicker effects for light sources are handled incorrectly
Bug #4617: First person sneaking offset is not applied while the character is in air
Bug #4618: Sneaking is possible while the character is flying
Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails
Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type
Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch
Bug #4641: GetPCJumping is handled incorrectly
Bug #4644: %Name should be available for all actors, not just for NPCs
Bug #4648: Hud thinks that throwing weapons have condition
Bug #4649: Levelup fully restores health
Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader
Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects
Bug #4668: Editor: Light source color is displayed as an integer
Bug #4669: ToggleCollision should trace the player down after collision being enabled
Bug #4671: knownEffect functions should use modified Alchemy skill
Bug #4672: Pitch factor is handled incorrectly for crossbow animations
Bug #4674: Journal can be opened when settings window is open
Feature #912: Editor: Add missing icons to UniversalId tables
Feature #1221: Editor: Creature/NPC rendering
Feature #1617: Editor: Enchantment effect record verifier
Feature #1645: Casting effects from objects
Feature #2606: Editor: Implemented (optional) case sensitive global search
Feature #2847: Content selector: allow to copy the path to a file by using the context menu
Feature #3083: Play animation when NPC is casting spell via script
Feature #3103: Provide option for disposition to get increased by successful trade
Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results
@ -121,8 +166,21 @@
Feature #4550: Weapon priority: make ranged weapon bonus more sensible
Feature #4579: Add option for applying Strength into hand to hand damage
Feature #4581: Use proper logging system
Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment
Feature #4625: Weapon priority: use weighted mean for melee damage rating
Feature #4626: Weapon priority: account for weapon speed
Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating
Feature #4636: Use sTo GMST in spellmaking menu
Feature #4642: Batching potion creation
Task #2490: Don't open command prompt window on Release-mode builds automatically
Task #4545: Enable is_pod string test
Task #4605: Optimize skinning
Task #4606: Support Rapture3D's OpenAL driver
Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9
Task #4621: Optimize combat AI
Task #4643: Revise editor record verifying functionality
Task #4645: Use constants instead of widely used magic numbers
Task #4652: Move call to enemiesNearby() from InputManager::rest() to World::canRest()
0.44.0
------

View file

@ -346,7 +346,7 @@ if [ -z $SKIP_DOWNLOAD ]; then
# OpenAL
download "OpenAL-Soft 1.17.2" \
"http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \
"http://openal-soft.org/openal-binaries/openal-soft-1.17.2-bin.zip" \
"OpenAL-Soft-1.17.2.zip"
# OSG

View file

@ -1,6 +1,6 @@
#!/bin/sh -e
git clone https://github.com/google/googletest.git
git clone -b release-1.8.1 https://github.com/google/googletest.git
cd googletest
mkdir build
cd build

View file

@ -624,7 +624,7 @@ if (WIN32)
# Warnings that aren't enabled normally and don't need to be enabled
# They're unneeded and sometimes completely retarded warnings that /Wall enables
# Not going to bother commenting them as they tend to warn on every standard library file
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
# Warnings that are thrown on standard libraries and not OpenMW
@ -643,6 +643,7 @@ if (WIN32)
# caused by boost
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
4643 # Forward declaring 'X' in namespace std is not permitted by the C++ Standard. (in *_std_fwd.h files)
# caused by MyGUI
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -36,6 +36,8 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
ui.setupUi (this);
setObjectName ("DataFilesPage");
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);
const QString encoding = mGameSettings.value("encoding", "win1252");
mSelector->setEncoding(encoding);
mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this);
@ -357,4 +359,4 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles));
std::sort(cellNamesList.begin(), cellNamesList.end());
emit signalLoadedCellsChanged(cellNamesList);
}
}

View file

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

View file

@ -22,7 +22,7 @@ CS::Editor::Editor (int argc, char **argv)
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""),
mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
mIpcServerName ("org.openmw.OpenCS"), mServer(nullptr), mClientSocket(nullptr)
{
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
@ -108,8 +108,9 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mCfgMgr.readConfiguration(variables, desc, quiet);
mDocumentManager.setEncoding (
ToUTF8::calculateEncoding (variables["encoding"].as<Files::EscapeHashString>().toStdString()));
const std::string encoding = variables["encoding"].as<Files::EscapeHashString>().toStdString();
mDocumentManager.setEncoding (ToUTF8::calculateEncoding (encoding));
mFileDialog.setEncoding (QString::fromUtf8(encoding.c_str()));
mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::EscapeHashString>().toStdString());
@ -338,7 +339,7 @@ bool CS::Editor::makeIPCServer()
}
mServer->close();
mServer = NULL;
mServer = nullptr;
return false;
}

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

View file

@ -56,9 +56,6 @@ namespace CSMDoc
const std::string& hint = "",
Message::Severity severity = Message::Severity_Default);
/// \deprecated Use add instead.
void push_back (const std::pair<CSMWorld::UniversalId, std::string>& data);
Iterator begin() const;
Iterator end() const;

View file

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

View file

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

View file

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

View file

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

View file

@ -34,25 +34,23 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
// Check BYDT
if (bodyPart.mData.mPart > 14 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." ));
messages.add(id, "Invalid part", "", CSMDoc::Message::Severity_Error);
if (bodyPart.mData.mFlags > 3 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." ));
messages.add(id, "Invalid flags", "", CSMDoc::Message::Severity_Error);
if (bodyPart.mData.mType > 2 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." ));
messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error);
// Check MODL
if ( bodyPart.mModel.empty() )
messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." ));
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if ( mMeshes.searchId( bodyPart.mModel ) == -1 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." ));
messages.add(id, "Model '" + bodyPart.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check FNAM
if ( bodyPart.mRace.empty() )
messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." ));
messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error);
else if ( mRaces.searchId( bodyPart.mRace ) == -1 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." ));
messages.add(id, "Race '" + bodyPart.mRace + " does not exist", "", CSMDoc::Message::Severity_Error);
}

View file

@ -1,6 +1,5 @@
#include "classcheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadclas.hpp>
@ -37,26 +36,22 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
// A class should have a name
if (class_.mName.empty())
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a name"));
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
// A playable class should have a description
if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty())
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a description and it's playable"));
messages.add(id, "Description of a playable class is missing", "", CSMDoc::Message::Severity_Warning);
// test for invalid attributes
for (int i=0; i<2; ++i)
if (class_.mData.mAttribute[i]==-1)
{
std::ostringstream stream;
stream << "Attribute #" << i << " of " << class_.mId << " is not set";
messages.push_back (std::make_pair (id, stream.str()));
messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error);
}
if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1)
{
messages.push_back (std::make_pair (id, "Class lists same attribute twice"));
messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error);
}
// test for non-unique skill
@ -66,10 +61,9 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
for (int i2=0; i2<2; ++i2)
++skills[class_.mData.mSkills[i][i2]];
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
if (iter->second>1)
for (auto &skill : skills)
if (skill.second>1)
{
messages.push_back (std::make_pair (id,
ESM::Skill::indexToId (iter->first) + " is listed more than once"));
messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error);
}
}

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

View file

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

View file

@ -4,79 +4,26 @@
#include "../prefs/state.hpp"
#include "../world/resources.hpp"
#include "../world/data.hpp"
namespace
{
void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text)
{
if (!text.empty())
{
messages.push_back(std::make_pair(id, text));
}
}
}
bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const
{
const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures;
bool exists = false;
if (textures.searchId(texture) != -1)
{
exists = true;
}
else
{
std::string ddsTexture = texture;
if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1)
{
exists = true;
}
}
return exists;
}
std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id,
std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id,
const CSMWorld::UniversalId &type,
const std::string &column) const
{
std::string error;
if (!id.empty())
{
CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id);
if (index.first == -1)
{
error = "No such " + column + " '" + id + "'";
}
else if (index.second != type.getType())
{
error = column + " is not of type " + type.getTypeName();
}
}
return error;
}
std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const
{
std::string error;
if (!id.empty() && mSounds.searchId(id) == -1)
{
error = "No such " + column + " '" + id + "'";
}
return error;
CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id);
if (index.first == -1)
return (column + " '" + id + "' does not exist");
else if (index.second != type.getType())
return (column + " '" + id + "' does not have " + type.getTypeName() + " type");
return std::string();
}
CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables,
const CSMWorld::RefIdCollection &objects,
const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures)
: mMagicEffects(effects),
mSounds(sounds),
mReferenceables(referenceables),
mObjects(objects),
mIcons(icons),
mTextures(textures)
{
@ -100,46 +47,75 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa
ESM::MagicEffect effect = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
if (effect.mDescription.empty())
{
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
}
if (effect.mData.mBaseCost < 0.0f)
{
messages.push_back(std::make_pair(id, "Base Cost is negative"));
messages.add(id, "Base cost is negative", "", CSMDoc::Message::Severity_Error);
}
if (effect.mIcon.empty())
{
messages.push_back(std::make_pair(id, "Icon is not specified"));
messages.add(id, "Icon is missing", "", CSMDoc::Message::Severity_Error);
}
else if (!isTextureExists(effect.mIcon, true))
else
{
messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'"));
if (mIcons.searchId(effect.mIcon) == -1)
{
std::string ddsIcon = effect.mIcon;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))
messages.add(id, "Icon '" + effect.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
}
if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false))
if (!effect.mParticle.empty())
{
messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'"));
if (mTextures.searchId(effect.mParticle) == -1)
{
std::string ddsParticle = effect.mParticle;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsParticle) && mTextures.searchId(ddsParticle) != -1))
messages.add(id, "Particle texture '" + effect.mParticle + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
}
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound"));
if (effect.mDescription.empty())
if (!effect.mCasting.empty())
{
messages.push_back(std::make_pair(id, "Description is empty"));
const std::string error = checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mHit.empty())
{
const std::string error = checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mArea.empty())
{
const std::string error = checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mBolt.empty())
{
const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mCastSound.empty() && mSounds.searchId(effect.mCastSound) == -1)
messages.add(id, "Casting sound '" + effect.mCastSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mHitSound.empty() && mSounds.searchId(effect.mHitSound) == -1)
messages.add(id, "Hit sound '" + effect.mHitSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mAreaSound.empty() && mSounds.searchId(effect.mAreaSound) == -1)
messages.add(id, "Area sound '" + effect.mAreaSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mBoltSound.empty() && mSounds.searchId(effect.mBoltSound) == -1)
messages.add(id, "Bolt sound '" + effect.mBoltSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
}

View file

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

View file

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

View file

@ -1,9 +1,5 @@
#include "racecheck.hpp"
#include <sstream>
#include <components/esm/loadrace.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
@ -29,24 +25,24 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me
// test for empty name and description
if (race.mName.empty())
messages.push_back (std::make_pair (id, race.mId + " has an empty name"));
messages.add(id, "Name is missing", "", (race.mData.mFlags & 0x1) ? CSMDoc::Message::Severity_Error : CSMDoc::Message::Severity_Warning);
if (race.mDescription.empty())
messages.push_back (std::make_pair (id, race.mId + " has an empty description"));
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
// test for positive height
if (race.mData.mHeight.mMale<=0)
messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height"));
messages.add(id, "Male height is non-positive", "", CSMDoc::Message::Severity_Error);
if (race.mData.mHeight.mFemale<=0)
messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height"));
messages.add(id, "Female height is non-positive", "", CSMDoc::Message::Severity_Error);
// test for non-negative weight
if (race.mData.mWeight.mMale<0)
messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight"));
messages.add(id, "Male weight is negative", "", CSMDoc::Message::Severity_Error);
if (race.mData.mWeight.mFemale<0)
messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight"));
messages.add(id, "Female weight is negative", "", CSMDoc::Message::Severity_Error);
/// \todo check data members that can't be edited in the table view
}
@ -56,7 +52,7 @@ void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);
if (!mPlayable)
messages.push_back (std::make_pair (id, "No playable race"));
messages.add(id, "No playable race", "", CSMDoc::Message::Severity_SeriousError);
}
CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)

View file

@ -1,6 +1,7 @@
#include "referenceablecheck.hpp"
#include <components/misc/stringops.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../prefs/state.hpp"
@ -11,13 +12,18 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,
const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& faction,
const CSMWorld::IdCollection<ESM::Script>& scripts)
:
mReferencables(referenceable),
const CSMWorld::IdCollection<ESM::Script>& scripts,
const CSMWorld::Resources& models,
const CSMWorld::Resources& icons,
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts)
:mReferencables(referenceable),
mRaces(races),
mClasses(classes),
mFactions(faction),
mScripts(scripts),
mModels(models),
mIcons(icons),
mBodyParts(bodyparts),
mPlayerPresent(false)
{
mIgnoreBaseRecords = false;
@ -270,9 +276,10 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId);
//Checking for model, IIRC all activators should have a model
if (activator.mModel.empty())
messages.push_back (std::make_pair (id, activator.mId + " has no model"));
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(activator.mModel) == -1)
messages.add(id, "Model '" + activator.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check that mentioned scripts exist
scriptCheck<ESM::Activator>(activator, messages, id.toString());
@ -293,7 +300,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId);
inventoryItemCheck<ESM::Potion>(potion, messages, id.toString());
//IIRC potion can have empty effects list just fine.
/// \todo Check magic effects for validity
// Check that mentioned scripts exist
scriptCheck<ESM::Potion>(potion, messages, id.toString());
@ -338,13 +345,13 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
inventoryItemCheck<ESM::Armor>(armor, messages, id.toString(), true);
//checking for armor class, armor should have poistive armor class, but 0 is considered legal
// Armor should have positive armor class, but 0 class is not an error
if (armor.mData.mArmor < 0)
messages.push_back (std::make_pair (id, armor.mId + " has negative armor class"));
messages.add(id, "Armor class is negative", "", CSMDoc::Message::Severity_Error);
//checking for health. Only positive numbers are allowed, or 0 is illegal
// Armor durability must be a positive number
if (armor.mData.mHealth <= 0)
messages.push_back (std::make_pair (id, armor.mId + " has non positive health"));
messages.add(id, "Durability is non-positive", "", CSMDoc::Message::Severity_Error);
// Check that mentioned scripts exist
scriptCheck<ESM::Armor>(armor, messages, id.toString());
@ -383,18 +390,19 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId);
//Checking for model, IIRC all containers should have a model
//checking for name
if (container.mName.empty())
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
//Checking for model
if (container.mModel.empty())
messages.push_back (std::make_pair (id, container.mId + " has no model"));
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(container.mModel) == -1)
messages.add(id, "Model '" + container.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
//Checking for capacity (weight)
if (container.mWeight < 0) //0 is allowed
messages.push_back (std::make_pair (id,
container.mId + " has negative weight (capacity)"));
//checking for name
if (container.mName.empty())
messages.push_back (std::make_pair (id, container.mId + " has an empty name"));
messages.add(id, "Capacity is negative", "", CSMDoc::Message::Severity_Error);
//checking contained items
inventoryListCheck(container.mInventory.mList, messages, id.toString());
@ -416,68 +424,81 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId);
if (creature.mModel.empty())
messages.push_back (std::make_pair (id, creature.mId + " has no model"));
if (creature.mName.empty())
messages.push_back (std::make_pair (id, creature.mId + " has an empty name"));
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
if (creature.mModel.empty())
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(creature.mModel) == -1)
messages.add(id, "Model '" + creature.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
//stats checks
if (creature.mData.mLevel < 1)
messages.push_back (std::make_pair (id, creature.mId + " has non-positive level"));
if (creature.mData.mLevel <= 0)
messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mStrength < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative strength"));
messages.add(id, "Strength is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mIntelligence < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative intelligence"));
messages.add(id, "Intelligence is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mWillpower < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative willpower"));
messages.add(id, "Willpower is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mAgility < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative agility"));
messages.add(id, "Agility is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mSpeed < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative speed"));
messages.add(id, "Speed is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mEndurance < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative endurance"));
messages.add(id, "Endurance is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mPersonality < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative personality"));
messages.add(id, "Personality is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mLuck < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative luck"));
messages.add(id, "Luck is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mCombat < 0)
messages.add(id, "Combat is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mMagic < 0)
messages.add(id, "Magic is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mStealth < 0)
messages.add(id, "Stealth is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mHealth < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative health"));
messages.add(id, "Health is negative", "", CSMDoc::Message::Severity_Error);
if (creature.mData.mMana < 0)
messages.add(id, "Magicka is negative", "", CSMDoc::Message::Severity_Error);
if (creature.mData.mFatigue < 0)
messages.add(id, "Fatigue is negative", "", CSMDoc::Message::Severity_Error);
if (creature.mData.mSoul < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative soul value"));
messages.add(id, "Soul value is negative", "", CSMDoc::Message::Severity_Error);
for (int i = 0; i < 6; ++i)
{
if (creature.mData.mAttack[i] < 0)
{
messages.push_back (std::make_pair (id,
creature.mId + " has negative attack strength"));
break;
}
messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error);
if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1])
messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Error);
}
//TODO, find meaning of other values
if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures
messages.push_back (std::make_pair (id, creature.mId + " has negative gold "));
if (creature.mData.mGold < 0)
messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error);
if (creature.mScale == 0)
messages.push_back (std::make_pair (id, creature.mId + " has zero scale value"));
messages.add(id, "Scale is equal to zero", "", CSMDoc::Message::Severity_Error);
if (!creature.mOriginal.empty())
{
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(creature.mOriginal);
if (index.first == -1)
messages.add(id, "Parent creature '" + creature.mOriginal + "' does not exist", "", CSMDoc::Message::Severity_Error);
else if (index.second != CSMWorld::UniversalId::Type_Creature)
messages.add(id, "'" + creature.mOriginal + "' is not a creature", "", CSMDoc::Message::Severity_Error);
}
// Check inventory
inventoryListCheck(creature.mInventory.mList, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Creature>(creature, messages, id.toString());
/// \todo Check spells, teleport table, AI data and AI packages for validity
}
void CSMTools::ReferenceableCheckStage::doorCheck(
@ -495,10 +516,12 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
//usual, name or model
if (door.mName.empty())
messages.push_back (std::make_pair (id, door.mId + " has an empty name"));
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
if (door.mModel.empty())
messages.push_back (std::make_pair (id, door.mId + " has no model"));
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(door.mModel) == -1)
messages.add(id, "Model '" + door.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check that mentioned scripts exist
scriptCheck<ESM::Door>(door, messages, id.toString());
@ -572,7 +595,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId);
if (light.mData.mRadius < 0)
messages.push_back (std::make_pair (id, light.mId + " has negative light radius"));
messages.add(id, "Light radius is negative", "", CSMDoc::Message::Severity_Error);
if (light.mData.mFlags & ESM::Light::Carry)
inventoryItemCheck<ESM::Light>(light, messages, id.toString());
@ -644,96 +667,75 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
return;
short level(npc.mNpdt.mLevel);
char disposition(npc.mNpdt.mDisposition);
char reputation(npc.mNpdt.mReputation);
char rank(npc.mNpdt.mRank);
//Don't know what unknown is for
int gold(npc.mNpdt.mGold);
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated
{
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
{
messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happen?
messages.add(id, "NPC with autocalculated stats doesn't have autocalc flag turned on", "", CSMDoc::Message::Severity_Error); //should not happen?
return;
}
level = npc.mNpdt.mLevel;
disposition = npc.mNpdt.mDisposition;
reputation = npc.mNpdt.mReputation;
rank = npc.mNpdt.mRank;
gold = npc.mNpdt.mGold;
}
else
{
if (npc.mNpdt.mAgility == 0)
messages.push_back (std::make_pair (id, npc.mId + " agility has zero value"));
if (npc.mNpdt.mEndurance == 0)
messages.push_back (std::make_pair (id, npc.mId + " endurance has zero value"));
if (npc.mNpdt.mIntelligence == 0)
messages.push_back (std::make_pair (id, npc.mId + " intelligence has zero value"));
if (npc.mNpdt.mLuck == 0)
messages.push_back (std::make_pair (id, npc.mId + " luck has zero value"));
if (npc.mNpdt.mPersonality == 0)
messages.push_back (std::make_pair (id, npc.mId + " personality has zero value"));
if (npc.mNpdt.mStrength == 0)
messages.push_back (std::make_pair (id, npc.mId + " strength has zero value"));
if (npc.mNpdt.mSpeed == 0)
messages.push_back (std::make_pair (id, npc.mId + " speed has zero value"));
messages.add(id, "Strength is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mIntelligence == 0)
messages.add(id, "Intelligence is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mWillpower == 0)
messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value"));
messages.add(id, "Willpower is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mAgility == 0)
messages.add(id, "Agility is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mSpeed == 0)
messages.add(id, "Speed is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mEndurance == 0)
messages.add(id, "Endurance is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mPersonality == 0)
messages.add(id, "Personality is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mLuck == 0)
messages.add(id, "Luck is equal to zero", "", CSMDoc::Message::Severity_Warning);
}
if (level < 1)
messages.push_back (std::make_pair (id, npc.mId + " level is non positive"));
if (level <= 0)
messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
if (gold < 0)
messages.push_back (std::make_pair (id, npc.mId + " gold has negative value"));
messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error);
if (npc.mName.empty())
messages.push_back (std::make_pair (id, npc.mId + " has any empty name"));
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
if (npc.mClass.empty())
messages.push_back (std::make_pair (id, npc.mId + " has an empty class"));
messages.add(id, "Class is missing", "", CSMDoc::Message::Severity_Error);
else if (mClasses.searchId (npc.mClass) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has invalid class"));
messages.add(id, "Class '" + npc.mClass + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (npc.mRace.empty())
messages.push_back (std::make_pair (id, npc.mId + " has an empty race"));
messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error);
else if (mRaces.searchId (npc.mRace) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has invalid race"));
messages.add(id, "Race '" + npc.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (disposition < 0)
messages.push_back (std::make_pair (id, npc.mId + " has negative disposition"));
if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid
{
messages.push_back (std::make_pair (id, npc.mId + " has negative reputation"));
}
if (!npc.mFaction.empty())
{
if (rank < 0)
messages.push_back (std::make_pair (id, npc.mId + " has negative rank"));
if (mFactions.searchId(npc.mFaction) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has invalid faction"));
}
if (!npc.mFaction.empty() && mFactions.searchId(npc.mFaction) == -1)
messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (npc.mHead.empty())
messages.push_back (std::make_pair (id, npc.mId + " has no head"));
messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error);
else
{
if (mBodyParts.searchId(npc.mHead) == -1)
messages.add(id, "Head body part '" + npc.mHead + "' does not exist", "", CSMDoc::Message::Severity_Error);
/// \todo Check gender, race and other body parts stuff validity for the specific NPC
}
if (npc.mHair.empty())
messages.push_back (std::make_pair (id, npc.mId + " has no hair"));
//TODO: reputation, Disposition, rank, everything else
messages.add(id, "Hair is missing", "", CSMDoc::Message::Severity_Error);
else
{
if (mBodyParts.searchId(npc.mHair) == -1)
messages.add(id, "Hair body part '" + npc.mHair + "' does not exist", "", CSMDoc::Message::Severity_Error);
/// \todo Check gender, race and other body part stuff validity for the specific NPC
}
// Check inventory
inventoryListCheck(npc.mInventory.mList, messages, id.toString());
@ -793,28 +795,25 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
weapon.mData.mType == ESM::Weapon::Bolt))
{
if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1])
messages.push_back (std::make_pair (id,
weapon.mId + " has minimum slash damage higher than maximum"));
messages.add(id, "Minimum slash damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1])
messages.push_back (std::make_pair (id,
weapon.mId + " has minimum thrust damage higher than maximum"));
messages.add(id, "Minimum thrust damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
}
if (weapon.mData.mChop[0] > weapon.mData.mChop[1])
messages.push_back (std::make_pair (id,
weapon.mId + " has minimum chop damage higher than maximum"));
messages.add(id, "Minimum chop damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
if (!(weapon.mData.mType == ESM::Weapon::Arrow ||
weapon.mData.mType == ESM::Weapon::Bolt ||
weapon.mData.mType == ESM::Weapon::MarksmanThrown))
{
//checking of health
if (weapon.mData.mHealth <= 0)
messages.push_back (std::make_pair (id, weapon.mId + " has non-positive health"));
if (weapon.mData.mHealth == 0)
messages.add(id, "Durability is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (weapon.mData.mReach < 0)
messages.push_back (std::make_pair (id, weapon.mId + " has negative reach"));
messages.add(id, "Reach is negative", "", CSMDoc::Message::Severity_Error);
}
}
@ -877,7 +876,9 @@ void CSMTools::ReferenceableCheckStage::staticCheck (
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId);
if (staticElement.mModel.empty())
messages.push_back (std::make_pair (id, staticElement.mId + " has no model"));
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(staticElement.mModel) == -1)
messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
//final check
@ -885,8 +886,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck (
void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages)
{
if (!mPlayerPresent)
messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables,
"There is no player record"));
messages.add(CSMWorld::UniversalId::Type_Referenceables, "Player record is missing", "", CSMDoc::Message::Severity_SeriousError);
}
void CSMTools::ReferenceableCheckStage::inventoryListCheck(
@ -900,8 +900,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName);
if (localIndex.first == -1)
messages.push_back (std::make_pair (id,
id + " contains non-existing item (" + itemName + ")"));
messages.add(id, "Item '" + itemName + "' does not exist", "", CSMDoc::Message::Severity_Error);
else
{
// Needs to accommodate containers, creatures, and NPCs
@ -922,8 +921,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
case CSMWorld::UniversalId::Type_ItemLevelledList:
break;
default:
messages.push_back (std::make_pair(id,
id + " contains item of invalid type (" + itemName + ")"));
messages.add(id, "'" + itemName + "' is not an item", "", CSMDoc::Message::Severity_Error);
}
}
}
@ -935,67 +933,82 @@ template<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemChe
const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable)
{
if (someItem.mName.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name"));
messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error);
//Checking for weight
if (someItem.mData.mWeight < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight"));
messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error);
//Checking for value
if (someItem.mData.mValue < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative value"));
messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error);
//checking for model
if (someItem.mModel.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has no model"));
messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(someItem.mModel) == -1)
messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
//checking for icon
if (someItem.mIcon.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has no icon"));
messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error);
else if (mIcons.searchId(someItem.mIcon) == -1)
{
std::string ddsIcon = someItem.mIcon;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))
messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
if (enchantable && someItem.mData.mEnchant < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative enchantment"));
messages.add(someID, "Enchantment points number is negative", "", CSMDoc::Message::Severity_Error);
}
template<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemCheck (
const Item& someItem, CSMDoc::Messages& messages, const std::string& someID)
{
if (someItem.mName.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name"));
messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error);
//Checking for weight
if (someItem.mData.mWeight < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight"));
messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error);
//Checking for value
if (someItem.mData.mValue < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative value"));
messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error);
//checking for model
if (someItem.mModel.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has no model"));
messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(someItem.mModel) == -1)
messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
//checking for icon
if (someItem.mIcon.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has no icon"));
messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error);
else if (mIcons.searchId(someItem.mIcon) == -1)
{
std::string ddsIcon = someItem.mIcon;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))
messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
}
template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck (
const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken)
{
if (someTool.mData.mQuality <= 0)
messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality"));
messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error);
if (canBeBroken && someTool.mData.mUses<=0)
messages.push_back (std::make_pair (someID,
someTool.mId + " has non-positive uses count"));
messages.add(someID, "Number of uses is non-positive", "", CSMDoc::Message::Severity_Error);
}
template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck (
const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID)
{
if (someTool.mData.mQuality <= 0)
messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality"));
messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error);
}
template<typename List> void CSMTools::ReferenceableCheckStage::listCheck (
@ -1004,12 +1017,10 @@ template<typename List> void CSMTools::ReferenceableCheckStage::listCheck (
for (unsigned i = 0; i < someList.mList.size(); ++i)
{
if (mReferencables.searchId(someList.mList[i].mId).first == -1)
messages.push_back (std::make_pair (someID,
someList.mId + " contains item without referencable"));
messages.add(someID, "Object '" + someList.mList[i].mId + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (someList.mList[i].mLevel < 1)
messages.push_back (std::make_pair (someID,
someList.mId + " contains item with non-positive level"));
messages.add(someID, "Level of item '" + someList.mList[i].mId + "' is non-positive", "", CSMDoc::Message::Severity_Error);
}
}
@ -1019,6 +1030,6 @@ template<typename Tool> void CSMTools::ReferenceableCheckStage::scriptCheck (
if (!someTool.mScript.empty())
{
if (mScripts.searchId(someTool.mScript) == -1)
messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\""));
messages.add(someID, "Script '" + someTool.mScript + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
}

View file

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

View file

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

View file

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

View file

@ -1,10 +1,5 @@
#include "regioncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadregn.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
@ -36,7 +31,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
// test for empty name
if (region.mName.empty())
messages.add(id, region.mId + " has an empty name", "", CSMDoc::Message::Severity_Error);
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
/// \todo test that the ID in mSleeplist exists

View file

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

View file

@ -1,9 +1,5 @@
#include "skillcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
@ -33,16 +29,12 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId);
if (skill.mDescription.empty())
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
for (int i=0; i<4; ++i)
if (skill.mData.mUseValue[i]<0)
{
std::ostringstream stream;
stream << "Use value #" << i << " of " << skill.mId << " is negative";
messages.push_back (std::make_pair (id, stream.str()));
messages.add(id, "Use value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error);
}
if (skill.mDescription.empty())
messages.push_back (std::make_pair (id, skill.mId + " has an empty description"));
}

View file

@ -1,15 +1,13 @@
#include "soundcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds)
: mSounds (sounds)
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::Resources &soundfiles)
: mSounds (sounds),
mSoundFiles (soundfiles)
{
mIgnoreBaseRecords = false;
}
@ -34,7 +32,16 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);
if (sound.mData.mMinRange>sound.mData.mMaxRange)
messages.push_back (std::make_pair (id, "Minimum range larger than maximum range"));
{
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 "../world/resources.hpp"
#include "../world/idcollection.hpp"
#include "../doc/stage.hpp"
@ -13,11 +14,13 @@ namespace CSMTools
class SoundCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Sound>& mSounds;
const CSMWorld::Resources &mSoundFiles;
bool mIgnoreBaseRecords;
public:
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds);
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds,
const CSMWorld::Resources &soundfiles);
virtual int setup();
///< \return number of steps

View file

@ -1,7 +1,5 @@
#include "soundgencheck.hpp"
#include <sstream>
#include "../prefs/state.hpp"
#include "../world/refiddata.hpp"
@ -9,10 +7,10 @@
CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables)
const CSMWorld::RefIdCollection &objects)
: mSoundGens(soundGens),
mSounds(sounds),
mReferenceables(referenceables)
mObjects(objects)
{
mIgnoreBaseRecords = false;
}
@ -37,23 +35,23 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages
if (!soundGen.mCreature.empty())
{
CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature);
CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature);
if (creatureIndex.first == -1)
{
messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'"));
messages.add(id, "Creature '" + soundGen.mCreature + "' doesn't exist", "", CSMDoc::Message::Severity_Error);
}
else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature)
{
messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature"));
messages.add(id, "'" + soundGen.mCreature + "' is not a creature", "", CSMDoc::Message::Severity_Error);
}
}
if (soundGen.mSound.empty())
{
messages.push_back(std::make_pair(id, "Sound is not specified"));
messages.add(id, "Sound is missing", "", CSMDoc::Message::Severity_Error);
}
else if (mSounds.searchId(soundGen.mSound) == -1)
{
messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'"));
messages.add(id, "Sound '" + soundGen.mSound + "' doesn't exist", "", CSMDoc::Message::Severity_Error);
}
}

View file

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

View file

@ -34,13 +34,13 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);
// test for empty name and description
// test for empty name
if (spell.mName.empty())
messages.push_back (std::make_pair (id, spell.mId + " has an empty name"));
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
// test for invalid cost values
if (spell.mData.mCost<0)
messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs"));
messages.add(id, "Spell cost is negative", "", CSMDoc::Message::Severity_Error);
/// \todo check data members that can't be edited in the table view
}

View file

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

View file

@ -32,6 +32,7 @@
#include "gmstcheck.hpp"
#include "topicinfocheck.hpp"
#include "journalcheck.hpp"
#include "enchantmentcheck.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{
@ -74,15 +75,17 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces()));
mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds()));
mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds(), mData.getResources (CSMWorld::UniversalId::Type_SoundsRes)));
mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions()));
mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns()));
mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns(), mData.getResources (CSMWorld::UniversalId::Type_Textures)));
mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells()));
mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts()));
mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(),
mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons),
mData.getBodyParts()));
mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
@ -126,6 +129,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos()));
mVerifierOperation->appendStage (new EnchantmentCheckStage(mData.getEnchantments()));
mVerifier.setOperation (mVerifierOperation);
}

View file

@ -133,8 +133,7 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)
{
std::ostringstream stream;
messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error);
messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error);
}
if (!topicInfo.mRace.empty())
@ -166,23 +165,24 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
const std::string specifier = "Actor";
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);
if (index.first == -1)
{
writeMissingIdError(specifier, actor, id, messages);
messages.add(id, "Actor '" + actor + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false;
}
else if (mReferencables.getRecord(index).isDeleted())
{
writeDeletedRecordError(specifier, actor, id, messages);
messages.add(id, "Deleted actor '" + actor + "' is being referenced", "", CSMDoc::Message::Severity_Error);
return false;
}
else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature)
{
writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages);
CSMWorld::UniversalId tempId(index.second, actor);
std::ostringstream stream;
stream << "Object '" << actor << "' has invalid type " << tempId.getTypeName() << " (an actor must be an NPC or a creature)";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false;
}
@ -192,11 +192,9 @@ bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const
bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
const std::string specifier = "Cell";
if (mCellNames.find(cell) == mCellNames.end())
{
writeMissingIdError(specifier, cell, id, messages);
messages.add(id, "Cell '" + cell + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false;
}
@ -209,9 +207,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
if (rank < -1)
{
std::ostringstream stream;
stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
stream << "Faction rank is set to " << rank << ", but it should be set to -1 if there are no rank requirements";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
return false;
}
@ -229,8 +226,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
if (rank >= limit)
{
std::ostringstream stream;
stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1
<< " for the " << factionName << " faction";
stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1
<< " for the '" << factionName << "' faction";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false;
@ -242,18 +239,16 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
const std::string specifier = "Item";
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);
if (index.first == -1)
{
writeMissingIdError(specifier, item, id, messages);
messages.add(id, ("Item '" + item + "' does not exist"), "", CSMDoc::Message::Severity_Error);
return false;
}
else if (mReferencables.getRecord(index).isDeleted())
{
writeDeletedRecordError(specifier, item, id, messages);
messages.add(id, ("Deleted item '" + item + "' is being referenced"), "", CSMDoc::Message::Severity_Error);
return false;
}
else
@ -276,8 +271,13 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS
break;
default:
writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages);
{
CSMWorld::UniversalId tempId(index.second, item);
std::ostringstream stream;
stream << "Object '" << item << "' has invalid type " << tempId.getTypeName() << " (an item can be a potion, an armor piece, a book and so on)";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false;
}
}
}
@ -291,13 +291,13 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)
{
messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error);
messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error);
return false;
}
else if (!infoCondition.variantTypeIsValid())
{
std::ostringstream stream;
stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of ";
stream << "Value of condition '" << infoCondition.toString() << "' has invalid ";
switch (select.mValue.getType())
{
@ -307,26 +307,21 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
case ESM::VT_Long: stream << "Long"; break;
case ESM::VT_Float: stream << "Float"; break;
case ESM::VT_String: stream << "String"; break;
default: stream << "Unknown"; break;
default: stream << "unknown"; break;
}
stream << " type";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false;
}
else if (infoCondition.conditionIsAlwaysTrue())
{
std::ostringstream stream;
stream << "Info Condition: " << infoCondition.toString() << " is always true";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
messages.add(id, "Condition '" + infoCondition.toString() + "' is always true", "", CSMDoc::Message::Severity_Warning);
return false;
}
else if (infoCondition.conditionIsNeverTrue())
{
std::ostringstream stream;
stream << "Info Condition: " << infoCondition.toString() << " is never true";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
messages.add(id, "Condition '" + infoCondition.toString() + "' is never true", "", CSMDoc::Message::Severity_Warning);
return false;
}
@ -383,11 +378,9 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
const std::string specifier = "Sound File";
if (mSoundFiles.searchId(sound) == -1)
{
writeMissingIdError(specifier, sound, id, messages);
messages.add(id, "Sound file '" + sound + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false;
}
@ -402,47 +395,14 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW
if (index == -1)
{
writeMissingIdError(T::getRecordType(), name, id, messages);
messages.add(id, T::getRecordType() + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false;
}
else if (collection.getRecord(index).isDeleted())
{
writeDeletedRecordError(T::getRecordType(), name, id, messages);
messages.add(id, "Deleted " + T::getRecordType() + " record '" + name + "' is being referenced", "", CSMDoc::Message::Severity_Error);
return false;
}
return true;
}
// Error functions
void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
std::ostringstream stream;
stream << specifier << ": ID or name \"" << missingId << "\" could not be found";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
std::ostringstream stream;
stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
CSMWorld::UniversalId tempId(invalidType, invalidId);
std::ostringstream stream;
stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \""
<< invalidId << "\" (can be of type " << expectedType << ")";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}

View file

@ -80,17 +80,6 @@ namespace CSMTools
template <typename T>
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
// Common error messages
void writeMissingIdError(const std::string& specifier, const std::string& missingId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
void writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
};
}

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

View file

@ -318,7 +318,7 @@ namespace CSMWorld
QVariant BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const
{
if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin)
if (mMeshType != nullptr && mMeshType->get(record) == ESM::BodyPart::MT_Skin)
{
return QString::fromUtf8(record.get().mRace.c_str());
}

View file

@ -244,7 +244,7 @@ void CSMWorld::CreateCommand::applyModifications()
if (!mNestedValues.empty())
{
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
if (tree == NULL)
if (tree == nullptr)
{
throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model");
}
@ -445,16 +445,14 @@ void CSMWorld::UpdateCellCommand::redo()
int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell);
mIndex = mModel.index (mRow, cellColumn);
const int cellSize = 8192;
QModelIndex xIndex = mModel.index (
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos));
QModelIndex yIndex = mModel.index (
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos));
int x = std::floor (mModel.data (xIndex).toFloat() / cellSize);
int y = std::floor (mModel.data (yIndex).toFloat() / cellSize);
int x = std::floor (mModel.data (xIndex).toFloat() / Constants::CellSizeInUnits);
int y = std::floor (mModel.data (yIndex).toFloat() / Constants::CellSizeInUnits);
std::ostringstream stream;

View file

@ -572,6 +572,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
UniversalId::Type_Video);
addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData);
mActorAdapter.reset(new ActorAdapter(*this));
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
}
@ -912,6 +914,16 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId&
return iter->second;
}
const CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() const
{
return mActorAdapter.get();
}
CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter()
{
return mActorAdapter.get();
}
void CSMWorld::Data::merge()
{
mGlobals.merge();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -437,7 +437,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer));
lightColumns.mRadius = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer));
mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Colour));
lightColumns.mColor = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound));

View file

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

View file

@ -18,47 +18,43 @@ namespace
static const TypeData sNoArg[] =
{
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Objects", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References,
"Instances", 0 },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap,
"Region Map", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", 0 },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", ":./global-variable.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", ":./skill.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", ":./class.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", ":./faction.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", ":./race.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", ":./sound.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", ":./script.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", ":./region.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", ":./birthsign.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", ":./spell.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", ":./dialogue-topics.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", ":./journal-topics.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", ":./dialogue-topic-infos.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", ":./journal-topic-infos.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", ":./cell.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", ":./enchantment.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", ":./body-part.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects", ":./object.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances", ":./instance.png" },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", ":./region-map.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", ":./filter.png" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", ":./resources-mesh" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", ":./resources-icon" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", ":./resources-music" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", ":resources-sound" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", ":./resources-texture" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":./resources-video" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", ":./debug-profile.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", ":./sound-generator.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", ":./magic-effect.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":./land-heightmap.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "Land Textures", ":./land-texture.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":./pathgrid.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", ":./start-script.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":./metadata.png" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
};
@ -81,7 +77,7 @@ namespace
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", ":./journal-topic-infos.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", ":./object.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" },
@ -93,9 +89,9 @@ namespace
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList,
"Creature Levelled List", ":./leveled-creature.png" },
"Creature Levelled List", ":./levelled-creature.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList,
"Item Levelled List", ":./leveled-item.png" },
"Item Levelled List", ":./levelled-item.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous,
@ -105,35 +101,35 @@ namespace
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", 0 },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", ":./instance.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", ":./scene.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", ":./record-preview.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", ":./enchantment.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":./body-part.png" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":resources-mesh"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":resources-icon"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":resources-music" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":resources-sound" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":resources-texture"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":resources-video"},
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":./resources-mesh"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":./resources-icon"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":./resources-music" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":./resources-sound" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":./resources-texture" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":./resources-video" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", ":./debug-profile.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", ":./sound-generator.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", ":./magic-effect.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", ":./land-heightmap.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", ":./land-texture.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "Land Texture", ":./land-texture.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", ":./pathgrid.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", ":./start-script.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
};
static const TypeData sIndexArg[] =
{
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", 0 },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", ":./menu-verify.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", ":./error-log.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", ":./menu-search.png" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
};
}

View file

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

View file

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

View file

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

View file

@ -47,58 +47,40 @@ void CSVDoc::View::setupFileMenu()
{
QMenu *file = menuBar()->addMenu (tr ("File"));
QAction *newGame = new QAction (tr ("New Game"), this);
QAction* newGame = createMenuEntry("New Game", ":./menu-new-game.png", file, "document-file-newgame");
connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest()));
setupShortcut("document-file-newgame", newGame);
file->addAction (newGame);
QAction *newAddon = new QAction (tr ("New Addon"), this);
QAction* newAddon = createMenuEntry("New Addon", ":./menu-new-addon.png", file, "document-file-newaddon");
connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest()));
setupShortcut("document-file-newaddon", newAddon);
file->addAction (newAddon);
QAction *open = new QAction (tr ("Open"), this);
QAction* open = createMenuEntry("Open", ":./menu-open.png", file, "document-file-open");
connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest()));
setupShortcut("document-file-open", open);
file->addAction (open);
mSave = new QAction (tr ("Save"), this);
connect (mSave, SIGNAL (triggered()), this, SLOT (save()));
setupShortcut("document-file-save", mSave);
file->addAction (mSave);
QAction* save = createMenuEntry("Save", ":./menu-save.png", file, "document-file-save");
connect (save, SIGNAL (triggered()), this, SLOT (save()));
mSave = save;
mVerify = new QAction (tr ("Verify"), this);
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
setupShortcut("document-file-verify", mVerify);
file->addAction (mVerify);
QAction* verify = createMenuEntry("Verify", ":./menu-verify.png", file, "document-file-verify");
connect (verify, SIGNAL (triggered()), this, SLOT (verify()));
mVerify = verify;
mMerge = new QAction (tr ("Merge"), this);
connect (mMerge, SIGNAL (triggered()), this, SLOT (merge()));
setupShortcut("document-file-merge", mMerge);
file->addAction (mMerge);
QAction* merge = createMenuEntry("Merge", ":./menu-merge.png", file, "document-file-merge");
connect (merge, SIGNAL (triggered()), this, SLOT (merge()));
mMerge = merge;
QAction *loadErrors = new QAction (tr ("Open Load Error Log"), this);
QAction* loadErrors = createMenuEntry("Error Log", ":./error-log.png", file, "document-file-errorlog");
connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog()));
setupShortcut("document-file-errorlog", loadErrors);
file->addAction (loadErrors);
QAction *meta = new QAction (tr ("Meta Data"), this);
QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata");
connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView()));
setupShortcut("document-file-metadata", meta);
file->addAction (meta);
QAction *close = new QAction (tr ("Close Document"), this);
QAction* close = createMenuEntry("Close", ":./menu-close.png", file, "document-file-close");
connect (close, SIGNAL (triggered()), this, SLOT (close()));
setupShortcut("document-file-close", close);
file->addAction(close);
QAction *exit = new QAction (tr ("Exit Application"), this);
QAction* exit = createMenuEntry("Exit", ":./menu-exit.png", file, "document-file-exit");
connect (exit, SIGNAL (triggered()), this, SLOT (exit()));
connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *)));
setupShortcut("document-file-exit", exit);
file->addAction(exit);
connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *)));
}
namespace
@ -130,251 +112,174 @@ void CSVDoc::View::setupEditMenu()
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
setupShortcut("document-edit-undo", mUndo);
connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));
mUndo->setIcon(QIcon(QString::fromStdString(":./menu-undo.png")));
edit->addAction (mUndo);
mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo"));
connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ()));
setupShortcut("document-edit-redo", mRedo);
mRedo->setIcon(QIcon(QString::fromStdString(":./menu-redo.png")));
edit->addAction (mRedo);
QAction *userSettings = new QAction (tr ("Preferences"), this);
QAction* userSettings = createMenuEntry("Preferences", ":./menu-preferences.png", edit, "document-edit-preferences");
connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest()));
setupShortcut("document-edit-preferences", userSettings);
edit->addAction (userSettings);
QAction *search = new QAction (tr ("Search"), this);
QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search");
connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView()));
setupShortcut("document-edit-search", search);
edit->addAction (search);
}
void CSVDoc::View::setupViewMenu()
{
QMenu *view = menuBar()->addMenu (tr ("View"));
QAction *newWindow = new QAction (tr ("New View"), this);
QAction *newWindow = createMenuEntry("New View", ":./menu-new-window.png", view, "document-view-newview");
connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
setupShortcut("document-view-newview", newWindow);
view->addAction (newWindow);
mShowStatusBar = new QAction (tr ("Toggle Status Bar"), this);
mShowStatusBar->setCheckable (true);
mShowStatusBar = createMenuEntry("Toggle Status Bar", ":./menu-status-bar.png", view, "document-view-statusbar");
connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool)));
setupShortcut("document-view-statusbar", mShowStatusBar);
mShowStatusBar->setCheckable (true);
mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue());
view->addAction (mShowStatusBar);
QAction *filters = new QAction (tr ("Filters"), this);
QAction *filters = createMenuEntry(CSMWorld::UniversalId::Type_Filters, view, "document-mechanics-filters");
connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView()));
setupShortcut("document-view-filters", filters);
view->addAction (filters);
}
void CSVDoc::View::setupWorldMenu()
{
QMenu *world = menuBar()->addMenu (tr ("World"));
QAction *regions = new QAction (tr ("Regions"), this);
QAction* regions = createMenuEntry(CSMWorld::UniversalId::Type_Regions, world, "document-world-regions");
connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));
setupShortcut("document-world-regions", regions);
world->addAction (regions);
QAction *cells = new QAction (tr ("Cells"), this);
QAction* cells = createMenuEntry(CSMWorld::UniversalId::Type_Cells, world, "document-world-cells");
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
setupShortcut("document-world-cells", cells);
world->addAction (cells);
QAction *referenceables = new QAction (tr ("Objects"), this);
QAction* referenceables = createMenuEntry(CSMWorld::UniversalId::Type_Referenceables, world, "document-world-referencables");
connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));
setupShortcut("document-world-referencables", referenceables);
world->addAction (referenceables);
QAction *references = new QAction (tr ("Instances"), this);
QAction* references = createMenuEntry(CSMWorld::UniversalId::Type_References, world, "document-world-references");
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
setupShortcut("document-world-references", references);
world->addAction (references);
QAction *lands = new QAction (tr ("Lands"), this);
QAction *lands = createMenuEntry(CSMWorld::UniversalId::Type_Lands, world, "document-world-lands");
connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView()));
setupShortcut("document-world-lands", lands);
world->addAction (lands);
QAction *landTextures = new QAction (tr ("Land Textures"), this);
QAction *landTextures = createMenuEntry(CSMWorld::UniversalId::Type_LandTextures, world, "document-world-landtextures");
connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView()));
setupShortcut("document-world-landtextures", landTextures);
world->addAction (landTextures);
QAction *grid = new QAction (tr ("Pathgrid"), this);
QAction *grid = createMenuEntry(CSMWorld::UniversalId::Type_Pathgrids, world, "document-world-pathgrid");
connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView()));
setupShortcut("document-world-pathgrid", grid);
world->addAction (grid);
world->addSeparator(); // items that don't represent single record lists follow here
QAction *regionMap = new QAction (tr ("Region Map"), this);
QAction *regionMap = createMenuEntry(CSMWorld::UniversalId::Type_RegionMap, world, "document-world-regionmap");
connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
setupShortcut("document-world-regionmap", regionMap);
world->addAction (regionMap);
}
void CSVDoc::View::setupMechanicsMenu()
{
QMenu *mechanics = menuBar()->addMenu (tr ("Mechanics"));
QAction *globals = new QAction (tr ("Globals"), this);
QAction* globals = createMenuEntry(CSMWorld::UniversalId::Type_Globals, mechanics, "document-mechanics-globals");
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
setupShortcut("document-mechanics-globals", globals);
mechanics->addAction (globals);
QAction *gmsts = new QAction (tr ("Game Settings"), this);
QAction* gmsts = createMenuEntry(CSMWorld::UniversalId::Type_Gmsts, mechanics, "document-mechanics-gamesettings");
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
setupShortcut("document-mechanics-gamesettings", gmsts);
mechanics->addAction (gmsts);
QAction *scripts = new QAction (tr ("Scripts"), this);
QAction* scripts = createMenuEntry(CSMWorld::UniversalId::Type_Scripts, mechanics, "document-mechanics-scripts");
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
setupShortcut("document-mechanics-scripts", scripts);
mechanics->addAction (scripts);
QAction *spells = new QAction (tr ("Spells"), this);
QAction* spells = createMenuEntry(CSMWorld::UniversalId::Type_Spells, mechanics, "document-mechanics-spells");
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
setupShortcut("document-mechanics-spells", spells);
mechanics->addAction (spells);
QAction *enchantments = new QAction (tr ("Enchantments"), this);
QAction* enchantments = createMenuEntry(CSMWorld::UniversalId::Type_Enchantments, mechanics, "document-mechanics-enchantments");
connect (enchantments, SIGNAL (triggered()), this, SLOT (addEnchantmentsSubView()));
setupShortcut("document-mechanics-enchantments", enchantments);
mechanics->addAction (enchantments);
QAction *effects = new QAction (tr ("Magic Effects"), this);
connect (effects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView()));
setupShortcut("document-mechanics-magiceffects", effects);
mechanics->addAction (effects);
QAction* magicEffects = createMenuEntry(CSMWorld::UniversalId::Type_MagicEffects, mechanics, "document-mechanics-magiceffects");
connect (magicEffects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView()));
QAction *startScripts = new QAction (tr ("Start Scripts"), this);
QAction* startScripts = createMenuEntry(CSMWorld::UniversalId::Type_StartScripts, mechanics, "document-mechanics-startscripts");
connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView()));
setupShortcut("document-mechanics-startscripts", startScripts);
mechanics->addAction (startScripts);
}
void CSVDoc::View::setupCharacterMenu()
{
QMenu *characters = menuBar()->addMenu (tr ("Characters"));
QAction *skills = new QAction (tr ("Skills"), this);
QAction* skills = createMenuEntry(CSMWorld::UniversalId::Type_Skills, characters, "document-character-skills");
connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView()));
setupShortcut("document-character-skills", skills);
characters->addAction (skills);
QAction *classes = new QAction (tr ("Classes"), this);
QAction* classes = createMenuEntry(CSMWorld::UniversalId::Type_Classes, characters, "document-character-classes");
connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView()));
setupShortcut("document-character-classes", classes);
characters->addAction (classes);
QAction *factions = new QAction (tr ("Factions"), this);
QAction* factions = createMenuEntry(CSMWorld::UniversalId::Type_Faction, characters, "document-character-factions");
connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView()));
setupShortcut("document-character-factions", factions);
characters->addAction (factions);
QAction *races = new QAction (tr ("Races"), this);
QAction* races = createMenuEntry(CSMWorld::UniversalId::Type_Races, characters, "document-character-races");
connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView()));
setupShortcut("document-character-races", races);
characters->addAction (races);
QAction *birthsigns = new QAction (tr ("Birthsigns"), this);
QAction* birthsigns = createMenuEntry(CSMWorld::UniversalId::Type_Birthsigns, characters, "document-character-birthsigns");
connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView()));
setupShortcut("document-character-birthsigns", birthsigns);
characters->addAction (birthsigns);
QAction *topics = new QAction (tr ("Topics"), this);
QAction* topics = createMenuEntry(CSMWorld::UniversalId::Type_Topics, characters, "document-character-topics");
connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView()));
setupShortcut("document-character-topics", topics);
characters->addAction (topics);
QAction *journals = new QAction (tr ("Journals"), this);
QAction* journals = createMenuEntry(CSMWorld::UniversalId::Type_Journals, characters, "document-character-journals");
connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));
setupShortcut("document-character-journals", journals);
characters->addAction (journals);
QAction *topicInfos = new QAction (tr ("Topic Infos"), this);
QAction* topicInfos = createMenuEntry(CSMWorld::UniversalId::Type_TopicInfos, characters, "document-character-topicinfos");
connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView()));
setupShortcut("document-character-topicinfos", topicInfos);
characters->addAction (topicInfos);
QAction *journalInfos = new QAction (tr ("Journal Infos"), this);
QAction* journalInfos = createMenuEntry(CSMWorld::UniversalId::Type_JournalInfos, characters, "document-character-journalinfos");
connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView()));
setupShortcut("document-character-journalinfos", journalInfos);
characters->addAction (journalInfos);
QAction *bodyParts = new QAction (tr ("Body Parts"), this);
QAction* bodyParts = createMenuEntry(CSMWorld::UniversalId::Type_BodyParts, characters, "document-character-bodyparts");
connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView()));
setupShortcut("document-character-bodyparts", bodyParts);
characters->addAction (bodyParts);
}
void CSVDoc::View::setupAssetsMenu()
{
QMenu *assets = menuBar()->addMenu (tr ("Assets"));
QAction *reload = new QAction (tr ("Reload"), this);
QAction* reload = createMenuEntry("Reload", ":./menu-reload.png", assets, "document-assets-reload");
connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged()));
setupShortcut("document-assets-reload", reload);
assets->addAction (reload);
assets->addSeparator();
QAction *sounds = new QAction (tr ("Sounds"), this);
QAction* sounds = createMenuEntry(CSMWorld::UniversalId::Type_Sounds, assets, "document-assets-sounds");
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
setupShortcut("document-assets-sounds", sounds);
assets->addAction (sounds);
QAction *soundGens = new QAction (tr ("Sound Generators"), this);
QAction* soundGens = createMenuEntry(CSMWorld::UniversalId::Type_SoundGens, assets, "document-assets-soundgens");
connect (soundGens, SIGNAL (triggered()), this, SLOT (addSoundGensSubView()));
setupShortcut("document-assets-soundgens", soundGens);
assets->addAction (soundGens);
assets->addSeparator(); // resources follow here
QAction *meshes = new QAction (tr ("Meshes"), this);
QAction* meshes = createMenuEntry(CSMWorld::UniversalId::Type_Meshes, assets, "document-assets-meshes");
connect (meshes, SIGNAL (triggered()), this, SLOT (addMeshesSubView()));
setupShortcut("document-assets-meshes", meshes);
assets->addAction (meshes);
QAction *icons = new QAction (tr ("Icons"), this);
QAction* icons = createMenuEntry(CSMWorld::UniversalId::Type_Icons, assets, "document-assets-icons");
connect (icons, SIGNAL (triggered()), this, SLOT (addIconsSubView()));
setupShortcut("document-assets-icons", icons);
assets->addAction (icons);
QAction *musics = new QAction (tr ("Music"), this);
QAction* musics = createMenuEntry(CSMWorld::UniversalId::Type_Musics, assets, "document-assets-musics");
connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView()));
setupShortcut("document-assets-music", musics);
assets->addAction (musics);
QAction *soundsRes = new QAction (tr ("Sound Files"), this);
connect (soundsRes, SIGNAL (triggered()), this, SLOT (addSoundsResSubView()));
setupShortcut("document-assets-soundres", soundsRes);
assets->addAction (soundsRes);
QAction* soundFiles = createMenuEntry(CSMWorld::UniversalId::Type_SoundsRes, assets, "document-assets-soundres");
connect (soundFiles, SIGNAL (triggered()), this, SLOT (addSoundsResSubView()));
QAction *textures = new QAction (tr ("Textures"), this);
QAction* textures = createMenuEntry(CSMWorld::UniversalId::Type_Textures, assets, "document-assets-textures");
connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView()));
setupShortcut("document-assets-textures", textures);
assets->addAction (textures);
QAction *videos = new QAction (tr ("Videos"), this);
QAction* videos = createMenuEntry(CSMWorld::UniversalId::Type_Videos, assets, "document-assets-videos");
connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView()));
setupShortcut("document-assets-videos", videos);
assets->addAction (videos);
}
void CSVDoc::View::setupDebugMenu()
{
QMenu *debug = menuBar()->addMenu (tr ("Debug"));
QAction *profiles = new QAction (tr ("Debug Profiles"), this);
QAction* profiles = createMenuEntry(CSMWorld::UniversalId::Type_DebugProfiles, debug, "document-debug-profiles");
connect (profiles, SIGNAL (triggered()), this, SLOT (addDebugProfilesSubView()));
debug->addAction (profiles);
debug->addSeparator();
@ -387,18 +292,41 @@ void CSVDoc::View::setupDebugMenu()
QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu);
runDebug->setText (tr ("Run OpenMW"));
setupShortcut("document-debug-run", runDebug);
runDebug->setIcon(QIcon(QString::fromStdString(":./run-openmw.png")));
mStopDebug = new QAction (tr ("Shutdown OpenMW"), this);
connect (mStopDebug, SIGNAL (triggered()), this, SLOT (stop()));
setupShortcut("document-debug-shutdown", mStopDebug);
debug->addAction (mStopDebug);
QAction* stopDebug = createMenuEntry("Stop OpenMW", ":./stop-openmw.png", debug, "document-debug-shutdown");
connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop()));
mStopDebug = stopDebug;
QAction *runLog = new QAction (tr ("Open Run Log"), this);
QAction* runLog = createMenuEntry(CSMWorld::UniversalId::Type_RunLog, debug, "document-debug-runlog");
connect (runLog, SIGNAL (triggered()), this, SLOT (addRunLogSubView()));
setupShortcut("document-debug-runlog", runLog);
debug->addAction (runLog);
}
QAction* CSVDoc::View::createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName)
{
const std::string title = CSMWorld::UniversalId (type).getTypeName();
QAction *entry = new QAction(QString::fromStdString(title), this);
setupShortcut(shortcutName, entry);
const std::string iconName = CSMWorld::UniversalId (type).getIcon();
if (!iconName.empty() && iconName != ":placeholder")
entry->setIcon(QIcon(QString::fromStdString(iconName)));
menu->addAction (entry);
return entry;
}
QAction* CSVDoc::View::createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName)
{
QAction *entry = new QAction(QString::fromStdString(title), this);
setupShortcut(shortcutName, entry);
if (!iconName.empty() && iconName != ":placeholder")
entry->setIcon(QIcon(QString::fromStdString(iconName)));
menu->addAction (entry);
return entry;
}
void CSVDoc::View::setupUi()
@ -472,7 +400,7 @@ void CSVDoc::View::updateSubViewIndices(SubView *view)
else
{
delete subView->titleBarWidget();
subView->setTitleBarWidget (NULL);
subView->setTitleBarWidget (nullptr);
}
}
}
@ -501,7 +429,7 @@ void CSVDoc::View::updateActions()
CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
mViewTotal (totalViews), mScroll(NULL), mScrollbarOnly(false)
mViewTotal (totalViews), mScroll(nullptr), mScrollbarOnly(false)
{
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
@ -635,7 +563,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
return;
}
SubView *view = NULL;
SubView *view = nullptr;
if(isReferenceable)
{
view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument);
@ -703,7 +631,7 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max)
void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
{
if (*setting=="Windows/hide-subview")
updateSubViewIndices (NULL);
updateSubViewIndices (nullptr);
else if (*setting=="Windows/mainwindow-scrollbar")
{
if (setting->toString()!="Grow Only")
@ -731,7 +659,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
mScroll->takeWidget();
setCentralWidget (&mSubViewWindow);
mScroll->deleteLater();
mScroll = NULL;
mScroll = nullptr;
}
}
}

View file

@ -66,6 +66,9 @@ namespace CSVDoc
void closeEvent (QCloseEvent *event);
QAction* createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName);
QAction* createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName);
void setupFileMenu();
void setupEditMenu();
@ -146,7 +149,7 @@ namespace CSVDoc
void updateTitle();
// called when subviews are added or removed
void updateSubViewIndices (SubView *view = NULL);
void updateSubViewIndices (SubView *view = nullptr);
private slots:

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)
, mSecondaryMoveMult(50)
, mWheelMoveMult(8)
, mCamera(NULL)
, mCamera(nullptr)
{
}
@ -81,7 +81,7 @@ namespace CSVRender
bool wasActive = mActive;
mCamera = camera;
mActive = (mCamera != NULL);
mActive = (mCamera != nullptr);
if (mActive != wasActive)
{

View file

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

View file

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

View file

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

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
{
const float Pi = 3.14159265f;
float x, y, z;
float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z());
if (std::abs(test) >= 1.f)
{
x = atan2(rot.x(), rot.w());
y = (test > 0) ? (Pi / 2) : (-Pi / 2);
y = (test > 0) ? (osg::PI / 2) : (-osg::PI / 2);
z = 0;
}
else

View file

@ -1,6 +1,8 @@
#include "object.hpp"
#include <stdexcept>
#include <string>
#include <iostream>
#include <osg/Depth>
#include <osg/Group>
@ -29,6 +31,7 @@
#include <components/sceneutil/lightmanager.hpp>
#include <components/fallback/fallback.hpp>
#include "actor.hpp"
#include "mask.hpp"
@ -78,64 +81,62 @@ void CSVRender::Object::update()
{
clear();
std::string model;
int error = 0; // 1 referenceable does not exist, 2 referenceable does not specify a mesh
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
const int TypeIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType);
const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model);
int index = referenceables.searchId (mReferenceableId);
const ESM::Light* light = NULL;
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;
}
const ESM::Light* light = nullptr;
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
if (error)
if (index == -1)
{
mBaseNode->addChild(createErrorCube());
return;
}
else
/// \todo check for Deleted state (error 1)
int recordType = referenceables.getData(index, TypeIndex).toInt();
std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData();
if (recordType == CSMWorld::UniversalId::Type_Light)
{
try
light = &dynamic_cast<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;
mResourceSystem->getSceneManager()->getInstance(path, mBaseNode);
}
catch (std::exception& e)
else
{
// TODO: use error marker mesh
Log(Debug::Error) << e.what();
throw std::runtime_error(mReferenceableId + " has no model");
}
}
catch (std::exception& e)
{
// TODO: use error marker mesh
Log(Debug::Error) << e.what();
}
if (light)
{
@ -313,20 +314,18 @@ osg::ref_ptr<osg::Node> CSVRender::Object::makeMoveOrScaleMarker (int axis)
osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)
{
const float Pi = 3.14159265f;
const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius());
const float OuterRadius = InnerRadius + MarkerShaftWidth;
const float SegmentDistance = 100.f;
const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * Pi / SegmentDistance)));
const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * osg::PI / SegmentDistance)));
const size_t VerticesPerSegment = 4;
const size_t IndicesPerSegment = 24;
const size_t VertexCount = SegmentCount * VerticesPerSegment;
const size_t IndexCount = SegmentCount * IndicesPerSegment;
const float Angle = 2 * Pi / SegmentCount;
const float Angle = 2 * osg::PI / SegmentCount;
const unsigned short IndexPattern[IndicesPerSegment] =
{

View file

@ -1,6 +1,7 @@
#ifndef OPENCS_VIEW_OBJECT_H
#define OPENCS_VIEW_OBJECT_H
#include <memory>
#include <string>
#include <osg/ref_ptr>
@ -41,6 +42,7 @@ namespace CSMWorld
namespace CSVRender
{
class Actor;
class Object;
// An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query
@ -98,6 +100,7 @@ namespace CSVRender
osg::ref_ptr<osg::Node> mMarker[3];
int mSubMode;
float mMarkerTransparency;
std::unique_ptr<Actor> mActor;
/// Not implemented
Object (const Object&);

View file

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

View file

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

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

View file

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

View file

@ -1,11 +1,9 @@
#include "coloreditor.hpp"
#include <QApplication>
#include <QColor>
#include <QColorDialog>
#include <QDesktopWidget>
#include <QPainter>
#include <QRect>
#include <QShowEvent>
#include "colorpickerpopup.hpp"
@ -27,9 +25,7 @@ CSVWidget::ColorEditor::ColorEditor(QWidget *parent, const bool popupOnStart)
mColorPicker(new ColorPickerPopup(this)),
mPopupOnStart(popupOnStart)
{
setCheckable(true);
connect(this, SIGNAL(clicked()), this, SLOT(showPicker()));
connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid()));
connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &)));
}
@ -85,20 +81,7 @@ void CSVWidget::ColorEditor::setColor(const int colorInt)
void CSVWidget::ColorEditor::showPicker()
{
if (isChecked())
{
mColorPicker->showPicker(calculatePopupPosition(), mColor);
}
else
{
mColorPicker->hide();
}
}
void CSVWidget::ColorEditor::pickerHid()
{
setChecked(false);
emit pickingFinished();
mColorPicker->showPicker(calculatePopupPosition(), mColor);
}
void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color)

View file

@ -45,7 +45,6 @@ namespace CSVWidget
private slots:
void showPicker();
void pickerHid();
void pickerColorChanged(const QColor &color);
signals:

View file

@ -4,7 +4,6 @@
#include <QPushButton>
#include <QEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QLayout>
#include <QStyleOption>
@ -19,7 +18,6 @@ CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent)
mColorPicker->setWindowFlags(Qt::Widget);
mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog);
mColorPicker->installEventFilter(this);
mColorPicker->open();
connect(mColorPicker,
SIGNAL(currentColorChanged(const QColor &)),
this,
@ -39,14 +37,15 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo
geometry.moveTo(position);
setGeometry(geometry);
mColorPicker->setCurrentColor(initialColor);
show();
// Calling getColor() creates a blocking dialog that will continue execution once the user chooses OK or Cancel
QColor color = mColorPicker->getColor(initialColor);
mColorPicker->setCurrentColor(color);
}
void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event)
{
QPushButton *button = qobject_cast<QPushButton *>(parentWidget());
if (button != NULL)
if (button != nullptr)
{
QStyleOptionButton option;
option.init(button);
@ -63,12 +62,6 @@ void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event)
QFrame::mousePressEvent(event);
}
void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event)
{
QFrame::hideEvent(event);
emit hid();
}
bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event)
{
if (object == mColorPicker && event->type() == QEvent::KeyPress)

View file

@ -20,11 +20,9 @@ namespace CSVWidget
protected:
virtual void mousePressEvent(QMouseEvent *event);
virtual void hideEvent(QHideEvent *event);
virtual bool eventFilter(QObject *object, QEvent *event);
signals:
void hid();
void colorChanged(const QColor &color);
};
}

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,6 @@
#include "util.hpp"
#include <stdexcept>
#include <climits>
#include <cfloat>
#include <QUndoStack>
#include <QMetaProperty>
@ -131,7 +129,7 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM
// Color columns use a custom editor, so we need to fetch selected color from it.
CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor);
if (colorEditor != NULL)
if (colorEditor != nullptr)
{
variant = colorEditor->colorInt();
}
@ -209,7 +207,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO
case CSMWorld::ColumnBase::Display_Integer:
{
DialogueSpinBox *sb = new DialogueSpinBox(parent);
sb->setRange(INT_MIN, INT_MAX);
sb->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
return sb;
}
@ -324,7 +322,7 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde
// Color columns use a custom editor, so we need explicitly set a data for it
CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor);
if (colorEditor != NULL)
if (colorEditor != nullptr)
{
colorEditor->setColor(variant.toInt());
return;

View file

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

View file

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

View file

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

View file

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

View file

@ -412,6 +412,7 @@ namespace MWBase
/// @note throws an exception when invoked on a teleport door
virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0;
virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) = 0; ///< get a list of actors standing on \a object
virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object
virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object
@ -437,12 +438,16 @@ namespace MWBase
virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0;
virtual int canRest() = 0;
///< check if the player is allowed to rest \n
/// 0 - yes \n
/// 1 - only waiting \n
/// 2 - player is underwater \n
/// 3 - enemies are nearby (not implemented)
enum RestPermitted
{
Rest_Allowed = 0,
Rest_OnlyWaiting = 1,
Rest_PlayerIsUnderwater = 2,
Rest_EnemiesAreNearby = 3
};
/// check if the player is allowed to rest
virtual RestPermitted canRest() const = 0;
/// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0;
@ -490,8 +495,8 @@ namespace MWBase
virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0;
virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0;
virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0;
virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0;
@ -566,6 +571,8 @@ namespace MWBase
virtual bool isPlayerInJail() const = 0;
virtual void rest() = 0;
virtual void setPlayerTraveling(bool traveling) = 0;
virtual bool isPlayerTraveling() const = 0;

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