mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 18:29:55 +00:00
Merge upstream/master
This commit is contained in:
commit
0124be5713
457 changed files with 5859 additions and 3173 deletions
|
@ -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)
|
||||
|
|
58
CHANGELOG.md
58
CHANGELOG.md
|
@ -1,9 +1,12 @@
|
|||
0.45.0
|
||||
------
|
||||
|
||||
Bug #1875: Actors in inactive cells don't heal from resting
|
||||
Bug #1990: Sunrise/sunset not set correct
|
||||
Bug #2131: Lustidrike's spell misses the player every time
|
||||
Bug #2222: Fatigue's effect on selling price is backwards
|
||||
Bug #2256: Landing sound not playing when jumping immediately after landing
|
||||
Bug #2274: Thin platform clips through player character instead of lifting
|
||||
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
|
||||
Bug #2455: Creatures attacks degrade armor
|
||||
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
|
||||
|
@ -15,17 +18,24 @@
|
|||
Bug #2872: Tab completion in console doesn't work with explicit reference
|
||||
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
|
||||
Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue
|
||||
Bug #3059: Unable to hit with marksman weapons when too close to an enemy
|
||||
Bug #3072: Fatal error on AddItem <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
|
||||
------
|
||||
|
|
|
@ -346,7 +346,7 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
|
||||
# OpenAL
|
||||
download "OpenAL-Soft 1.17.2" \
|
||||
"http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \
|
||||
"http://openal-soft.org/openal-binaries/openal-soft-1.17.2-bin.zip" \
|
||||
"OpenAL-Soft-1.17.2.zip"
|
||||
|
||||
# OSG
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
git clone https://github.com/google/googletest.git
|
||||
git clone -b release-1.8.1 https://github.com/google/googletest.git
|
||||
cd googletest
|
||||
mkdir build
|
||||
cd build
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <components/esm/creaturestate.hpp>
|
||||
#include <components/esm/containerstate.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
#include "convertcrec.hpp"
|
||||
#include "convertcntc.hpp"
|
||||
#include "convertscri.hpp"
|
||||
|
@ -288,12 +290,12 @@ namespace ESSImport
|
|||
notepos[1] += 31.f;
|
||||
notepos[0] += 0.5;
|
||||
notepos[1] += 0.5;
|
||||
notepos[0] = 8192 * notepos[0] / 32.f;
|
||||
notepos[1] = 8192 * notepos[1] / 32.f;
|
||||
notepos[0] = Constants::CellSizeInUnits * notepos[0] / 32.f;
|
||||
notepos[1] = Constants::CellSizeInUnits * notepos[1] / 32.f;
|
||||
if (cell.isExterior())
|
||||
{
|
||||
notepos[0] += 8192 * cell.mData.mX;
|
||||
notepos[1] += 8192 * cell.mData.mY;
|
||||
notepos[0] += Constants::CellSizeInUnits * cell.mData.mX;
|
||||
notepos[1] += Constants::CellSizeInUnits * cell.mData.mY;
|
||||
}
|
||||
// TODO: what encoding is this in?
|
||||
std::string note = esm.getHNString("MPNT");
|
||||
|
|
|
@ -526,7 +526,10 @@ public:
|
|||
class ConvertGAME : public Converter
|
||||
{
|
||||
public:
|
||||
ConvertGAME() : mHasGame(false) {}
|
||||
ConvertGAME()
|
||||
: mHasGame(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "convertplayer.hpp"
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
|
@ -78,9 +79,8 @@ namespace ESSImport
|
|||
|
||||
if (pcdt.mHasENAM)
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize;
|
||||
out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize;
|
||||
out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * Constants::CellSizeInUnits;
|
||||
out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * Constants::CellSizeInUnits;
|
||||
out.mLastKnownExteriorPosition[2] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <components/esm/loadlevlist.hpp>
|
||||
#include <components/esm/loadglob.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "importercontext.hpp"
|
||||
|
@ -413,9 +415,8 @@ namespace ESSImport
|
|||
if (context.mPlayer.mCellId.mPaged)
|
||||
{
|
||||
// exterior cell -> determine cell coordinates based on position
|
||||
const int cellSize = 8192;
|
||||
int cellX = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize));
|
||||
int cellY = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize));
|
||||
int cellX = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0] / Constants::CellSizeInUnits));
|
||||
int cellY = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / Constants::CellSizeInUnits));
|
||||
context.mPlayer.mCellId.mIndex.mX = cellX;
|
||||
context.mPlayer.mCellId.mIndex.mY = cellY;
|
||||
}
|
||||
|
|
|
@ -14,13 +14,13 @@ namespace ESSImport
|
|||
{
|
||||
struct GMDT
|
||||
{
|
||||
char mCellName[64];
|
||||
int mFogColour;
|
||||
float mFogDensity;
|
||||
int mCurrentWeather, mNextWeather;
|
||||
int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage
|
||||
float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition
|
||||
int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage
|
||||
char mCellName[64] {};
|
||||
int mFogColour {0};
|
||||
float mFogDensity {0.f};
|
||||
int mCurrentWeather {0}, mNextWeather {0};
|
||||
int mWeatherTransition {0}; // 0-100 transition between weathers, top 3 bytes may be garbage
|
||||
float mTimeOfNextTransition {0.f}; // weather changes when gamehour == timeOfNextTransition
|
||||
int mMasserPhase {0}, mSecundaPhase {0}; // top 3 bytes may be garbage
|
||||
};
|
||||
|
||||
GMDT mGMDT;
|
||||
|
|
|
@ -72,6 +72,7 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
||||
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
||||
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
||||
loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
|
||||
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
||||
loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
|
||||
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
|
||||
|
@ -131,6 +132,7 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
||||
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
||||
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
||||
saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
|
||||
saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
|
||||
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
|
||||
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
|
||||
|
|
|
@ -36,6 +36,8 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
|
|||
ui.setupUi (this);
|
||||
setObjectName ("DataFilesPage");
|
||||
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);
|
||||
const QString encoding = mGameSettings.value("encoding", "win1252");
|
||||
mSelector->setEncoding(encoding);
|
||||
|
||||
mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this);
|
||||
|
||||
|
@ -357,4 +359,4 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
|
|||
QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles));
|
||||
std::sort(cellNamesList.begin(), cellNamesList.end());
|
||||
emit signalLoadedCellsChanged(cellNamesList);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ opencs_hdrs_noqt (model/doc
|
|||
|
||||
opencs_units (model/world
|
||||
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel
|
||||
actoradapter
|
||||
)
|
||||
|
||||
|
||||
|
@ -42,7 +43,7 @@ opencs_units_noqt (model/tools
|
|||
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
|
||||
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
|
||||
mergestages gmstcheck topicinfocheck journalcheck
|
||||
mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/tools
|
||||
|
@ -88,7 +89,7 @@ opencs_units (view/render
|
|||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
||||
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
|
||||
cellwater terraintexturemode
|
||||
cellwater terraintexturemode actor
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/render
|
||||
|
|
|
@ -22,7 +22,7 @@ CS::Editor::Editor (int argc, char **argv)
|
|||
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
|
||||
mViewManager (mDocumentManager), mPid(""),
|
||||
mLock(), mMerge (mDocumentManager),
|
||||
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
|
||||
mIpcServerName ("org.openmw.OpenCS"), mServer(nullptr), mClientSocket(nullptr)
|
||||
{
|
||||
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
|
||||
|
||||
|
@ -108,8 +108,9 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
|||
|
||||
mCfgMgr.readConfiguration(variables, desc, quiet);
|
||||
|
||||
mDocumentManager.setEncoding (
|
||||
ToUTF8::calculateEncoding (variables["encoding"].as<Files::EscapeHashString>().toStdString()));
|
||||
const std::string encoding = variables["encoding"].as<Files::EscapeHashString>().toStdString();
|
||||
mDocumentManager.setEncoding (ToUTF8::calculateEncoding (encoding));
|
||||
mFileDialog.setEncoding (QString::fromUtf8(encoding.c_str()));
|
||||
|
||||
mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::EscapeHashString>().toStdString());
|
||||
|
||||
|
@ -338,7 +339,7 @@ bool CS::Editor::makeIPCServer()
|
|||
}
|
||||
|
||||
mServer->close();
|
||||
mServer = NULL;
|
||||
mServer = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,11 +35,6 @@ void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string&
|
|||
mMessages.push_back (Message (id, message, hint, severity));
|
||||
}
|
||||
|
||||
void CSMDoc::Messages::push_back (const std::pair<CSMWorld::UniversalId, std::string>& data)
|
||||
{
|
||||
add (data.first, data.second);
|
||||
}
|
||||
|
||||
CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const
|
||||
{
|
||||
return mMessages.begin();
|
||||
|
|
|
@ -56,9 +56,6 @@ namespace CSMDoc
|
|||
const std::string& hint = "",
|
||||
Message::Severity severity = Message::Severity_Default);
|
||||
|
||||
/// \deprecated Use add instead.
|
||||
void push_back (const std::pair<CSMWorld::UniversalId, std::string>& data);
|
||||
|
||||
Iterator begin() const;
|
||||
|
||||
Iterator end() const;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "operation.hpp"
|
||||
|
||||
CSMDoc::OperationHolder::OperationHolder (Operation *operation)
|
||||
: mOperation(NULL)
|
||||
: mOperation(nullptr)
|
||||
, mRunning (false)
|
||||
{
|
||||
if (operation)
|
||||
|
|
|
@ -305,6 +305,7 @@ void CSMPrefs::State::declare()
|
|||
declareShortcut ("document-assets-videos", "Open Video Asset List", QKeySequence());
|
||||
declareShortcut ("document-debug-run", "Run Debug", QKeySequence());
|
||||
declareShortcut ("document-debug-shutdown", "Stop Debug", QKeySequence());
|
||||
declareShortcut ("document-debug-profiles", "Debug Profiles", QKeySequence());
|
||||
declareShortcut ("document-debug-runlog", "Open Run Log", QKeySequence());
|
||||
|
||||
declareSubcategory ("Table");
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
#include "birthsigncheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadbsgn.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns)
|
||||
: mBirthsigns (birthsigns)
|
||||
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns,
|
||||
const CSMWorld::Resources &textures)
|
||||
: mBirthsigns(birthsigns),
|
||||
mTextures(textures)
|
||||
{
|
||||
mIgnoreBaseRecords = false;
|
||||
}
|
||||
|
@ -34,17 +33,20 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag
|
|||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId);
|
||||
|
||||
// test for empty name, description and texture
|
||||
if (birthsign.mName.empty())
|
||||
messages.push_back (std::make_pair (id, birthsign.mId + " has an empty name"));
|
||||
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (birthsign.mDescription.empty())
|
||||
messages.push_back (std::make_pair (id, birthsign.mId + " has an empty description"));
|
||||
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
|
||||
|
||||
if (birthsign.mTexture.empty())
|
||||
messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture"));
|
||||
|
||||
/// \todo test if the texture exists
|
||||
messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error);
|
||||
else if (mTextures.searchId(birthsign.mTexture) == -1)
|
||||
{
|
||||
std::string ddsTexture = birthsign.mTexture;
|
||||
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1))
|
||||
messages.add(id, "Image '" + birthsign.mTexture + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <components/esm/loadbsgn.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
#include "../world/resources.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
|
@ -12,12 +13,14 @@ namespace CSMTools
|
|||
/// \brief VerifyStage: make sure that birthsign records are internally consistent
|
||||
class BirthsignCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;
|
||||
const CSMWorld::IdCollection<ESM::BirthSign> &mBirthsigns;
|
||||
const CSMWorld::Resources &mTextures;
|
||||
bool mIgnoreBaseRecords;
|
||||
|
||||
public:
|
||||
|
||||
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns);
|
||||
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign> &birthsigns,
|
||||
const CSMWorld::Resources &textures);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
|
|
@ -34,25 +34,23 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
|
|||
|
||||
// Check BYDT
|
||||
if (bodyPart.mData.mPart > 14 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." ));
|
||||
messages.add(id, "Invalid part", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (bodyPart.mData.mFlags > 3 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." ));
|
||||
messages.add(id, "Invalid flags", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (bodyPart.mData.mType > 2 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." ));
|
||||
messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// Check MODL
|
||||
|
||||
if ( bodyPart.mModel.empty() )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." ));
|
||||
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
|
||||
else if ( mMeshes.searchId( bodyPart.mModel ) == -1 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." ));
|
||||
messages.add(id, "Model '" + bodyPart.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// Check FNAM
|
||||
|
||||
if ( bodyPart.mRace.empty() )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." ));
|
||||
messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error);
|
||||
else if ( mRaces.searchId( bodyPart.mRace ) == -1 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." ));
|
||||
messages.add(id, "Race '" + bodyPart.mRace + " does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "classcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadclas.hpp>
|
||||
|
@ -37,26 +36,22 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|||
|
||||
// A class should have a name
|
||||
if (class_.mName.empty())
|
||||
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a name"));
|
||||
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// A playable class should have a description
|
||||
if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty())
|
||||
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a description and it's playable"));
|
||||
messages.add(id, "Description of a playable class is missing", "", CSMDoc::Message::Severity_Warning);
|
||||
|
||||
// test for invalid attributes
|
||||
for (int i=0; i<2; ++i)
|
||||
if (class_.mData.mAttribute[i]==-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "Attribute #" << i << " of " << class_.mId << " is not set";
|
||||
|
||||
messages.push_back (std::make_pair (id, stream.str()));
|
||||
messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1)
|
||||
{
|
||||
messages.push_back (std::make_pair (id, "Class lists same attribute twice"));
|
||||
messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
// test for non-unique skill
|
||||
|
@ -66,10 +61,9 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|||
for (int i2=0; i2<2; ++i2)
|
||||
++skills[class_.mData.mSkills[i][i2]];
|
||||
|
||||
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
|
||||
if (iter->second>1)
|
||||
for (auto &skill : skills)
|
||||
if (skill.second>1)
|
||||
{
|
||||
messages.push_back (std::make_pair (id,
|
||||
ESM::Skill::indexToId (iter->first) + " is listed more than once"));
|
||||
messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
|
82
apps/opencs/model/tools/enchantmentcheck.cpp
Normal file
82
apps/opencs/model/tools/enchantmentcheck.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
31
apps/opencs/model/tools/enchantmentcheck.hpp
Normal file
31
apps/opencs/model/tools/enchantmentcheck.hpp
Normal 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
|
|
@ -1,9 +1,7 @@
|
|||
#include "factioncheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadfact.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
@ -37,12 +35,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages
|
|||
|
||||
// test for empty name
|
||||
if (faction.mName.empty())
|
||||
messages.push_back (std::make_pair (id, faction.mId + " has an empty name"));
|
||||
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// test for invalid attributes
|
||||
if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1)
|
||||
{
|
||||
messages.push_back (std::make_pair (id , "Faction lists same attribute twice"));
|
||||
messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
// test for non-unique skill
|
||||
|
@ -52,11 +50,10 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages
|
|||
if (faction.mData.mSkills[i]!=-1)
|
||||
++skills[faction.mData.mSkills[i]];
|
||||
|
||||
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
|
||||
if (iter->second>1)
|
||||
for (auto &skill : skills)
|
||||
if (skill.second>1)
|
||||
{
|
||||
messages.push_back (std::make_pair (id,
|
||||
ESM::Skill::indexToId (iter->first) + " is listed more than once"));
|
||||
messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "journalcheck.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
|
@ -57,34 +56,27 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
|||
if (journalInfo.mResponse.empty())
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||
|
||||
messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning);
|
||||
messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
|
||||
std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);
|
||||
|
||||
// Duplicate index
|
||||
if (result.second == false)
|
||||
if (!result.second)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex;
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
messages.add(id, "Duplicated quest index " + std::to_string(journalInfo.mData.mJournalIndex), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
||||
if (totalInfoCount == 0)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
|
||||
|
||||
messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning);
|
||||
messages.add(id, "No related journal entry", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
else if (statusNamedCount > 1)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
|
||||
|
||||
messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error);
|
||||
messages.add(id, "Multiple entries with quest status 'Named'", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,79 +4,26 @@
|
|||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
#include "../world/resources.hpp"
|
||||
#include "../world/data.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text)
|
||||
{
|
||||
if (!text.empty())
|
||||
{
|
||||
messages.push_back(std::make_pair(id, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const
|
||||
{
|
||||
const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures;
|
||||
bool exists = false;
|
||||
|
||||
if (textures.searchId(texture) != -1)
|
||||
{
|
||||
exists = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ddsTexture = texture;
|
||||
if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1)
|
||||
{
|
||||
exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id,
|
||||
std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id,
|
||||
const CSMWorld::UniversalId &type,
|
||||
const std::string &column) const
|
||||
{
|
||||
std::string error;
|
||||
if (!id.empty())
|
||||
{
|
||||
CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id);
|
||||
if (index.first == -1)
|
||||
{
|
||||
error = "No such " + column + " '" + id + "'";
|
||||
}
|
||||
else if (index.second != type.getType())
|
||||
{
|
||||
error = column + " is not of type " + type.getTypeName();
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const
|
||||
{
|
||||
std::string error;
|
||||
if (!id.empty() && mSounds.searchId(id) == -1)
|
||||
{
|
||||
error = "No such " + column + " '" + id + "'";
|
||||
}
|
||||
return error;
|
||||
CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id);
|
||||
if (index.first == -1)
|
||||
return (column + " '" + id + "' does not exist");
|
||||
else if (index.second != type.getType())
|
||||
return (column + " '" + id + "' does not have " + type.getTypeName() + " type");
|
||||
return std::string();
|
||||
}
|
||||
|
||||
CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
|
||||
const CSMWorld::IdCollection<ESM::Sound> &sounds,
|
||||
const CSMWorld::RefIdCollection &referenceables,
|
||||
const CSMWorld::RefIdCollection &objects,
|
||||
const CSMWorld::Resources &icons,
|
||||
const CSMWorld::Resources &textures)
|
||||
: mMagicEffects(effects),
|
||||
mSounds(sounds),
|
||||
mReferenceables(referenceables),
|
||||
mObjects(objects),
|
||||
mIcons(icons),
|
||||
mTextures(textures)
|
||||
{
|
||||
|
@ -100,46 +47,75 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa
|
|||
|
||||
ESM::MagicEffect effect = record.get();
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
|
||||
|
||||
|
||||
if (effect.mDescription.empty())
|
||||
{
|
||||
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
|
||||
if (effect.mData.mBaseCost < 0.0f)
|
||||
{
|
||||
messages.push_back(std::make_pair(id, "Base Cost is negative"));
|
||||
messages.add(id, "Base cost is negative", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
if (effect.mIcon.empty())
|
||||
{
|
||||
messages.push_back(std::make_pair(id, "Icon is not specified"));
|
||||
messages.add(id, "Icon is missing", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
else if (!isTextureExists(effect.mIcon, true))
|
||||
else
|
||||
{
|
||||
messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'"));
|
||||
if (mIcons.searchId(effect.mIcon) == -1)
|
||||
{
|
||||
std::string ddsIcon = effect.mIcon;
|
||||
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))
|
||||
messages.add(id, "Icon '" + effect.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false))
|
||||
if (!effect.mParticle.empty())
|
||||
{
|
||||
messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'"));
|
||||
if (mTextures.searchId(effect.mParticle) == -1)
|
||||
{
|
||||
std::string ddsParticle = effect.mParticle;
|
||||
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsParticle) && mTextures.searchId(ddsParticle) != -1))
|
||||
messages.add(id, "Particle texture '" + effect.mParticle + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
||||
addMessageIfNotEmpty(messages,
|
||||
id,
|
||||
checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object"));
|
||||
addMessageIfNotEmpty(messages,
|
||||
id,
|
||||
checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object"));
|
||||
addMessageIfNotEmpty(messages,
|
||||
id,
|
||||
checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object"));
|
||||
addMessageIfNotEmpty(messages,
|
||||
id,
|
||||
checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object"));
|
||||
|
||||
addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound"));
|
||||
addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound"));
|
||||
addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound"));
|
||||
addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound"));
|
||||
|
||||
if (effect.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);
|
||||
}
|
||||
|
|
|
@ -6,14 +6,10 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
#include "../world/refidcollection.hpp"
|
||||
#include "../world/resources.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Resources;
|
||||
}
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that magic effect records are internally consistent
|
||||
|
@ -21,23 +17,18 @@ namespace CSMTools
|
|||
{
|
||||
const CSMWorld::IdCollection<ESM::MagicEffect> &mMagicEffects;
|
||||
const CSMWorld::IdCollection<ESM::Sound> &mSounds;
|
||||
const CSMWorld::RefIdCollection &mReferenceables;
|
||||
const CSMWorld::RefIdCollection &mObjects;
|
||||
const CSMWorld::Resources &mIcons;
|
||||
const CSMWorld::Resources &mTextures;
|
||||
bool mIgnoreBaseRecords;
|
||||
|
||||
private:
|
||||
bool isTextureExists(const std::string &texture, bool isIcon) const;
|
||||
|
||||
std::string checkReferenceable(const std::string &id,
|
||||
const CSMWorld::UniversalId &type,
|
||||
const std::string &column) const;
|
||||
std::string checkSound(const std::string &id, const std::string &column) const;
|
||||
std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const;
|
||||
|
||||
public:
|
||||
MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
|
||||
const CSMWorld::IdCollection<ESM::Sound> &sounds,
|
||||
const CSMWorld::RefIdCollection &referenceables,
|
||||
const CSMWorld::RefIdCollection &objects,
|
||||
const CSMWorld::Resources &icons,
|
||||
const CSMWorld::Resources &textures);
|
||||
|
||||
|
|
|
@ -37,9 +37,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
|
|||
|
||||
// check the number of pathgrid points
|
||||
if (pathgrid.mData.mS2 < static_cast<int>(pathgrid.mPoints.size()))
|
||||
messages.add (id, pathgrid.mId + " has less points than expected", "", CSMDoc::Message::Severity_Error);
|
||||
messages.add (id, "Less points than expected", "", CSMDoc::Message::Severity_Error);
|
||||
else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
|
||||
messages.add (id, pathgrid.mId + " has more points than expected", "", CSMDoc::Message::Severity_Error);
|
||||
messages.add (id, "More points than expected", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
std::vector<CSMTools::Point> pointList(pathgrid.mPoints.size());
|
||||
std::vector<int> duplList;
|
||||
|
@ -56,9 +56,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
|
|||
if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0
|
||||
<< " and " << pathgrid.mEdges[i].mV1;
|
||||
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
|
||||
ss << "Duplicate edge between points #" << pathgrid.mEdges[i].mV0 << " and #" << pathgrid.mEdges[i].mV1;
|
||||
messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -70,8 +69,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
|
|||
else
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0;
|
||||
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
|
||||
ss << "An edge is connected to a non-existent point #" << pathgrid.mEdges[i].mV0;
|
||||
messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
#include "racecheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <components/esm/loadrace.hpp>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
@ -29,24 +25,24 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me
|
|||
|
||||
// test for empty name and description
|
||||
if (race.mName.empty())
|
||||
messages.push_back (std::make_pair (id, race.mId + " has an empty name"));
|
||||
messages.add(id, "Name is missing", "", (race.mData.mFlags & 0x1) ? CSMDoc::Message::Severity_Error : CSMDoc::Message::Severity_Warning);
|
||||
|
||||
if (race.mDescription.empty())
|
||||
messages.push_back (std::make_pair (id, race.mId + " has an empty description"));
|
||||
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
|
||||
|
||||
// test for positive height
|
||||
if (race.mData.mHeight.mMale<=0)
|
||||
messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height"));
|
||||
messages.add(id, "Male height is non-positive", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (race.mData.mHeight.mFemale<=0)
|
||||
messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height"));
|
||||
messages.add(id, "Female height is non-positive", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// test for non-negative weight
|
||||
if (race.mData.mWeight.mMale<0)
|
||||
messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight"));
|
||||
messages.add(id, "Male weight is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (race.mData.mWeight.mFemale<0)
|
||||
messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight"));
|
||||
messages.add(id, "Female weight is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
||||
|
@ -56,7 +52,7 @@ void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages)
|
|||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);
|
||||
|
||||
if (!mPlayable)
|
||||
messages.push_back (std::make_pair (id, "No playable race"));
|
||||
messages.add(id, "No playable race", "", CSMDoc::Message::Severity_SeriousError);
|
||||
}
|
||||
|
||||
CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "referenceablecheck.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
|
@ -11,13 +12,18 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
|
|||
const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,
|
||||
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||
const CSMWorld::IdCollection<ESM::Faction>& faction,
|
||||
const CSMWorld::IdCollection<ESM::Script>& scripts)
|
||||
:
|
||||
mReferencables(referenceable),
|
||||
const CSMWorld::IdCollection<ESM::Script>& scripts,
|
||||
const CSMWorld::Resources& models,
|
||||
const CSMWorld::Resources& icons,
|
||||
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts)
|
||||
:mReferencables(referenceable),
|
||||
mRaces(races),
|
||||
mClasses(classes),
|
||||
mFactions(faction),
|
||||
mScripts(scripts),
|
||||
mModels(models),
|
||||
mIcons(icons),
|
||||
mBodyParts(bodyparts),
|
||||
mPlayerPresent(false)
|
||||
{
|
||||
mIgnoreBaseRecords = false;
|
||||
|
@ -270,9 +276,10 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
|
|||
const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get();
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId);
|
||||
|
||||
//Checking for model, IIRC all activators should have a model
|
||||
if (activator.mModel.empty())
|
||||
messages.push_back (std::make_pair (id, activator.mId + " has no model"));
|
||||
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
|
||||
else if (mModels.searchId(activator.mModel) == -1)
|
||||
messages.add(id, "Model '" + activator.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// Check that mentioned scripts exist
|
||||
scriptCheck<ESM::Activator>(activator, messages, id.toString());
|
||||
|
@ -293,7 +300,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
|
|||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId);
|
||||
|
||||
inventoryItemCheck<ESM::Potion>(potion, messages, id.toString());
|
||||
//IIRC potion can have empty effects list just fine.
|
||||
/// \todo Check magic effects for validity
|
||||
|
||||
// Check that mentioned scripts exist
|
||||
scriptCheck<ESM::Potion>(potion, messages, id.toString());
|
||||
|
@ -338,13 +345,13 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
|
|||
|
||||
inventoryItemCheck<ESM::Armor>(armor, messages, id.toString(), true);
|
||||
|
||||
//checking for armor class, armor should have poistive armor class, but 0 is considered legal
|
||||
// Armor should have positive armor class, but 0 class is not an error
|
||||
if (armor.mData.mArmor < 0)
|
||||
messages.push_back (std::make_pair (id, armor.mId + " has negative armor class"));
|
||||
messages.add(id, "Armor class is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
//checking for health. Only positive numbers are allowed, or 0 is illegal
|
||||
// Armor durability must be a positive number
|
||||
if (armor.mData.mHealth <= 0)
|
||||
messages.push_back (std::make_pair (id, armor.mId + " has non positive health"));
|
||||
messages.add(id, "Durability is non-positive", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// Check that mentioned scripts exist
|
||||
scriptCheck<ESM::Armor>(armor, messages, id.toString());
|
||||
|
@ -383,18 +390,19 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
|
|||
const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get();
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId);
|
||||
|
||||
//Checking for model, IIRC all containers should have a model
|
||||
//checking for name
|
||||
if (container.mName.empty())
|
||||
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
//Checking for model
|
||||
if (container.mModel.empty())
|
||||
messages.push_back (std::make_pair (id, container.mId + " has no model"));
|
||||
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
|
||||
else if (mModels.searchId(container.mModel) == -1)
|
||||
messages.add(id, "Model '" + container.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
//Checking for capacity (weight)
|
||||
if (container.mWeight < 0) //0 is allowed
|
||||
messages.push_back (std::make_pair (id,
|
||||
container.mId + " has negative weight (capacity)"));
|
||||
|
||||
//checking for name
|
||||
if (container.mName.empty())
|
||||
messages.push_back (std::make_pair (id, container.mId + " has an empty name"));
|
||||
messages.add(id, "Capacity is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
//checking contained items
|
||||
inventoryListCheck(container.mInventory.mList, messages, id.toString());
|
||||
|
@ -416,68 +424,81 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
|
|||
const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get();
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId);
|
||||
|
||||
if (creature.mModel.empty())
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has no model"));
|
||||
|
||||
if (creature.mName.empty())
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has an empty name"));
|
||||
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (creature.mModel.empty())
|
||||
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
|
||||
else if (mModels.searchId(creature.mModel) == -1)
|
||||
messages.add(id, "Model '" + creature.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
//stats checks
|
||||
if (creature.mData.mLevel < 1)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has non-positive level"));
|
||||
if (creature.mData.mLevel <= 0)
|
||||
messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
|
||||
|
||||
if (creature.mData.mStrength < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative strength"));
|
||||
|
||||
messages.add(id, "Strength is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mIntelligence < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative intelligence"));
|
||||
|
||||
messages.add(id, "Intelligence is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mWillpower < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative willpower"));
|
||||
|
||||
messages.add(id, "Willpower is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mAgility < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative agility"));
|
||||
|
||||
messages.add(id, "Agility is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mSpeed < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative speed"));
|
||||
|
||||
messages.add(id, "Speed is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mEndurance < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative endurance"));
|
||||
|
||||
messages.add(id, "Endurance is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mPersonality < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative personality"));
|
||||
|
||||
messages.add(id, "Personality is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mLuck < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative luck"));
|
||||
messages.add(id, "Luck is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
|
||||
if (creature.mData.mCombat < 0)
|
||||
messages.add(id, "Combat is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mMagic < 0)
|
||||
messages.add(id, "Magic is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
if (creature.mData.mStealth < 0)
|
||||
messages.add(id, "Stealth is negative", "", CSMDoc::Message::Severity_Warning);
|
||||
|
||||
if (creature.mData.mHealth < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative health"));
|
||||
messages.add(id, "Health is negative", "", CSMDoc::Message::Severity_Error);
|
||||
if (creature.mData.mMana < 0)
|
||||
messages.add(id, "Magicka is negative", "", CSMDoc::Message::Severity_Error);
|
||||
if (creature.mData.mFatigue < 0)
|
||||
messages.add(id, "Fatigue is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (creature.mData.mSoul < 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative soul value"));
|
||||
messages.add(id, "Soul value is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
if (creature.mData.mAttack[i] < 0)
|
||||
{
|
||||
messages.push_back (std::make_pair (id,
|
||||
creature.mId + " has negative attack strength"));
|
||||
break;
|
||||
}
|
||||
messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error);
|
||||
if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1])
|
||||
messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
//TODO, find meaning of other values
|
||||
if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has negative gold "));
|
||||
if (creature.mData.mGold < 0)
|
||||
messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (creature.mScale == 0)
|
||||
messages.push_back (std::make_pair (id, creature.mId + " has zero scale value"));
|
||||
messages.add(id, "Scale is equal to zero", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (!creature.mOriginal.empty())
|
||||
{
|
||||
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(creature.mOriginal);
|
||||
if (index.first == -1)
|
||||
messages.add(id, "Parent creature '" + creature.mOriginal + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
else if (index.second != CSMWorld::UniversalId::Type_Creature)
|
||||
messages.add(id, "'" + creature.mOriginal + "' is not a creature", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
// Check inventory
|
||||
inventoryListCheck(creature.mInventory.mList, messages, id.toString());
|
||||
|
||||
// Check that mentioned scripts exist
|
||||
scriptCheck<ESM::Creature>(creature, messages, id.toString());
|
||||
/// \todo Check spells, teleport table, AI data and AI packages for validity
|
||||
}
|
||||
|
||||
void CSMTools::ReferenceableCheckStage::doorCheck(
|
||||
|
@ -495,10 +516,12 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
|
|||
|
||||
//usual, name or model
|
||||
if (door.mName.empty())
|
||||
messages.push_back (std::make_pair (id, door.mId + " has an empty name"));
|
||||
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (door.mModel.empty())
|
||||
messages.push_back (std::make_pair (id, door.mId + " has no model"));
|
||||
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
|
||||
else if (mModels.searchId(door.mModel) == -1)
|
||||
messages.add(id, "Model '" + door.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// Check that mentioned scripts exist
|
||||
scriptCheck<ESM::Door>(door, messages, id.toString());
|
||||
|
@ -572,7 +595,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
|
|||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId);
|
||||
|
||||
if (light.mData.mRadius < 0)
|
||||
messages.push_back (std::make_pair (id, light.mId + " has negative light radius"));
|
||||
messages.add(id, "Light radius is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
if (light.mData.mFlags & ESM::Light::Carry)
|
||||
inventoryItemCheck<ESM::Light>(light, messages, id.toString());
|
||||
|
@ -644,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../doc/stage.hpp"
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/refiddata.hpp"
|
||||
#include "../world/resources.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
|
@ -16,7 +17,10 @@ namespace CSMTools
|
|||
const CSMWorld::IdCollection<ESM::Race>& races,
|
||||
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||
const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||
const CSMWorld::IdCollection<ESM::Script>& scripts);
|
||||
const CSMWorld::IdCollection<ESM::Script>& scripts,
|
||||
const CSMWorld::Resources& models,
|
||||
const CSMWorld::Resources& icons,
|
||||
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts);
|
||||
|
||||
virtual void perform(int stage, CSMDoc::Messages& messages);
|
||||
virtual int setup();
|
||||
|
@ -81,6 +85,9 @@ namespace CSMTools
|
|||
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
const CSMWorld::IdCollection<ESM::Script>& mScripts;
|
||||
const CSMWorld::Resources& mModels;
|
||||
const CSMWorld::Resources& mIcons;
|
||||
const CSMWorld::IdCollection<ESM::BodyPart>& mBodyParts;
|
||||
bool mPlayerPresent;
|
||||
bool mIgnoreBaseRecords;
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage(
|
|||
const CSMWorld::IdCollection<ESM::Faction>& factions)
|
||||
:
|
||||
mReferences(references),
|
||||
mReferencables(referencables),
|
||||
mObjects(referencables),
|
||||
mDataSet(referencables.getDataSet()),
|
||||
mCells(cells),
|
||||
mFactions(factions)
|
||||
|
@ -28,78 +28,59 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message
|
|||
const CSMWorld::CellRef& cellRef = record.get();
|
||||
const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId);
|
||||
|
||||
// Check for empty reference id
|
||||
if (cellRef.mRefID.empty()) {
|
||||
messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)"));
|
||||
} else {
|
||||
// Check reference id
|
||||
if (cellRef.mRefID.empty())
|
||||
messages.add(id, "Instance is not based on an object", "", CSMDoc::Message::Severity_Error);
|
||||
else
|
||||
{
|
||||
// Check for non existing referenced object
|
||||
if (mReferencables.searchId(cellRef.mRefID) == -1) {
|
||||
messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID));
|
||||
} else {
|
||||
if (mObjects.searchId(cellRef.mRefID) == -1)
|
||||
messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID + "'", "", CSMDoc::Message::Severity_Error);
|
||||
else
|
||||
{
|
||||
// Check if reference charge is valid for it's proper referenced type
|
||||
CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID);
|
||||
bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light;
|
||||
if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) {
|
||||
std::string str = " has invalid charge ";
|
||||
if (localIndex.second == CSMWorld::UniversalId::Type_Light)
|
||||
str += std::to_string(cellRef.mChargeFloat);
|
||||
else
|
||||
str += std::to_string(cellRef.mChargeInt);
|
||||
messages.push_back(std::make_pair(id, id.getId() + str));
|
||||
}
|
||||
if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1))
|
||||
messages.add(id, "Invalid charge", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
||||
// If object have owner, check if that owner reference is valid
|
||||
if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1)
|
||||
messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner));
|
||||
if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1)
|
||||
messages.add(id, "Owner object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// If object have creature soul trapped, check if that creature reference is valid
|
||||
if (!cellRef.mSoul.empty())
|
||||
if (mReferencables.searchId(cellRef.mSoul) == -1)
|
||||
messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul));
|
||||
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()
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace CSMTools
|
|||
|
||||
private:
|
||||
const CSMWorld::RefCollection& mReferences;
|
||||
const CSMWorld::RefIdCollection& mReferencables;
|
||||
const CSMWorld::RefIdCollection& mObjects;
|
||||
const CSMWorld::RefIdData& mDataSet;
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
#include "regioncheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadregn.hpp>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
@ -36,7 +31,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|||
|
||||
// test for empty name
|
||||
if (region.mName.empty())
|
||||
messages.add(id, region.mId + " has an empty name", "", CSMDoc::Message::Severity_Error);
|
||||
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
/// \todo test that the ID in mSleeplist exists
|
||||
|
||||
|
|
|
@ -30,10 +30,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi
|
|||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
|
||||
|
||||
stream
|
||||
<< "script " << mFile
|
||||
<< ", line " << loc.mLine << ", column " << loc.mColumn
|
||||
<< " (" << loc.mLiteral << "): " << message;
|
||||
stream << "line " << loc.mLine << ", column " << loc.mColumn << ": " << message << " (" << loc.mLiteral << ")";
|
||||
|
||||
std::ostringstream hintStream;
|
||||
|
||||
|
@ -47,7 +44,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type)
|
|||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "script " << mFile << ": " << message;
|
||||
stream << message;
|
||||
|
||||
mMessages->add (id, stream.str(), "", getSeverity (type));
|
||||
}
|
||||
|
@ -128,7 +125,7 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "script " << mFile << ": " << error.what();
|
||||
stream << error.what();
|
||||
|
||||
messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
#include "skillcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
@ -33,16 +29,12 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId);
|
||||
|
||||
if (skill.mDescription.empty())
|
||||
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
if (skill.mData.mUseValue[i]<0)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "Use value #" << i << " of " << skill.mId << " is negative";
|
||||
|
||||
messages.push_back (std::make_pair (id, stream.str()));
|
||||
messages.add(id, "Use value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
if (skill.mDescription.empty())
|
||||
messages.push_back (std::make_pair (id, skill.mId + " has an empty description"));
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
#include "soundcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds)
|
||||
: mSounds (sounds)
|
||||
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound> &sounds,
|
||||
const CSMWorld::Resources &soundfiles)
|
||||
: mSounds (sounds),
|
||||
mSoundFiles (soundfiles)
|
||||
{
|
||||
mIgnoreBaseRecords = false;
|
||||
}
|
||||
|
@ -34,7 +32,16 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);
|
||||
|
||||
if (sound.mData.mMinRange>sound.mData.mMaxRange)
|
||||
messages.push_back (std::make_pair (id, "Minimum range larger than maximum range"));
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <components/esm/loadsoun.hpp>
|
||||
|
||||
#include "../world/resources.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
@ -13,11 +14,13 @@ namespace CSMTools
|
|||
class SoundCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Sound>& mSounds;
|
||||
const CSMWorld::Resources &mSoundFiles;
|
||||
bool mIgnoreBaseRecords;
|
||||
|
||||
public:
|
||||
|
||||
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds);
|
||||
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds,
|
||||
const CSMWorld::Resources &soundfiles);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "soundgencheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "../prefs/state.hpp"
|
||||
|
||||
#include "../world/refiddata.hpp"
|
||||
|
@ -9,10 +7,10 @@
|
|||
|
||||
CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
|
||||
const CSMWorld::IdCollection<ESM::Sound> &sounds,
|
||||
const CSMWorld::RefIdCollection &referenceables)
|
||||
const CSMWorld::RefIdCollection &objects)
|
||||
: mSoundGens(soundGens),
|
||||
mSounds(sounds),
|
||||
mReferenceables(referenceables)
|
||||
mObjects(objects)
|
||||
{
|
||||
mIgnoreBaseRecords = false;
|
||||
}
|
||||
|
@ -37,23 +35,23 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages
|
|||
|
||||
if (!soundGen.mCreature.empty())
|
||||
{
|
||||
CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature);
|
||||
CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature);
|
||||
if (creatureIndex.first == -1)
|
||||
{
|
||||
messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'"));
|
||||
messages.add(id, "Creature '" + soundGen.mCreature + "' doesn't exist", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature)
|
||||
{
|
||||
messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature"));
|
||||
messages.add(id, "'" + soundGen.mCreature + "' is not a creature", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
||||
if (soundGen.mSound.empty())
|
||||
{
|
||||
messages.push_back(std::make_pair(id, "Sound is not specified"));
|
||||
messages.add(id, "Sound is missing", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
else if (mSounds.searchId(soundGen.mSound) == -1)
|
||||
{
|
||||
messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'"));
|
||||
messages.add(id, "Sound '" + soundGen.mSound + "' doesn't exist", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,13 @@ namespace CSMTools
|
|||
{
|
||||
const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens;
|
||||
const CSMWorld::IdCollection<ESM::Sound> &mSounds;
|
||||
const CSMWorld::RefIdCollection &mReferenceables;
|
||||
const CSMWorld::RefIdCollection &mObjects;
|
||||
bool mIgnoreBaseRecords;
|
||||
|
||||
public:
|
||||
SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
|
||||
const CSMWorld::IdCollection<ESM::Sound> &sounds,
|
||||
const CSMWorld::RefIdCollection &referenceables);
|
||||
const CSMWorld::RefIdCollection &objects);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
|
|
@ -34,13 +34,13 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);
|
||||
|
||||
// test for empty name and description
|
||||
// test for empty name
|
||||
if (spell.mName.empty())
|
||||
messages.push_back (std::make_pair (id, spell.mId + " has an empty name"));
|
||||
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
// test for invalid cost values
|
||||
if (spell.mData.mCost<0)
|
||||
messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs"));
|
||||
messages.add(id, "Spell cost is negative", "", CSMDoc::Message::Severity_Error);
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
||||
|
|
|
@ -25,8 +25,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa
|
|||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId);
|
||||
|
||||
if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1)
|
||||
messages.push_back (
|
||||
std::make_pair (id, "Start script " + scriptId + " does not exist"));
|
||||
messages.add(id, "Start script " + scriptId + " does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
int CSMTools::StartScriptCheckStage::setup()
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "gmstcheck.hpp"
|
||||
#include "topicinfocheck.hpp"
|
||||
#include "journalcheck.hpp"
|
||||
#include "enchantmentcheck.hpp"
|
||||
|
||||
CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
|
||||
{
|
||||
|
@ -74,15 +75,17 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
|
|||
|
||||
mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces()));
|
||||
|
||||
mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds()));
|
||||
mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds(), mData.getResources (CSMWorld::UniversalId::Type_SoundsRes)));
|
||||
|
||||
mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions()));
|
||||
|
||||
mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns()));
|
||||
mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns(), mData.getResources (CSMWorld::UniversalId::Type_Textures)));
|
||||
|
||||
mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells()));
|
||||
|
||||
mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts()));
|
||||
mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(),
|
||||
mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons),
|
||||
mData.getBodyParts()));
|
||||
|
||||
mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
|
||||
|
||||
|
@ -126,6 +129,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
|
|||
|
||||
mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos()));
|
||||
|
||||
mVerifierOperation->appendStage (new EnchantmentCheckStage(mData.getEnchantments()));
|
||||
|
||||
mVerifier.setOperation (mVerifierOperation);
|
||||
}
|
||||
|
||||
|
|
|
@ -133,8 +133,7 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
|
|||
|
||||
if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error);
|
||||
messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
if (!topicInfo.mRace.empty())
|
||||
|
@ -166,23 +165,24 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
|
|||
bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Actor";
|
||||
|
||||
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);
|
||||
|
||||
if (index.first == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, actor, id, messages);
|
||||
messages.add(id, "Actor '" + actor + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (mReferencables.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(specifier, actor, id, messages);
|
||||
messages.add(id, "Deleted actor '" + actor + "' is being referenced", "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature)
|
||||
{
|
||||
writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages);
|
||||
CSMWorld::UniversalId tempId(index.second, actor);
|
||||
std::ostringstream stream;
|
||||
stream << "Object '" << actor << "' has invalid type " << tempId.getTypeName() << " (an actor must be an NPC or a creature)";
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -192,11 +192,9 @@ bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const
|
|||
bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Cell";
|
||||
|
||||
if (mCellNames.find(cell) == mCellNames.end())
|
||||
{
|
||||
writeMissingIdError(specifier, cell, id, messages);
|
||||
messages.add(id, "Cell '" + cell + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -209,9 +207,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
|
|||
if (rank < -1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
stream << "Faction rank is set to " << rank << ", but it should be set to -1 if there are no rank requirements";
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -229,8 +226,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
|
|||
if (rank >= limit)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1
|
||||
<< " for the " << factionName << " faction";
|
||||
stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1
|
||||
<< " for the '" << factionName << "' faction";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
|
@ -242,18 +239,16 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
|
|||
bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Item";
|
||||
|
||||
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);
|
||||
|
||||
if (index.first == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, item, id, messages);
|
||||
messages.add(id, ("Item '" + item + "' does not exist"), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (mReferencables.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(specifier, item, id, messages);
|
||||
messages.add(id, ("Deleted item '" + item + "' is being referenced"), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
@ -276,8 +271,13 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS
|
|||
break;
|
||||
|
||||
default:
|
||||
writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages);
|
||||
{
|
||||
CSMWorld::UniversalId tempId(index.second, item);
|
||||
std::ostringstream stream;
|
||||
stream << "Object '" << item << "' has invalid type " << tempId.getTypeName() << " (an item can be a potion, an armor piece, a book and so on)";
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,13 +291,13 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
|
|||
|
||||
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)
|
||||
{
|
||||
messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error);
|
||||
messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (!infoCondition.variantTypeIsValid())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of ";
|
||||
stream << "Value of condition '" << infoCondition.toString() << "' has invalid ";
|
||||
|
||||
switch (select.mValue.getType())
|
||||
{
|
||||
|
@ -307,26 +307,21 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
|
|||
case ESM::VT_Long: stream << "Long"; break;
|
||||
case ESM::VT_Float: stream << "Float"; break;
|
||||
case ESM::VT_String: stream << "String"; break;
|
||||
default: stream << "Unknown"; break;
|
||||
default: stream << "unknown"; break;
|
||||
}
|
||||
stream << " type";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.conditionIsAlwaysTrue())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: " << infoCondition.toString() << " is always true";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||
messages.add(id, "Condition '" + infoCondition.toString() + "' is always true", "", CSMDoc::Message::Severity_Warning);
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.conditionIsNeverTrue())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: " << infoCondition.toString() << " is never true";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||
messages.add(id, "Condition '" + infoCondition.toString() + "' is never true", "", CSMDoc::Message::Severity_Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -383,11 +378,9 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
|
|||
bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Sound File";
|
||||
|
||||
if (mSoundFiles.searchId(sound) == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, sound, id, messages);
|
||||
messages.add(id, "Sound file '" + sound + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -402,47 +395,14 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW
|
|||
|
||||
if (index == -1)
|
||||
{
|
||||
writeMissingIdError(T::getRecordType(), name, id, messages);
|
||||
messages.add(id, T::getRecordType() + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (collection.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(T::getRecordType(), name, id, messages);
|
||||
messages.add(id, "Deleted " + T::getRecordType() + " record '" + name + "' is being referenced", "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Error functions
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": ID or name \"" << missingId << "\" could not be found";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
|
||||
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
CSMWorld::UniversalId tempId(invalidType, invalidId);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \""
|
||||
<< invalidId << "\" (can be of type " << expectedType << ")";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
|
|
@ -80,17 +80,6 @@ namespace CSMTools
|
|||
template <typename T>
|
||||
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
// Common error messages
|
||||
void writeMissingIdError(const std::string& specifier, const std::string& missingId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
void writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
|
||||
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
686
apps/opencs/model/world/actoradapter.cpp
Normal file
686
apps/opencs/model/world/actoradapter.cpp
Normal 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();
|
||||
}
|
||||
}
|
174
apps/opencs/model/world/actoradapter.hpp
Normal file
174
apps/opencs/model/world/actoradapter.hpp
Normal 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
|
|
@ -5,6 +5,8 @@
|
|||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}
|
||||
|
||||
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {}
|
||||
|
@ -61,9 +63,7 @@ std::pair<CSMWorld::CellCoordinates, bool> CSMWorld::CellCoordinates::fromId (
|
|||
|
||||
std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y)
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
|
||||
return std::make_pair (std::floor (x/cellSize), std::floor (y/cellSize));
|
||||
return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits));
|
||||
}
|
||||
|
||||
bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)
|
||||
|
|
|
@ -318,7 +318,7 @@ namespace CSMWorld
|
|||
|
||||
QVariant BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const
|
||||
{
|
||||
if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin)
|
||||
if (mMeshType != nullptr && mMeshType->get(record) == ESM::BodyPart::MT_Skin)
|
||||
{
|
||||
return QString::fromUtf8(record.get().mRace.c_str());
|
||||
}
|
||||
|
|
|
@ -244,7 +244,7 @@ void CSMWorld::CreateCommand::applyModifications()
|
|||
if (!mNestedValues.empty())
|
||||
{
|
||||
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
|
||||
if (tree == NULL)
|
||||
if (tree == nullptr)
|
||||
{
|
||||
throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model");
|
||||
}
|
||||
|
@ -445,16 +445,14 @@ void CSMWorld::UpdateCellCommand::redo()
|
|||
int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell);
|
||||
mIndex = mModel.index (mRow, cellColumn);
|
||||
|
||||
const int cellSize = 8192;
|
||||
|
||||
QModelIndex xIndex = mModel.index (
|
||||
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos));
|
||||
|
||||
QModelIndex yIndex = mModel.index (
|
||||
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos));
|
||||
|
||||
int x = std::floor (mModel.data (xIndex).toFloat() / cellSize);
|
||||
int y = std::floor (mModel.data (yIndex).toFloat() / cellSize);
|
||||
int x = std::floor (mModel.data (xIndex).toFloat() / Constants::CellSizeInUnits);
|
||||
int y = std::floor (mModel.data (yIndex).toFloat() / Constants::CellSizeInUnits);
|
||||
|
||||
std::ostringstream stream;
|
||||
|
||||
|
|
|
@ -572,6 +572,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
|
|||
UniversalId::Type_Video);
|
||||
addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData);
|
||||
|
||||
mActorAdapter.reset(new ActorAdapter(*this));
|
||||
|
||||
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
|
||||
}
|
||||
|
||||
|
@ -912,6 +914,16 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId&
|
|||
return iter->second;
|
||||
}
|
||||
|
||||
const CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() const
|
||||
{
|
||||
return mActorAdapter.get();
|
||||
}
|
||||
|
||||
CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter()
|
||||
{
|
||||
return mActorAdapter.get();
|
||||
}
|
||||
|
||||
void CSMWorld::Data::merge()
|
||||
{
|
||||
mGlobals.merge();
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
#include "actoradapter.hpp"
|
||||
#include "idcollection.hpp"
|
||||
#include "nestedidcollection.hpp"
|
||||
#include "universalid.hpp"
|
||||
|
@ -110,6 +111,7 @@ namespace CSMWorld
|
|||
RefCollection mRefs;
|
||||
IdCollection<ESM::Filter> mFilters;
|
||||
Collection<MetaData> mMetaData;
|
||||
std::unique_ptr<ActorAdapter> mActorAdapter;
|
||||
const Fallback::Map* mFallbackMap;
|
||||
std::vector<QAbstractItemModel *> mModels;
|
||||
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
|
||||
|
@ -287,6 +289,10 @@ namespace CSMWorld
|
|||
/// \note The returned table may either be the model for the ID itself or the model that
|
||||
/// contains the record specified by the ID.
|
||||
|
||||
const ActorAdapter* getActorAdapter() const;
|
||||
|
||||
ActorAdapter* getActorAdapter();
|
||||
|
||||
void merge();
|
||||
///< Merge modified into base.
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data)
|
|||
{
|
||||
QAbstractItemModel *model = data.getTableModel(current->second);
|
||||
CSMWorld::IdTableBase *table = dynamic_cast<CSMWorld::IdTableBase *>(model);
|
||||
if (table != NULL)
|
||||
if (table != nullptr)
|
||||
{
|
||||
int idColumn = table->searchColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||
if (idColumn != -1)
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace
|
|||
|
||||
void CSMWorld::IdTableProxyModel::updateColumnMap()
|
||||
{
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
Q_ASSERT(mSourceModel != nullptr);
|
||||
|
||||
mColumnMap.clear();
|
||||
if (mFilter)
|
||||
|
@ -33,7 +33,7 @@ void CSMWorld::IdTableProxyModel::updateColumnMap()
|
|||
bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent)
|
||||
const
|
||||
{
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
Q_ASSERT(mSourceModel != nullptr);
|
||||
|
||||
// It is not possible to use filterAcceptsColumn() and check for
|
||||
// sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags)
|
||||
|
@ -51,14 +51,14 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI
|
|||
|
||||
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
|
||||
: QSortFilterProxyModel (parent),
|
||||
mSourceModel(NULL)
|
||||
mSourceModel(nullptr)
|
||||
{
|
||||
setSortCaseSensitivity (Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
|
||||
{
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
Q_ASSERT(mSourceModel != nullptr);
|
||||
|
||||
return mapFromSource(mSourceModel->getModelIndex (id, column));
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModel
|
|||
|
||||
QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const
|
||||
{
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
Q_ASSERT(mSourceModel != nullptr);
|
||||
|
||||
int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id);
|
||||
return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString();
|
||||
|
|
|
@ -28,7 +28,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod
|
|||
{
|
||||
IdTableProxyModel::setSourceModel(sourceModel);
|
||||
|
||||
if (mSourceModel != NULL)
|
||||
if (mSourceModel != nullptr)
|
||||
{
|
||||
mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId);
|
||||
mFirstRowCache.clear();
|
||||
|
@ -37,7 +37,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod
|
|||
|
||||
bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
Q_ASSERT(mSourceModel != nullptr);
|
||||
|
||||
QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column());
|
||||
QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column());
|
||||
|
@ -52,7 +52,7 @@ bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QMod
|
|||
|
||||
int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const
|
||||
{
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
Q_ASSERT(mSourceModel != nullptr);
|
||||
|
||||
int row = currentRow;
|
||||
int column = mInfoColumnIndex;
|
||||
|
|
|
@ -14,8 +14,8 @@ void CSMWorld::MetaData::blank()
|
|||
void CSMWorld::MetaData::load (ESM::ESMReader& esm)
|
||||
{
|
||||
mFormat = esm.getHeader().mFormat;
|
||||
mAuthor = esm.getHeader().mData.author.toString();
|
||||
mDescription = esm.getHeader().mData.desc.toString();
|
||||
mAuthor = esm.getHeader().mData.author;
|
||||
mDescription = esm.getHeader().mData.desc;
|
||||
}
|
||||
|
||||
void CSMWorld::MetaData::save (ESM::ESMWriter& esm) const
|
||||
|
|
|
@ -453,13 +453,13 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD
|
|||
|
||||
CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns)
|
||||
: ActorColumns (actorColumns),
|
||||
mType(NULL),
|
||||
mScale(NULL),
|
||||
mOriginal(NULL),
|
||||
mAttributes(NULL),
|
||||
mAttacks(NULL),
|
||||
mMisc(NULL),
|
||||
mBloodType(NULL)
|
||||
mType(nullptr),
|
||||
mScale(nullptr),
|
||||
mOriginal(nullptr),
|
||||
mAttributes(nullptr),
|
||||
mAttacks(nullptr),
|
||||
mMisc(nullptr),
|
||||
mBloodType(nullptr)
|
||||
{}
|
||||
|
||||
CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns)
|
||||
|
@ -748,16 +748,16 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData&
|
|||
|
||||
CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns)
|
||||
: ActorColumns (actorColumns),
|
||||
mRace(NULL),
|
||||
mClass(NULL),
|
||||
mFaction(NULL),
|
||||
mHair(NULL),
|
||||
mHead(NULL),
|
||||
mAttributes(NULL),
|
||||
mSkills(NULL),
|
||||
mMisc(NULL),
|
||||
mBloodType(NULL),
|
||||
mGender(NULL)
|
||||
mRace(nullptr),
|
||||
mClass(nullptr),
|
||||
mFaction(nullptr),
|
||||
mHair(nullptr),
|
||||
mHead(nullptr),
|
||||
mAttributes(nullptr),
|
||||
mSkills(nullptr),
|
||||
mMisc(nullptr),
|
||||
mBloodType(nullptr),
|
||||
mGender(nullptr)
|
||||
{}
|
||||
|
||||
CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)
|
||||
|
|
|
@ -437,7 +437,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
|
|||
mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer));
|
||||
lightColumns.mRadius = &mColumns.back();
|
||||
|
||||
mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer));
|
||||
mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Colour));
|
||||
lightColumns.mColor = &mColumns.back();
|
||||
|
||||
mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound));
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <stdexcept>
|
||||
|
||||
CSMWorld::ResourcesManager::ResourcesManager()
|
||||
: mVFS(NULL)
|
||||
: mVFS(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -18,47 +18,43 @@ namespace
|
|||
static const TypeData sNoArg[] =
|
||||
{
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
|
||||
"Objects", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References,
|
||||
"Instances", 0 },
|
||||
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap,
|
||||
"Region Map", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", 0 },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", 0 },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", 0 },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", 0 },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", 0 },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 },
|
||||
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", ":./global-variable.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", ":./skill.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", ":./class.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", ":./faction.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", ":./race.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", ":./sound.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", ":./script.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", ":./region.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", ":./birthsign.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", ":./spell.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", ":./dialogue-topics.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", ":./journal-topics.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", ":./dialogue-topic-infos.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", ":./journal-topic-infos.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", ":./cell.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", ":./enchantment.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", ":./body-part.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects", ":./object.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances", ":./instance.png" },
|
||||
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", ":./region-map.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", ":./filter.png" },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", ":./resources-mesh" },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", ":./resources-icon" },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", ":./resources-music" },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", ":resources-sound" },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", ":./resources-texture" },
|
||||
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":./resources-video" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", ":./debug-profile.png" },
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", ":./sound-generator.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", ":./magic-effect.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":./land-heightmap.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "Land Textures", ":./land-texture.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":./pathgrid.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", ":./start-script.png" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":./metadata.png" },
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
|
||||
};
|
||||
|
||||
|
@ -81,7 +77,7 @@ namespace
|
|||
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", ":./journal-topic-infos.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", ":./object.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" },
|
||||
|
@ -93,9 +89,9 @@ namespace
|
|||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList,
|
||||
"Creature Levelled List", ":./leveled-creature.png" },
|
||||
"Creature Levelled List", ":./levelled-creature.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList,
|
||||
"Item Levelled List", ":./leveled-item.png" },
|
||||
"Item Levelled List", ":./levelled-item.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous,
|
||||
|
@ -105,35 +101,35 @@ namespace
|
|||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
|
||||
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", 0 },
|
||||
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", ":./instance.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" },
|
||||
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", ":./scene.png" },
|
||||
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", ":./record-preview.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", ":./enchantment.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":./body-part.png" },
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":resources-mesh"},
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":resources-icon"},
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":resources-music" },
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":resources-sound" },
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":resources-texture"},
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":resources-video"},
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":./resources-mesh"},
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":./resources-icon"},
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":./resources-music" },
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":./resources-sound" },
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":./resources-texture" },
|
||||
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":./resources-video" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", ":./debug-profile.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", ":./sound-generator.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", ":./magic-effect.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", ":./land-heightmap.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", ":./land-texture.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "Land Texture", ":./land-texture.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", ":./pathgrid.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", ":./start-script.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" },
|
||||
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
|
||||
};
|
||||
|
||||
static const TypeData sIndexArg[] =
|
||||
{
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", ":./menu-verify.png" },
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", ":./error-log.png" },
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", ":./menu-search.png" },
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,6 +33,11 @@ void CSVDoc::FileDialog::addFiles(const QString &path)
|
|||
mSelector->addFiles(path);
|
||||
}
|
||||
|
||||
void CSVDoc::FileDialog::setEncoding(const QString &encoding)
|
||||
{
|
||||
mSelector->setEncoding(encoding);
|
||||
}
|
||||
|
||||
void CSVDoc::FileDialog::clearFiles()
|
||||
{
|
||||
mSelector->clearFiles();
|
||||
|
@ -184,7 +189,7 @@ void CSVDoc::FileDialog::slotRejected()
|
|||
if(mFileWidget)
|
||||
{
|
||||
delete mFileWidget;
|
||||
mFileWidget = NULL;
|
||||
mFileWidget = nullptr;
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
@ -195,7 +200,7 @@ void CSVDoc::FileDialog::slotNewFile()
|
|||
if(mFileWidget)
|
||||
{
|
||||
delete mFileWidget;
|
||||
mFileWidget = NULL;
|
||||
mFileWidget = nullptr;
|
||||
}
|
||||
disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile()));
|
||||
close();
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace CSVDoc
|
|||
void showDialog (ContentAction action);
|
||||
|
||||
void addFiles (const QString &path);
|
||||
void setEncoding (const QString &encoding);
|
||||
void clearFiles ();
|
||||
|
||||
QString filename() const;
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace CSVDoc
|
|||
|
||||
void updateTitle();
|
||||
|
||||
void updateSubViewIndices (SubView *view = NULL);
|
||||
void updateSubViewIndices (SubView *view = nullptr);
|
||||
|
||||
void universalIdChanged (const CSMWorld::UniversalId& universalId);
|
||||
|
||||
|
|
|
@ -47,58 +47,40 @@ void CSVDoc::View::setupFileMenu()
|
|||
{
|
||||
QMenu *file = menuBar()->addMenu (tr ("File"));
|
||||
|
||||
QAction *newGame = new QAction (tr ("New Game"), this);
|
||||
QAction* newGame = createMenuEntry("New Game", ":./menu-new-game.png", file, "document-file-newgame");
|
||||
connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest()));
|
||||
setupShortcut("document-file-newgame", newGame);
|
||||
file->addAction (newGame);
|
||||
|
||||
|
||||
QAction *newAddon = new QAction (tr ("New Addon"), this);
|
||||
QAction* newAddon = createMenuEntry("New Addon", ":./menu-new-addon.png", file, "document-file-newaddon");
|
||||
connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest()));
|
||||
setupShortcut("document-file-newaddon", newAddon);
|
||||
file->addAction (newAddon);
|
||||
|
||||
QAction *open = new QAction (tr ("Open"), this);
|
||||
QAction* open = createMenuEntry("Open", ":./menu-open.png", file, "document-file-open");
|
||||
connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest()));
|
||||
setupShortcut("document-file-open", open);
|
||||
file->addAction (open);
|
||||
|
||||
mSave = new QAction (tr ("Save"), this);
|
||||
connect (mSave, SIGNAL (triggered()), this, SLOT (save()));
|
||||
setupShortcut("document-file-save", mSave);
|
||||
file->addAction (mSave);
|
||||
QAction* save = createMenuEntry("Save", ":./menu-save.png", file, "document-file-save");
|
||||
connect (save, SIGNAL (triggered()), this, SLOT (save()));
|
||||
mSave = save;
|
||||
|
||||
mVerify = new QAction (tr ("Verify"), this);
|
||||
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
|
||||
setupShortcut("document-file-verify", mVerify);
|
||||
file->addAction (mVerify);
|
||||
QAction* verify = createMenuEntry("Verify", ":./menu-verify.png", file, "document-file-verify");
|
||||
connect (verify, SIGNAL (triggered()), this, SLOT (verify()));
|
||||
mVerify = verify;
|
||||
|
||||
mMerge = new QAction (tr ("Merge"), this);
|
||||
connect (mMerge, SIGNAL (triggered()), this, SLOT (merge()));
|
||||
setupShortcut("document-file-merge", mMerge);
|
||||
file->addAction (mMerge);
|
||||
QAction* merge = createMenuEntry("Merge", ":./menu-merge.png", file, "document-file-merge");
|
||||
connect (merge, SIGNAL (triggered()), this, SLOT (merge()));
|
||||
mMerge = merge;
|
||||
|
||||
QAction *loadErrors = new QAction (tr ("Open Load Error Log"), this);
|
||||
QAction* loadErrors = createMenuEntry("Error Log", ":./error-log.png", file, "document-file-errorlog");
|
||||
connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog()));
|
||||
setupShortcut("document-file-errorlog", loadErrors);
|
||||
file->addAction (loadErrors);
|
||||
|
||||
QAction *meta = new QAction (tr ("Meta Data"), this);
|
||||
QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata");
|
||||
connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView()));
|
||||
setupShortcut("document-file-metadata", meta);
|
||||
file->addAction (meta);
|
||||
|
||||
QAction *close = new QAction (tr ("Close Document"), this);
|
||||
QAction* close = createMenuEntry("Close", ":./menu-close.png", file, "document-file-close");
|
||||
connect (close, SIGNAL (triggered()), this, SLOT (close()));
|
||||
setupShortcut("document-file-close", close);
|
||||
file->addAction(close);
|
||||
|
||||
QAction *exit = new QAction (tr ("Exit Application"), this);
|
||||
QAction* exit = createMenuEntry("Exit", ":./menu-exit.png", file, "document-file-exit");
|
||||
connect (exit, SIGNAL (triggered()), this, SLOT (exit()));
|
||||
connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *)));
|
||||
setupShortcut("document-file-exit", exit);
|
||||
|
||||
file->addAction(exit);
|
||||
connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *)));
|
||||
}
|
||||
|
||||
namespace
|
||||
|
@ -130,251 +112,174 @@ void CSVDoc::View::setupEditMenu()
|
|||
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
|
||||
setupShortcut("document-edit-undo", mUndo);
|
||||
connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));
|
||||
mUndo->setIcon(QIcon(QString::fromStdString(":./menu-undo.png")));
|
||||
edit->addAction (mUndo);
|
||||
|
||||
mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo"));
|
||||
connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ()));
|
||||
setupShortcut("document-edit-redo", mRedo);
|
||||
mRedo->setIcon(QIcon(QString::fromStdString(":./menu-redo.png")));
|
||||
edit->addAction (mRedo);
|
||||
|
||||
QAction *userSettings = new QAction (tr ("Preferences"), this);
|
||||
QAction* userSettings = createMenuEntry("Preferences", ":./menu-preferences.png", edit, "document-edit-preferences");
|
||||
connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest()));
|
||||
setupShortcut("document-edit-preferences", userSettings);
|
||||
edit->addAction (userSettings);
|
||||
|
||||
QAction *search = new QAction (tr ("Search"), this);
|
||||
QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search");
|
||||
connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView()));
|
||||
setupShortcut("document-edit-search", search);
|
||||
edit->addAction (search);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupViewMenu()
|
||||
{
|
||||
QMenu *view = menuBar()->addMenu (tr ("View"));
|
||||
|
||||
QAction *newWindow = new QAction (tr ("New View"), this);
|
||||
QAction *newWindow = createMenuEntry("New View", ":./menu-new-window.png", view, "document-view-newview");
|
||||
connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
|
||||
setupShortcut("document-view-newview", newWindow);
|
||||
view->addAction (newWindow);
|
||||
|
||||
mShowStatusBar = new QAction (tr ("Toggle Status Bar"), this);
|
||||
mShowStatusBar->setCheckable (true);
|
||||
mShowStatusBar = createMenuEntry("Toggle Status Bar", ":./menu-status-bar.png", view, "document-view-statusbar");
|
||||
connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool)));
|
||||
setupShortcut("document-view-statusbar", mShowStatusBar);
|
||||
|
||||
mShowStatusBar->setCheckable (true);
|
||||
mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue());
|
||||
|
||||
view->addAction (mShowStatusBar);
|
||||
|
||||
QAction *filters = new QAction (tr ("Filters"), this);
|
||||
QAction *filters = createMenuEntry(CSMWorld::UniversalId::Type_Filters, view, "document-mechanics-filters");
|
||||
connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView()));
|
||||
setupShortcut("document-view-filters", filters);
|
||||
view->addAction (filters);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupWorldMenu()
|
||||
{
|
||||
QMenu *world = menuBar()->addMenu (tr ("World"));
|
||||
|
||||
QAction *regions = new QAction (tr ("Regions"), this);
|
||||
QAction* regions = createMenuEntry(CSMWorld::UniversalId::Type_Regions, world, "document-world-regions");
|
||||
connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));
|
||||
setupShortcut("document-world-regions", regions);
|
||||
world->addAction (regions);
|
||||
|
||||
QAction *cells = new QAction (tr ("Cells"), this);
|
||||
QAction* cells = createMenuEntry(CSMWorld::UniversalId::Type_Cells, world, "document-world-cells");
|
||||
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
|
||||
setupShortcut("document-world-cells", cells);
|
||||
world->addAction (cells);
|
||||
|
||||
QAction *referenceables = new QAction (tr ("Objects"), this);
|
||||
QAction* referenceables = createMenuEntry(CSMWorld::UniversalId::Type_Referenceables, world, "document-world-referencables");
|
||||
connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));
|
||||
setupShortcut("document-world-referencables", referenceables);
|
||||
world->addAction (referenceables);
|
||||
|
||||
QAction *references = new QAction (tr ("Instances"), this);
|
||||
QAction* references = createMenuEntry(CSMWorld::UniversalId::Type_References, world, "document-world-references");
|
||||
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
|
||||
setupShortcut("document-world-references", references);
|
||||
world->addAction (references);
|
||||
|
||||
QAction *lands = new QAction (tr ("Lands"), this);
|
||||
QAction *lands = createMenuEntry(CSMWorld::UniversalId::Type_Lands, world, "document-world-lands");
|
||||
connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView()));
|
||||
setupShortcut("document-world-lands", lands);
|
||||
world->addAction (lands);
|
||||
|
||||
QAction *landTextures = new QAction (tr ("Land Textures"), this);
|
||||
QAction *landTextures = createMenuEntry(CSMWorld::UniversalId::Type_LandTextures, world, "document-world-landtextures");
|
||||
connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView()));
|
||||
setupShortcut("document-world-landtextures", landTextures);
|
||||
world->addAction (landTextures);
|
||||
|
||||
QAction *grid = new QAction (tr ("Pathgrid"), this);
|
||||
QAction *grid = createMenuEntry(CSMWorld::UniversalId::Type_Pathgrids, world, "document-world-pathgrid");
|
||||
connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView()));
|
||||
setupShortcut("document-world-pathgrid", grid);
|
||||
world->addAction (grid);
|
||||
|
||||
world->addSeparator(); // items that don't represent single record lists follow here
|
||||
|
||||
QAction *regionMap = new QAction (tr ("Region Map"), this);
|
||||
QAction *regionMap = createMenuEntry(CSMWorld::UniversalId::Type_RegionMap, world, "document-world-regionmap");
|
||||
connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
|
||||
setupShortcut("document-world-regionmap", regionMap);
|
||||
world->addAction (regionMap);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupMechanicsMenu()
|
||||
{
|
||||
QMenu *mechanics = menuBar()->addMenu (tr ("Mechanics"));
|
||||
|
||||
QAction *globals = new QAction (tr ("Globals"), this);
|
||||
QAction* globals = createMenuEntry(CSMWorld::UniversalId::Type_Globals, mechanics, "document-mechanics-globals");
|
||||
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
|
||||
setupShortcut("document-mechanics-globals", globals);
|
||||
mechanics->addAction (globals);
|
||||
|
||||
QAction *gmsts = new QAction (tr ("Game Settings"), this);
|
||||
QAction* gmsts = createMenuEntry(CSMWorld::UniversalId::Type_Gmsts, mechanics, "document-mechanics-gamesettings");
|
||||
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
|
||||
setupShortcut("document-mechanics-gamesettings", gmsts);
|
||||
mechanics->addAction (gmsts);
|
||||
|
||||
QAction *scripts = new QAction (tr ("Scripts"), this);
|
||||
QAction* scripts = createMenuEntry(CSMWorld::UniversalId::Type_Scripts, mechanics, "document-mechanics-scripts");
|
||||
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
|
||||
setupShortcut("document-mechanics-scripts", scripts);
|
||||
mechanics->addAction (scripts);
|
||||
|
||||
QAction *spells = new QAction (tr ("Spells"), this);
|
||||
QAction* spells = createMenuEntry(CSMWorld::UniversalId::Type_Spells, mechanics, "document-mechanics-spells");
|
||||
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
|
||||
setupShortcut("document-mechanics-spells", spells);
|
||||
mechanics->addAction (spells);
|
||||
|
||||
QAction *enchantments = new QAction (tr ("Enchantments"), this);
|
||||
QAction* enchantments = createMenuEntry(CSMWorld::UniversalId::Type_Enchantments, mechanics, "document-mechanics-enchantments");
|
||||
connect (enchantments, SIGNAL (triggered()), this, SLOT (addEnchantmentsSubView()));
|
||||
setupShortcut("document-mechanics-enchantments", enchantments);
|
||||
mechanics->addAction (enchantments);
|
||||
|
||||
QAction *effects = new QAction (tr ("Magic Effects"), this);
|
||||
connect (effects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView()));
|
||||
setupShortcut("document-mechanics-magiceffects", effects);
|
||||
mechanics->addAction (effects);
|
||||
QAction* magicEffects = createMenuEntry(CSMWorld::UniversalId::Type_MagicEffects, mechanics, "document-mechanics-magiceffects");
|
||||
connect (magicEffects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView()));
|
||||
|
||||
QAction *startScripts = new QAction (tr ("Start Scripts"), this);
|
||||
QAction* startScripts = createMenuEntry(CSMWorld::UniversalId::Type_StartScripts, mechanics, "document-mechanics-startscripts");
|
||||
connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView()));
|
||||
setupShortcut("document-mechanics-startscripts", startScripts);
|
||||
mechanics->addAction (startScripts);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupCharacterMenu()
|
||||
{
|
||||
QMenu *characters = menuBar()->addMenu (tr ("Characters"));
|
||||
|
||||
QAction *skills = new QAction (tr ("Skills"), this);
|
||||
QAction* skills = createMenuEntry(CSMWorld::UniversalId::Type_Skills, characters, "document-character-skills");
|
||||
connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView()));
|
||||
setupShortcut("document-character-skills", skills);
|
||||
characters->addAction (skills);
|
||||
|
||||
QAction *classes = new QAction (tr ("Classes"), this);
|
||||
QAction* classes = createMenuEntry(CSMWorld::UniversalId::Type_Classes, characters, "document-character-classes");
|
||||
connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView()));
|
||||
setupShortcut("document-character-classes", classes);
|
||||
characters->addAction (classes);
|
||||
|
||||
QAction *factions = new QAction (tr ("Factions"), this);
|
||||
QAction* factions = createMenuEntry(CSMWorld::UniversalId::Type_Faction, characters, "document-character-factions");
|
||||
connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView()));
|
||||
setupShortcut("document-character-factions", factions);
|
||||
characters->addAction (factions);
|
||||
|
||||
QAction *races = new QAction (tr ("Races"), this);
|
||||
QAction* races = createMenuEntry(CSMWorld::UniversalId::Type_Races, characters, "document-character-races");
|
||||
connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView()));
|
||||
setupShortcut("document-character-races", races);
|
||||
characters->addAction (races);
|
||||
|
||||
QAction *birthsigns = new QAction (tr ("Birthsigns"), this);
|
||||
QAction* birthsigns = createMenuEntry(CSMWorld::UniversalId::Type_Birthsigns, characters, "document-character-birthsigns");
|
||||
connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView()));
|
||||
setupShortcut("document-character-birthsigns", birthsigns);
|
||||
characters->addAction (birthsigns);
|
||||
|
||||
QAction *topics = new QAction (tr ("Topics"), this);
|
||||
QAction* topics = createMenuEntry(CSMWorld::UniversalId::Type_Topics, characters, "document-character-topics");
|
||||
connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView()));
|
||||
setupShortcut("document-character-topics", topics);
|
||||
characters->addAction (topics);
|
||||
|
||||
QAction *journals = new QAction (tr ("Journals"), this);
|
||||
QAction* journals = createMenuEntry(CSMWorld::UniversalId::Type_Journals, characters, "document-character-journals");
|
||||
connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));
|
||||
setupShortcut("document-character-journals", journals);
|
||||
characters->addAction (journals);
|
||||
|
||||
QAction *topicInfos = new QAction (tr ("Topic Infos"), this);
|
||||
QAction* topicInfos = createMenuEntry(CSMWorld::UniversalId::Type_TopicInfos, characters, "document-character-topicinfos");
|
||||
connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView()));
|
||||
setupShortcut("document-character-topicinfos", topicInfos);
|
||||
characters->addAction (topicInfos);
|
||||
|
||||
QAction *journalInfos = new QAction (tr ("Journal Infos"), this);
|
||||
QAction* journalInfos = createMenuEntry(CSMWorld::UniversalId::Type_JournalInfos, characters, "document-character-journalinfos");
|
||||
connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView()));
|
||||
setupShortcut("document-character-journalinfos", journalInfos);
|
||||
characters->addAction (journalInfos);
|
||||
|
||||
QAction *bodyParts = new QAction (tr ("Body Parts"), this);
|
||||
QAction* bodyParts = createMenuEntry(CSMWorld::UniversalId::Type_BodyParts, characters, "document-character-bodyparts");
|
||||
connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView()));
|
||||
setupShortcut("document-character-bodyparts", bodyParts);
|
||||
characters->addAction (bodyParts);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupAssetsMenu()
|
||||
{
|
||||
QMenu *assets = menuBar()->addMenu (tr ("Assets"));
|
||||
|
||||
QAction *reload = new QAction (tr ("Reload"), this);
|
||||
QAction* reload = createMenuEntry("Reload", ":./menu-reload.png", assets, "document-assets-reload");
|
||||
connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged()));
|
||||
setupShortcut("document-assets-reload", reload);
|
||||
assets->addAction (reload);
|
||||
|
||||
assets->addSeparator();
|
||||
|
||||
QAction *sounds = new QAction (tr ("Sounds"), this);
|
||||
QAction* sounds = createMenuEntry(CSMWorld::UniversalId::Type_Sounds, assets, "document-assets-sounds");
|
||||
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
|
||||
setupShortcut("document-assets-sounds", sounds);
|
||||
assets->addAction (sounds);
|
||||
|
||||
QAction *soundGens = new QAction (tr ("Sound Generators"), this);
|
||||
QAction* soundGens = createMenuEntry(CSMWorld::UniversalId::Type_SoundGens, assets, "document-assets-soundgens");
|
||||
connect (soundGens, SIGNAL (triggered()), this, SLOT (addSoundGensSubView()));
|
||||
setupShortcut("document-assets-soundgens", soundGens);
|
||||
assets->addAction (soundGens);
|
||||
|
||||
assets->addSeparator(); // resources follow here
|
||||
|
||||
QAction *meshes = new QAction (tr ("Meshes"), this);
|
||||
QAction* meshes = createMenuEntry(CSMWorld::UniversalId::Type_Meshes, assets, "document-assets-meshes");
|
||||
connect (meshes, SIGNAL (triggered()), this, SLOT (addMeshesSubView()));
|
||||
setupShortcut("document-assets-meshes", meshes);
|
||||
assets->addAction (meshes);
|
||||
|
||||
QAction *icons = new QAction (tr ("Icons"), this);
|
||||
QAction* icons = createMenuEntry(CSMWorld::UniversalId::Type_Icons, assets, "document-assets-icons");
|
||||
connect (icons, SIGNAL (triggered()), this, SLOT (addIconsSubView()));
|
||||
setupShortcut("document-assets-icons", icons);
|
||||
assets->addAction (icons);
|
||||
|
||||
QAction *musics = new QAction (tr ("Music"), this);
|
||||
QAction* musics = createMenuEntry(CSMWorld::UniversalId::Type_Musics, assets, "document-assets-musics");
|
||||
connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView()));
|
||||
setupShortcut("document-assets-music", musics);
|
||||
assets->addAction (musics);
|
||||
|
||||
QAction *soundsRes = new QAction (tr ("Sound Files"), this);
|
||||
connect (soundsRes, SIGNAL (triggered()), this, SLOT (addSoundsResSubView()));
|
||||
setupShortcut("document-assets-soundres", soundsRes);
|
||||
assets->addAction (soundsRes);
|
||||
QAction* soundFiles = createMenuEntry(CSMWorld::UniversalId::Type_SoundsRes, assets, "document-assets-soundres");
|
||||
connect (soundFiles, SIGNAL (triggered()), this, SLOT (addSoundsResSubView()));
|
||||
|
||||
QAction *textures = new QAction (tr ("Textures"), this);
|
||||
QAction* textures = createMenuEntry(CSMWorld::UniversalId::Type_Textures, assets, "document-assets-textures");
|
||||
connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView()));
|
||||
setupShortcut("document-assets-textures", textures);
|
||||
assets->addAction (textures);
|
||||
|
||||
QAction *videos = new QAction (tr ("Videos"), this);
|
||||
QAction* videos = createMenuEntry(CSMWorld::UniversalId::Type_Videos, assets, "document-assets-videos");
|
||||
connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView()));
|
||||
setupShortcut("document-assets-videos", videos);
|
||||
assets->addAction (videos);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupDebugMenu()
|
||||
{
|
||||
QMenu *debug = menuBar()->addMenu (tr ("Debug"));
|
||||
|
||||
QAction *profiles = new QAction (tr ("Debug Profiles"), this);
|
||||
QAction* profiles = createMenuEntry(CSMWorld::UniversalId::Type_DebugProfiles, debug, "document-debug-profiles");
|
||||
connect (profiles, SIGNAL (triggered()), this, SLOT (addDebugProfilesSubView()));
|
||||
debug->addAction (profiles);
|
||||
|
||||
debug->addSeparator();
|
||||
|
||||
|
@ -387,18 +292,41 @@ void CSVDoc::View::setupDebugMenu()
|
|||
|
||||
QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu);
|
||||
runDebug->setText (tr ("Run OpenMW"));
|
||||
|
||||
setupShortcut("document-debug-run", runDebug);
|
||||
runDebug->setIcon(QIcon(QString::fromStdString(":./run-openmw.png")));
|
||||
|
||||
mStopDebug = new QAction (tr ("Shutdown OpenMW"), this);
|
||||
connect (mStopDebug, SIGNAL (triggered()), this, SLOT (stop()));
|
||||
setupShortcut("document-debug-shutdown", mStopDebug);
|
||||
debug->addAction (mStopDebug);
|
||||
QAction* stopDebug = createMenuEntry("Stop OpenMW", ":./stop-openmw.png", debug, "document-debug-shutdown");
|
||||
connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop()));
|
||||
mStopDebug = stopDebug;
|
||||
|
||||
QAction *runLog = new QAction (tr ("Open Run Log"), this);
|
||||
QAction* runLog = createMenuEntry(CSMWorld::UniversalId::Type_RunLog, debug, "document-debug-runlog");
|
||||
connect (runLog, SIGNAL (triggered()), this, SLOT (addRunLogSubView()));
|
||||
setupShortcut("document-debug-runlog", runLog);
|
||||
debug->addAction (runLog);
|
||||
}
|
||||
|
||||
QAction* CSVDoc::View::createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName)
|
||||
{
|
||||
const std::string title = CSMWorld::UniversalId (type).getTypeName();
|
||||
QAction *entry = new QAction(QString::fromStdString(title), this);
|
||||
setupShortcut(shortcutName, entry);
|
||||
const std::string iconName = CSMWorld::UniversalId (type).getIcon();
|
||||
if (!iconName.empty() && iconName != ":placeholder")
|
||||
entry->setIcon(QIcon(QString::fromStdString(iconName)));
|
||||
|
||||
menu->addAction (entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
QAction* CSVDoc::View::createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName)
|
||||
{
|
||||
QAction *entry = new QAction(QString::fromStdString(title), this);
|
||||
setupShortcut(shortcutName, entry);
|
||||
if (!iconName.empty() && iconName != ":placeholder")
|
||||
entry->setIcon(QIcon(QString::fromStdString(iconName)));
|
||||
|
||||
menu->addAction (entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupUi()
|
||||
|
@ -472,7 +400,7 @@ void CSVDoc::View::updateSubViewIndices(SubView *view)
|
|||
else
|
||||
{
|
||||
delete subView->titleBarWidget();
|
||||
subView->setTitleBarWidget (NULL);
|
||||
subView->setTitleBarWidget (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -501,7 +429,7 @@ void CSVDoc::View::updateActions()
|
|||
|
||||
CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)
|
||||
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
|
||||
mViewTotal (totalViews), mScroll(NULL), mScrollbarOnly(false)
|
||||
mViewTotal (totalViews), mScroll(nullptr), mScrollbarOnly(false)
|
||||
{
|
||||
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
|
||||
|
||||
|
@ -635,7 +563,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
|
|||
return;
|
||||
}
|
||||
|
||||
SubView *view = NULL;
|
||||
SubView *view = nullptr;
|
||||
if(isReferenceable)
|
||||
{
|
||||
view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument);
|
||||
|
@ -703,7 +631,7 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max)
|
|||
void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
|
||||
{
|
||||
if (*setting=="Windows/hide-subview")
|
||||
updateSubViewIndices (NULL);
|
||||
updateSubViewIndices (nullptr);
|
||||
else if (*setting=="Windows/mainwindow-scrollbar")
|
||||
{
|
||||
if (setting->toString()!="Grow Only")
|
||||
|
@ -731,7 +659,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
|
|||
mScroll->takeWidget();
|
||||
setCentralWidget (&mSubViewWindow);
|
||||
mScroll->deleteLater();
|
||||
mScroll = NULL;
|
||||
mScroll = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,9 @@ namespace CSVDoc
|
|||
|
||||
void closeEvent (QCloseEvent *event);
|
||||
|
||||
QAction* createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName);
|
||||
QAction* createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName);
|
||||
|
||||
void setupFileMenu();
|
||||
|
||||
void setupEditMenu();
|
||||
|
@ -146,7 +149,7 @@ namespace CSVDoc
|
|||
void updateTitle();
|
||||
|
||||
// called when subviews are added or removed
|
||||
void updateSubViewIndices (SubView *view = NULL);
|
||||
void updateSubViewIndices (SubView *view = nullptr);
|
||||
|
||||
private slots:
|
||||
|
||||
|
|
128
apps/opencs/view/render/actor.cpp
Normal file
128
apps/opencs/view/render/actor.cpp
Normal 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 "";
|
||||
}
|
||||
}
|
71
apps/opencs/view/render/actor.hpp
Normal file
71
apps/opencs/view/render/actor.hpp
Normal 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
|
|
@ -38,7 +38,7 @@ namespace CSVRender
|
|||
, mCameraSensitivity(1/650.f)
|
||||
, mSecondaryMoveMult(50)
|
||||
, mWheelMoveMult(8)
|
||||
, mCamera(NULL)
|
||||
, mCamera(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ namespace CSVRender
|
|||
bool wasActive = mActive;
|
||||
|
||||
mCamera = camera;
|
||||
mActive = (mCamera != NULL);
|
||||
mActive = (mCamera != nullptr);
|
||||
|
||||
if (mActive != wasActive)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace CSVRender
|
|||
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
traverse(node, nv);
|
||||
CellNodeContainer* container = static_cast<CellNodeContainer*>(node->getUserData());
|
||||
container->getCell()->updateLand();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "../../model/prefs/state.hpp"
|
||||
#include "../../model/prefs/shortcutmanager.hpp"
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
#include "mask.hpp"
|
||||
|
||||
CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow)
|
||||
|
@ -57,7 +59,7 @@ QString CSVRender::CellArrowTag::getToolTip (bool hideBasics) const
|
|||
void CSVRender::CellArrow::adjustTransform()
|
||||
{
|
||||
// position
|
||||
const int cellSize = 8192;
|
||||
const int cellSize = Constants::CellSizeInUnits;
|
||||
const int offset = cellSize / 2 + 800;
|
||||
|
||||
int x = mCoordinates.getX()*cellSize + cellSize/2;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <osg/Geode>
|
||||
#include <osgText/Text>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker)
|
||||
: TagBase(Mask_CellMarker), mMarker(marker)
|
||||
{}
|
||||
|
@ -49,7 +51,7 @@ void CSVRender::CellMarker::buildMarker()
|
|||
|
||||
void CSVRender::CellMarker::positionMarker()
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
const int cellSize = Constants::CellSizeInUnits;
|
||||
const int markerHeight = 0;
|
||||
|
||||
// Move marker to center of cell.
|
||||
|
|
|
@ -29,15 +29,13 @@ int CSVRender::InstanceMode::getSubModeFromId (const std::string& id) const
|
|||
|
||||
osg::Vec3f CSVRender::InstanceMode::quatToEuler(const osg::Quat& rot) const
|
||||
{
|
||||
const float Pi = 3.14159265f;
|
||||
|
||||
float x, y, z;
|
||||
float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z());
|
||||
|
||||
if (std::abs(test) >= 1.f)
|
||||
{
|
||||
x = atan2(rot.x(), rot.w());
|
||||
y = (test > 0) ? (Pi / 2) : (-Pi / 2);
|
||||
y = (test > 0) ? (osg::PI / 2) : (-osg::PI / 2);
|
||||
z = 0;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "object.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <osg/Depth>
|
||||
#include <osg/Group>
|
||||
|
@ -29,6 +31,7 @@
|
|||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/fallback/fallback.hpp>
|
||||
|
||||
#include "actor.hpp"
|
||||
#include "mask.hpp"
|
||||
|
||||
|
||||
|
@ -78,64 +81,62 @@ void CSVRender::Object::update()
|
|||
{
|
||||
clear();
|
||||
|
||||
std::string model;
|
||||
int error = 0; // 1 referenceable does not exist, 2 referenceable does not specify a mesh
|
||||
|
||||
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
|
||||
const int TypeIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType);
|
||||
const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model);
|
||||
|
||||
int index = referenceables.searchId (mReferenceableId);
|
||||
const ESM::Light* light = NULL;
|
||||
|
||||
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] =
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef OPENCS_VIEW_OBJECT_H
|
||||
#define OPENCS_VIEW_OBJECT_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
@ -41,6 +42,7 @@ namespace CSMWorld
|
|||
|
||||
namespace CSVRender
|
||||
{
|
||||
class Actor;
|
||||
class Object;
|
||||
|
||||
// An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query
|
||||
|
@ -98,6 +100,7 @@ namespace CSVRender
|
|||
osg::ref_ptr<osg::Node> mMarker[3];
|
||||
int mSubMode;
|
||||
float mMarkerTransparency;
|
||||
std::unique_ptr<Actor> mActor;
|
||||
|
||||
/// Not implemented
|
||||
Object (const Object&);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <components/esm/loadland.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
#include "../../model/prefs/shortcut.hpp"
|
||||
|
||||
#include "../../model/world/tablemimedata.hpp"
|
||||
|
@ -506,13 +508,11 @@ void CSVRender::PagedWorldspaceWidget::moveCellSelection (int x, int y)
|
|||
|
||||
void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, int offsetY)
|
||||
{
|
||||
const int CellSize = 8192;
|
||||
|
||||
osg::Vec3f eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up);
|
||||
|
||||
int cellX = (int)std::floor(center.x() / CellSize) + offsetX;
|
||||
int cellY = (int)std::floor(center.y() / CellSize) + offsetY;
|
||||
int cellX = (int)std::floor(center.x() / Constants::CellSizeInUnits) + offsetX;
|
||||
int cellY = (int)std::floor(center.y() / Constants::CellSizeInUnits) + offsetY;
|
||||
|
||||
CSMWorld::CellCoordinates cellCoordinates(cellX, cellY);
|
||||
|
||||
|
@ -527,7 +527,7 @@ void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, in
|
|||
|
||||
CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document)
|
||||
: WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"),
|
||||
mControlElements(NULL), mDisplayCellCoord(true)
|
||||
mControlElements(nullptr), mDisplayCellCoord(true)
|
||||
{
|
||||
QAbstractItemModel *cells =
|
||||
document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells);
|
||||
|
@ -738,22 +738,18 @@ void CSVRender::PagedWorldspaceWidget::selectAllWithSameParentId (int elementMas
|
|||
|
||||
std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
|
||||
CSMWorld::CellCoordinates cellCoordinates (
|
||||
static_cast<int> (std::floor (point.x()/cellSize)),
|
||||
static_cast<int> (std::floor (point.y()/cellSize)));
|
||||
static_cast<int> (std::floor (point.x() / Constants::CellSizeInUnits)),
|
||||
static_cast<int> (std::floor (point.y() / Constants::CellSizeInUnits)));
|
||||
|
||||
return cellCoordinates.getId (mWorldspace);
|
||||
}
|
||||
|
||||
CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
|
||||
CSMWorld::CellCoordinates coords(
|
||||
static_cast<int> (std::floor (point.x()/cellSize)),
|
||||
static_cast<int> (std::floor (point.y()/cellSize)));
|
||||
static_cast<int> (std::floor (point.x() / Constants::CellSizeInUnits)),
|
||||
static_cast<int> (std::floor (point.y() / Constants::CellSizeInUnits)));
|
||||
|
||||
std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator searchResult = mCells.find(coords);
|
||||
if (searchResult != mCells.end())
|
||||
|
|
|
@ -185,7 +185,7 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
|
|||
bool retrieveInput)
|
||||
: RenderWidget(parent, f)
|
||||
, mResourceSystem(resourceSystem)
|
||||
, mLighting(NULL)
|
||||
, mLighting(nullptr)
|
||||
, mHasDefaultAmbient(false)
|
||||
, mPrevMouseX(0)
|
||||
, mPrevMouseY(0)
|
||||
|
@ -425,21 +425,21 @@ void SceneWidget::selectNavigationMode (const std::string& mode)
|
|||
{
|
||||
if (mode=="1st")
|
||||
{
|
||||
mCurrentCamControl->setCamera(NULL);
|
||||
mCurrentCamControl->setCamera(nullptr);
|
||||
mCurrentCamControl = mFreeCamControl;
|
||||
mFreeCamControl->setCamera(getCamera());
|
||||
mFreeCamControl->fixUpAxis(CameraController::WorldUp);
|
||||
}
|
||||
else if (mode=="free")
|
||||
{
|
||||
mCurrentCamControl->setCamera(NULL);
|
||||
mCurrentCamControl->setCamera(nullptr);
|
||||
mCurrentCamControl = mFreeCamControl;
|
||||
mFreeCamControl->setCamera(getCamera());
|
||||
mFreeCamControl->unfixUpAxis();
|
||||
}
|
||||
else if (mode=="orbit")
|
||||
{
|
||||
mCurrentCamControl->setCamera(NULL);
|
||||
mCurrentCamControl->setCamera(nullptr);
|
||||
mCurrentCamControl = mOrbitCamControl;
|
||||
mOrbitCamControl->setCamera(getCamera());
|
||||
mOrbitCamControl->reset();
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace CSVRender
|
|||
// has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
|
||||
int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY));
|
||||
if (index == -1)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
const ESM::Land& land = mData.getLand().getRecord(index).get();
|
||||
return new ESMTerrain::LandObject(&land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX);
|
||||
|
|
|
@ -648,11 +648,6 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
|
|||
mDragX = event->posF().x();
|
||||
mDragY = height() - event->posF().y();
|
||||
#endif
|
||||
|
||||
if (mDragMode == InteractionType_PrimaryEdit)
|
||||
{
|
||||
editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -45,7 +45,6 @@ namespace CSVWidget
|
|||
|
||||
private slots:
|
||||
void showPicker();
|
||||
void pickerHid();
|
||||
void pickerColorChanged(const QColor &color);
|
||||
|
||||
signals:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ CSVWidget::CompleterPopup::CompleterPopup(QWidget *parent)
|
|||
|
||||
int CSVWidget::CompleterPopup::sizeHintForRow(int row) const
|
||||
{
|
||||
if (model() == NULL)
|
||||
if (model() == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ std::string CSVWorld::CellCreator::getId() const
|
|||
void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const
|
||||
{
|
||||
CSMWorld::IdTree *model = dynamic_cast<CSMWorld::IdTree *>(getData().getTableModel(getCollectionId()));
|
||||
Q_ASSERT(model != NULL);
|
||||
Q_ASSERT(model != nullptr);
|
||||
int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell);
|
||||
int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior);
|
||||
command.addNestedValue(parentIndex, index, mType->currentIndex() == 0);
|
||||
|
|
|
@ -158,7 +158,7 @@ mNotEditableDelegate(table, parent)
|
|||
|
||||
CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display)
|
||||
{
|
||||
CommandDelegate *delegate = NULL;
|
||||
CommandDelegate *delegate = nullptr;
|
||||
std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));
|
||||
if (delegateIt == mDelegates.end())
|
||||
{
|
||||
|
@ -251,11 +251,11 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::
|
|||
variant = index.data(Qt::DisplayRole);
|
||||
if (!variant.isValid())
|
||||
{
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* editor = NULL;
|
||||
QWidget* editor = nullptr;
|
||||
if (! (mTable->flags (index) & Qt::ItemIsEditable))
|
||||
{
|
||||
return mNotEditableDelegate.createEditor(qobject_cast<QWidget*>(mParent),
|
||||
|
@ -325,7 +325,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di
|
|||
mWidget(widget),
|
||||
mIdType(CSMWorld::TableMimeData::convertEnums(display))
|
||||
{
|
||||
Q_ASSERT(mWidget != NULL);
|
||||
Q_ASSERT(mWidget != nullptr);
|
||||
Q_ASSERT(CSMWorld::ColumnBase::isId(display));
|
||||
Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None);
|
||||
|
||||
|
@ -339,7 +339,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di
|
|||
connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest()));
|
||||
|
||||
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget);
|
||||
if (lineEdit != NULL)
|
||||
if (lineEdit != nullptr)
|
||||
{
|
||||
mContextMenu = lineEdit->createStandardContextMenu();
|
||||
}
|
||||
|
@ -360,11 +360,11 @@ QString CSVWorld::IdContextMenu::getWidgetValue() const
|
|||
QLabel *label = qobject_cast<QLabel *>(mWidget);
|
||||
|
||||
QString value = "";
|
||||
if (lineEdit != NULL)
|
||||
if (lineEdit != nullptr)
|
||||
{
|
||||
value = lineEdit->text();
|
||||
}
|
||||
else if (label != NULL)
|
||||
else if (label != nullptr)
|
||||
{
|
||||
value = label->text();
|
||||
}
|
||||
|
@ -436,7 +436,7 @@ void CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor,
|
|||
CSMWorld::ColumnBase::Display display,
|
||||
int currentRow) const
|
||||
{
|
||||
Q_ASSERT(editor != NULL);
|
||||
Q_ASSERT(editor != nullptr);
|
||||
|
||||
if (CSMWorld::ColumnBase::isId(display) &&
|
||||
CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None)
|
||||
|
@ -470,11 +470,11 @@ CSVWorld::EditWidget::EditWidget(QWidget *parent,
|
|||
int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher,
|
||||
CSMDoc::Document& document, bool createAndDelete) :
|
||||
QScrollArea(parent),
|
||||
mWidgetMapper(NULL),
|
||||
mNestedTableMapper(NULL),
|
||||
mDispatcher(NULL),
|
||||
mNestedTableDispatcher(NULL),
|
||||
mMainWidget(NULL),
|
||||
mWidgetMapper(nullptr),
|
||||
mNestedTableMapper(nullptr),
|
||||
mDispatcher(nullptr),
|
||||
mNestedTableDispatcher(nullptr),
|
||||
mMainWidget(nullptr),
|
||||
mTable(table),
|
||||
mCommandDispatcher (commandDispatcher),
|
||||
mDocument (document)
|
||||
|
@ -733,7 +733,7 @@ bool CSVWorld::SimpleDialogueSubView::isLocked() const
|
|||
CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) :
|
||||
SubView (id),
|
||||
mEditWidget(0),
|
||||
mMainLayout(NULL),
|
||||
mMainLayout(nullptr),
|
||||
mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))),
|
||||
mLocked(false),
|
||||
mDocument(document),
|
||||
|
|
|
@ -12,7 +12,7 @@ const CSMWorld::TableMimeData *CSVWorld::DragDropUtils::getTableMimeData(const Q
|
|||
bool CSVWorld::DragDropUtils::canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type)
|
||||
{
|
||||
const CSMWorld::TableMimeData *data = getTableMimeData(event);
|
||||
return data != NULL && data->holdsType(type);
|
||||
return data != nullptr && data->holdsType(type);
|
||||
}
|
||||
|
||||
CSMWorld::UniversalId CSVWorld::DragDropUtils::getAcceptedData(const QDropEvent &event,
|
||||
|
|
|
@ -83,7 +83,7 @@ void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event)
|
|||
|
||||
CSMWorld::ColumnBase::Display CSVWorld::DragRecordTable::getIndexDisplayType(const QModelIndex &index) const
|
||||
{
|
||||
Q_ASSERT(model() != NULL);
|
||||
Q_ASSERT(model() != nullptr);
|
||||
|
||||
if (index.isValid())
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace CSVWorld
|
|||
bool mEditLock;
|
||||
|
||||
public:
|
||||
DragRecordTable(CSMDoc::Document& document, QWidget* parent = NULL);
|
||||
DragRecordTable(CSMDoc::Document& document, QWidget* parent = nullptr);
|
||||
|
||||
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const = 0;
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ void CSVWorld::ExtendedCommandConfigurator::setupGroupLayout()
|
|||
int divider = 1;
|
||||
do
|
||||
{
|
||||
while (layout->itemAt(0) != NULL)
|
||||
while (layout->itemAt(0) != nullptr)
|
||||
{
|
||||
layout->removeItem(layout->itemAt(0));
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent,
|
|||
{
|
||||
if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid())
|
||||
{
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The completer for InfoCondVar needs to return a completer based on the first column
|
||||
|
|
|
@ -23,9 +23,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
|
|||
bool editable,
|
||||
bool fixedRows)
|
||||
: DragRecordTable(document, parent),
|
||||
mAddNewRowAction(NULL),
|
||||
mRemoveRowAction(NULL),
|
||||
mEditIdAction(NULL),
|
||||
mAddNewRowAction(nullptr),
|
||||
mRemoveRowAction(nullptr),
|
||||
mEditIdAction(nullptr),
|
||||
mModel(model)
|
||||
{
|
||||
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace CSVWorld
|
|||
NestedTable(CSMDoc::Document& document,
|
||||
CSMWorld::UniversalId id,
|
||||
CSMWorld::NestedTableProxyModel* model,
|
||||
QWidget* parent = NULL,
|
||||
QWidget* parent = nullptr,
|
||||
bool editable = true,
|
||||
bool fixedRows = false);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "creator.hpp"
|
||||
|
||||
CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
|
||||
: SubView (id), mScene(NULL), mLayout(new QHBoxLayout), mDocument(document), mToolbar(NULL)
|
||||
: SubView (id), mScene(nullptr), mLayout(new QHBoxLayout), mDocument(document), mToolbar(nullptr)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
|
||||
|
@ -35,7 +35,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D
|
|||
|
||||
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
CSVRender::WorldspaceWidget* worldspaceWidget = NULL;
|
||||
CSVRender::WorldspaceWidget* worldspaceWidget = nullptr;
|
||||
widgetType whatWidget;
|
||||
|
||||
if (id.getId()==ESM::CellId::sDefaultWorldspace)
|
||||
|
@ -189,9 +189,9 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection
|
|||
|
||||
void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& universalIdData)
|
||||
{
|
||||
CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL;
|
||||
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL;
|
||||
CSVWidget::SceneToolbar* toolbar = NULL;
|
||||
CSVRender::PagedWorldspaceWidget* pagedNewWidget = nullptr;
|
||||
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = nullptr;
|
||||
CSVWidget::SceneToolbar* toolbar = nullptr;
|
||||
|
||||
CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (universalIdData);
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include "util.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <climits>
|
||||
#include <cfloat>
|
||||
|
||||
#include <QUndoStack>
|
||||
#include <QMetaProperty>
|
||||
|
@ -131,7 +129,7 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM
|
|||
|
||||
// Color columns use a custom editor, so we need to fetch selected color from it.
|
||||
CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor);
|
||||
if (colorEditor != NULL)
|
||||
if (colorEditor != nullptr)
|
||||
{
|
||||
variant = colorEditor->colorInt();
|
||||
}
|
||||
|
@ -209,7 +207,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO
|
|||
case CSMWorld::ColumnBase::Display_Integer:
|
||||
{
|
||||
DialogueSpinBox *sb = new DialogueSpinBox(parent);
|
||||
sb->setRange(INT_MIN, INT_MAX);
|
||||
sb->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
|
||||
return sb;
|
||||
}
|
||||
|
||||
|
@ -324,7 +322,7 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde
|
|||
|
||||
// Color columns use a custom editor, so we need explicitly set a data for it
|
||||
CSVWidget::ColorEditor *colorEditor = qobject_cast<CSVWidget::ColorEditor *>(editor);
|
||||
if (colorEditor != NULL)
|
||||
if (colorEditor != nullptr)
|
||||
{
|
||||
colorEditor->setColor(variant.toInt());
|
||||
return;
|
||||
|
|
|
@ -20,14 +20,14 @@ void releaseArgv();
|
|||
|
||||
int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) {
|
||||
int ret = 0;
|
||||
SDL_GetMouseState(&ret, NULL);
|
||||
SDL_GetMouseState(&ret, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) {
|
||||
int ret = 0;
|
||||
SDL_GetMouseState(NULL, &ret);
|
||||
SDL_GetMouseState(nullptr, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,9 +197,9 @@ bool OMW::Engine::frame(float frametime)
|
|||
}
|
||||
|
||||
OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||
: mWindow(NULL)
|
||||
: mWindow(nullptr)
|
||||
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||
, mEncoder(NULL)
|
||||
, mEncoder(nullptr)
|
||||
, mScreenCaptureOperation(nullptr)
|
||||
, mSkipMenu (false)
|
||||
, mUseSound (true)
|
||||
|
@ -237,18 +237,18 @@ OMW::Engine::~Engine()
|
|||
mEnvironment.cleanup();
|
||||
|
||||
delete mScriptContext;
|
||||
mScriptContext = NULL;
|
||||
mScriptContext = nullptr;
|
||||
|
||||
mWorkQueue = NULL;
|
||||
mWorkQueue = nullptr;
|
||||
|
||||
mResourceSystem.reset();
|
||||
|
||||
mViewer = NULL;
|
||||
mViewer = nullptr;
|
||||
|
||||
if (mWindow)
|
||||
{
|
||||
SDL_DestroyWindow(mWindow);
|
||||
mWindow = NULL;
|
||||
mWindow = nullptr;
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
|
@ -516,7 +516,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
|
||||
mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,
|
||||
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap,
|
||||
Version::getOpenmwVersionDescription(mResDir.string()));
|
||||
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
|
||||
mEnvironment.setWindowManager (window);
|
||||
|
||||
// Create sound system
|
||||
|
|
|
@ -90,6 +90,8 @@ namespace MWBase
|
|||
virtual void setPlayerClass (const ESM::Class& class_) = 0;
|
||||
///< Set player class to custom class.
|
||||
|
||||
virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) = 0;
|
||||
|
||||
virtual void rest(bool sleep) = 0;
|
||||
///< If the player is sleeping or waiting, this should be called every hour.
|
||||
/// @param sleep is the player sleeping or waiting?
|
||||
|
|
|
@ -219,6 +219,7 @@ namespace MWBase
|
|||
virtual const MWWorld::Ptr& getSelectedEnchantItem() const = 0;
|
||||
virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0;
|
||||
virtual const MWWorld::Ptr& getSelectedWeapon() const = 0;
|
||||
virtual int getFontHeight() const = 0;
|
||||
virtual void unsetSelectedSpell() = 0;
|
||||
virtual void unsetSelectedWeapon() = 0;
|
||||
|
||||
|
@ -289,6 +290,8 @@ namespace MWBase
|
|||
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
|
||||
virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;
|
||||
|
||||
virtual void loadUserFonts() = 0;
|
||||
|
||||
virtual Loading::Listener* getLoadingScreen() = 0;
|
||||
|
||||
/// Should the cursor be visible?
|
||||
|
@ -350,7 +353,8 @@ namespace MWBase
|
|||
|
||||
virtual const MWGui::TextColours& getTextColours() = 0;
|
||||
|
||||
virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text) = 0;
|
||||
virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0;
|
||||
virtual bool injectKeyRelease(MyGUI::KeyCode key) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue