mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-17 10:21:11 +00:00
Merge pull request #14 from cc9cii/minor-enhancements
Minor enhancements and performance improvements
This commit is contained in:
commit
40bacc04c5
@ -576,6 +576,7 @@ add_subdirectory (extern/shiny)
|
|||||||
add_subdirectory (extern/ogre-ffmpeg-videoplayer)
|
add_subdirectory (extern/ogre-ffmpeg-videoplayer)
|
||||||
add_subdirectory (extern/oics)
|
add_subdirectory (extern/oics)
|
||||||
add_subdirectory (extern/sdl4ogre)
|
add_subdirectory (extern/sdl4ogre)
|
||||||
|
add_subdirectory (extern/murmurhash)
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
add_subdirectory (components)
|
add_subdirectory (components)
|
||||||
@ -704,6 +705,17 @@ if (WIN32)
|
|||||||
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
|
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# MSVC 2015
|
||||||
|
if (MSVC_VERSION GREATER 1899)
|
||||||
|
set(WARNINGS_DISABLE ${WARNINGS_DISABLE}
|
||||||
|
4464 # relative include path contains '..'
|
||||||
|
5026 # move constructor was implicitly defined as deleted
|
||||||
|
5027 # move assignment operator was implicitly defined as deleted
|
||||||
|
5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file
|
||||||
|
5032 # detected #pragma warning(push) with no corresponding #pragma warning(pop)
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
foreach(d ${WARNINGS_DISABLE})
|
foreach(d ${WARNINGS_DISABLE})
|
||||||
set(WARNINGS "${WARNINGS} /wd${d}")
|
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||||
endforeach(d)
|
endforeach(d)
|
||||||
|
@ -19,7 +19,7 @@ opencs_hdrs_noqt (model/doc
|
|||||||
|
|
||||||
opencs_units (model/world
|
opencs_units (model/world
|
||||||
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel
|
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel
|
||||||
pathgridcommands
|
pathgridcommands npcautocalc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ opencs_hdrs_noqt (model/world
|
|||||||
|
|
||||||
|
|
||||||
opencs_units (model/tools
|
opencs_units (model/tools
|
||||||
tools reportmodel mergeoperation
|
tools reportmodel mergeoperation
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units_noqt (model/tools
|
opencs_units_noqt (model/tools
|
||||||
@ -211,6 +211,7 @@ target_link_libraries(openmw-cs
|
|||||||
${OGRE_Overlay_LIBRARIES}
|
${OGRE_Overlay_LIBRARIES}
|
||||||
${OGRE_STATIC_PLUGINS}
|
${OGRE_STATIC_PLUGINS}
|
||||||
${SHINY_LIBRARIES}
|
${SHINY_LIBRARIES}
|
||||||
|
${MURMURHASH_LIBRARIES}
|
||||||
${Boost_SYSTEM_LIBRARY}
|
${Boost_SYSTEM_LIBRARY}
|
||||||
${Boost_FILESYSTEM_LIBRARY}
|
${Boost_FILESYSTEM_LIBRARY}
|
||||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
@ -278,264 +279,264 @@ void CSMDoc::Document::addGmsts()
|
|||||||
|
|
||||||
static const float gmstFloatsValues[] =
|
static const float gmstFloatsValues[] =
|
||||||
{
|
{
|
||||||
0.3, // fAIFleeFleeMult
|
0.3f, // fAIFleeFleeMult
|
||||||
7.0, // fAIFleeHealthMult
|
7.0f, // fAIFleeHealthMult
|
||||||
3.0, // fAIMagicSpellMult
|
3.0f, // fAIMagicSpellMult
|
||||||
1.0, // fAIMeleeArmorMult
|
1.0f, // fAIMeleeArmorMult
|
||||||
1.0, // fAIMeleeSummWeaponMult
|
1.0f, // fAIMeleeSummWeaponMult
|
||||||
2.0, // fAIMeleeWeaponMult
|
2.0f, // fAIMeleeWeaponMult
|
||||||
5.0, // fAIRangeMagicSpellMult
|
5.0f, // fAIRangeMagicSpellMult
|
||||||
5.0, // fAIRangeMeleeWeaponMult
|
5.0f, // fAIRangeMeleeWeaponMult
|
||||||
2000.0, // fAlarmRadius
|
2000.0f, // fAlarmRadius
|
||||||
1.0, // fAthleticsRunBonus
|
1.0f, // fAthleticsRunBonus
|
||||||
40.0, // fAudioDefaultMaxDistance
|
40.0f, // fAudioDefaultMaxDistance
|
||||||
5.0, // fAudioDefaultMinDistance
|
5.0f, // fAudioDefaultMinDistance
|
||||||
50.0, // fAudioMaxDistanceMult
|
50.0f, // fAudioMaxDistanceMult
|
||||||
20.0, // fAudioMinDistanceMult
|
20.0f, // fAudioMinDistanceMult
|
||||||
60.0, // fAudioVoiceDefaultMaxDistance
|
60.0f, // fAudioVoiceDefaultMaxDistance
|
||||||
10.0, // fAudioVoiceDefaultMinDistance
|
10.0f, // fAudioVoiceDefaultMinDistance
|
||||||
50.0, // fAutoPCSpellChance
|
50.0f, // fAutoPCSpellChance
|
||||||
80.0, // fAutoSpellChance
|
80.0f, // fAutoSpellChance
|
||||||
50.0, // fBargainOfferBase
|
50.0f, // fBargainOfferBase
|
||||||
-4.0, // fBargainOfferMulti
|
-4.0f, // fBargainOfferMulti
|
||||||
24.0, // fBarterGoldResetDelay
|
24.0f, // fBarterGoldResetDelay
|
||||||
1.75, // fBaseRunMultiplier
|
1.75f, // fBaseRunMultiplier
|
||||||
1.25, // fBlockStillBonus
|
1.25f, // fBlockStillBonus
|
||||||
150.0, // fBribe1000Mod
|
150.0f, // fBribe1000Mod
|
||||||
75.0, // fBribe100Mod
|
75.0f, // fBribe100Mod
|
||||||
35.0, // fBribe10Mod
|
35.0f, // fBribe10Mod
|
||||||
60.0, // fCombatAngleXY
|
60.0f, // fCombatAngleXY
|
||||||
60.0, // fCombatAngleZ
|
60.0f, // fCombatAngleZ
|
||||||
0.25, // fCombatArmorMinMult
|
0.25f, // fCombatArmorMinMult
|
||||||
-90.0, // fCombatBlockLeftAngle
|
-90.0f, // fCombatBlockLeftAngle
|
||||||
30.0, // fCombatBlockRightAngle
|
30.0f, // fCombatBlockRightAngle
|
||||||
4.0, // fCombatCriticalStrikeMult
|
4.0f, // fCombatCriticalStrikeMult
|
||||||
0.1, // fCombatDelayCreature
|
0.1f, // fCombatDelayCreature
|
||||||
0.1, // fCombatDelayNPC
|
0.1f, // fCombatDelayNPC
|
||||||
128.0, // fCombatDistance
|
128.0f, // fCombatDistance
|
||||||
0.3, // fCombatDistanceWerewolfMod
|
0.3f, // fCombatDistanceWerewolfMod
|
||||||
30.0, // fCombatForceSideAngle
|
30.0f, // fCombatForceSideAngle
|
||||||
0.2, // fCombatInvisoMult
|
0.2f, // fCombatInvisoMult
|
||||||
1.5, // fCombatKODamageMult
|
1.5f, // fCombatKODamageMult
|
||||||
45.0, // fCombatTorsoSideAngle
|
45.0f, // fCombatTorsoSideAngle
|
||||||
0.3, // fCombatTorsoStartPercent
|
0.3f, // fCombatTorsoStartPercent
|
||||||
0.8, // fCombatTorsoStopPercent
|
0.8f, // fCombatTorsoStopPercent
|
||||||
15.0, // fConstantEffectMult
|
15.0f, // fConstantEffectMult
|
||||||
72.0, // fCorpseClearDelay
|
72.0f, // fCorpseClearDelay
|
||||||
72.0, // fCorpseRespawnDelay
|
72.0f, // fCorpseRespawnDelay
|
||||||
0.5, // fCrimeGoldDiscountMult
|
0.5f, // fCrimeGoldDiscountMult
|
||||||
0.9, // fCrimeGoldTurnInMult
|
0.9f, // fCrimeGoldTurnInMult
|
||||||
1.0, // fCrimeStealing
|
1.0f, // fCrimeStealing
|
||||||
0.5, // fDamageStrengthBase
|
0.5f, // fDamageStrengthBase
|
||||||
0.1, // fDamageStrengthMult
|
0.1f, // fDamageStrengthMult
|
||||||
5.0, // fDifficultyMult
|
5.0f, // fDifficultyMult
|
||||||
2.5, // fDiseaseXferChance
|
2.5f, // fDiseaseXferChance
|
||||||
-10.0, // fDispAttacking
|
-10.0f, // fDispAttacking
|
||||||
-1.0, // fDispBargainFailMod
|
-1.0f, // fDispBargainFailMod
|
||||||
1.0, // fDispBargainSuccessMod
|
1.0f, // fDispBargainSuccessMod
|
||||||
0.0, // fDispCrimeMod
|
0.0f, // fDispCrimeMod
|
||||||
-10.0, // fDispDiseaseMod
|
-10.0f, // fDispDiseaseMod
|
||||||
3.0, // fDispFactionMod
|
3.0f, // fDispFactionMod
|
||||||
1.0, // fDispFactionRankBase
|
1.0f, // fDispFactionRankBase
|
||||||
0.5, // fDispFactionRankMult
|
0.5f, // fDispFactionRankMult
|
||||||
1.0, // fDispositionMod
|
1.0f, // fDispositionMod
|
||||||
50.0, // fDispPersonalityBase
|
50.0f, // fDispPersonalityBase
|
||||||
0.5, // fDispPersonalityMult
|
0.5f, // fDispPersonalityMult
|
||||||
-25.0, // fDispPickPocketMod
|
-25.0f, // fDispPickPocketMod
|
||||||
5.0, // fDispRaceMod
|
5.0f, // fDispRaceMod
|
||||||
-0.5, // fDispStealing
|
-0.5f, // fDispStealing
|
||||||
-5.0, // fDispWeaponDrawn
|
-5.0f, // fDispWeaponDrawn
|
||||||
0.5, // fEffectCostMult
|
0.5f, // fEffectCostMult
|
||||||
0.1, // fElementalShieldMult
|
0.1f, // fElementalShieldMult
|
||||||
3.0, // fEnchantmentChanceMult
|
3.0f, // fEnchantmentChanceMult
|
||||||
0.5, // fEnchantmentConstantChanceMult
|
0.5f, // fEnchantmentConstantChanceMult
|
||||||
100.0, // fEnchantmentConstantDurationMult
|
100.0f, // fEnchantmentConstantDurationMult
|
||||||
0.1, // fEnchantmentMult
|
0.1f, // fEnchantmentMult
|
||||||
1000.0, // fEnchantmentValueMult
|
1000.0f, // fEnchantmentValueMult
|
||||||
0.3, // fEncumberedMoveEffect
|
0.3f, // fEncumberedMoveEffect
|
||||||
5.0, // fEncumbranceStrMult
|
5.0f, // fEncumbranceStrMult
|
||||||
0.04, // fEndFatigueMult
|
0.04f, // fEndFatigueMult
|
||||||
0.25, // fFallAcroBase
|
0.25f, // fFallAcroBase
|
||||||
0.01, // fFallAcroMult
|
0.01f, // fFallAcroMult
|
||||||
400.0, // fFallDamageDistanceMin
|
400.0f, // fFallDamageDistanceMin
|
||||||
0.0, // fFallDistanceBase
|
0.0f, // fFallDistanceBase
|
||||||
0.07, // fFallDistanceMult
|
0.07f, // fFallDistanceMult
|
||||||
2.0, // fFatigueAttackBase
|
2.0f, // fFatigueAttackBase
|
||||||
0.0, // fFatigueAttackMult
|
0.0f, // fFatigueAttackMult
|
||||||
1.25, // fFatigueBase
|
1.25f, // fFatigueBase
|
||||||
4.0, // fFatigueBlockBase
|
4.0f, // fFatigueBlockBase
|
||||||
0.0, // fFatigueBlockMult
|
0.0f, // fFatigueBlockMult
|
||||||
5.0, // fFatigueJumpBase
|
5.0f, // fFatigueJumpBase
|
||||||
0.0, // fFatigueJumpMult
|
0.0f, // fFatigueJumpMult
|
||||||
0.5, // fFatigueMult
|
0.5f, // fFatigueMult
|
||||||
2.5, // fFatigueReturnBase
|
2.5f, // fFatigueReturnBase
|
||||||
0.02, // fFatigueReturnMult
|
0.02f, // fFatigueReturnMult
|
||||||
5.0, // fFatigueRunBase
|
5.0f, // fFatigueRunBase
|
||||||
2.0, // fFatigueRunMult
|
2.0f, // fFatigueRunMult
|
||||||
1.5, // fFatigueSneakBase
|
1.5f, // fFatigueSneakBase
|
||||||
1.5, // fFatigueSneakMult
|
1.5f, // fFatigueSneakMult
|
||||||
0.0, // fFatigueSpellBase
|
0.0f, // fFatigueSpellBase
|
||||||
0.0, // fFatigueSpellCostMult
|
0.0f, // fFatigueSpellCostMult
|
||||||
0.0, // fFatigueSpellMult
|
0.0f, // fFatigueSpellMult
|
||||||
7.0, // fFatigueSwimRunBase
|
7.0f, // fFatigueSwimRunBase
|
||||||
0.0, // fFatigueSwimRunMult
|
0.0f, // fFatigueSwimRunMult
|
||||||
2.5, // fFatigueSwimWalkBase
|
2.5f, // fFatigueSwimWalkBase
|
||||||
0.0, // fFatigueSwimWalkMult
|
0.0f, // fFatigueSwimWalkMult
|
||||||
0.2, // fFightDispMult
|
0.2f, // fFightDispMult
|
||||||
0.005, // fFightDistanceMultiplier
|
0.005f, // fFightDistanceMultiplier
|
||||||
50.0, // fFightStealing
|
50.0f, // fFightStealing
|
||||||
3000.0, // fFleeDistance
|
3000.0f, // fFleeDistance
|
||||||
512.0, // fGreetDistanceReset
|
512.0f, // fGreetDistanceReset
|
||||||
0.1, // fHandtoHandHealthPer
|
0.1f, // fHandtoHandHealthPer
|
||||||
1.0, // fHandToHandReach
|
1.0f, // fHandToHandReach
|
||||||
0.5, // fHoldBreathEndMult
|
0.5f, // fHoldBreathEndMult
|
||||||
20.0, // fHoldBreathTime
|
20.0f, // fHoldBreathTime
|
||||||
0.75, // fIdleChanceMultiplier
|
0.75f, // fIdleChanceMultiplier
|
||||||
1.0, // fIngredientMult
|
1.0f, // fIngredientMult
|
||||||
0.5, // fInteriorHeadTrackMult
|
0.5f, // fInteriorHeadTrackMult
|
||||||
128.0, // fJumpAcrobaticsBase
|
128.0f, // fJumpAcrobaticsBase
|
||||||
4.0, // fJumpAcroMultiplier
|
4.0f, // fJumpAcroMultiplier
|
||||||
0.5, // fJumpEncumbranceBase
|
0.5f, // fJumpEncumbranceBase
|
||||||
1.0, // fJumpEncumbranceMultiplier
|
1.0f, // fJumpEncumbranceMultiplier
|
||||||
0.5, // fJumpMoveBase
|
0.5f, // fJumpMoveBase
|
||||||
0.5, // fJumpMoveMult
|
0.5f, // fJumpMoveMult
|
||||||
1.0, // fJumpRunMultiplier
|
1.0f, // fJumpRunMultiplier
|
||||||
0.5, // fKnockDownMult
|
0.5f, // fKnockDownMult
|
||||||
5.0, // fLevelMod
|
5.0f, // fLevelMod
|
||||||
0.1, // fLevelUpHealthEndMult
|
0.1f, // fLevelUpHealthEndMult
|
||||||
0.6, // fLightMaxMod
|
0.6f, // fLightMaxMod
|
||||||
10.0, // fLuckMod
|
10.0f, // fLuckMod
|
||||||
10.0, // fMagesGuildTravel
|
10.0f, // fMagesGuildTravel
|
||||||
1.5, // fMagicCreatureCastDelay
|
1.5f, // fMagicCreatureCastDelay
|
||||||
0.0167, // fMagicDetectRefreshRate
|
0.0167f, // fMagicDetectRefreshRate
|
||||||
1.0, // fMagicItemConstantMult
|
1.0f, // fMagicItemConstantMult
|
||||||
1.0, // fMagicItemCostMult
|
1.0f, // fMagicItemCostMult
|
||||||
1.0, // fMagicItemOnceMult
|
1.0f, // fMagicItemOnceMult
|
||||||
1.0, // fMagicItemPriceMult
|
1.0f, // fMagicItemPriceMult
|
||||||
0.05, // fMagicItemRechargePerSecond
|
0.05f, // fMagicItemRechargePerSecond
|
||||||
1.0, // fMagicItemStrikeMult
|
1.0f, // fMagicItemStrikeMult
|
||||||
1.0, // fMagicItemUsedMult
|
1.0f, // fMagicItemUsedMult
|
||||||
3.0, // fMagicStartIconBlink
|
3.0f, // fMagicStartIconBlink
|
||||||
0.5, // fMagicSunBlockedMult
|
0.5f, // fMagicSunBlockedMult
|
||||||
0.75, // fMajorSkillBonus
|
0.75f, // fMajorSkillBonus
|
||||||
300.0, // fMaxFlySpeed
|
300.0f, // fMaxFlySpeed
|
||||||
0.5, // fMaxHandToHandMult
|
0.5f, // fMaxHandToHandMult
|
||||||
400.0, // fMaxHeadTrackDistance
|
400.0f, // fMaxHeadTrackDistance
|
||||||
200.0, // fMaxWalkSpeed
|
200.0f, // fMaxWalkSpeed
|
||||||
300.0, // fMaxWalkSpeedCreature
|
300.0f, // fMaxWalkSpeedCreature
|
||||||
0.9, // fMedMaxMod
|
0.9f, // fMedMaxMod
|
||||||
0.1, // fMessageTimePerChar
|
0.1f, // fMessageTimePerChar
|
||||||
5.0, // fMinFlySpeed
|
5.0f, // fMinFlySpeed
|
||||||
0.1, // fMinHandToHandMult
|
0.1f, // fMinHandToHandMult
|
||||||
1.0, // fMinorSkillBonus
|
1.0f, // fMinorSkillBonus
|
||||||
100.0, // fMinWalkSpeed
|
100.0f, // fMinWalkSpeed
|
||||||
5.0, // fMinWalkSpeedCreature
|
5.0f, // fMinWalkSpeedCreature
|
||||||
1.25, // fMiscSkillBonus
|
1.25f, // fMiscSkillBonus
|
||||||
2.0, // fNPCbaseMagickaMult
|
2.0f, // fNPCbaseMagickaMult
|
||||||
0.5, // fNPCHealthBarFade
|
0.5f, // fNPCHealthBarFade
|
||||||
3.0, // fNPCHealthBarTime
|
3.0f, // fNPCHealthBarTime
|
||||||
1.0, // fPCbaseMagickaMult
|
1.0f, // fPCbaseMagickaMult
|
||||||
0.3, // fPerDieRollMult
|
0.3f, // fPerDieRollMult
|
||||||
5.0, // fPersonalityMod
|
5.0f, // fPersonalityMod
|
||||||
1.0, // fPerTempMult
|
1.0f, // fPerTempMult
|
||||||
-1.0, // fPickLockMult
|
-1.0f, // fPickLockMult
|
||||||
0.3, // fPickPocketMod
|
0.3f, // fPickPocketMod
|
||||||
20.0, // fPotionMinUsefulDuration
|
20.0f, // fPotionMinUsefulDuration
|
||||||
0.5, // fPotionStrengthMult
|
0.5f, // fPotionStrengthMult
|
||||||
0.5, // fPotionT1DurMult
|
0.5f, // fPotionT1DurMult
|
||||||
1.5, // fPotionT1MagMult
|
1.5f, // fPotionT1MagMult
|
||||||
20.0, // fPotionT4BaseStrengthMult
|
20.0f, // fPotionT4BaseStrengthMult
|
||||||
12.0, // fPotionT4EquipStrengthMult
|
12.0f, // fPotionT4EquipStrengthMult
|
||||||
3000.0, // fProjectileMaxSpeed
|
3000.0f, // fProjectileMaxSpeed
|
||||||
400.0, // fProjectileMinSpeed
|
400.0f, // fProjectileMinSpeed
|
||||||
25.0, // fProjectileThrownStoreChance
|
25.0f, // fProjectileThrownStoreChance
|
||||||
3.0, // fRepairAmountMult
|
3.0f, // fRepairAmountMult
|
||||||
1.0, // fRepairMult
|
1.0f, // fRepairMult
|
||||||
1.0, // fReputationMod
|
1.0f, // fReputationMod
|
||||||
0.15, // fRestMagicMult
|
0.15f, // fRestMagicMult
|
||||||
0.0, // fSeriousWoundMult
|
0.0f, // fSeriousWoundMult
|
||||||
0.25, // fSleepRandMod
|
0.25f, // fSleepRandMod
|
||||||
0.3, // fSleepRestMod
|
0.3f, // fSleepRestMod
|
||||||
-1.0, // fSneakBootMult
|
-1.0f, // fSneakBootMult
|
||||||
0.5, // fSneakDistanceBase
|
0.5f, // fSneakDistanceBase
|
||||||
0.002, // fSneakDistanceMultiplier
|
0.002f, // fSneakDistanceMultiplier
|
||||||
0.5, // fSneakNoViewMult
|
0.5f, // fSneakNoViewMult
|
||||||
1.0, // fSneakSkillMult
|
1.0f, // fSneakSkillMult
|
||||||
0.75, // fSneakSpeedMultiplier
|
0.75f, // fSneakSpeedMultiplier
|
||||||
1.0, // fSneakUseDelay
|
1.0f, // fSneakUseDelay
|
||||||
500.0, // fSneakUseDist
|
500.0f, // fSneakUseDist
|
||||||
1.5, // fSneakViewMult
|
1.5f, // fSneakViewMult
|
||||||
3.0, // fSoulGemMult
|
3.0f, // fSoulGemMult
|
||||||
0.8, // fSpecialSkillBonus
|
0.8f, // fSpecialSkillBonus
|
||||||
7.0, // fSpellMakingValueMult
|
7.0f, // fSpellMakingValueMult
|
||||||
2.0, // fSpellPriceMult
|
2.0f, // fSpellPriceMult
|
||||||
10.0, // fSpellValueMult
|
10.0f, // fSpellValueMult
|
||||||
0.25, // fStromWalkMult
|
0.25f, // fStromWalkMult
|
||||||
0.7, // fStromWindSpeed
|
0.7f, // fStromWindSpeed
|
||||||
3.0, // fSuffocationDamage
|
3.0f, // fSuffocationDamage
|
||||||
0.9, // fSwimHeightScale
|
0.9f, // fSwimHeightScale
|
||||||
0.1, // fSwimRunAthleticsMult
|
0.1f, // fSwimRunAthleticsMult
|
||||||
0.5, // fSwimRunBase
|
0.5f, // fSwimRunBase
|
||||||
0.02, // fSwimWalkAthleticsMult
|
0.02f, // fSwimWalkAthleticsMult
|
||||||
0.5, // fSwimWalkBase
|
0.5f, // fSwimWalkBase
|
||||||
1.0, // fSwingBlockBase
|
1.0f, // fSwingBlockBase
|
||||||
1.0, // fSwingBlockMult
|
1.0f, // fSwingBlockMult
|
||||||
1000.0, // fTargetSpellMaxSpeed
|
1000.0f, // fTargetSpellMaxSpeed
|
||||||
1000.0, // fThrownWeaponMaxSpeed
|
1000.0f, // fThrownWeaponMaxSpeed
|
||||||
300.0, // fThrownWeaponMinSpeed
|
300.0f, // fThrownWeaponMinSpeed
|
||||||
0.0, // fTrapCostMult
|
0.0f, // fTrapCostMult
|
||||||
4000.0, // fTravelMult
|
4000.0f, // fTravelMult
|
||||||
16000.0,// fTravelTimeMult
|
16000.0f,// fTravelTimeMult
|
||||||
0.1, // fUnarmoredBase1
|
0.1f, // fUnarmoredBase1
|
||||||
0.065, // fUnarmoredBase2
|
0.065f, // fUnarmoredBase2
|
||||||
30.0, // fVanityDelay
|
30.0f, // fVanityDelay
|
||||||
10.0, // fVoiceIdleOdds
|
10.0f, // fVoiceIdleOdds
|
||||||
0.0, // fWaterReflectUpdateAlways
|
0.0f, // fWaterReflectUpdateAlways
|
||||||
10.0, // fWaterReflectUpdateSeldom
|
10.0f, // fWaterReflectUpdateSeldom
|
||||||
0.1, // fWeaponDamageMult
|
0.1f, // fWeaponDamageMult
|
||||||
1.0, // fWeaponFatigueBlockMult
|
1.0f, // fWeaponFatigueBlockMult
|
||||||
0.25, // fWeaponFatigueMult
|
0.25f, // fWeaponFatigueMult
|
||||||
150.0, // fWereWolfAcrobatics
|
150.0f, // fWereWolfAcrobatics
|
||||||
150.0, // fWereWolfAgility
|
150.0f, // fWereWolfAgility
|
||||||
1.0, // fWereWolfAlchemy
|
1.0f, // fWereWolfAlchemy
|
||||||
1.0, // fWereWolfAlteration
|
1.0f, // fWereWolfAlteration
|
||||||
1.0, // fWereWolfArmorer
|
1.0f, // fWereWolfArmorer
|
||||||
150.0, // fWereWolfAthletics
|
150.0f, // fWereWolfAthletics
|
||||||
1.0, // fWereWolfAxe
|
1.0f, // fWereWolfAxe
|
||||||
1.0, // fWereWolfBlock
|
1.0f, // fWereWolfBlock
|
||||||
1.0, // fWereWolfBluntWeapon
|
1.0f, // fWereWolfBluntWeapon
|
||||||
1.0, // fWereWolfConjuration
|
1.0f, // fWereWolfConjuration
|
||||||
1.0, // fWereWolfDestruction
|
1.0f, // fWereWolfDestruction
|
||||||
1.0, // fWereWolfEnchant
|
1.0f, // fWereWolfEnchant
|
||||||
150.0, // fWereWolfEndurance
|
150.0f, // fWereWolfEndurance
|
||||||
400.0, // fWereWolfFatigue
|
400.0f, // fWereWolfFatigue
|
||||||
100.0, // fWereWolfHandtoHand
|
100.0f, // fWereWolfHandtoHand
|
||||||
2.0, // fWereWolfHealth
|
2.0f, // fWereWolfHealth
|
||||||
1.0, // fWereWolfHeavyArmor
|
1.0f, // fWereWolfHeavyArmor
|
||||||
1.0, // fWereWolfIllusion
|
1.0f, // fWereWolfIllusion
|
||||||
1.0, // fWereWolfIntellegence
|
1.0f, // fWereWolfIntellegence
|
||||||
1.0, // fWereWolfLightArmor
|
1.0f, // fWereWolfLightArmor
|
||||||
1.0, // fWereWolfLongBlade
|
1.0f, // fWereWolfLongBlade
|
||||||
1.0, // fWereWolfLuck
|
1.0f, // fWereWolfLuck
|
||||||
100.0, // fWereWolfMagicka
|
100.0f, // fWereWolfMagicka
|
||||||
1.0, // fWereWolfMarksman
|
1.0f, // fWereWolfMarksman
|
||||||
1.0, // fWereWolfMediumArmor
|
1.0f, // fWereWolfMediumArmor
|
||||||
1.0, // fWereWolfMerchantile
|
1.0f, // fWereWolfMerchantile
|
||||||
1.0, // fWereWolfMysticism
|
1.0f, // fWereWolfMysticism
|
||||||
1.0, // fWereWolfPersonality
|
1.0f, // fWereWolfPersonality
|
||||||
1.0, // fWereWolfRestoration
|
1.0f, // fWereWolfRestoration
|
||||||
1.5, // fWereWolfRunMult
|
1.5f, // fWereWolfRunMult
|
||||||
1.0, // fWereWolfSecurity
|
1.0f, // fWereWolfSecurity
|
||||||
1.0, // fWereWolfShortBlade
|
1.0f, // fWereWolfShortBlade
|
||||||
1.5, // fWereWolfSilverWeaponDamageMult
|
1.5f, // fWereWolfSilverWeaponDamageMult
|
||||||
1.0, // fWereWolfSneak
|
1.0f, // fWereWolfSneak
|
||||||
1.0, // fWereWolfSpear
|
1.0f, // fWereWolfSpear
|
||||||
1.0, // fWereWolfSpeechcraft
|
1.0f, // fWereWolfSpeechcraft
|
||||||
150.0, // fWereWolfSpeed
|
150.0f, // fWereWolfSpeed
|
||||||
150.0, // fWereWolfStrength
|
150.0f, // fWereWolfStrength
|
||||||
100.0, // fWereWolfUnarmored
|
100.0f, // fWereWolfUnarmored
|
||||||
1.0, // fWereWolfWillPower
|
1.0f, // fWereWolfWillPower
|
||||||
15.0, // fWortChanceValue
|
15.0f, // fWortChanceValue
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *gmstIntegers[] =
|
static const char *gmstIntegers[] =
|
||||||
@ -2091,10 +2092,10 @@ void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst)
|
|||||||
{
|
{
|
||||||
if (getData().getGmsts().searchId (gmst.mId)==-1)
|
if (getData().getGmsts().searchId (gmst.mId)==-1)
|
||||||
{
|
{
|
||||||
CSMWorld::Record<ESM::GameSetting> record;
|
std::unique_ptr<CSMWorld::Record<ESM::GameSetting> > record(new CSMWorld::Record<ESM::GameSetting>);
|
||||||
record.mBase = gmst;
|
record->mBase = gmst;
|
||||||
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
getData().getGmsts().appendRecord (record);
|
getData().getGmsts().appendRecord (std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2102,10 +2103,10 @@ void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global)
|
|||||||
{
|
{
|
||||||
if (getData().getGlobals().searchId (global.mId)==-1)
|
if (getData().getGlobals().searchId (global.mId)==-1)
|
||||||
{
|
{
|
||||||
CSMWorld::Record<ESM::Global> record;
|
std::unique_ptr<CSMWorld::Record<ESM::Global> > record(new CSMWorld::Record<ESM::Global>);
|
||||||
record.mBase = global;
|
record->mBase = global;
|
||||||
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
getData().getGlobals().appendRecord (record);
|
getData().getGlobals().appendRecord (std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2113,10 +2114,10 @@ void CSMDoc::Document::addOptionalMagicEffect (const ESM::MagicEffect& magicEffe
|
|||||||
{
|
{
|
||||||
if (getData().getMagicEffects().searchId (magicEffect.mId)==-1)
|
if (getData().getMagicEffects().searchId (magicEffect.mId)==-1)
|
||||||
{
|
{
|
||||||
CSMWorld::Record<ESM::MagicEffect> record;
|
std::unique_ptr<CSMWorld::Record<ESM::MagicEffect> > record(new CSMWorld::Record<ESM::MagicEffect>);
|
||||||
record.mBase = magicEffect;
|
record->mBase = magicEffect;
|
||||||
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
getData().getMagicEffects().appendRecord (record);
|
getData().getMagicEffects().appendRecord (std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,11 +64,11 @@ void CSMDoc::Loader::load()
|
|||||||
CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0);
|
CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0);
|
||||||
|
|
||||||
{ // silence a g++ warning
|
{ // silence a g++ warning
|
||||||
for (CSMDoc::Messages::Iterator iter (messages.begin());
|
for (CSMDoc::Messages::Iterator iter2 (messages.begin());
|
||||||
iter!=messages.end(); ++iter)
|
iter2!=messages.end(); ++iter2)
|
||||||
{
|
{
|
||||||
document->getReport (log)->add (*iter);
|
document->getReport (log)->add (*iter2);
|
||||||
emit loadMessage (document, iter->mMessage);
|
emit loadMessage (document, iter2->mMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,19 +77,19 @@ void CSMDoc::Loader::load()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter->second.mFile<size)
|
if (iter->second.mFile<size) // start loading the files
|
||||||
{
|
{
|
||||||
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];
|
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];
|
||||||
|
|
||||||
int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false);
|
int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, /*project*/false);
|
||||||
iter->second.mRecordsLeft = true;
|
iter->second.mRecordsLeft = true;
|
||||||
iter->second.mRecordsLoaded = 0;
|
iter->second.mRecordsLoaded = 0;
|
||||||
|
|
||||||
emit nextStage (document, path.filename().string(), steps);
|
emit nextStage (document, path.filename().string(), steps);
|
||||||
}
|
}
|
||||||
else if (iter->second.mFile==size)
|
else if (iter->second.mFile==size) // start loading the last (project) file
|
||||||
{
|
{
|
||||||
int steps = document->getData().startLoading (document->getProjectPath(), false, true);
|
int steps = document->getData().startLoading (document->getProjectPath(), /*base*/false, true);
|
||||||
iter->second.mRecordsLeft = true;
|
iter->second.mRecordsLeft = true;
|
||||||
iter->second.mRecordsLoaded = 0;
|
iter->second.mRecordsLoaded = 0;
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
|
|||||||
|
|
||||||
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
||||||
{
|
{
|
||||||
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
|
if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
{
|
{
|
||||||
infoModified = true;
|
infoModified = true;
|
||||||
break;
|
break;
|
||||||
@ -144,9 +144,9 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
|
|||||||
// write modified selected info records
|
// write modified selected info records
|
||||||
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
||||||
{
|
{
|
||||||
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
|
if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
{
|
{
|
||||||
ESM::DialInfo info = iter->get();
|
ESM::DialInfo info = (*iter)->get();
|
||||||
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
|
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
|
||||||
|
|
||||||
info.mPrev = "";
|
info.mPrev = "";
|
||||||
@ -155,7 +155,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
|
|||||||
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
|
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
|
||||||
--prev;
|
--prev;
|
||||||
|
|
||||||
info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1);
|
info.mPrev = (*prev)->get().mId.substr ((*prev)->get().mId.find_last_of ('#')+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::InfoCollection::RecordConstIterator next = iter;
|
CSMWorld::InfoCollection::RecordConstIterator next = iter;
|
||||||
@ -164,11 +164,11 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
|
|||||||
info.mNext = "";
|
info.mNext = "";
|
||||||
if (next!=range.second)
|
if (next!=range.second)
|
||||||
{
|
{
|
||||||
info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1);
|
info.mNext = (*next)->get().mId.substr ((*next)->get().mId.find_last_of ('#')+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.startRecord (info.sRecordId);
|
writer.startRecord (info.sRecordId);
|
||||||
info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted);
|
info.save (writer, (*iter)->mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
writer.endRecord (info.sRecordId);
|
writer.endRecord (info.sRecordId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,15 +313,15 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseNAry (const Token& ke
|
|||||||
|
|
||||||
nodes.push_back (node);
|
nodes.push_back (node);
|
||||||
|
|
||||||
Token token = getNextToken();
|
Token token2 = getNextToken();
|
||||||
|
|
||||||
if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma))
|
if (!token2 || (token2.mType!=Token::Type_Close && token2.mType!=Token::Type_Comma))
|
||||||
{
|
{
|
||||||
error();
|
error();
|
||||||
return boost::shared_ptr<Node>();
|
return boost::shared_ptr<Node>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.mType==Token::Type_Close)
|
if (token2.mType==Token::Type_Close)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,10 +100,9 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa
|
|||||||
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
|
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
|
||||||
ref.mRefNum.mContentFile = 0;
|
ref.mRefNum.mContentFile = 0;
|
||||||
|
|
||||||
CSMWorld::Record<CSMWorld::CellRef> newRecord (
|
mState.mTarget->getData().getReferences().appendRecord (
|
||||||
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref);
|
std::make_unique<CSMWorld::Record<CSMWorld::CellRef> >(
|
||||||
|
CSMWorld::Record<CSMWorld::CellRef>(CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref)));
|
||||||
mState.mTarget->getData().getReferences().appendRecord (newRecord);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,16 +182,15 @@ void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& mes
|
|||||||
CSMWorld::LandTexture texture =
|
CSMWorld::LandTexture texture =
|
||||||
mState.mSource.getData().getLandTextures().getRecord (index).get();
|
mState.mSource.getData().getLandTextures().getRecord (index).get();
|
||||||
|
|
||||||
std::ostringstream stream;
|
std::ostringstream stream2;
|
||||||
stream << mNext->second-1 << "_0";
|
stream2 << mNext->second-1 << "_0";
|
||||||
|
|
||||||
texture.mIndex = mNext->second-1;
|
texture.mIndex = mNext->second-1;
|
||||||
texture.mId = stream.str();
|
texture.mId = stream2.str();
|
||||||
|
|
||||||
CSMWorld::Record<CSMWorld::LandTexture> newRecord (
|
mState.mTarget->getData().getLandTextures().appendRecord (
|
||||||
CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture);
|
std::make_unique<CSMWorld::Record<CSMWorld::LandTexture> >(
|
||||||
|
CSMWorld::Record<CSMWorld::LandTexture>(CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture)));
|
||||||
mState.mTarget->getData().getLandTextures().appendRecord (newRecord);
|
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@ -250,9 +248,8 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::Record<CSMWorld::Land> newRecord (
|
mState.mTarget->getData().getLand().appendRecord (
|
||||||
CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand);
|
std::make_unique<CSMWorld::Record<CSMWorld::Land> >(
|
||||||
|
CSMWorld::Record<CSMWorld::Land>(CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand)));
|
||||||
mState.mTarget->getData().getLand().appendRecord (newRecord);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <components/to_utf8/to_utf8.hpp>
|
#include <components/to_utf8/to_utf8.hpp>
|
||||||
|
|
||||||
@ -82,7 +83,8 @@ namespace CSMTools
|
|||||||
const CSMWorld::Record<RecordType>& record = source.getRecord (stage);
|
const CSMWorld::Record<RecordType>& record = source.getRecord (stage);
|
||||||
|
|
||||||
if (!record.isDeleted())
|
if (!record.isDeleted())
|
||||||
target.appendRecord (CSMWorld::Record<RecordType> (CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get()));
|
target.appendRecord (std::make_unique<CSMWorld::Record<RecordType> >(
|
||||||
|
CSMWorld::Record<RecordType>(CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MergeRefIdsStage : public CSMDoc::Stage
|
class MergeRefIdsStage : public CSMDoc::Stage
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
@ -49,7 +50,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<Record<ESXRecordT> > mRecords;
|
std::vector<std::unique_ptr<Record<ESXRecordT> > > mRecords;
|
||||||
std::map<std::string, int> mIndex;
|
std::map<std::string, int> mIndex;
|
||||||
std::vector<Column<ESXRecordT> *> mColumns;
|
std::vector<Column<ESXRecordT> *> mColumns;
|
||||||
|
|
||||||
@ -59,9 +60,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
const std::map<std::string, int>& getIdMap() const;
|
const std::vector<std::unique_ptr<Record<ESXRecordT> > >& getRecords() const;
|
||||||
|
|
||||||
const std::vector<Record<ESXRecordT> >& getRecords() const;
|
|
||||||
|
|
||||||
bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);
|
bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);
|
||||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||||
@ -112,12 +111,12 @@ namespace CSMWorld
|
|||||||
////< Search record with \a id.
|
////< Search record with \a id.
|
||||||
/// \return index of record (if found) or -1 (not found)
|
/// \return index of record (if found) or -1 (not found)
|
||||||
|
|
||||||
virtual void replace (int index, const RecordBase& record);
|
virtual void replace (int index, std::unique_ptr<RecordBase> record);
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///
|
///
|
||||||
/// \attention \a record must not change the ID.
|
/// \attention \a record must not change the ID.
|
||||||
|
|
||||||
virtual void appendRecord (const RecordBase& record,
|
virtual void appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type = UniversalId::Type_None);
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
@ -135,7 +134,7 @@ namespace CSMWorld
|
|||||||
///
|
///
|
||||||
/// \param listDeleted include deleted record in the list
|
/// \param listDeleted include deleted record in the list
|
||||||
|
|
||||||
virtual void insertRecord (const RecordBase& record, int index,
|
virtual void insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
UniversalId::Type type = UniversalId::Type_None);
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
///< Insert record before index.
|
///< Insert record before index.
|
||||||
///
|
///
|
||||||
@ -152,20 +151,14 @@ namespace CSMWorld
|
|||||||
|
|
||||||
void addColumn (Column<ESXRecordT> *column);
|
void addColumn (Column<ESXRecordT> *column);
|
||||||
|
|
||||||
void setRecord (int index, const Record<ESXRecordT>& record);
|
void setRecord (int index, std::unique_ptr<Record<ESXRecordT> > record);
|
||||||
///< \attention This function must not change the ID.
|
///< \attention This function must not change the ID.
|
||||||
|
|
||||||
NestableColumn *getNestableColumn (int column) const;
|
NestableColumn *getNestableColumn (int column) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
const std::map<std::string, int>& Collection<ESXRecordT, IdAccessorT>::getIdMap() const
|
const std::vector<std::unique_ptr<Record<ESXRecordT> > >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
|
||||||
{
|
|
||||||
return mIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
|
||||||
const std::vector<Record<ESXRecordT> >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
|
|
||||||
{
|
{
|
||||||
return mRecords;
|
return mRecords;
|
||||||
}
|
}
|
||||||
@ -185,15 +178,15 @@ namespace CSMWorld
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// reorder records
|
// reorder records
|
||||||
std::vector<Record<ESXRecordT> > buffer (size);
|
std::vector<std::unique_ptr<Record<ESXRecordT> > > buffer (size);
|
||||||
|
|
||||||
for (int i=0; i<size; ++i)
|
for (int i=0; i<size; ++i)
|
||||||
{
|
{
|
||||||
buffer[newOrder[i]] = mRecords [baseIndex+i];
|
buffer[newOrder[i]] = std::move(mRecords [baseIndex+i]);
|
||||||
buffer[newOrder[i]].setModified (buffer[newOrder[i]].get());
|
buffer[newOrder[i]]->setModified (buffer[newOrder[i]]->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
|
std::move (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
|
||||||
|
|
||||||
// adjust index
|
// adjust index
|
||||||
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
|
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
|
||||||
@ -210,12 +203,12 @@ namespace CSMWorld
|
|||||||
const std::string& destination,
|
const std::string& destination,
|
||||||
const UniversalId::Type type)
|
const UniversalId::Type type)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> copy;
|
std::unique_ptr<Record<ESXRecordT> > copy(new Record<ESXRecordT>);
|
||||||
copy.mModified = getRecord(origin).get();
|
copy->mModified = getRecord(origin).get();
|
||||||
copy.mState = RecordBase::State_ModifiedOnly;
|
copy->mState = RecordBase::State_ModifiedOnly;
|
||||||
copy.get().mId = destination;
|
copy->get().mId = destination;
|
||||||
|
|
||||||
insertRecord(copy, getAppendIndex(destination, type));
|
insertRecord(std::move(copy), getAppendIndex(destination, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
@ -238,15 +231,15 @@ namespace CSMWorld
|
|||||||
|
|
||||||
if (iter==mIndex.end())
|
if (iter==mIndex.end())
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record2;
|
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
|
||||||
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
|
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||||
record2.mModified = record;
|
record2->mModified = record;
|
||||||
|
|
||||||
insertRecord (record2, getAppendIndex (id));
|
insertRecord (std::move(record2), getAppendIndex (id));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mRecords[iter->second].setModified (record);
|
mRecords[iter->second]->setModified (record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +252,7 @@ namespace CSMWorld
|
|||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
|
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
|
||||||
{
|
{
|
||||||
return IdAccessorT().getId (mRecords.at (index).get());
|
return IdAccessorT().getId (mRecords.at (index)->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
@ -282,13 +275,13 @@ namespace CSMWorld
|
|||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
|
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
|
||||||
{
|
{
|
||||||
return mColumns.at (column)->get (mRecords.at (index));
|
return mColumns.at (column)->get (*mRecords.at (index));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
|
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
|
||||||
{
|
{
|
||||||
return mColumns.at (column)->set (mRecords.at (index), data);
|
return mColumns.at (column)->set (*mRecords.at (index), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
@ -315,8 +308,8 @@ namespace CSMWorld
|
|||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::merge()
|
void Collection<ESXRecordT, IdAccessorT>::merge()
|
||||||
{
|
{
|
||||||
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
|
for (typename std::vector<std::unique_ptr<Record<ESXRecordT> > >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
|
||||||
iter->merge();
|
(*iter)->merge();
|
||||||
|
|
||||||
purge();
|
purge();
|
||||||
}
|
}
|
||||||
@ -328,7 +321,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
while (i<static_cast<int> (mRecords.size()))
|
while (i<static_cast<int> (mRecords.size()))
|
||||||
{
|
{
|
||||||
if (mRecords[i].isErased())
|
if (mRecords[i]->isErased())
|
||||||
removeRows (i, 1);
|
removeRows (i, 1);
|
||||||
else
|
else
|
||||||
++i;
|
++i;
|
||||||
@ -369,11 +362,11 @@ namespace CSMWorld
|
|||||||
IdAccessorT().getId (record) = id;
|
IdAccessorT().getId (record) = id;
|
||||||
record.blank();
|
record.blank();
|
||||||
|
|
||||||
Record<ESXRecordT> record2;
|
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
|
||||||
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
|
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||||
record2.mModified = record;
|
record2->mModified = record;
|
||||||
|
|
||||||
insertRecord (record2, getAppendIndex (id, type), type);
|
insertRecord (std::move(record2), getAppendIndex (id, type), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
@ -390,18 +383,19 @@ namespace CSMWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::replace (int index, const RecordBase& record)
|
void Collection<ESXRecordT, IdAccessorT>::replace (int index, std::unique_ptr<RecordBase> record)
|
||||||
{
|
{
|
||||||
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
|
std::unique_ptr<Record<ESXRecordT> > tmp(static_cast<Record<ESXRecordT>*>(record.release()));
|
||||||
|
mRecords.at (index) = std::move(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
|
void Collection<ESXRecordT, IdAccessorT>::appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type)
|
UniversalId::Type type)
|
||||||
{
|
{
|
||||||
insertRecord (record,
|
int index =
|
||||||
getAppendIndex (IdAccessorT().getId (
|
getAppendIndex(IdAccessorT().getId(static_cast<Record<ESXRecordT>*>(record.get())->get()), type);
|
||||||
dynamic_cast<const Record<ESXRecordT>&> (record).get()), type), type);
|
insertRecord (std::move(record), index, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
@ -419,8 +413,8 @@ namespace CSMWorld
|
|||||||
for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin();
|
for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin();
|
||||||
iter!=mIndex.end(); ++iter)
|
iter!=mIndex.end(); ++iter)
|
||||||
{
|
{
|
||||||
if (listDeleted || !mRecords[iter->second].isDeleted())
|
if (listDeleted || !mRecords[iter->second]->isDeleted())
|
||||||
ids.push_back (IdAccessorT().getId (mRecords[iter->second].get()));
|
ids.push_back (IdAccessorT().getId (mRecords[iter->second]->get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ids;
|
return ids;
|
||||||
@ -430,46 +424,52 @@ namespace CSMWorld
|
|||||||
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
|
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
|
||||||
{
|
{
|
||||||
int index = getIndex (id);
|
int index = getIndex (id);
|
||||||
return mRecords.at (index);
|
return *mRecords.at (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
|
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
|
||||||
{
|
{
|
||||||
return mRecords.at (index);
|
return *mRecords.at (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::insertRecord (const RecordBase& record, int index,
|
void Collection<ESXRecordT, IdAccessorT>::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
UniversalId::Type type)
|
UniversalId::Type type)
|
||||||
{
|
{
|
||||||
if (index<0 || index>static_cast<int> (mRecords.size()))
|
int size = static_cast<int>(mRecords.size());
|
||||||
|
if (index < 0 || index > size)
|
||||||
throw std::runtime_error ("index out of range");
|
throw std::runtime_error ("index out of range");
|
||||||
|
|
||||||
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
|
std::unique_ptr<Record<ESXRecordT> > record2(static_cast<Record<ESXRecordT>*>(record.release()));
|
||||||
|
std::string lowerId = Misc::StringUtils::lowerCase(IdAccessorT().getId(record2->get()));
|
||||||
|
|
||||||
mRecords.insert (mRecords.begin()+index, record2);
|
if (index == size)
|
||||||
|
mRecords.push_back (std::move(record2));
|
||||||
|
else
|
||||||
|
mRecords.insert (mRecords.begin()+index, std::move(record2));
|
||||||
|
|
||||||
if (index<static_cast<int> (mRecords.size())-1)
|
if (index < size-1)
|
||||||
{
|
{
|
||||||
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
|
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end(); ++iter)
|
||||||
++iter)
|
{
|
||||||
if (iter->second>=index)
|
if (iter->second >= index)
|
||||||
++(iter->second);
|
++(iter->second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
|
mIndex.insert (std::make_pair (lowerId, index));
|
||||||
record2.get())), index));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
|
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index,
|
||||||
|
std::unique_ptr<Record<ESXRecordT> > record)
|
||||||
{
|
{
|
||||||
if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index).get()))!=
|
if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index)->get())) !=
|
||||||
Misc::StringUtils::lowerCase (IdAccessorT().getId (record.get())))
|
Misc::StringUtils::lowerCase (IdAccessorT().getId (record->get())))
|
||||||
throw std::runtime_error ("attempt to change the ID of a record");
|
throw std::runtime_error ("attempt to change the ID of a record");
|
||||||
|
|
||||||
mRecords.at (index) = record;
|
mRecords.at (index) = std::move(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
@ -8,6 +8,11 @@ CSMWorld::CollectionBase::CollectionBase() {}
|
|||||||
|
|
||||||
CSMWorld::CollectionBase::~CollectionBase() {}
|
CSMWorld::CollectionBase::~CollectionBase() {}
|
||||||
|
|
||||||
|
int CSMWorld::CollectionBase::getInsertIndex (const std::string& id, UniversalId::Type type, RecordBase *record) const
|
||||||
|
{
|
||||||
|
return getAppendIndex(id, type);
|
||||||
|
}
|
||||||
|
|
||||||
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
|
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
|
||||||
{
|
{
|
||||||
int columns = getColumns();
|
int columns = getColumns();
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
#include "columns.hpp"
|
#include "columns.hpp"
|
||||||
@ -64,13 +65,13 @@ namespace CSMWorld
|
|||||||
////< Search record with \a id.
|
////< Search record with \a id.
|
||||||
/// \return index of record (if found) or -1 (not found)
|
/// \return index of record (if found) or -1 (not found)
|
||||||
|
|
||||||
virtual void replace (int index, const RecordBase& record) = 0;
|
virtual void replace (int index, std::unique_ptr<RecordBase> record) = 0;
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///
|
///
|
||||||
/// \attention \a record must not change the ID.
|
/// \attention \a record must not change the ID.
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
|
||||||
virtual void appendRecord (const RecordBase& record,
|
virtual void appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type = UniversalId::Type_None) = 0;
|
UniversalId::Type type = UniversalId::Type_None) = 0;
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
|
|
||||||
@ -97,6 +98,12 @@ namespace CSMWorld
|
|||||||
///
|
///
|
||||||
/// \return Success?
|
/// \return Success?
|
||||||
|
|
||||||
|
virtual int getInsertIndex (const std::string& id,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None,
|
||||||
|
RecordBase *record = nullptr) const;
|
||||||
|
///< Works like getAppendIndex unless an overloaded method uses the record pointer
|
||||||
|
/// to get additional info about the record that results in an alternative index.
|
||||||
|
|
||||||
int searchColumnIndex (Columns::ColumnId id) const;
|
int searchColumnIndex (Columns::ColumnId id) const;
|
||||||
///< Return index of column with the given \a id. If no such column exists, -1 is returned.
|
///< Return index of column with the given \a id. If no such column exists, -1 is returned.
|
||||||
|
|
||||||
|
@ -124,16 +124,15 @@ void CSMWorld::CreateCommand::undo()
|
|||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
|
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
|
||||||
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
|
: QUndoCommand (parent), mModel (model), mId (id)
|
||||||
{
|
{
|
||||||
setText (("Revert record " + id).c_str());
|
setText (("Revert record " + id).c_str());
|
||||||
|
|
||||||
mOld = model.getRecord (id).clone();
|
mOld = std::move(model.getRecord (id).clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::RevertCommand::~RevertCommand()
|
CSMWorld::RevertCommand::~RevertCommand()
|
||||||
{
|
{
|
||||||
delete mOld;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RevertCommand::redo()
|
void CSMWorld::RevertCommand::redo()
|
||||||
@ -145,6 +144,7 @@ void CSMWorld::RevertCommand::redo()
|
|||||||
|
|
||||||
if (state==RecordBase::State_ModifiedOnly)
|
if (state==RecordBase::State_ModifiedOnly)
|
||||||
{
|
{
|
||||||
|
mOld = std::move(mModel.getRecord (mId).clone());
|
||||||
mModel.removeRows (index.row(), 1);
|
mModel.removeRows (index.row(), 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -155,21 +155,20 @@ void CSMWorld::RevertCommand::redo()
|
|||||||
|
|
||||||
void CSMWorld::RevertCommand::undo()
|
void CSMWorld::RevertCommand::undo()
|
||||||
{
|
{
|
||||||
mModel.setRecord (mId, *mOld);
|
mModel.setRecord (mId, std::move(mOld));
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
|
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
|
||||||
const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent)
|
const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent)
|
||||||
: QUndoCommand (parent), mModel (model), mId (id), mOld (0), mType(type)
|
: QUndoCommand (parent), mModel (model), mId (id), mType(type)
|
||||||
{
|
{
|
||||||
setText (("Delete record " + id).c_str());
|
setText (("Delete record " + id).c_str());
|
||||||
|
|
||||||
mOld = model.getRecord (id).clone();
|
mOld = std::move(model.getRecord (id).clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::DeleteCommand::~DeleteCommand()
|
CSMWorld::DeleteCommand::~DeleteCommand()
|
||||||
{
|
{
|
||||||
delete mOld;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::DeleteCommand::redo()
|
void CSMWorld::DeleteCommand::redo()
|
||||||
@ -181,6 +180,7 @@ void CSMWorld::DeleteCommand::redo()
|
|||||||
|
|
||||||
if (state==RecordBase::State_ModifiedOnly)
|
if (state==RecordBase::State_ModifiedOnly)
|
||||||
{
|
{
|
||||||
|
mOld = std::move(mModel.getRecord (mId).clone());
|
||||||
mModel.removeRows (index.row(), 1);
|
mModel.removeRows (index.row(), 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -191,7 +191,7 @@ void CSMWorld::DeleteCommand::redo()
|
|||||||
|
|
||||||
void CSMWorld::DeleteCommand::undo()
|
void CSMWorld::DeleteCommand::undo()
|
||||||
{
|
{
|
||||||
mModel.setRecord (mId, *mOld, mType);
|
mModel.setRecord (mId, std::move(mOld), mType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QUndoCommand>
|
#include <QUndoCommand>
|
||||||
@ -97,7 +98,7 @@ namespace CSMWorld
|
|||||||
{
|
{
|
||||||
IdTable& mModel;
|
IdTable& mModel;
|
||||||
std::string mId;
|
std::string mId;
|
||||||
RecordBase *mOld;
|
std::unique_ptr<RecordBase> mOld;
|
||||||
|
|
||||||
// not implemented
|
// not implemented
|
||||||
RevertCommand (const RevertCommand&);
|
RevertCommand (const RevertCommand&);
|
||||||
@ -118,7 +119,7 @@ namespace CSMWorld
|
|||||||
{
|
{
|
||||||
IdTable& mModel;
|
IdTable& mModel;
|
||||||
std::string mId;
|
std::string mId;
|
||||||
RecordBase *mOld;
|
std::unique_ptr<RecordBase> mOld;
|
||||||
UniversalId::Type mType;
|
UniversalId::Type mType;
|
||||||
|
|
||||||
// not implemented
|
// not implemented
|
||||||
|
@ -10,10 +10,6 @@
|
|||||||
#include <components/esm/loadglob.hpp>
|
#include <components/esm/loadglob.hpp>
|
||||||
#include <components/esm/cellref.hpp>
|
#include <components/esm/cellref.hpp>
|
||||||
|
|
||||||
#include <components/autocalc/autocalc.hpp>
|
|
||||||
#include <components/autocalc/autocalcspell.hpp>
|
|
||||||
#include <components/autocalc/store.hpp>
|
|
||||||
|
|
||||||
#include "idtable.hpp"
|
#include "idtable.hpp"
|
||||||
#include "idtree.hpp"
|
#include "idtree.hpp"
|
||||||
#include "columnimp.hpp"
|
#include "columnimp.hpp"
|
||||||
@ -22,71 +18,7 @@
|
|||||||
#include "resourcesmanager.hpp"
|
#include "resourcesmanager.hpp"
|
||||||
#include "resourcetable.hpp"
|
#include "resourcetable.hpp"
|
||||||
#include "nestedcoladapterimp.hpp"
|
#include "nestedcoladapterimp.hpp"
|
||||||
#include "npcstats.hpp"
|
#include "npcautocalc.hpp"
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
class CSStore : public AutoCalc::StoreCommon
|
|
||||||
{
|
|
||||||
const CSMWorld::IdCollection<ESM::GameSetting>& mGmstTable;
|
|
||||||
const CSMWorld::IdCollection<ESM::Skill>& mSkillTable;
|
|
||||||
const CSMWorld::IdCollection<ESM::MagicEffect>& mMagicEffectTable;
|
|
||||||
const CSMWorld::NestedIdCollection<ESM::Spell>& mSpells;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
CSStore(const CSMWorld::IdCollection<ESM::GameSetting>& gmst,
|
|
||||||
const CSMWorld::IdCollection<ESM::Skill>& skills,
|
|
||||||
const CSMWorld::IdCollection<ESM::MagicEffect>& magicEffects,
|
|
||||||
const CSMWorld::NestedIdCollection<ESM::Spell>& spells)
|
|
||||||
: mGmstTable(gmst), mSkillTable(skills), mMagicEffectTable(magicEffects), mSpells(spells)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
~CSStore() {}
|
|
||||||
|
|
||||||
virtual int findGmstInt(const std::string& name) const
|
|
||||||
{
|
|
||||||
return mGmstTable.getRecord(name).get().getInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual float findGmstFloat(const std::string& name) const
|
|
||||||
{
|
|
||||||
return mGmstTable.getRecord(name).get().getFloat();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const ESM::Skill *findSkill(int index) const
|
|
||||||
{
|
|
||||||
// if the skill does not exist, throws std::runtime_error ("invalid ID: " + id)
|
|
||||||
return &mSkillTable.getRecord(ESM::Skill::indexToId(index)).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const ESM::MagicEffect* findMagicEffect(int id) const
|
|
||||||
{
|
|
||||||
// if the magic effect does not exist, throws std::runtime_error ("invalid ID: " + id)
|
|
||||||
return &mMagicEffectTable.getRecord(ESM::MagicEffect::indexToId((short)id)).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void getSpells(std::vector<ESM::Spell*>& spells)
|
|
||||||
{
|
|
||||||
// prepare data in a format used by OpenMW store
|
|
||||||
for (int index = 0; index < mSpells.getSize(); ++index)
|
|
||||||
spells.push_back(const_cast<ESM::Spell *>(&mSpells.getRecord(index).get()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
unsigned short autoCalculateMana(AutoCalc::StatsBase& stats)
|
|
||||||
{
|
|
||||||
return stats.getBaseAttribute(ESM::Attribute::Intelligence) * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short autoCalculateFatigue(AutoCalc::StatsBase& stats)
|
|
||||||
{
|
|
||||||
return stats.getBaseAttribute(ESM::Attribute::Strength)
|
|
||||||
+ stats.getBaseAttribute(ESM::Attribute::Willpower)
|
|
||||||
+ stats.getBaseAttribute(ESM::Attribute::Agility)
|
|
||||||
+ stats.getBaseAttribute(ESM::Attribute::Endurance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update)
|
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update)
|
||||||
{
|
{
|
||||||
@ -129,8 +61,9 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
|
|||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager)
|
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager)
|
||||||
: mEncoder (encoding), mPathgrids (mCells), mReferenceables(self()), mRefs (mCells),
|
: mEncoder (encoding), mPathgrids (mCells), mReferenceables (self()), mRefs (mCells),
|
||||||
mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0)
|
mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0),
|
||||||
|
mNpcAutoCalc (0)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
@ -621,32 +554,18 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
|
|||||||
CSMWorld::IdTree *objects =
|
CSMWorld::IdTree *objects =
|
||||||
static_cast<CSMWorld::IdTree*>(getTableModel(UniversalId::Type_Referenceable));
|
static_cast<CSMWorld::IdTree*>(getTableModel(UniversalId::Type_Referenceable));
|
||||||
|
|
||||||
connect (gmsts, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
mNpcAutoCalc = new NpcAutoCalc (self(), gmsts, skills, classes, races, objects);
|
||||||
this, SLOT (gmstDataChanged (const QModelIndex&, const QModelIndex&)));
|
|
||||||
connect (skills, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
|
||||||
this, SLOT (skillDataChanged (const QModelIndex&, const QModelIndex&)));
|
|
||||||
connect (classes, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
|
||||||
this, SLOT (classDataChanged (const QModelIndex&, const QModelIndex&)));
|
|
||||||
connect (races, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
|
||||||
this, SLOT (raceDataChanged (const QModelIndex&, const QModelIndex&)));
|
|
||||||
connect (objects, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
|
||||||
this, SLOT (npcDataChanged (const QModelIndex&, const QModelIndex&)));
|
|
||||||
connect (this, SIGNAL (updateNpcAutocalc (int, const std::string&)),
|
|
||||||
objects, SLOT (updateNpcAutocalc (int, const std::string&)));
|
|
||||||
connect (this, SIGNAL (cacheNpcStats (const std::string&, NpcStats*)),
|
|
||||||
this, SLOT (cacheNpcStatsEvent (const std::string&, NpcStats*)));
|
|
||||||
|
|
||||||
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
|
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::Data::~Data()
|
CSMWorld::Data::~Data()
|
||||||
{
|
{
|
||||||
clearNpcStatsCache();
|
|
||||||
|
|
||||||
for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
|
for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
|
||||||
delete *iter;
|
delete *iter;
|
||||||
|
|
||||||
delete mReader;
|
delete mReader;
|
||||||
|
delete mNpcAutoCalc;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const
|
const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const
|
||||||
@ -942,8 +861,13 @@ const CSMWorld::MetaData& CSMWorld::Data::getMetaData() const
|
|||||||
|
|
||||||
void CSMWorld::Data::setMetaData (const MetaData& metaData)
|
void CSMWorld::Data::setMetaData (const MetaData& metaData)
|
||||||
{
|
{
|
||||||
Record<MetaData> record (RecordBase::State_ModifiedOnly, 0, &metaData);
|
mMetaData.setRecord (0, std::make_unique<Record<MetaData> >(
|
||||||
mMetaData.setRecord (0, record);
|
Record<MetaData>(RecordBase::State_ModifiedOnly, 0, &metaData)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMWorld::NpcAutoCalc& CSMWorld::Data::getNpcAutoCalc() const
|
||||||
|
{
|
||||||
|
return *mNpcAutoCalc;
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
|
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
|
||||||
@ -973,6 +897,22 @@ void CSMWorld::Data::merge()
|
|||||||
mGlobals.merge();
|
mGlobals.merge();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CSMWorld::Data::getTotalRecords (const std::vector<boost::filesystem::path>& files)
|
||||||
|
{
|
||||||
|
int records = 0;
|
||||||
|
|
||||||
|
std::unique_ptr<ESM::ESMReader> reader = std::unique_ptr<ESM::ESMReader>(new ESM::ESMReader);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < files.size(); ++i)
|
||||||
|
{
|
||||||
|
reader->open(files[i].string());
|
||||||
|
records += reader->getRecordCount();
|
||||||
|
reader->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
|
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
|
||||||
{
|
{
|
||||||
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
|
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
|
||||||
@ -987,6 +927,35 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
|
|||||||
mReader->setIndex(mReaderIndex++);
|
mReader->setIndex(mReaderIndex++);
|
||||||
mReader->open (path.string());
|
mReader->open (path.string());
|
||||||
|
|
||||||
|
mLoadedFiles.push_back(path.filename().string());
|
||||||
|
|
||||||
|
// at this point mReader->mHeader.mMaster have been populated for the file being loaded
|
||||||
|
for (size_t f = 0; f < mReader->getGameFiles().size(); ++f)
|
||||||
|
{
|
||||||
|
ESM::Header::MasterData& m = const_cast<ESM::Header::MasterData&>(mReader->getGameFiles().at(f));
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
for (size_t i = 0; i < mLoadedFiles.size()-1; ++i) // -1 to ignore the current file
|
||||||
|
{
|
||||||
|
if (Misc::StringUtils::ciEqual(m.name, mLoadedFiles.at(i)))
|
||||||
|
{
|
||||||
|
index = static_cast<int>(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
// Tried to load a parent file that has not been loaded yet. This is bad,
|
||||||
|
// the launcher should have taken care of this.
|
||||||
|
std::string fstring = "File " + mReader->getName() + " asks for parent file " + m.name
|
||||||
|
+ ", but it has not been loaded yet. Please check your load order.";
|
||||||
|
mReader->fail(fstring);
|
||||||
|
}
|
||||||
|
|
||||||
|
m.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
mBase = base;
|
mBase = base;
|
||||||
mProject = project;
|
mProject = project;
|
||||||
|
|
||||||
@ -996,7 +965,8 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
|
|||||||
metaData.mId = "sys::meta";
|
metaData.mId = "sys::meta";
|
||||||
metaData.load (*mReader);
|
metaData.load (*mReader);
|
||||||
|
|
||||||
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData));
|
mMetaData.setRecord (0, std::make_unique<Record<MetaData> >(
|
||||||
|
Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mReader->getRecordCount();
|
return mReader->getRecordCount();
|
||||||
@ -1299,271 +1269,3 @@ const CSMWorld::Data& CSMWorld::Data::self ()
|
|||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::Data::skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
|
||||||
{
|
|
||||||
// mData.mAttribute (affects attributes skill bonus autocalc)
|
|
||||||
// mData.mSpecialization (affects skills autocalc)
|
|
||||||
CSMWorld::IdTable *skillModel =
|
|
||||||
static_cast<CSMWorld::IdTable*>(getTableModel(CSMWorld::UniversalId::Type_Skill));
|
|
||||||
|
|
||||||
int attributeColumn = skillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute);
|
|
||||||
int specialisationColumn = skillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
|
|
||||||
|
|
||||||
if ((topLeft.column() <= attributeColumn && attributeColumn <= bottomRight.column())
|
|
||||||
|| (topLeft.column() <= specialisationColumn && specialisationColumn <= bottomRight.column()))
|
|
||||||
{
|
|
||||||
clearNpcStatsCache();
|
|
||||||
|
|
||||||
std::string empty;
|
|
||||||
emit updateNpcAutocalc(0/*all*/, empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSMWorld::Data::classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
|
||||||
{
|
|
||||||
// update autocalculated attributes/skills of every NPC with matching class
|
|
||||||
// - mData.mAttribute[2]
|
|
||||||
// - mData.mSkills[5][2]
|
|
||||||
// - mData.mSpecialization
|
|
||||||
CSMWorld::IdTable *classModel =
|
|
||||||
static_cast<CSMWorld::IdTable*>(getTableModel(CSMWorld::UniversalId::Type_Class));
|
|
||||||
|
|
||||||
int attribute1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute1); // +1
|
|
||||||
int majorSkill1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_MajorSkill1); // +4
|
|
||||||
int minorSkill1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_MinorSkill1); // +4
|
|
||||||
int specialisationColumn = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
|
|
||||||
|
|
||||||
if ((topLeft.column() > attribute1Column+1 || attribute1Column > bottomRight.column())
|
|
||||||
&& (topLeft.column() > majorSkill1Column+4 || majorSkill1Column > bottomRight.column())
|
|
||||||
&& (topLeft.column() > minorSkill1Column+4 || minorSkill1Column > bottomRight.column())
|
|
||||||
&& (topLeft.column() > specialisationColumn || specialisationColumn > bottomRight.column()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the affected class
|
|
||||||
int idColumn = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
|
||||||
for (int classRow = topLeft.row(); classRow <= bottomRight.row(); ++classRow)
|
|
||||||
{
|
|
||||||
clearNpcStatsCache();
|
|
||||||
|
|
||||||
std::string classId =
|
|
||||||
classModel->data(classModel->index(classRow, idColumn)).toString().toUtf8().constData();
|
|
||||||
emit updateNpcAutocalc(1/*class*/, classId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSMWorld::Data::raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
|
||||||
{
|
|
||||||
// affects racial bonus attributes & skills
|
|
||||||
// - mData.mAttributeValues[]
|
|
||||||
// - mData.mBonus[].mBonus
|
|
||||||
// - mPowers.mList[]
|
|
||||||
CSMWorld::IdTree *raceModel =
|
|
||||||
static_cast<CSMWorld::IdTree*>(getTableModel(CSMWorld::UniversalId::Type_Race));
|
|
||||||
|
|
||||||
int attrColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceAttributes);
|
|
||||||
int bonusColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceSkillBonus);
|
|
||||||
int powersColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_PowerList);
|
|
||||||
|
|
||||||
bool match = false;
|
|
||||||
int raceRow = topLeft.row();
|
|
||||||
int raceEnd = bottomRight.row();
|
|
||||||
if (topLeft.parent().isValid() && bottomRight.parent().isValid())
|
|
||||||
{
|
|
||||||
if ((topLeft.parent().column() <= attrColumn && attrColumn <= bottomRight.parent().column())
|
|
||||||
|| (topLeft.parent().column() <= bonusColumn && bonusColumn <= bottomRight.parent().column())
|
|
||||||
|| (topLeft.parent().column() <= powersColumn && powersColumn <= bottomRight.parent().column()))
|
|
||||||
{
|
|
||||||
match = true; // TODO: check for specific nested column?
|
|
||||||
raceRow = topLeft.parent().row();
|
|
||||||
raceEnd = bottomRight.parent().row();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((topLeft.column() <= attrColumn && attrColumn <= bottomRight.column())
|
|
||||||
|| (topLeft.column() <= bonusColumn && bonusColumn <= bottomRight.column())
|
|
||||||
|| (topLeft.column() <= powersColumn && powersColumn <= bottomRight.column()))
|
|
||||||
{
|
|
||||||
match = true; // maybe the whole table changed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// update autocalculated attributes/skills of every NPC with matching race
|
|
||||||
int idColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
|
||||||
for (; raceRow <= raceEnd; ++raceRow)
|
|
||||||
{
|
|
||||||
clearNpcStatsCache();
|
|
||||||
|
|
||||||
std::string raceId =
|
|
||||||
raceModel->data(raceModel->index(raceRow, idColumn)).toString().toUtf8().constData();
|
|
||||||
emit updateNpcAutocalc(2/*race*/, raceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSMWorld::Data::npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
|
||||||
{
|
|
||||||
// TODO: for now always recalculate
|
|
||||||
clearNpcStatsCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSMWorld::Data::gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
|
||||||
{
|
|
||||||
static const QStringList gmsts(QStringList()<< "fNPCbaseMagickaMult" << "fAutoSpellChance"
|
|
||||||
<< "fEffectCostMult" << "iAutoSpellAlterationMax" << "iAutoSpellConjurationMax"
|
|
||||||
<< "iAutoSpellDestructionMax" << "iAutoSpellIllusionMax" << "iAutoSpellMysticismMax"
|
|
||||||
<< "iAutoSpellRestorationMax" << "iAutoSpellTimesCanCast" << "iAutoSpellAttSkillMin");
|
|
||||||
|
|
||||||
bool match = false;
|
|
||||||
for (int row = topLeft.row(); row <= bottomRight.row(); ++row)
|
|
||||||
{
|
|
||||||
if (gmsts.contains(mGmsts.getRecord(row).get().mId.c_str()))
|
|
||||||
{
|
|
||||||
match = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match)
|
|
||||||
return;
|
|
||||||
|
|
||||||
clearNpcStatsCache();
|
|
||||||
|
|
||||||
std::string empty;
|
|
||||||
emit updateNpcAutocalc(0/*all*/, empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSMWorld::Data::clearNpcStatsCache ()
|
|
||||||
{
|
|
||||||
for (std::map<std::string, CSMWorld::NpcStats*>::iterator it (mNpcStatCache.begin());
|
|
||||||
it != mNpcStatCache.end(); ++it)
|
|
||||||
delete it->second;
|
|
||||||
|
|
||||||
mNpcStatCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
CSMWorld::NpcStats* CSMWorld::Data::npcAutoCalculate(const ESM::NPC& npc) const
|
|
||||||
{
|
|
||||||
CSMWorld::NpcStats * cachedStats = getCachedNpcData (npc.mId);
|
|
||||||
if (cachedStats)
|
|
||||||
return cachedStats;
|
|
||||||
|
|
||||||
int raceIndex = mRaces.searchId(npc.mRace);
|
|
||||||
int classIndex = mClasses.searchId(npc.mClass);
|
|
||||||
// this can happen when creating a new game from scratch
|
|
||||||
if (raceIndex == -1 || classIndex == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const ESM::Race *race = &mRaces.getRecord(raceIndex).get();
|
|
||||||
const ESM::Class *class_ = &mClasses.getRecord(classIndex).get();
|
|
||||||
|
|
||||||
bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
|
|
||||||
short level = npc.mNpdt52.mLevel;
|
|
||||||
if (autoCalc)
|
|
||||||
level = npc.mNpdt12.mLevel;
|
|
||||||
|
|
||||||
std::auto_ptr<CSMWorld::NpcStats> stats (new CSMWorld::NpcStats());
|
|
||||||
|
|
||||||
CSStore store(mGmsts, mSkills, mMagicEffects, mSpells);
|
|
||||||
|
|
||||||
if (autoCalc)
|
|
||||||
{
|
|
||||||
AutoCalc::autoCalcAttributesImpl (&npc, race, class_, level, *stats, &store);
|
|
||||||
|
|
||||||
stats->setHealth(autoCalculateHealth(level, class_, *stats));
|
|
||||||
stats->setMana(autoCalculateMana(*stats));
|
|
||||||
stats->setFatigue(autoCalculateFatigue(*stats));
|
|
||||||
|
|
||||||
AutoCalc::autoCalcSkillsImpl(&npc, race, class_, level, *stats, &store);
|
|
||||||
|
|
||||||
AutoCalc::autoCalculateSpells(race, *stats, &store);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin();
|
|
||||||
it != npc.mSpells.mList.end(); ++it)
|
|
||||||
{
|
|
||||||
stats->addSpell(*it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update spell info
|
|
||||||
const std::vector<std::string> &racePowers = race->mPowers.mList;
|
|
||||||
for (unsigned int i = 0; i < racePowers.size(); ++i)
|
|
||||||
{
|
|
||||||
int type = -1;
|
|
||||||
int spellIndex = mSpells.searchId(racePowers[i]);
|
|
||||||
if (spellIndex != -1)
|
|
||||||
type = mSpells.getRecord(spellIndex).get().mData.mType;
|
|
||||||
stats->addPowers(racePowers[i], type);
|
|
||||||
}
|
|
||||||
// cost/chance
|
|
||||||
int skills[ESM::Skill::Length];
|
|
||||||
if (autoCalc)
|
|
||||||
for (int i = 0; i< ESM::Skill::Length; ++i)
|
|
||||||
skills[i] = stats->getBaseSkill(i);
|
|
||||||
else
|
|
||||||
for (int i = 0; i< ESM::Skill::Length; ++i)
|
|
||||||
skills[i] = npc.mNpdt52.mSkills[i];
|
|
||||||
|
|
||||||
int attributes[ESM::Attribute::Length];
|
|
||||||
if (autoCalc)
|
|
||||||
for (int i = 0; i< ESM::Attribute::Length; ++i)
|
|
||||||
attributes[i] = stats->getBaseAttribute(i);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
attributes[ESM::Attribute::Strength] = npc.mNpdt52.mStrength;
|
|
||||||
attributes[ESM::Attribute::Willpower] = npc.mNpdt52.mWillpower;
|
|
||||||
attributes[ESM::Attribute::Agility] = npc.mNpdt52.mAgility;
|
|
||||||
attributes[ESM::Attribute::Speed] = npc.mNpdt52.mSpeed;
|
|
||||||
attributes[ESM::Attribute::Endurance] = npc.mNpdt52.mEndurance;
|
|
||||||
attributes[ESM::Attribute::Personality] = npc.mNpdt52.mPersonality;
|
|
||||||
attributes[ESM::Attribute::Luck] = npc.mNpdt52.mLuck;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<CSMWorld::SpellInfo>& spells = stats->spells();
|
|
||||||
for (std::vector<SpellInfo>::const_iterator it = spells.begin(); it != spells.end(); ++it)
|
|
||||||
{
|
|
||||||
int cost = -1;
|
|
||||||
int spellIndex = mSpells.searchId((*it).mName);
|
|
||||||
const ESM::Spell* spell = 0;
|
|
||||||
if (spellIndex != -1)
|
|
||||||
{
|
|
||||||
spell = &mSpells.getRecord(spellIndex).get();
|
|
||||||
cost = spell->mData.mCost;
|
|
||||||
|
|
||||||
int school;
|
|
||||||
float skillTerm;
|
|
||||||
AutoCalc::calcWeakestSchool(spell, skills, school, skillTerm, &store);
|
|
||||||
float chance = calcAutoCastChance(spell, skills, attributes, school, &store);
|
|
||||||
|
|
||||||
stats->addCostAndChance((*it).mName, cost, (int)ceil(chance)); // percent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stats.get() == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
CSMWorld::NpcStats *result = stats.release();
|
|
||||||
emit cacheNpcStats (npc.mId, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSMWorld::Data::cacheNpcStatsEvent (const std::string& id, CSMWorld::NpcStats *stats)
|
|
||||||
{
|
|
||||||
mNpcStatCache[id] = stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
CSMWorld::NpcStats* CSMWorld::Data::getCachedNpcData (const std::string& id) const
|
|
||||||
{
|
|
||||||
std::map<std::string, CSMWorld::NpcStats*>::const_iterator it = mNpcStatCache.find(id);
|
|
||||||
if (it != mNpcStatCache.end())
|
|
||||||
return it->second;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
@ -61,7 +61,7 @@ namespace CSMWorld
|
|||||||
{
|
{
|
||||||
class ResourcesManager;
|
class ResourcesManager;
|
||||||
class Resources;
|
class Resources;
|
||||||
class NpcStats;
|
class NpcAutoCalc;
|
||||||
|
|
||||||
class Data : public QObject
|
class Data : public QObject
|
||||||
{
|
{
|
||||||
@ -104,12 +104,13 @@ namespace CSMWorld
|
|||||||
const ESM::Dialogue *mDialogue; // last loaded dialogue
|
const ESM::Dialogue *mDialogue; // last loaded dialogue
|
||||||
bool mBase;
|
bool mBase;
|
||||||
bool mProject;
|
bool mProject;
|
||||||
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
|
std::map<std::string, std::map<unsigned int, unsigned int> > mRefLoadCache;
|
||||||
int mReaderIndex;
|
int mReaderIndex;
|
||||||
|
std::vector<std::string> mLoadedFiles;
|
||||||
|
|
||||||
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;
|
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;
|
||||||
|
|
||||||
std::map<std::string, NpcStats*> mNpcStatCache;
|
NpcAutoCalc *mNpcAutoCalc;
|
||||||
|
|
||||||
// not implemented
|
// not implemented
|
||||||
Data (const Data&);
|
Data (const Data&);
|
||||||
@ -126,8 +127,6 @@ namespace CSMWorld
|
|||||||
|
|
||||||
const Data& self ();
|
const Data& self ();
|
||||||
|
|
||||||
void clearNpcStatsCache ();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager);
|
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager);
|
||||||
@ -262,6 +261,8 @@ namespace CSMWorld
|
|||||||
void merge();
|
void merge();
|
||||||
///< Merge modified into base.
|
///< Merge modified into base.
|
||||||
|
|
||||||
|
int getTotalRecords (const std::vector<boost::filesystem::path>& files); // for better loading bar
|
||||||
|
|
||||||
int startLoading (const boost::filesystem::path& path, bool base, bool project);
|
int startLoading (const boost::filesystem::path& path, bool base, bool project);
|
||||||
///< Begin merging content of a file into base or modified.
|
///< Begin merging content of a file into base or modified.
|
||||||
///
|
///
|
||||||
@ -282,37 +283,17 @@ namespace CSMWorld
|
|||||||
int count (RecordBase::State state) const;
|
int count (RecordBase::State state) const;
|
||||||
///< Return number of top-level records with the given \a state.
|
///< Return number of top-level records with the given \a state.
|
||||||
|
|
||||||
NpcStats* npcAutoCalculate (const ESM::NPC& npc) const;
|
const NpcAutoCalc& getNpcAutoCalc() const;
|
||||||
|
|
||||||
NpcStats* getCachedNpcData (const std::string& id) const;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void idListChanged();
|
void idListChanged();
|
||||||
|
|
||||||
// refresh NPC dialogue subviews via object table model
|
|
||||||
void updateNpcAutocalc (int type, const std::string& id);
|
|
||||||
|
|
||||||
void cacheNpcStats (const std::string& id, NpcStats *stats) const;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||||
|
|
||||||
void rowsChanged (const QModelIndex& parent, int start, int end);
|
void rowsChanged (const QModelIndex& parent, int start, int end);
|
||||||
|
|
||||||
// for autocalc updates when gmst/race/class/skils tables change
|
|
||||||
void gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
|
||||||
|
|
||||||
void raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
|
||||||
|
|
||||||
void classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
|
||||||
|
|
||||||
void skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
|
||||||
|
|
||||||
void npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
|
||||||
|
|
||||||
void cacheNpcStatsEvent (const std::string& id, NpcStats *stats);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,9 +66,9 @@ namespace CSMWorld
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Record<ESXRecordT> baseRecord = this->getRecord (index);
|
std::unique_ptr<Record<ESXRecordT> > baseRecord(new Record<ESXRecordT>(this->getRecord(index)));
|
||||||
baseRecord.mState = RecordBase::State_Deleted;
|
baseRecord->mState = RecordBase::State_Deleted;
|
||||||
this->setRecord (index, baseRecord);
|
this->setRecord(index, std::move(baseRecord));
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,30 +79,31 @@ namespace CSMWorld
|
|||||||
int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,
|
int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
if (index==-2)
|
if (index==-2) // index unknown
|
||||||
index = this->searchId (IdAccessorT().getId (record));
|
index = this->searchId (IdAccessorT().getId (record));
|
||||||
|
|
||||||
if (index==-1)
|
if (index==-1)
|
||||||
{
|
{
|
||||||
// new record
|
// new record
|
||||||
Record<ESXRecordT> record2;
|
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
|
||||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
(base ? record2.mBase : record2.mModified) = record;
|
(base ? record2->mBase : record2->mModified) = record;
|
||||||
|
|
||||||
index = this->getSize();
|
index = this->getSize();
|
||||||
this->appendRecord (record2);
|
this->appendRecord(std::move(record2));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// old record
|
// old record
|
||||||
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
std::unique_ptr<Record<ESXRecordT> > record2(
|
||||||
|
new Record<ESXRecordT>(Collection<ESXRecordT, IdAccessorT>::getRecord(index)));
|
||||||
|
|
||||||
if (base)
|
if (base)
|
||||||
record2.mBase = record;
|
record2->mBase = record;
|
||||||
else
|
else
|
||||||
record2.setModified (record);
|
record2->setModified(record);
|
||||||
|
|
||||||
this->setRecord (index, record2);
|
this->setRecord(index, std::move(record2));
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
@ -116,7 +117,7 @@ namespace CSMWorld
|
|||||||
if (index==-1)
|
if (index==-1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
const Record<ESXRecordT>& record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
||||||
|
|
||||||
if (record.isDeleted())
|
if (record.isDeleted())
|
||||||
return false;
|
return false;
|
||||||
@ -127,8 +128,10 @@ namespace CSMWorld
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
record.mState = RecordBase::State_Deleted;
|
std::unique_ptr<Record<ESXRecordT> > record2(
|
||||||
this->setRecord (index, record);
|
new Record<ESXRecordT>(Collection<ESXRecordT, IdAccessorT>::getRecord(index)));
|
||||||
|
record2->mState = RecordBase::State_Deleted;
|
||||||
|
this->setRecord(index, std::move(record2));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -163,26 +163,37 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin,
|
|||||||
///This method can return only indexes to the top level table cells
|
///This method can return only indexes to the top level table cells
|
||||||
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
|
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
|
||||||
{
|
{
|
||||||
return index(mIdCollection->getIndex (id), column);
|
int row = mIdCollection->searchId (id);
|
||||||
|
if (row != -1)
|
||||||
|
return index(row, column);
|
||||||
|
else
|
||||||
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type)
|
void CSMWorld::IdTable::setRecord (const std::string& id,
|
||||||
|
std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type)
|
||||||
{
|
{
|
||||||
int index = mIdCollection->searchId (id);
|
int index = mIdCollection->searchId (id);
|
||||||
|
|
||||||
if (index==-1)
|
if (index==-1)
|
||||||
{
|
{
|
||||||
int index = mIdCollection->getAppendIndex (id, type);
|
// For info records, appendRecord may use a different index than the one returned by
|
||||||
|
// getAppendIndex (because of prev/next links). This can result in the display not
|
||||||
|
// updating correctly after an undo
|
||||||
|
//
|
||||||
|
// Use an alternative method to get the correct index. For non-Info records the
|
||||||
|
// record pointer is ignored and internally calls getAppendIndex.
|
||||||
|
int index2 = mIdCollection->getInsertIndex (id, type, record.get());
|
||||||
|
|
||||||
beginInsertRows (QModelIndex(), index, index);
|
beginInsertRows (QModelIndex(), index2, index2);
|
||||||
|
|
||||||
mIdCollection->appendRecord (record, type);
|
mIdCollection->appendRecord (std::move(record), type);
|
||||||
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mIdCollection->replace (index, record);
|
mIdCollection->replace (index, std::move(record));
|
||||||
emit dataChanged (CSMWorld::IdTable::index (index, 0),
|
emit dataChanged (CSMWorld::IdTable::index (index, 0),
|
||||||
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
|
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define CSM_WOLRD_IDTABLE_H
|
#define CSM_WOLRD_IDTABLE_H
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "idtablebase.hpp"
|
#include "idtablebase.hpp"
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
@ -59,7 +60,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
|
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
|
||||||
|
|
||||||
void setRecord (const std::string& id, const RecordBase& record,
|
void setRecord (const std::string& id, std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type = UniversalId::Type_None);
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
///< Add record or overwrite existing recrod.
|
///< Add record or overwrite existing recrod.
|
||||||
|
|
||||||
|
@ -8,83 +8,165 @@
|
|||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
void Collection<Info, IdAccessor<Info> >::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count);
|
||||||
|
|
||||||
|
// index map is updated in InfoCollection::removeRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<Info, IdAccessor<Info> >::insertRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
int index, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int size = static_cast<int>(mRecords.size());
|
||||||
|
if (index < 0 || index > size)
|
||||||
|
throw std::runtime_error("index out of range");
|
||||||
|
|
||||||
|
std::unique_ptr<Record<Info> > record2(static_cast<Record<Info>*>(record.release()));
|
||||||
|
|
||||||
|
if (index == size)
|
||||||
|
mRecords.push_back(std::move(record2));
|
||||||
|
else
|
||||||
|
mRecords.insert(mRecords.begin()+index, std::move(record2));
|
||||||
|
|
||||||
|
// index map is updated in InfoCollection::insertRecord()
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool Collection<Info, IdAccessor<Info> >::reorderRowsImp (int baseIndex,
|
||||||
|
const std::vector<int>& newOrder)
|
||||||
|
{
|
||||||
|
if (!newOrder.empty())
|
||||||
|
{
|
||||||
|
int size = static_cast<int>(newOrder.size());
|
||||||
|
|
||||||
|
// check that all indices are present
|
||||||
|
std::vector<int> test(newOrder);
|
||||||
|
std::sort(test.begin(), test.end());
|
||||||
|
if (*test.begin() != 0 || *--test.end() != size-1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// reorder records
|
||||||
|
std::vector<std::unique_ptr<Record<Info> > > buffer(size);
|
||||||
|
|
||||||
|
// FIXME: BUG: undo does not remove modified flag
|
||||||
|
for (int i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
buffer[newOrder[i]] = std::move(mRecords[baseIndex+i]);
|
||||||
|
buffer[newOrder[i]]->setModified(buffer[newOrder[i]]->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::move(buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
|
||||||
|
|
||||||
|
// index map is updated in InfoCollection::reorderRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CSMWorld::InfoCollection::load (const Info& record, bool base)
|
void CSMWorld::InfoCollection::load (const Info& record, bool base)
|
||||||
{
|
{
|
||||||
int index = searchId (record.mId);
|
int index = searchId(record.mId);
|
||||||
|
|
||||||
if (index==-1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
// new record
|
// new record
|
||||||
Record<Info> record2;
|
std::unique_ptr<Record<Info> > record2(new Record<Info>);
|
||||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
(base ? record2.mBase : record2.mModified) = record;
|
(base ? record2->mBase : record2->mModified) = record;
|
||||||
|
|
||||||
int index = -1;
|
appendRecord(std::move(record2));
|
||||||
|
|
||||||
std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId);
|
|
||||||
|
|
||||||
if (!record2.get().mPrev.empty())
|
|
||||||
{
|
|
||||||
index = getInfoIndex (record2.get().mPrev, topic);
|
|
||||||
|
|
||||||
if (index!=-1)
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index==-1 && !record2.get().mNext.empty())
|
|
||||||
{
|
|
||||||
index = getInfoIndex (record2.get().mNext, topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index==-1)
|
|
||||||
{
|
|
||||||
Range range = getTopicRange (topic);
|
|
||||||
|
|
||||||
index = std::distance (getRecords().begin(), range.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertRecord (record2, index);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// old record
|
// old record
|
||||||
Record<Info> record2 = getRecord (index);
|
std::unique_ptr<Record<Info> > record2(new Record<Info>(getRecord(index)));
|
||||||
|
|
||||||
if (base)
|
if (base)
|
||||||
record2.mBase = record;
|
record2->mBase = record;
|
||||||
else
|
else
|
||||||
record2.setModified (record);
|
record2->setModified (record);
|
||||||
|
|
||||||
setRecord (index, record2);
|
setRecord (index, std::move(record2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const
|
int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const
|
||||||
{
|
{
|
||||||
std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id;
|
// find the topic first
|
||||||
|
std::map<StringHash, std::vector<std::pair<std::string, int> > >::const_iterator iter
|
||||||
|
= mInfoIndex.find(StringHash(std::make_shared<std::string>(Misc::StringUtils::lowerCase(topic))));
|
||||||
|
|
||||||
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic);
|
if (iter == mInfoIndex.end())
|
||||||
|
return -1;
|
||||||
|
|
||||||
for (; range.first!=range.second; ++range.first)
|
// brute force loop
|
||||||
if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId))
|
std::string lowerId = Misc::StringUtils::lowerCase (id);
|
||||||
return std::distance (getRecords().begin(), range.first);
|
for (std::vector<std::pair<std::string, int> >::const_iterator it = iter->second.begin();
|
||||||
|
it != iter->second.end(); ++it)
|
||||||
|
{
|
||||||
|
if (Misc::StringUtils::cEqual(it->first, lowerId))
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const
|
// Calling insertRecord() using index from getInsertIndex() needs to take into account of
|
||||||
|
// prev/next records; an example is deleting a record then undo
|
||||||
|
int CSMWorld::InfoCollection::getInsertIndex (const std::string& id,
|
||||||
|
UniversalId::Type type, RecordBase *record) const
|
||||||
{
|
{
|
||||||
std::string::size_type separator = id.find_last_of ('#');
|
if (record == nullptr)
|
||||||
|
{
|
||||||
|
std::string::size_type separator = id.find_last_of('#');
|
||||||
|
|
||||||
if (separator==std::string::npos)
|
if (separator == std::string::npos)
|
||||||
throw std::runtime_error ("invalid info ID: " + id);
|
throw std::runtime_error("invalid info ID: " + id);
|
||||||
|
|
||||||
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (id.substr (0, separator));
|
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange(id.substr(0, separator));
|
||||||
|
|
||||||
if (range.first==range.second)
|
if (range.first == range.second)
|
||||||
return Collection<Info, IdAccessor<Info> >::getAppendIndex (id, type);
|
return Collection<Info, IdAccessor<Info> >::getAppendIndex(id, type);
|
||||||
|
|
||||||
return std::distance (getRecords().begin(), range.second);
|
return std::distance(getRecords().begin(), range.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
const Info& info = static_cast<Record<Info>*>(record)->get();
|
||||||
|
std::string topic = info.mTopicId;
|
||||||
|
|
||||||
|
// if the record has a prev, find its index value
|
||||||
|
if (!info.mPrev.empty())
|
||||||
|
{
|
||||||
|
index = getInfoIndex(info.mPrev, topic);
|
||||||
|
|
||||||
|
if (index != -1)
|
||||||
|
++index; // if prev exists, set current index to one above prev
|
||||||
|
}
|
||||||
|
|
||||||
|
// if prev doesn't exist or not found and the record has a next, find its index value
|
||||||
|
if (index == -1 && !info.mNext.empty())
|
||||||
|
{
|
||||||
|
// if next exists, use its index as the current index
|
||||||
|
index = getInfoIndex(info.mNext, topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if next doesn't exist or not found (i.e. neither exist yet) then start a new one
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
Range range = getTopicRange(topic); // getTopicRange converts topic to lower case first
|
||||||
|
|
||||||
|
index = std::distance(getRecords().begin(), range.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
|
bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
|
||||||
@ -101,7 +183,23 @@ bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// reorder
|
// reorder
|
||||||
return reorderRowsImp (baseIndex, newOrder);
|
if (!Collection<Info, IdAccessor<Info> >::reorderRowsImp(baseIndex, newOrder))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// adjust index
|
||||||
|
int size = static_cast<int>(newOrder.size());
|
||||||
|
for (std::map<StringHash, std::vector<std::pair<std::string, int> > >::iterator iter
|
||||||
|
= mInfoIndex.begin(); iter != mInfoIndex.end(); ++iter)
|
||||||
|
{
|
||||||
|
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
|
||||||
|
it != iter->second.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->second >= baseIndex && it->second < baseIndex+size)
|
||||||
|
it->second = newOrder.at(it->second-baseIndex)+baseIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
|
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
|
||||||
@ -116,7 +214,7 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
|
|||||||
{
|
{
|
||||||
int index = searchId (id);
|
int index = searchId (id);
|
||||||
|
|
||||||
if (index==-1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
// deleting a record that does not exist
|
// deleting a record that does not exist
|
||||||
// ignore it for now
|
// ignore it for now
|
||||||
@ -128,9 +226,9 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Record<Info> record = getRecord (index);
|
std::unique_ptr<Record<Info> > record(new Record<Info>(getRecord(index)));
|
||||||
record.mState = RecordBase::State_Deleted;
|
record->mState = RecordBase::State_Deleted;
|
||||||
setRecord (index, record);
|
setRecord (index, std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -144,73 +242,54 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
|
|||||||
CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)
|
CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
std::string topic2 = Misc::StringUtils::lowerCase (topic);
|
std::string lowerTopic = Misc::StringUtils::lowerCase (topic);
|
||||||
|
|
||||||
std::map<std::string, int>::const_iterator iter = getIdMap().lower_bound (topic2);
|
// find the topic
|
||||||
|
std::map<StringHash, std::vector<std::pair<std::string, int> > >::const_iterator iter
|
||||||
|
= mInfoIndex.find(StringHash(std::make_shared<std::string>(lowerTopic)));
|
||||||
|
|
||||||
// Skip invalid records: The beginning of a topic string could be identical to another topic
|
if (iter == mInfoIndex.end())
|
||||||
// string.
|
|
||||||
for (; iter!=getIdMap().end(); ++iter)
|
|
||||||
{
|
|
||||||
std::string testTopicId =
|
|
||||||
Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId);
|
|
||||||
|
|
||||||
if (testTopicId==topic2)
|
|
||||||
break;
|
|
||||||
|
|
||||||
std::size_t size = topic2.size();
|
|
||||||
|
|
||||||
if (testTopicId.size()<size || testTopicId.substr (0, size)!=topic2)
|
|
||||||
return Range (getRecords().end(), getRecords().end());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iter==getIdMap().end())
|
|
||||||
return Range (getRecords().end(), getRecords().end());
|
return Range (getRecords().end(), getRecords().end());
|
||||||
|
|
||||||
RecordConstIterator begin = getRecords().begin()+iter->second;
|
// topic found, find the starting index
|
||||||
|
int low = INT_MAX;
|
||||||
while (begin != getRecords().begin())
|
for (std::vector<std::pair<std::string, int> >::const_iterator it = iter->second.begin();
|
||||||
|
it != iter->second.end(); ++it)
|
||||||
{
|
{
|
||||||
if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2))
|
low = std::min(low, it->second);
|
||||||
{
|
|
||||||
// we've gone one too far, go back
|
|
||||||
++begin;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
--begin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find end
|
RecordConstIterator begin = getRecords().begin() + low;
|
||||||
RecordConstIterator end = begin;
|
|
||||||
|
|
||||||
for (; end!=getRecords().end(); ++end)
|
// Find end (one past the range)
|
||||||
if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2))
|
RecordConstIterator end = begin + iter->second.size();
|
||||||
break;
|
if (end != getRecords().end())
|
||||||
|
++end;
|
||||||
|
|
||||||
return Range (begin, end);
|
return Range (begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId)
|
void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId)
|
||||||
{
|
{
|
||||||
std::string id = Misc::StringUtils::lowerCase(dialogueId);
|
|
||||||
std::vector<int> erasedRecords;
|
std::vector<int> erasedRecords;
|
||||||
|
|
||||||
std::map<std::string, int>::const_iterator current = getIdMap().lower_bound(id);
|
Range range = getTopicRange(dialogueId); // getTopicRange converts dialogueId to lower case first
|
||||||
std::map<std::string, int>::const_iterator end = getIdMap().end();
|
|
||||||
for (; current != end; ++current)
|
for (; range.first != range.second; ++range.first)
|
||||||
{
|
{
|
||||||
Record<Info> record = getRecord(current->second);
|
const Record<Info>& record = **range.first;
|
||||||
|
|
||||||
if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))
|
if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))
|
||||||
{
|
{
|
||||||
if (record.mState == RecordBase::State_ModifiedOnly)
|
if (record.mState == RecordBase::State_ModifiedOnly)
|
||||||
{
|
{
|
||||||
erasedRecords.push_back(current->second);
|
erasedRecords.push_back(range.first - getRecords().begin());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
record.mState = RecordBase::State_Deleted;
|
std::unique_ptr<Record<Info> > record2(new Record<Info>(record));
|
||||||
setRecord(current->second, record);
|
record2->mState = RecordBase::State_Deleted;
|
||||||
|
setRecord(range.first - getRecords().begin(), std::move(record2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -225,3 +304,105 @@ void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId
|
|||||||
erasedRecords.pop_back();
|
erasedRecords.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: removing a record should adjust prev/next and mark those records as modified
|
||||||
|
// accordingly (also consider undo)
|
||||||
|
void CSMWorld::InfoCollection::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
Collection<Info, IdAccessor<Info> >::removeRows(index, count); // erase records only
|
||||||
|
|
||||||
|
for (std::map<StringHash, std::vector<std::pair<std::string, int> > >::iterator iter
|
||||||
|
= mInfoIndex.begin(); iter != mInfoIndex.end();)
|
||||||
|
{
|
||||||
|
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
|
||||||
|
it != iter->second.end();)
|
||||||
|
{
|
||||||
|
if (it->second >= index)
|
||||||
|
{
|
||||||
|
if (it->second >= index+count)
|
||||||
|
{
|
||||||
|
it->second -= count;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
iter->second.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for an empty vector
|
||||||
|
if (iter->second.empty())
|
||||||
|
mInfoIndex.erase(iter++);
|
||||||
|
else
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Record<Info> > record2(new Record<Info>);
|
||||||
|
|
||||||
|
record2->mState = Record<Info>::State_ModifiedOnly;
|
||||||
|
record2->mModified.blank();
|
||||||
|
|
||||||
|
record2->get().mId = id;
|
||||||
|
|
||||||
|
insertRecord(std::move(record2), getInsertIndex(id, type, nullptr), type); // call InfoCollection::insertRecord()
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::InfoCollection::searchId (const std::string& id) const
|
||||||
|
{
|
||||||
|
std::string::size_type separator = id.find_last_of('#');
|
||||||
|
|
||||||
|
if (separator == std::string::npos)
|
||||||
|
throw std::runtime_error("invalid info ID: " + id);
|
||||||
|
|
||||||
|
return getInfoIndex(id.substr(separator+1), id.substr(0, separator));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoCollection::appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int index = getInsertIndex(static_cast<Record<Info>*>(record.get())->get().mId, type, record.get());
|
||||||
|
|
||||||
|
insertRecord(std::move(record), index, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoCollection::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
|
UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int size = static_cast<int>(getRecords().size());
|
||||||
|
|
||||||
|
std::string id = static_cast<Record<Info>*>(record.get())->get().mId;
|
||||||
|
std::string::size_type separator = id.find_last_of('#');
|
||||||
|
|
||||||
|
if (separator == std::string::npos)
|
||||||
|
throw std::runtime_error("invalid info ID: " + id);
|
||||||
|
|
||||||
|
Collection<Info, IdAccessor<Info> >::insertRecord(std::move(record), index, type); // add records only
|
||||||
|
|
||||||
|
// adjust index
|
||||||
|
if (index < size-1)
|
||||||
|
{
|
||||||
|
for (std::map<StringHash, std::vector<std::pair<std::string, int> > >::iterator iter
|
||||||
|
= mInfoIndex.begin(); iter != mInfoIndex.end(); ++iter)
|
||||||
|
{
|
||||||
|
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
|
||||||
|
it != iter->second.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->second >= index)
|
||||||
|
++(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get iterator for existing topic or a new topic
|
||||||
|
std::string lowerId = Misc::StringUtils::lowerCase(id);
|
||||||
|
std::pair<std::map<StringHash, std::vector<std::pair<std::string, int> > >::iterator, bool> res
|
||||||
|
= mInfoIndex.insert(
|
||||||
|
std::make_pair(StringHash(std::make_shared<std::string>(lowerId.substr(0, separator))),
|
||||||
|
std::vector<std::pair<std::string, int> >())); // empty vector
|
||||||
|
|
||||||
|
// insert info and index
|
||||||
|
res.first->second.push_back(std::make_pair(lowerId.substr(separator+1), index));
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef CSM_WOLRD_INFOCOLLECTION_H
|
#ifndef CSM_WOLRD_INFOCOLLECTION_H
|
||||||
#define CSM_WOLRD_INFOCOLLECTION_H
|
#define CSM_WOLRD_INFOCOLLECTION_H
|
||||||
|
|
||||||
|
#include <extern/murmurhash/MurmurHash2.h>
|
||||||
|
|
||||||
#include "collection.hpp"
|
#include "collection.hpp"
|
||||||
#include "info.hpp"
|
#include "info.hpp"
|
||||||
|
|
||||||
@ -11,27 +13,83 @@ namespace ESM
|
|||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
|
struct StringHash
|
||||||
|
{
|
||||||
|
uint64_t mHash;
|
||||||
|
std::shared_ptr<std::string> mString;
|
||||||
|
|
||||||
|
StringHash (std::shared_ptr<std::string> str) : mString(str)
|
||||||
|
{
|
||||||
|
mHash = MurmurHash64A(str->c_str(), str->size(), /*seed*/1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<> struct less<CSMWorld::StringHash>
|
||||||
|
{
|
||||||
|
bool operator() (const CSMWorld::StringHash& lhs, const CSMWorld::StringHash& rhs) const
|
||||||
|
{
|
||||||
|
if (lhs.mHash < rhs.mHash)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (lhs.mHash > rhs.mHash)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return *lhs.mString < *rhs.mString;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
void Collection<Info, IdAccessor<Info> >::removeRows (int index, int count);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<Info, IdAccessor<Info> >::insertRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
int index, UniversalId::Type type);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool Collection<Info, IdAccessor<Info> >::reorderRowsImp (int baseIndex,
|
||||||
|
const std::vector<int>& newOrder);
|
||||||
|
|
||||||
class InfoCollection : public Collection<Info, IdAccessor<Info> >
|
class InfoCollection : public Collection<Info, IdAccessor<Info> >
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef std::vector<Record<Info> >::const_iterator RecordConstIterator;
|
typedef std::vector<std::unique_ptr<Record<Info> > >::const_iterator RecordConstIterator;
|
||||||
typedef std::pair<RecordConstIterator, RecordConstIterator> Range;
|
typedef std::pair<RecordConstIterator, RecordConstIterator> Range;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
// The general strategy is to keep the records in Collection kept in order (within
|
||||||
|
// a topic group) while the index lookup maps are not ordered. It is assumed that
|
||||||
|
// each topic has a small number of infos, which allows the use of vectors for
|
||||||
|
// iterating through them without too much penalty.
|
||||||
|
//
|
||||||
|
// NOTE: hashed topic string as well as id string are stored in lower case.
|
||||||
|
std::map<StringHash, std::vector<std::pair<std::string, int> > > mInfoIndex;
|
||||||
|
|
||||||
void load (const Info& record, bool base);
|
void load (const Info& record, bool base);
|
||||||
|
|
||||||
int getInfoIndex (const std::string& id, const std::string& topic) const;
|
int getInfoIndex (const std::string& id, const std::string& topic) const;
|
||||||
///< Return index for record \a id or -1 (if not present; deleted records are considered)
|
///< Return index for record \a id or -1 (if not present; deleted records are considered)
|
||||||
///
|
///
|
||||||
/// \param id info ID without topic prefix
|
/// \param id info ID without topic prefix
|
||||||
|
//
|
||||||
|
/// \attention id and topic are assumed to be in lower case
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual int getAppendIndex (const std::string& id,
|
virtual int getInsertIndex (const std::string& id,
|
||||||
UniversalId::Type type = UniversalId::Type_None) const;
|
UniversalId::Type type = UniversalId::Type_None,
|
||||||
|
RecordBase *record = nullptr) const;
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
///
|
||||||
|
/// Works like getAppendIndex unless an overloaded method uses the record pointer
|
||||||
|
/// to get additional info about the record that results in an alternative index.
|
||||||
|
|
||||||
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder);
|
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder);
|
||||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||||
@ -46,6 +104,20 @@ namespace CSMWorld
|
|||||||
/// the given topic.
|
/// the given topic.
|
||||||
|
|
||||||
void removeDialogueInfos(const std::string& dialogueId);
|
void removeDialogueInfos(const std::string& dialogueId);
|
||||||
|
|
||||||
|
virtual void removeRows (int index, int count);
|
||||||
|
|
||||||
|
virtual void appendBlankRecord (const std::string& id,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
||||||
|
virtual int searchId (const std::string& id) const;
|
||||||
|
|
||||||
|
virtual void appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
||||||
|
virtual void insertRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
int index,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,23 +90,23 @@ namespace CSMWorld
|
|||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position)
|
void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record;
|
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
|
||||||
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(record, position);
|
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(*record, position);
|
||||||
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
|
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow)
|
void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record;
|
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
|
||||||
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(record, subRow);
|
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(*record, subRow);
|
||||||
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
|
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
@ -121,13 +121,13 @@ namespace CSMWorld
|
|||||||
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row,
|
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row,
|
||||||
int column, const QVariant& data, int subRow, int subColumn)
|
int column, const QVariant& data, int subRow, int subColumn)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record;
|
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
|
||||||
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData(
|
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData(
|
||||||
record, data, subRow, subColumn);
|
*record, data, subRow, subColumn);
|
||||||
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
|
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
@ -142,13 +142,13 @@ namespace CSMWorld
|
|||||||
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row,
|
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row,
|
||||||
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
|
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record;
|
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
|
||||||
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable(
|
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable(
|
||||||
record, nestedTable);
|
*record, nestedTable);
|
||||||
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
|
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
@ -35,22 +35,22 @@ namespace CSMWorld
|
|||||||
|
|
||||||
void NestedInfoCollection::addNestedRow(int row, int column, int position)
|
void NestedInfoCollection::addNestedRow(int row, int column, int position)
|
||||||
{
|
{
|
||||||
Record<Info> record;
|
std::unique_ptr<Record<Info> > record(new Record<Info>);
|
||||||
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(record, position);
|
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(*record, position);
|
||||||
|
|
||||||
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
|
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NestedInfoCollection::removeNestedRows(int row, int column, int subRow)
|
void NestedInfoCollection::removeNestedRows(int row, int column, int subRow)
|
||||||
{
|
{
|
||||||
Record<Info> record;
|
std::unique_ptr<Record<Info> > record(new Record<Info>);
|
||||||
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(record, subRow);
|
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(*record, subRow);
|
||||||
|
|
||||||
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
|
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant NestedInfoCollection::getNestedData (int row,
|
QVariant NestedInfoCollection::getNestedData (int row,
|
||||||
@ -63,13 +63,13 @@ namespace CSMWorld
|
|||||||
void NestedInfoCollection::setNestedData(int row,
|
void NestedInfoCollection::setNestedData(int row,
|
||||||
int column, const QVariant& data, int subRow, int subColumn)
|
int column, const QVariant& data, int subRow, int subColumn)
|
||||||
{
|
{
|
||||||
Record<Info> record;
|
std::unique_ptr<Record<Info> > record(new Record<Info>);
|
||||||
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData(
|
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData(
|
||||||
record, data, subRow, subColumn);
|
*record, data, subRow, subColumn);
|
||||||
|
|
||||||
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
|
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row,
|
CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row,
|
||||||
@ -82,13 +82,13 @@ namespace CSMWorld
|
|||||||
void NestedInfoCollection::setNestedTable(int row,
|
void NestedInfoCollection::setNestedTable(int row,
|
||||||
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
|
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
|
||||||
{
|
{
|
||||||
Record<Info> record;
|
std::unique_ptr<Record<Info> > record(new Record<Info>);
|
||||||
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable(
|
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable(
|
||||||
record, nestedTable);
|
*record, nestedTable);
|
||||||
|
|
||||||
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
|
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
int NestedInfoCollection::getNestedRowsCount(int row, int column) const
|
int NestedInfoCollection::getNestedRowsCount(int row, int column) const
|
||||||
|
367
apps/opencs/model/world/npcautocalc.cpp
Normal file
367
apps/opencs/model/world/npcautocalc.cpp
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
#include "npcautocalc.hpp"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include <components/autocalc/autocalc.hpp>
|
||||||
|
#include <components/autocalc/autocalcspell.hpp>
|
||||||
|
#include <components/autocalc/store.hpp>
|
||||||
|
|
||||||
|
#include "npcstats.hpp"
|
||||||
|
#include "data.hpp"
|
||||||
|
#include "idtable.hpp"
|
||||||
|
#include "idtree.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class CSStore : public AutoCalc::StoreCommon
|
||||||
|
{
|
||||||
|
const CSMWorld::IdCollection<ESM::GameSetting>& mGmstTable;
|
||||||
|
const CSMWorld::IdCollection<ESM::Skill>& mSkillTable;
|
||||||
|
const CSMWorld::IdCollection<ESM::MagicEffect>& mMagicEffectTable;
|
||||||
|
const CSMWorld::NestedIdCollection<ESM::Spell>& mSpells;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
CSStore(const CSMWorld::IdCollection<ESM::GameSetting>& gmst,
|
||||||
|
const CSMWorld::IdCollection<ESM::Skill>& skills,
|
||||||
|
const CSMWorld::IdCollection<ESM::MagicEffect>& magicEffects,
|
||||||
|
const CSMWorld::NestedIdCollection<ESM::Spell>& spells)
|
||||||
|
: mGmstTable(gmst), mSkillTable(skills), mMagicEffectTable(magicEffects), mSpells(spells)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~CSStore() {}
|
||||||
|
|
||||||
|
virtual int findGmstInt(const std::string& name) const
|
||||||
|
{
|
||||||
|
return mGmstTable.getRecord(name).get().getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual float findGmstFloat(const std::string& name) const
|
||||||
|
{
|
||||||
|
return mGmstTable.getRecord(name).get().getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const ESM::Skill *findSkill(int index) const
|
||||||
|
{
|
||||||
|
// if the skill does not exist, throws std::runtime_error ("invalid ID: " + id)
|
||||||
|
return &mSkillTable.getRecord(ESM::Skill::indexToId(index)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const ESM::MagicEffect* findMagicEffect(int id) const
|
||||||
|
{
|
||||||
|
// if the magic effect does not exist, throws std::runtime_error ("invalid ID: " + id)
|
||||||
|
return &mMagicEffectTable.getRecord(ESM::MagicEffect::indexToId((short)id)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void getSpells(std::vector<ESM::Spell*>& spells)
|
||||||
|
{
|
||||||
|
// prepare data in a format used by OpenMW store
|
||||||
|
for (int index = 0; index < mSpells.getSize(); ++index)
|
||||||
|
spells.push_back(const_cast<ESM::Spell *>(&mSpells.getRecord(index).get()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned short autoCalculateMana(const AutoCalc::StatsBase& stats)
|
||||||
|
{
|
||||||
|
return stats.getBaseAttribute(ESM::Attribute::Intelligence) * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short autoCalculateFatigue(const AutoCalc::StatsBase& stats)
|
||||||
|
{
|
||||||
|
return stats.getBaseAttribute(ESM::Attribute::Strength)
|
||||||
|
+ stats.getBaseAttribute(ESM::Attribute::Willpower)
|
||||||
|
+ stats.getBaseAttribute(ESM::Attribute::Agility)
|
||||||
|
+ stats.getBaseAttribute(ESM::Attribute::Endurance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::NpcAutoCalc::NpcAutoCalc (const Data& data,
|
||||||
|
const IdTable *gmsts, const IdTable *skills, const IdTable *classes, const IdTree *races, const IdTree *objects)
|
||||||
|
: mData(data), mSkillModel(skills), mClassModel(classes), mRaceModel(races)
|
||||||
|
{
|
||||||
|
// for autocalc updates when gmst/race/class/skils tables change
|
||||||
|
connect (gmsts, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT (gmstDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||||
|
connect (mSkillModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT (skillDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||||
|
connect (mClassModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT (classDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||||
|
connect (mRaceModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT (raceDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||||
|
connect (objects, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT (npcDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||||
|
connect (this, SIGNAL (updateNpcAutocalc (int, const std::string&)),
|
||||||
|
objects, SLOT (updateNpcAutocalc (int, const std::string&)));
|
||||||
|
|
||||||
|
//connect (this, SIGNAL (cacheNpcStats (const std::string&, NpcStats*)),
|
||||||
|
//this, SLOT (cacheNpcStatsEvent (const std::string&, NpcStats*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::NpcAutoCalc::~NpcAutoCalc()
|
||||||
|
{
|
||||||
|
clearNpcStatsCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::NpcAutoCalc::skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||||
|
{
|
||||||
|
// mData.mAttribute (affects attributes skill bonus autocalc)
|
||||||
|
// mData.mSpecialization (affects skills autocalc)
|
||||||
|
int attributeColumn = mSkillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute);
|
||||||
|
int specialisationColumn = mSkillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
|
||||||
|
|
||||||
|
if ((topLeft.column() <= attributeColumn && attributeColumn <= bottomRight.column())
|
||||||
|
|| (topLeft.column() <= specialisationColumn && specialisationColumn <= bottomRight.column()))
|
||||||
|
{
|
||||||
|
clearNpcStatsCache();
|
||||||
|
|
||||||
|
std::string empty;
|
||||||
|
emit updateNpcAutocalc(0/*all*/, empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::NpcAutoCalc::classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||||
|
{
|
||||||
|
// update autocalculated attributes/skills of every NPC with matching class
|
||||||
|
// - mData.mAttribute[2]
|
||||||
|
// - mData.mSkills[5][2]
|
||||||
|
// - mData.mSpecialization
|
||||||
|
int attribute1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute1); // +1
|
||||||
|
int majorSkill1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_MajorSkill1); // +4
|
||||||
|
int minorSkill1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_MinorSkill1); // +4
|
||||||
|
int specialisationColumn = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
|
||||||
|
|
||||||
|
if ((topLeft.column() > attribute1Column+1 || attribute1Column > bottomRight.column())
|
||||||
|
&& (topLeft.column() > majorSkill1Column+4 || majorSkill1Column > bottomRight.column())
|
||||||
|
&& (topLeft.column() > minorSkill1Column+4 || minorSkill1Column > bottomRight.column())
|
||||||
|
&& (topLeft.column() > specialisationColumn || specialisationColumn > bottomRight.column()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the affected class
|
||||||
|
int idColumn = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||||
|
for (int classRow = topLeft.row(); classRow <= bottomRight.row(); ++classRow)
|
||||||
|
{
|
||||||
|
clearNpcStatsCache();
|
||||||
|
|
||||||
|
std::string classId =
|
||||||
|
mClassModel->data(mClassModel->index(classRow, idColumn)).toString().toUtf8().constData();
|
||||||
|
emit updateNpcAutocalc(1/*class*/, classId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::NpcAutoCalc::raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||||
|
{
|
||||||
|
// affects racial bonus attributes & skills
|
||||||
|
// - mData.mAttributeValues[]
|
||||||
|
// - mData.mBonus[].mBonus
|
||||||
|
// - mPowers.mList[]
|
||||||
|
int attrColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceAttributes);
|
||||||
|
int bonusColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceSkillBonus);
|
||||||
|
int powersColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_PowerList);
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
int raceRow = topLeft.row();
|
||||||
|
int raceEnd = bottomRight.row();
|
||||||
|
if (topLeft.parent().isValid() && bottomRight.parent().isValid())
|
||||||
|
{
|
||||||
|
if ((topLeft.parent().column() <= attrColumn && attrColumn <= bottomRight.parent().column())
|
||||||
|
|| (topLeft.parent().column() <= bonusColumn && bonusColumn <= bottomRight.parent().column())
|
||||||
|
|| (topLeft.parent().column() <= powersColumn && powersColumn <= bottomRight.parent().column()))
|
||||||
|
{
|
||||||
|
match = true; // TODO: check for specific nested column?
|
||||||
|
raceRow = topLeft.parent().row();
|
||||||
|
raceEnd = bottomRight.parent().row();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((topLeft.column() <= attrColumn && attrColumn <= bottomRight.column())
|
||||||
|
|| (topLeft.column() <= bonusColumn && bonusColumn <= bottomRight.column())
|
||||||
|
|| (topLeft.column() <= powersColumn && powersColumn <= bottomRight.column()))
|
||||||
|
{
|
||||||
|
match = true; // maybe the whole table changed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// update autocalculated attributes/skills of every NPC with matching race
|
||||||
|
int idColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||||
|
for (; raceRow <= raceEnd; ++raceRow)
|
||||||
|
{
|
||||||
|
clearNpcStatsCache();
|
||||||
|
|
||||||
|
std::string raceId =
|
||||||
|
mRaceModel->data(mRaceModel->index(raceRow, idColumn)).toString().toUtf8().constData();
|
||||||
|
emit updateNpcAutocalc(2/*race*/, raceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::NpcAutoCalc::npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||||
|
{
|
||||||
|
// TODO: for now always recalculate
|
||||||
|
clearNpcStatsCache();
|
||||||
|
|
||||||
|
// TODO: check if below signal slows things down
|
||||||
|
std::string empty;
|
||||||
|
emit updateNpcAutocalc(0/*all*/, empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::NpcAutoCalc::gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||||
|
{
|
||||||
|
static const QStringList gmsts(QStringList()<< "fNPCbaseMagickaMult" << "fAutoSpellChance"
|
||||||
|
<< "fEffectCostMult" << "iAutoSpellAlterationMax" << "iAutoSpellConjurationMax"
|
||||||
|
<< "iAutoSpellDestructionMax" << "iAutoSpellIllusionMax" << "iAutoSpellMysticismMax"
|
||||||
|
<< "iAutoSpellRestorationMax" << "iAutoSpellTimesCanCast" << "iAutoSpellAttSkillMin");
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
for (int row = topLeft.row(); row <= bottomRight.row(); ++row)
|
||||||
|
{
|
||||||
|
if (gmsts.contains(mData.getGmsts().getRecord(row).get().mId.c_str()))
|
||||||
|
{
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clearNpcStatsCache();
|
||||||
|
|
||||||
|
std::string empty;
|
||||||
|
emit updateNpcAutocalc(0/*all*/, empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::NpcAutoCalc::clearNpcStatsCache ()
|
||||||
|
{
|
||||||
|
for (std::map<const std::string, CSMWorld::NpcStats*>::iterator it (mNpcStatCache.begin());
|
||||||
|
it != mNpcStatCache.end(); ++it)
|
||||||
|
delete it->second;
|
||||||
|
|
||||||
|
mNpcStatCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::NpcStats* CSMWorld::NpcAutoCalc::npcAutoCalculate(const ESM::NPC& npc) const
|
||||||
|
{
|
||||||
|
CSMWorld::NpcStats *cachedStats = getCachedNpcData (npc.mId);
|
||||||
|
if (cachedStats)
|
||||||
|
return cachedStats;
|
||||||
|
|
||||||
|
int raceIndex = mData.getRaces().searchId(npc.mRace);
|
||||||
|
int classIndex = mData.getClasses().searchId(npc.mClass);
|
||||||
|
// this can happen when creating a new game from scratch
|
||||||
|
if (raceIndex == -1 || classIndex == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const ESM::Race *race = &mData.getRaces().getRecord(raceIndex).get();
|
||||||
|
const ESM::Class *class_ = &mData.getClasses().getRecord(classIndex).get();
|
||||||
|
|
||||||
|
bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
|
||||||
|
short level = npc.mNpdt52.mLevel;
|
||||||
|
if (autoCalc)
|
||||||
|
level = npc.mNpdt12.mLevel;
|
||||||
|
|
||||||
|
std::auto_ptr<CSMWorld::NpcStats> stats (new CSMWorld::NpcStats());
|
||||||
|
|
||||||
|
CSStore store(mData.getGmsts(), mData.getSkills(), mData.getMagicEffects(), static_cast<const CSMWorld::NestedIdCollection<ESM::Spell>&>(mData.getSpells()));
|
||||||
|
|
||||||
|
if (autoCalc)
|
||||||
|
{
|
||||||
|
AutoCalc::autoCalcAttributesImpl (&npc, race, class_, level, *stats, &store);
|
||||||
|
|
||||||
|
stats->setHealth(autoCalculateHealth(level, class_, *stats));
|
||||||
|
stats->setMana(autoCalculateMana(*stats));
|
||||||
|
stats->setFatigue(autoCalculateFatigue(*stats));
|
||||||
|
|
||||||
|
AutoCalc::autoCalcSkillsImpl(&npc, race, class_, level, *stats, &store);
|
||||||
|
|
||||||
|
AutoCalc::autoCalculateSpells(race, *stats, &store);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin();
|
||||||
|
it != npc.mSpells.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
stats->addSpell(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update spell info
|
||||||
|
const std::vector<std::string> &racePowers = race->mPowers.mList;
|
||||||
|
for (unsigned int i = 0; i < racePowers.size(); ++i)
|
||||||
|
{
|
||||||
|
int type = -1;
|
||||||
|
int spellIndex = mData.getSpells().searchId(racePowers[i]);
|
||||||
|
if (spellIndex != -1)
|
||||||
|
type = mData.getSpells().getRecord(spellIndex).get().mData.mType;
|
||||||
|
stats->addPowers(racePowers[i], type);
|
||||||
|
}
|
||||||
|
// cost/chance
|
||||||
|
int skills[ESM::Skill::Length];
|
||||||
|
if (autoCalc)
|
||||||
|
for (int i = 0; i< ESM::Skill::Length; ++i)
|
||||||
|
skills[i] = stats->getBaseSkill(i);
|
||||||
|
else
|
||||||
|
for (int i = 0; i< ESM::Skill::Length; ++i)
|
||||||
|
skills[i] = npc.mNpdt52.mSkills[i];
|
||||||
|
|
||||||
|
int attributes[ESM::Attribute::Length];
|
||||||
|
if (autoCalc)
|
||||||
|
for (int i = 0; i< ESM::Attribute::Length; ++i)
|
||||||
|
attributes[i] = stats->getBaseAttribute(i);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
attributes[ESM::Attribute::Strength] = npc.mNpdt52.mStrength;
|
||||||
|
attributes[ESM::Attribute::Willpower] = npc.mNpdt52.mWillpower;
|
||||||
|
attributes[ESM::Attribute::Agility] = npc.mNpdt52.mAgility;
|
||||||
|
attributes[ESM::Attribute::Speed] = npc.mNpdt52.mSpeed;
|
||||||
|
attributes[ESM::Attribute::Endurance] = npc.mNpdt52.mEndurance;
|
||||||
|
attributes[ESM::Attribute::Personality] = npc.mNpdt52.mPersonality;
|
||||||
|
attributes[ESM::Attribute::Luck] = npc.mNpdt52.mLuck;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<CSMWorld::SpellInfo>& spells = stats->spells();
|
||||||
|
for (std::vector<SpellInfo>::const_iterator it = spells.begin(); it != spells.end(); ++it)
|
||||||
|
{
|
||||||
|
int cost = -1;
|
||||||
|
int spellIndex = mData.getSpells().searchId((*it).mName);
|
||||||
|
const ESM::Spell* spell = 0;
|
||||||
|
if (spellIndex != -1)
|
||||||
|
{
|
||||||
|
spell = &mData.getSpells().getRecord(spellIndex).get();
|
||||||
|
cost = spell->mData.mCost;
|
||||||
|
|
||||||
|
int school;
|
||||||
|
float skillTerm;
|
||||||
|
AutoCalc::calcWeakestSchool(spell, skills, school, skillTerm, &store);
|
||||||
|
float chance = calcAutoCastChance(spell, skills, attributes, school, &store);
|
||||||
|
|
||||||
|
stats->addCostAndChance((*it).mName, cost, (int)ceil(chance)); // percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.get() == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
CSMWorld::NpcStats *result = stats.release();
|
||||||
|
//emit cacheNpcStats (npc.mId, result);
|
||||||
|
mNpcStatCache[npc.mId] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//void CSMWorld::NpcAutoCalc::cacheNpcStatsEvent (const std::string& id, CSMWorld::NpcStats *stats)
|
||||||
|
//{
|
||||||
|
//mNpcStatCache[id] = stats;
|
||||||
|
//}
|
||||||
|
|
||||||
|
CSMWorld::NpcStats* CSMWorld::NpcAutoCalc::getCachedNpcData (const std::string& id) const
|
||||||
|
{
|
||||||
|
std::map<const std::string, CSMWorld::NpcStats*>::const_iterator it = mNpcStatCache.find(id);
|
||||||
|
if (it != mNpcStatCache.end())
|
||||||
|
return it->second;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
75
apps/opencs/model/world/npcautocalc.hpp
Normal file
75
apps/opencs/model/world/npcautocalc.hpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#ifndef CSM_WORLD_NPCAUTOCALC_H
|
||||||
|
#define CSM_WORLD_NPCAUTOCALC_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QModelIndex>
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct NPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class Data;
|
||||||
|
class NpcStats;
|
||||||
|
class IdTable;
|
||||||
|
class IdTree;
|
||||||
|
|
||||||
|
class NpcAutoCalc : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
const Data& mData;
|
||||||
|
const IdTable *mSkillModel;
|
||||||
|
const IdTable *mClassModel;
|
||||||
|
const IdTree *mRaceModel;
|
||||||
|
mutable std::map<const std::string, NpcStats*> mNpcStatCache;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NpcAutoCalc (const Data& data, const IdTable *gmsts, const IdTable *skills, const IdTable *classes,
|
||||||
|
const IdTree *races, const IdTree *objects);
|
||||||
|
|
||||||
|
~NpcAutoCalc ();
|
||||||
|
|
||||||
|
NpcStats* npcAutoCalculate (const ESM::NPC& npc) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
NpcAutoCalc (const NpcAutoCalc&);
|
||||||
|
NpcAutoCalc& operator= (const NpcAutoCalc&);
|
||||||
|
|
||||||
|
NpcStats* getCachedNpcData (const std::string& id) const;
|
||||||
|
|
||||||
|
void clearNpcStatsCache ();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
// refresh NPC dialogue subviews via object table model
|
||||||
|
void updateNpcAutocalc (int type, const std::string& id);
|
||||||
|
|
||||||
|
//void cacheNpcStats (const std::string& id, NpcStats *stats) const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
// for autocalc updates when gmst/race/class/skils tables change
|
||||||
|
void gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||||
|
|
||||||
|
void raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||||
|
|
||||||
|
void classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||||
|
|
||||||
|
void skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||||
|
|
||||||
|
void npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||||
|
|
||||||
|
//void cacheNpcStatsEvent (const std::string& id, NpcStats *stats);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CSM_WORLD_NPCAUTOCALC_H
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef CSM_WOLRD_RECORD_H
|
#ifndef CSM_WOLRD_RECORD_H
|
||||||
#define CSM_WOLRD_RECORD_H
|
#define CSM_WOLRD_RECORD_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
@ -20,9 +21,9 @@ namespace CSMWorld
|
|||||||
|
|
||||||
virtual ~RecordBase();
|
virtual ~RecordBase();
|
||||||
|
|
||||||
virtual RecordBase *clone() const = 0;
|
virtual std::unique_ptr<RecordBase> clone() const = 0;
|
||||||
|
|
||||||
virtual RecordBase *modifiedCopy() const = 0;
|
virtual std::unique_ptr<RecordBase> modifiedCopy() const = 0;
|
||||||
|
|
||||||
virtual void assign (const RecordBase& record) = 0;
|
virtual void assign (const RecordBase& record) = 0;
|
||||||
///< Will throw an exception if the types don't match.
|
///< Will throw an exception if the types don't match.
|
||||||
@ -45,9 +46,9 @@ namespace CSMWorld
|
|||||||
Record(State state,
|
Record(State state,
|
||||||
const ESXRecordT *base = 0, const ESXRecordT *modified = 0);
|
const ESXRecordT *base = 0, const ESXRecordT *modified = 0);
|
||||||
|
|
||||||
virtual RecordBase *clone() const;
|
virtual std::unique_ptr<RecordBase> clone() const;
|
||||||
|
|
||||||
virtual RecordBase *modifiedCopy() const;
|
virtual std::unique_ptr<RecordBase> modifiedCopy() const;
|
||||||
|
|
||||||
virtual void assign (const RecordBase& record);
|
virtual void assign (const RecordBase& record);
|
||||||
|
|
||||||
@ -85,15 +86,16 @@ namespace CSMWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ESXRecordT>
|
template <typename ESXRecordT>
|
||||||
RecordBase *Record<ESXRecordT>::modifiedCopy() const
|
std::unique_ptr<RecordBase> Record<ESXRecordT>::modifiedCopy() const
|
||||||
{
|
{
|
||||||
return new Record<ESXRecordT> (State_ModifiedOnly, 0, &(this->get()));
|
return std::make_unique<Record<ESXRecordT> >(
|
||||||
|
Record<ESXRecordT>(State_ModifiedOnly, 0, &(this->get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ESXRecordT>
|
template <typename ESXRecordT>
|
||||||
RecordBase *Record<ESXRecordT>::clone() const
|
std::unique_ptr<RecordBase> Record<ESXRecordT>::clone() const
|
||||||
{
|
{
|
||||||
return new Record<ESXRecordT> (*this);
|
return std::make_unique<Record<ESXRecordT> >(Record<ESXRecordT>(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ESXRecordT>
|
template <typename ESXRecordT>
|
||||||
|
@ -3,11 +3,35 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
CSMWorld::CellRef::CellRef()
|
CSMWorld::CellRef::CellRef()
|
||||||
|
: mIdNum(0)
|
||||||
{
|
{
|
||||||
|
mId.clear();
|
||||||
|
mCell.clear();
|
||||||
|
mOriginalCell.clear();
|
||||||
|
|
||||||
mRefNum.mIndex = 0;
|
mRefNum.mIndex = 0;
|
||||||
mRefNum.mContentFile = 0;
|
mRefNum.mContentFile = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSMWorld::CellRef::CellRef (CSMWorld::CellRef&& other) : ESM::CellRef (other)
|
||||||
|
{
|
||||||
|
*this = std::move(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::CellRef& CSMWorld::CellRef::operator= (CSMWorld::CellRef&& other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
ESM::CellRef::operator= (other);
|
||||||
|
mIdNum = other.mIdNum;
|
||||||
|
mId = std::move(other.mId);
|
||||||
|
mCell = std::move(other.mCell);
|
||||||
|
mOriginalCell = std::move(other.mOriginalCell);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<int, int> CSMWorld::CellRef::getCellIndex() const
|
std::pair<int, int> CSMWorld::CellRef::getCellIndex() const
|
||||||
{
|
{
|
||||||
const int cellSize = 8192;
|
const int cellSize = 8192;
|
||||||
|
@ -10,11 +10,18 @@ namespace CSMWorld
|
|||||||
/// \brief Wrapper for CellRef sub record
|
/// \brief Wrapper for CellRef sub record
|
||||||
struct CellRef : public ESM::CellRef
|
struct CellRef : public ESM::CellRef
|
||||||
{
|
{
|
||||||
|
unsigned int mIdNum;
|
||||||
|
|
||||||
std::string mId;
|
std::string mId;
|
||||||
std::string mCell;
|
std::string mCell;
|
||||||
std::string mOriginalCell;
|
std::string mOriginalCell;
|
||||||
|
|
||||||
CellRef();
|
CellRef();
|
||||||
|
CellRef(const CellRef&) = default;
|
||||||
|
CellRef& operator= (const CellRef&) = default;
|
||||||
|
|
||||||
|
CellRef (CellRef&& other);
|
||||||
|
CellRef& operator= (CellRef&& other);
|
||||||
|
|
||||||
/// Calculate cell index based on coordinates (x and y)
|
/// Calculate cell index based on coordinates (x and y)
|
||||||
std::pair<int, int> getCellIndex() const;
|
std::pair<int, int> getCellIndex() const;
|
||||||
|
@ -1,20 +1,48 @@
|
|||||||
#include "refcollection.hpp"
|
#include "refcollection.hpp"
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
#include <components/esm/loadcell.hpp>
|
#include <components/esm/loadcell.hpp>
|
||||||
|
|
||||||
|
#include <libs/platform/strings.h>
|
||||||
|
|
||||||
#include "ref.hpp"
|
#include "ref.hpp"
|
||||||
#include "cell.hpp"
|
#include "cell.hpp"
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
#include "record.hpp"
|
#include "record.hpp"
|
||||||
|
|
||||||
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,
|
namespace CSMWorld
|
||||||
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages)
|
|
||||||
{
|
{
|
||||||
Record<Cell> cell = mCells.getRecord (cellIndex);
|
template<>
|
||||||
|
void Collection<CellRef, IdAccessor<CellRef> >::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count);
|
||||||
|
|
||||||
|
// index map is updated in RefCollection::removeRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<CellRef, IdAccessor<CellRef> >::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
|
UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int size = static_cast<int>(mRecords.size());
|
||||||
|
if (index < 0 || index > size)
|
||||||
|
throw std::runtime_error("index out of range");
|
||||||
|
|
||||||
|
std::unique_ptr<Record<CellRef> > record2(static_cast<Record<CellRef>*>(record.release()));
|
||||||
|
|
||||||
|
if (index == size)
|
||||||
|
mRecords.push_back(std::move(record2));
|
||||||
|
else
|
||||||
|
mRecords.insert(mRecords.begin()+index, std::move(record2));
|
||||||
|
|
||||||
|
// index map is updated in RefCollection::insertRecord()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,
|
||||||
|
std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
Record<Cell> cell = mCells.getRecord(cellIndex);
|
||||||
|
|
||||||
Cell& cell2 = base ? cell.mBase : cell.mModified;
|
Cell& cell2 = base ? cell.mBase : cell.mModified;
|
||||||
|
|
||||||
@ -34,10 +62,12 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
|||||||
// ignoring moved references sub-record; instead calculate cell from coordinates
|
// ignoring moved references sub-record; instead calculate cell from coordinates
|
||||||
std::pair<int, int> index = ref.getCellIndex();
|
std::pair<int, int> index = ref.getCellIndex();
|
||||||
|
|
||||||
std::ostringstream stream;
|
char buf[100];
|
||||||
stream << "#" << index.first << " " << index.second;
|
int res = snprintf(buf, 100, "#%d %d", index.first, index.second);
|
||||||
|
if (res > 0 && res < 100)
|
||||||
ref.mCell = stream.str();
|
ref.mCell = std::string(buf);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("getNewId possible buffer overflow");
|
||||||
|
|
||||||
if (!base && // don't try to update base records
|
if (!base && // don't try to update base records
|
||||||
mref.mRefNum.mIndex != 0) // MVRF tag found
|
mref.mRefNum.mIndex != 0) // MVRF tag found
|
||||||
@ -57,89 +87,225 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
|||||||
// message
|
// message
|
||||||
if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1])
|
if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1])
|
||||||
{
|
{
|
||||||
std::cerr << "The Position of moved ref "
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex));
|
||||||
<< ref.mRefID << " does not match the target cell" << std::endl;
|
messages.add(id, "The Position of moved ref " + ref.mRefID + " (#"
|
||||||
std::cerr << "Position: #" << index.first << " " << index.second
|
+ std::to_string(index.first) + " " + std::to_string(index.second)
|
||||||
<<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl;
|
+ ") does not match the target cell (#"
|
||||||
|
+ std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]) + ")",
|
||||||
|
/*hint*/"",
|
||||||
|
CSMDoc::Message::Severity_Warning);
|
||||||
|
|
||||||
std::ostringstream stream;
|
// overwrite
|
||||||
stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1];
|
ref.mCell = "#" + std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]);
|
||||||
ref.mCell = stream.str(); // overwrite
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ref.mCell = cell2.mId;
|
ref.mCell = cell2.mId;
|
||||||
|
|
||||||
// ignore content file number
|
unsigned int refNum = (ref.mRefNum.mIndex & 0x00ffffff) |
|
||||||
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();
|
(ref.mRefNum.hasContentFile() ? ref.mRefNum.mContentFile : 0xff) << 24;
|
||||||
for (; iter != cache.end(); ++iter)
|
|
||||||
{
|
std::map<unsigned int, unsigned int>::iterator iter = cache.find(refNum);
|
||||||
if (ref.mRefNum.mIndex == iter->first.mIndex)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDeleted)
|
if (isDeleted)
|
||||||
{
|
{
|
||||||
if (iter==cache.end())
|
if (iter == cache.end())
|
||||||
{
|
{
|
||||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex));
|
||||||
mCells.getId (cellIndex));
|
messages.add(id, "Attempt to delete a non-existing reference - RefNum index "
|
||||||
|
+ std::to_string(ref.mRefNum.mIndex) + ", refID " + ref.mRefID + ", content file index "
|
||||||
messages.add (id, "Attempt to delete a non-existing reference");
|
+ std::to_string(ref.mRefNum.mContentFile),
|
||||||
|
/*hint*/"",
|
||||||
|
CSMDoc::Message::Severity_Warning);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = getIndex (iter->second);
|
int index = getIndex(iter->second);
|
||||||
|
|
||||||
Record<CellRef> record = getRecord (index);
|
|
||||||
|
|
||||||
if (base)
|
if (base)
|
||||||
{
|
{
|
||||||
removeRows (index, 1);
|
removeRows(index, 1);
|
||||||
cache.erase (iter);
|
cache.erase(iter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
record.mState = RecordBase::State_Deleted;
|
std::unique_ptr<Record<CellRef> > record2(new Record<CellRef>(getRecord(index)));
|
||||||
setRecord (index, record);
|
record2->mState = RecordBase::State_Deleted;
|
||||||
|
setRecord(index, std::move(record2));
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter==cache.end())
|
if (iter == cache.end())
|
||||||
{
|
{
|
||||||
// new reference
|
// new reference
|
||||||
|
ref.mIdNum = mNextId; // FIXME: fragile
|
||||||
ref.mId = getNewId();
|
ref.mId = getNewId();
|
||||||
|
|
||||||
Record<CellRef> record;
|
cache.insert(std::make_pair(refNum, ref.mIdNum));
|
||||||
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
|
||||||
(base ? record.mBase : record.mModified) = ref;
|
|
||||||
|
|
||||||
appendRecord (record);
|
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>);
|
||||||
|
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
|
(base ? record->mBase : record->mModified) = std::move(ref);
|
||||||
|
|
||||||
cache.insert (std::make_pair (ref.mRefNum, ref.mId));
|
appendRecord(std::move(record));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// old reference -> merge
|
// old reference -> merge
|
||||||
ref.mId = iter->second;
|
int index = getIndex(iter->second);
|
||||||
|
#if 0
|
||||||
|
// ref.mRefNum.mIndex : the key
|
||||||
|
// iter->second : previously cached idNum for the key
|
||||||
|
// index : position of the record for that idNum
|
||||||
|
// getRecord(index).get() : record in the index position
|
||||||
|
assert(iter->second != getRecord(index).get().mIdNum); // sanity check
|
||||||
|
|
||||||
int index = getIndex (ref.mId);
|
// check if the plugin used the same RefNum index for a different record
|
||||||
|
if (ref.mRefID != getRecord(index).get().mRefID)
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex));
|
||||||
|
messages.add(id,
|
||||||
|
"RefNum renamed from RefID \"" + getRecord(index).get().mRefID + "\" to \""
|
||||||
|
+ ref.mRefID + "\" (RefNum index " + std::to_string(ref.mRefNum.mIndex) + ")",
|
||||||
|
/*hint*/"",
|
||||||
|
CSMDoc::Message::Severity_Info);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ref.mId = getRecord(index).get().mId;
|
||||||
|
ref.mIdNum = extractIdNum(ref.mId);
|
||||||
|
|
||||||
Record<CellRef> record = getRecord (index);
|
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>(getRecord(index)));
|
||||||
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
|
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
|
||||||
(base ? record.mBase : record.mModified) = ref;
|
(base ? record->mBase : record->mModified) = std::move(ref);
|
||||||
|
|
||||||
setRecord (index, record);
|
setRecord(index, std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CSMWorld::RefCollection::getNewId()
|
std::string CSMWorld::RefCollection::getNewId()
|
||||||
{
|
{
|
||||||
std::ostringstream stream;
|
char buf[100];
|
||||||
stream << "ref#" << mNextId++;
|
int res = snprintf(buf, 100, "ref#%d", mNextId++);
|
||||||
return stream.str();
|
if (res > 0 && res < 100)
|
||||||
|
return std::string(buf);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("getNewId possible buffer overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int CSMWorld::RefCollection::extractIdNum (const std::string& id) const
|
||||||
|
{
|
||||||
|
std::string::size_type separator = id.find_last_of('#');
|
||||||
|
|
||||||
|
if (separator == std::string::npos)
|
||||||
|
throw std::runtime_error("invalid ref ID: " + id);
|
||||||
|
|
||||||
|
return static_cast<unsigned int>(std::stoi(id.substr(separator+1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::RefCollection::getIndex (unsigned int id) const
|
||||||
|
{
|
||||||
|
int index = searchId(id);
|
||||||
|
|
||||||
|
if (index == -1)
|
||||||
|
throw std::runtime_error("invalid RefNum: " + std::to_string(id));
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::RefCollection::searchId (unsigned int id) const
|
||||||
|
{
|
||||||
|
std::map<unsigned int, int>::const_iterator iter = mRefIndex.find(id);
|
||||||
|
|
||||||
|
if (iter == mRefIndex.end())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
Collection<CellRef, IdAccessor<CellRef> >::removeRows(index, count); // erase records only
|
||||||
|
|
||||||
|
std::map<unsigned int, int>::iterator iter = mRefIndex.begin();
|
||||||
|
while (iter != mRefIndex.end())
|
||||||
|
{
|
||||||
|
if (iter->second>=index)
|
||||||
|
{
|
||||||
|
if (iter->second >= index+count)
|
||||||
|
{
|
||||||
|
iter->second -= count;
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mRefIndex.erase(iter++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Record<CellRef> > record2(new Record<CellRef>);
|
||||||
|
|
||||||
|
record2->mState = Record<CellRef>::State_ModifiedOnly;
|
||||||
|
record2->mModified.blank();
|
||||||
|
|
||||||
|
record2->get().mId = id;
|
||||||
|
record2->get().mIdNum = extractIdNum(id);
|
||||||
|
|
||||||
|
Collection<CellRef, IdAccessor<CellRef> >::appendRecord(std::move(record2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::cloneRecord (const std::string& origin,
|
||||||
|
const std::string& destination,
|
||||||
|
const UniversalId::Type type)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Record<CellRef> > copy(new Record<CellRef>);
|
||||||
|
|
||||||
|
copy->mModified = getRecord(origin).get();
|
||||||
|
copy->mState = RecordBase::State_ModifiedOnly;
|
||||||
|
|
||||||
|
copy->get().mId = destination;
|
||||||
|
copy->get().mIdNum = extractIdNum(destination);
|
||||||
|
|
||||||
|
insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord()
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::RefCollection::searchId (const std::string& id) const
|
||||||
|
{
|
||||||
|
return searchId(extractIdNum(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int index = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored
|
||||||
|
|
||||||
|
mRefIndex.insert(std::make_pair(static_cast<Record<CellRef>*>(record.get())->get().mIdNum, index));
|
||||||
|
|
||||||
|
Collection<CellRef, IdAccessor<CellRef> >::insertRecord(std::move(record), index, type); // add records only
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
|
UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int size = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored
|
||||||
|
unsigned int idNum = static_cast<Record<CellRef>*>(record.get())->get().mIdNum;
|
||||||
|
|
||||||
|
Collection<CellRef, IdAccessor<CellRef> >::insertRecord(std::move(record), index, type); // add records only
|
||||||
|
|
||||||
|
if (index < size-1)
|
||||||
|
{
|
||||||
|
for (std::map<unsigned int, int>::iterator iter(mRefIndex.begin()); iter != mRefIndex.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (iter->second >= index)
|
||||||
|
++(iter->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mRefIndex.insert(std::make_pair(idNum, index));
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,27 @@ namespace CSMWorld
|
|||||||
struct Cell;
|
struct Cell;
|
||||||
class UniversalId;
|
class UniversalId;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<CellRef, IdAccessor<CellRef> >::removeRows (int index, int count);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<CellRef, IdAccessor<CellRef> >::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
|
UniversalId::Type type);
|
||||||
|
|
||||||
/// \brief References in cells
|
/// \brief References in cells
|
||||||
class RefCollection : public Collection<CellRef>
|
class RefCollection : public Collection<CellRef>
|
||||||
{
|
{
|
||||||
Collection<Cell>& mCells;
|
Collection<Cell>& mCells;
|
||||||
|
std::map<unsigned int, int> mRefIndex;
|
||||||
|
|
||||||
int mNextId;
|
int mNextId;
|
||||||
|
|
||||||
|
unsigned int extractIdNum(const std::string& id) const;
|
||||||
|
|
||||||
|
int getIndex (unsigned int id) const;
|
||||||
|
|
||||||
|
int searchId (unsigned int id) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// MSVC needs the constructor for a class inheriting a template to be defined in header
|
// MSVC needs the constructor for a class inheriting a template to be defined in header
|
||||||
RefCollection (Collection<Cell>& cells)
|
RefCollection (Collection<Cell>& cells)
|
||||||
@ -27,10 +42,28 @@ namespace CSMWorld
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
void load (ESM::ESMReader& reader, int cellIndex, bool base,
|
void load (ESM::ESMReader& reader, int cellIndex, bool base,
|
||||||
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages);
|
std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages);
|
||||||
///< Load a sequence of references.
|
///< Load a sequence of references.
|
||||||
|
|
||||||
std::string getNewId();
|
std::string getNewId();
|
||||||
|
|
||||||
|
virtual void removeRows (int index, int count);
|
||||||
|
|
||||||
|
virtual void appendBlankRecord (const std::string& id,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
||||||
|
virtual void cloneRecord (const std::string& origin,
|
||||||
|
const std::string& destination,
|
||||||
|
const UniversalId::Type type);
|
||||||
|
|
||||||
|
virtual int searchId (const std::string& id) const;
|
||||||
|
|
||||||
|
virtual void appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
||||||
|
virtual void insertRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
int index,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "usertype.hpp"
|
#include "usertype.hpp"
|
||||||
#include "idtree.hpp"
|
#include "idtree.hpp"
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
|
#include "npcautocalc.hpp"
|
||||||
|
|
||||||
CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)
|
CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)
|
||||||
: InventoryColumns (columns) {}
|
: InventoryColumns (columns) {}
|
||||||
@ -777,7 +778,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
|
|||||||
{
|
{
|
||||||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||||
{
|
{
|
||||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||||
if (!stats)
|
if (!stats)
|
||||||
{
|
{
|
||||||
record.setModified (npc);
|
record.setModified (npc);
|
||||||
@ -817,7 +818,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
|
|||||||
{
|
{
|
||||||
npc.mNpdtType = ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
|
npc.mNpdtType = ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
|
||||||
npc.mNpdt12.mLevel = npc.mNpdt52.mLevel; // for NPC's loaded as non-autocalc
|
npc.mNpdt12.mLevel = npc.mNpdt52.mLevel; // for NPC's loaded as non-autocalc
|
||||||
mData.npcAutoCalculate(npc);
|
mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -888,7 +889,7 @@ QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *
|
|||||||
else if (subColIndex == 1)
|
else if (subColIndex == 1)
|
||||||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||||
{
|
{
|
||||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||||
if (!stats)
|
if (!stats)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
@ -1020,7 +1021,7 @@ QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *colu
|
|||||||
{
|
{
|
||||||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||||
{
|
{
|
||||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||||
if (!stats)
|
if (!stats)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
@ -1108,7 +1109,7 @@ QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column
|
|||||||
|
|
||||||
if (autoCalc)
|
if (autoCalc)
|
||||||
{
|
{
|
||||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||||
|
|
||||||
switch (subColIndex)
|
switch (subColIndex)
|
||||||
{
|
{
|
||||||
@ -1732,7 +1733,7 @@ QVariant NestedSpellRefIdAdapter<ESM::NPC>::getNestedData (const RefIdColumn *co
|
|||||||
const Record<ESM::NPC>& record =
|
const Record<ESM::NPC>& record =
|
||||||
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
||||||
|
|
||||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get());
|
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(record.get());
|
||||||
if (!stats)
|
if (!stats)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
@ -1766,7 +1767,7 @@ int NestedSpellRefIdAdapter<ESM::NPC>::getNestedRowsCount(const RefIdColumn *col
|
|||||||
const Record<ESM::NPC>& record =
|
const Record<ESM::NPC>& record =
|
||||||
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
||||||
|
|
||||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get());
|
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(record.get());
|
||||||
if (!stats)
|
if (!stats)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -810,30 +810,31 @@ int CSMWorld::RefIdCollection::searchId (const std::string& id) const
|
|||||||
return mData.localToGlobalIndex (localIndex);
|
return mData.localToGlobalIndex (localIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record)
|
void CSMWorld::RefIdCollection::replace (int index, std::unique_ptr<RecordBase> record)
|
||||||
{
|
{
|
||||||
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
|
mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
|
void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
|
||||||
const std::string& destination,
|
const std::string& destination,
|
||||||
const CSMWorld::UniversalId::Type type)
|
const CSMWorld::UniversalId::Type type)
|
||||||
{
|
{
|
||||||
std::auto_ptr<RecordBase> newRecord(mData.getRecord(mData.searchId(origin)).modifiedCopy());
|
std::unique_ptr<RecordBase> newRecord =
|
||||||
|
std::move(mData.getRecord(mData.searchId(origin)).modifiedCopy());
|
||||||
mAdapters.find(type)->second->setId(*newRecord, destination);
|
mAdapters.find(type)->second->setId(*newRecord, destination);
|
||||||
mData.insertRecord(*newRecord, type, destination);
|
mData.insertRecord(std::move(newRecord), type, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
|
void CSMWorld::RefIdCollection::appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type)
|
UniversalId::Type type)
|
||||||
{
|
{
|
||||||
std::string id = findAdapter (type).getId (record);
|
std::string id = findAdapter (type).getId (*record.get());
|
||||||
|
|
||||||
int index = mData.getAppendIndex (type);
|
int index = mData.getAppendIndex (type);
|
||||||
|
|
||||||
mData.appendRecord (type, id, false);
|
mData.appendRecord (type, id, false);
|
||||||
|
|
||||||
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
|
mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const
|
const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const
|
||||||
|
@ -89,12 +89,12 @@ namespace CSMWorld
|
|||||||
////< Search record with \a id.
|
////< Search record with \a id.
|
||||||
/// \return index of record (if found) or -1 (not found)
|
/// \return index of record (if found) or -1 (not found)
|
||||||
|
|
||||||
virtual void replace (int index, const RecordBase& record);
|
virtual void replace (int index, std::unique_ptr<RecordBase> record);
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///
|
///
|
||||||
/// \attention \a record must not change the ID.
|
/// \attention \a record must not change the ID.
|
||||||
|
|
||||||
virtual void appendRecord (const RecordBase& record, UniversalId::Type type);
|
virtual void appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type);
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///
|
///
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
@ -367,7 +367,7 @@ const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStati
|
|||||||
return mStatics;
|
return mStatics;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id)
|
void CSMWorld::RefIdData::insertRecord (std::unique_ptr<CSMWorld::RecordBase> record, CSMWorld::UniversalId::Type type, const std::string& id)
|
||||||
{
|
{
|
||||||
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
|
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
|
||||||
mRecordContainers.find (type);
|
mRecordContainers.find (type);
|
||||||
@ -375,7 +375,7 @@ void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::
|
|||||||
if (iter==mRecordContainers.end())
|
if (iter==mRecordContainers.end())
|
||||||
throw std::logic_error ("invalid local index type");
|
throw std::logic_error ("invalid local index type");
|
||||||
|
|
||||||
iter->second->insertRecord(record);
|
iter->second->insertRecord(std::move(record));
|
||||||
|
|
||||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
|
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
|
||||||
LocalIndex (iter->second->getSize()-1, type)));
|
LocalIndex (iter->second->getSize()-1, type)));
|
||||||
@ -387,9 +387,7 @@ void CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const
|
|||||||
|
|
||||||
RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;
|
RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;
|
||||||
|
|
||||||
std::string id = source->getId (localIndex.first);
|
target.insertRecord(source->getRecord(localIndex.first).modifiedCopy(),
|
||||||
|
localIndex.second,
|
||||||
std::auto_ptr<CSMWorld::RecordBase> newRecord (source->getRecord (localIndex.first).modifiedCopy());
|
source->getId(localIndex.first));
|
||||||
|
|
||||||
target.insertRecord (*newRecord, localIndex.second, id);
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <components/esm/loadacti.hpp>
|
#include <components/esm/loadacti.hpp>
|
||||||
#include <components/esm/loadalch.hpp>
|
#include <components/esm/loadalch.hpp>
|
||||||
@ -49,7 +50,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
virtual void appendRecord (const std::string& id, bool base) = 0;
|
virtual void appendRecord (const std::string& id, bool base) = 0;
|
||||||
|
|
||||||
virtual void insertRecord (RecordBase& record) = 0;
|
virtual void insertRecord (std::unique_ptr<RecordBase> record) = 0;
|
||||||
|
|
||||||
virtual int load (ESM::ESMReader& reader, bool base) = 0;
|
virtual int load (ESM::ESMReader& reader, bool base) = 0;
|
||||||
///< \return index of a loaded record or -1 if no record was loaded
|
///< \return index of a loaded record or -1 if no record was loaded
|
||||||
@ -64,7 +65,7 @@ namespace CSMWorld
|
|||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
struct RefIdDataContainer : public RefIdDataContainerBase
|
struct RefIdDataContainer : public RefIdDataContainerBase
|
||||||
{
|
{
|
||||||
std::vector<Record<RecordT> > mContainer;
|
std::vector<std::unique_ptr<Record<RecordT> > > mContainer;
|
||||||
|
|
||||||
virtual int getSize() const;
|
virtual int getSize() const;
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
virtual void appendRecord (const std::string& id, bool base);
|
virtual void appendRecord (const std::string& id, bool base);
|
||||||
|
|
||||||
virtual void insertRecord (RecordBase& record);
|
virtual void insertRecord (std::unique_ptr<RecordBase> record);
|
||||||
|
|
||||||
virtual int load (ESM::ESMReader& reader, bool base);
|
virtual int load (ESM::ESMReader& reader, bool base);
|
||||||
///< \return index of a loaded record or -1 if no record was loaded
|
///< \return index of a loaded record or -1 if no record was loaded
|
||||||
@ -87,10 +88,18 @@ namespace CSMWorld
|
|||||||
};
|
};
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
void RefIdDataContainer<RecordT>::insertRecord(RecordBase& record)
|
void RefIdDataContainer<RecordT>::insertRecord(std::unique_ptr<RecordBase> record)
|
||||||
{
|
{
|
||||||
Record<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record);
|
Record<RecordT> *tmp = dynamic_cast<Record<RecordT>*>(record.get());
|
||||||
mContainer.push_back(newRecord);
|
if(tmp != nullptr)
|
||||||
|
{
|
||||||
|
record.release();
|
||||||
|
std::unique_ptr<Record<RecordT> > newRecord;
|
||||||
|
newRecord.reset(tmp);
|
||||||
|
mContainer.push_back(std::move(newRecord));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error ("invalid record for RefIdDataContainer");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
@ -102,27 +111,27 @@ namespace CSMWorld
|
|||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const
|
const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const
|
||||||
{
|
{
|
||||||
return mContainer.at (index);
|
return *mContainer.at (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
RecordBase& RefIdDataContainer<RecordT>::getRecord (int index)
|
RecordBase& RefIdDataContainer<RecordT>::getRecord (int index)
|
||||||
{
|
{
|
||||||
return mContainer.at (index);
|
return *mContainer.at (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)
|
void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)
|
||||||
{
|
{
|
||||||
Record<RecordT> record;
|
std::unique_ptr<Record<RecordT> > record(new Record<RecordT>);
|
||||||
|
|
||||||
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
|
|
||||||
record.mBase.mId = id;
|
record->mBase.mId = id;
|
||||||
record.mModified.mId = id;
|
record->mModified.mId = id;
|
||||||
(base ? record.mBase : record.mModified).blank();
|
(base ? record->mBase : record->mModified).blank();
|
||||||
|
|
||||||
mContainer.push_back (record);
|
mContainer.push_back (std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
@ -137,7 +146,7 @@ namespace CSMWorld
|
|||||||
int numRecords = static_cast<int>(mContainer.size());
|
int numRecords = static_cast<int>(mContainer.size());
|
||||||
for (; index < numRecords; ++index)
|
for (; index < numRecords; ++index)
|
||||||
{
|
{
|
||||||
if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId))
|
if (Misc::StringUtils::ciEqual(mContainer[index]->get().mId, record.mId))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -155,7 +164,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
// Flag the record as Deleted even for a base content file.
|
// Flag the record as Deleted even for a base content file.
|
||||||
// RefIdData is responsible for its erasure.
|
// RefIdData is responsible for its erasure.
|
||||||
mContainer[index].mState = RecordBase::State_Deleted;
|
mContainer[index]->mState = RecordBase::State_Deleted;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -164,16 +173,16 @@ namespace CSMWorld
|
|||||||
appendRecord(record.mId, base);
|
appendRecord(record.mId, base);
|
||||||
if (base)
|
if (base)
|
||||||
{
|
{
|
||||||
mContainer.back().mBase = record;
|
mContainer.back()->mBase = record;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mContainer.back().mModified = record;
|
mContainer.back()->mModified = record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!base)
|
else if (!base)
|
||||||
{
|
{
|
||||||
mContainer[index].setModified(record);
|
mContainer[index]->setModified(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,13 +201,13 @@ namespace CSMWorld
|
|||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
std::string RefIdDataContainer<RecordT>::getId (int index) const
|
std::string RefIdDataContainer<RecordT>::getId (int index) const
|
||||||
{
|
{
|
||||||
return mContainer.at (index).get().mId;
|
return mContainer.at (index)->get().mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
|
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
|
||||||
{
|
{
|
||||||
Record<RecordT> record = mContainer.at(index);
|
const Record<RecordT>& record = *mContainer.at(index);
|
||||||
|
|
||||||
if (record.isModified() || record.mState == RecordBase::State_Deleted)
|
if (record.isModified() || record.mState == RecordBase::State_Deleted)
|
||||||
{
|
{
|
||||||
@ -260,7 +269,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
void erase (int index, int count);
|
void erase (int index, int count);
|
||||||
|
|
||||||
void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type,
|
void insertRecord (std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type,
|
||||||
const std::string& id);
|
const std::string& id);
|
||||||
|
|
||||||
const RecordBase& getRecord (const LocalIndex& index) const;
|
const RecordBase& getRecord (const LocalIndex& index) const;
|
||||||
|
@ -4,44 +4,32 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <OgreResourceGroupManager.h>
|
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::Type type,
|
CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::Type type,
|
||||||
const char * const *extensions)
|
std::vector<Ogre::StringVectorPtr> resources, const char * const *extensions)
|
||||||
: mBaseDirectory (baseDirectory), mType (type)
|
: mBaseDirectory (baseDirectory), mType (type)
|
||||||
{
|
{
|
||||||
int baseSize = mBaseDirectory.size();
|
int baseSize = mBaseDirectory.size();
|
||||||
|
|
||||||
Ogre::StringVector resourcesGroups =
|
for (std::vector<Ogre::StringVectorPtr>::iterator iter(resources.begin()); iter != resources.end(); ++iter)
|
||||||
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
|
|
||||||
|
|
||||||
for (Ogre::StringVector::iterator iter (resourcesGroups.begin());
|
|
||||||
iter!=resourcesGroups.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect")
|
// populate mFiles and mIndex
|
||||||
continue;
|
for (Ogre::StringVector::const_iterator iter2 ((*iter)->begin()); iter2 != (*iter)->end(); ++iter2)
|
||||||
|
|
||||||
Ogre::StringVectorPtr resources =
|
|
||||||
Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter);
|
|
||||||
|
|
||||||
for (Ogre::StringVector::const_iterator iter (resources->begin());
|
|
||||||
iter!=resources->end(); ++iter)
|
|
||||||
{
|
{
|
||||||
if (static_cast<int> (iter->size())<baseSize+1 ||
|
if (static_cast<int> (iter2->size())<baseSize+1 ||
|
||||||
iter->substr (0, baseSize)!=mBaseDirectory ||
|
iter2->substr (0, baseSize)!=mBaseDirectory ||
|
||||||
((*iter)[baseSize]!='/' && (*iter)[baseSize]!='\\'))
|
((*iter2)[baseSize]!='/' && (*iter2)[baseSize]!='\\'))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (extensions)
|
if (extensions)
|
||||||
{
|
{
|
||||||
std::string::size_type index = iter->find_last_of ('.');
|
std::string::size_type index = iter2->find_last_of ('.');
|
||||||
|
|
||||||
if (index==std::string::npos)
|
if (index==std::string::npos)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string extension = iter->substr (index+1);
|
std::string extension = iter2->substr (index+1);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@ -53,7 +41,7 @@ CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::T
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string file = iter->substr (baseSize+1);
|
std::string file = iter2->substr (baseSize+1);
|
||||||
mFiles.push_back (file);
|
mFiles.push_back (file);
|
||||||
std::replace (file.begin(), file.end(), '\\', '/');
|
std::replace (file.begin(), file.end(), '\\', '/');
|
||||||
mIndex.insert (std::make_pair (
|
mIndex.insert (std::make_pair (
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <OgreStringVector.h>
|
||||||
|
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
@ -20,6 +22,7 @@ namespace CSMWorld
|
|||||||
|
|
||||||
/// \param type Type of resources in this table.
|
/// \param type Type of resources in this table.
|
||||||
Resources (const std::string& baseDirectory, UniversalId::Type type,
|
Resources (const std::string& baseDirectory, UniversalId::Type type,
|
||||||
|
std::vector<Ogre::StringVectorPtr> resources,
|
||||||
const char * const *extensions = 0);
|
const char * const *extensions = 0);
|
||||||
|
|
||||||
int getSize() const;
|
int getSize() const;
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <OgreResourceGroupManager.h>
|
||||||
|
|
||||||
void CSMWorld::ResourcesManager::addResources (const Resources& resources)
|
void CSMWorld::ResourcesManager::addResources (const Resources& resources)
|
||||||
{
|
{
|
||||||
mResources.insert (std::make_pair (resources.getType(), resources));
|
mResources.insert (std::make_pair (resources.getType(), resources));
|
||||||
@ -11,14 +13,45 @@ void CSMWorld::ResourcesManager::addResources (const Resources& resources)
|
|||||||
|
|
||||||
void CSMWorld::ResourcesManager::listResources()
|
void CSMWorld::ResourcesManager::listResources()
|
||||||
{
|
{
|
||||||
|
// Following code was taken out of Resources ctor, since it was being executed each time
|
||||||
|
// and slow enough to showe up in the profiler.
|
||||||
|
//
|
||||||
|
// See Editor ctor which calls Bsa::registerResources()
|
||||||
|
//
|
||||||
|
// resourceGroups include those from config files, e.g.:
|
||||||
|
//
|
||||||
|
// C:/Program Files\OpenMW\data
|
||||||
|
// C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files
|
||||||
|
//
|
||||||
|
// and from archives:
|
||||||
|
//
|
||||||
|
// C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Morrowind.bsa
|
||||||
|
// C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Tribunal.bsa
|
||||||
|
// C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Bloodmoon.bsa
|
||||||
|
//
|
||||||
|
std::vector<Ogre::StringVectorPtr> resources;
|
||||||
|
|
||||||
|
Ogre::StringVector resourcesGroups =
|
||||||
|
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
|
||||||
|
|
||||||
|
for (Ogre::StringVector::iterator iter (resourcesGroups.begin());
|
||||||
|
iter!=resourcesGroups.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
resources.push_back(
|
||||||
|
Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter));
|
||||||
|
}
|
||||||
|
|
||||||
static const char * const sMeshTypes[] = { "nif", 0 };
|
static const char * const sMeshTypes[] = { "nif", 0 };
|
||||||
|
|
||||||
addResources (Resources ("meshes", UniversalId::Type_Mesh, sMeshTypes));
|
addResources (Resources ("meshes", UniversalId::Type_Mesh, resources, sMeshTypes));
|
||||||
addResources (Resources ("icons", UniversalId::Type_Icon));
|
addResources (Resources ("icons", UniversalId::Type_Icon, resources));
|
||||||
addResources (Resources ("music", UniversalId::Type_Music));
|
addResources (Resources ("music", UniversalId::Type_Music, resources));
|
||||||
addResources (Resources ("sound", UniversalId::Type_SoundRes));
|
addResources (Resources ("sound", UniversalId::Type_SoundRes, resources));
|
||||||
addResources (Resources ("textures", UniversalId::Type_Texture));
|
addResources (Resources ("textures", UniversalId::Type_Texture, resources));
|
||||||
addResources (Resources ("videos", UniversalId::Type_Video));
|
addResources (Resources ("videos", UniversalId::Type_Video, resources));
|
||||||
}
|
}
|
||||||
|
|
||||||
const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const
|
const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const
|
||||||
|
@ -17,7 +17,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
|
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
|
||||||
: mDocument (document), mAborted (false), mMessages (0), mTotalRecords (0)
|
: mDocument (document), mAborted (false), mMessages (0), mRecordsLabel (0), mTotalRecordsLabel (0)
|
||||||
{
|
{
|
||||||
setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str()));
|
setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str()));
|
||||||
|
|
||||||
@ -25,26 +25,25 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
|
|||||||
|
|
||||||
mLayout = new QVBoxLayout (this);
|
mLayout = new QVBoxLayout (this);
|
||||||
|
|
||||||
// file progress
|
// total progress
|
||||||
mFile = new QLabel (this);
|
mTotalRecordsLabel = new QLabel (this);
|
||||||
|
|
||||||
mLayout->addWidget (mFile);
|
mLayout->addWidget (mTotalRecordsLabel);
|
||||||
|
|
||||||
mFileProgress = new QProgressBar (this);
|
mTotalProgress = new QProgressBar (this);
|
||||||
|
|
||||||
mLayout->addWidget (mFileProgress);
|
mLayout->addWidget (mTotalProgress);
|
||||||
|
|
||||||
int size = static_cast<int> (document->getContentFiles().size())+1;
|
mTotalProgress->setMinimum (0);
|
||||||
if (document->isNew())
|
mTotalProgress->setMaximum (document->getData().getTotalRecords(document->getContentFiles()));
|
||||||
--size;
|
mTotalProgress->setTextVisible (true);
|
||||||
|
mTotalProgress->setValue (0);
|
||||||
|
mTotalRecords = 0;
|
||||||
|
|
||||||
mFileProgress->setMinimum (0);
|
mFilesLoaded = 0;
|
||||||
mFileProgress->setMaximum (size);
|
|
||||||
mFileProgress->setTextVisible (true);
|
|
||||||
mFileProgress->setValue (0);
|
|
||||||
|
|
||||||
// record progress
|
// record progress
|
||||||
mLayout->addWidget (mRecords = new QLabel ("Records", this));
|
mLayout->addWidget (mRecordsLabel = new QLabel ("Records", this));
|
||||||
|
|
||||||
mRecordProgress = new QProgressBar (this);
|
mRecordProgress = new QProgressBar (this);
|
||||||
|
|
||||||
@ -74,29 +73,32 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
|
|||||||
connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));
|
connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords)
|
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int fileRecords)
|
||||||
{
|
{
|
||||||
mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str()));
|
++mFilesLoaded;
|
||||||
|
size_t numFiles = mDocument->getContentFiles().size();
|
||||||
|
|
||||||
mFileProgress->setValue (mFileProgress->value()+1);
|
mTotalRecordsLabel->setText (QString::fromUtf8 (("Loading: "+name
|
||||||
|
+" ("+std::to_string(mFilesLoaded)+" of "+std::to_string((numFiles))+")").c_str()));
|
||||||
|
|
||||||
|
mTotalRecords = mTotalProgress->value();
|
||||||
|
|
||||||
mRecordProgress->setValue (0);
|
mRecordProgress->setValue (0);
|
||||||
mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1);
|
mRecordProgress->setMaximum (fileRecords>0 ? fileRecords : 1);
|
||||||
|
|
||||||
mTotalRecords = totalRecords;
|
mRecords = fileRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVDoc::LoadingDocument::nextRecord (int records)
|
void CSVDoc::LoadingDocument::nextRecord (int records)
|
||||||
{
|
{
|
||||||
if (records<=mTotalRecords)
|
if (records <= mRecords)
|
||||||
{
|
{
|
||||||
mRecordProgress->setValue (records);
|
mTotalProgress->setValue (mTotalRecords+records);
|
||||||
|
|
||||||
std::ostringstream stream;
|
mRecordProgress->setValue(records);
|
||||||
|
|
||||||
stream << "Records: " << records << " of " << mTotalRecords;
|
mRecordsLabel->setText(QString::fromStdString(
|
||||||
|
"Records: "+std::to_string(records)+" of "+std::to_string(mRecords)));
|
||||||
mRecords->setText (QString::fromUtf8 (stream.str().c_str()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,12 +178,12 @@ void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name,
|
void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name,
|
||||||
int totalRecords)
|
int fileRecords)
|
||||||
{
|
{
|
||||||
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
|
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
|
||||||
|
|
||||||
if (iter!=mDocuments.end())
|
if (iter!=mDocuments.end())
|
||||||
iter->second->nextStage (name, totalRecords);
|
iter->second->nextStage (name, fileRecords);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records)
|
void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records)
|
||||||
|
@ -25,16 +25,18 @@ namespace CSVDoc
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
CSMDoc::Document *mDocument;
|
CSMDoc::Document *mDocument;
|
||||||
QLabel *mFile;
|
QLabel *mTotalRecordsLabel;
|
||||||
QLabel *mRecords;
|
QLabel *mRecordsLabel;
|
||||||
QProgressBar *mFileProgress;
|
QProgressBar *mTotalProgress;
|
||||||
QProgressBar *mRecordProgress;
|
QProgressBar *mRecordProgress;
|
||||||
bool mAborted;
|
bool mAborted;
|
||||||
QDialogButtonBox *mButtons;
|
QDialogButtonBox *mButtons;
|
||||||
QLabel *mError;
|
QLabel *mError;
|
||||||
QListWidget *mMessages;
|
QListWidget *mMessages;
|
||||||
QVBoxLayout *mLayout;
|
QVBoxLayout *mLayout;
|
||||||
|
int mRecords;
|
||||||
int mTotalRecords;
|
int mTotalRecords;
|
||||||
|
int mFilesLoaded;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -38,9 +38,9 @@ void CSVFilter::FilterBox::dropEvent (QDropEvent* event)
|
|||||||
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
|
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<CSMWorld::UniversalId> data = mime->getData();
|
std::vector<CSMWorld::UniversalId> mimeData = mime->getData();
|
||||||
|
|
||||||
emit recordDropped(data, event->proposedAction());
|
emit recordDropped(mimeData, event->proposedAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event)
|
void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event)
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include <OgreSceneManager.h>
|
#include <OgreSceneManager.h>
|
||||||
#include <OgreSceneNode.h>
|
#include <OgreSceneNode.h>
|
||||||
#include <OgreManualObject.h>
|
#include <OgreManualObject.h>
|
||||||
|
#include <OgreMaterialManager.h>
|
||||||
|
#include <OgreTechnique.h>
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
#include <components/esm/loadland.hpp>
|
#include <components/esm/loadland.hpp>
|
||||||
|
@ -536,18 +536,18 @@ std::pair< int, int > CSVRender::PagedWorldspaceWidget::getCoordinatesFromId (co
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CSVRender::PagedWorldspaceWidget::handleDrop (
|
bool CSVRender::PagedWorldspaceWidget::handleDrop (
|
||||||
const std::vector< CSMWorld::UniversalId >& data, DropType type)
|
const std::vector< CSMWorld::UniversalId >& dropData, DropType type)
|
||||||
{
|
{
|
||||||
if (WorldspaceWidget::handleDrop (data, type))
|
if (WorldspaceWidget::handleDrop (dropData, type))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (type!=Type_CellsExterior)
|
if (type!=Type_CellsExterior)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool selectionChanged = false;
|
bool selectionChanged = false;
|
||||||
for (unsigned i = 0; i < data.size(); ++i)
|
for (unsigned i = 0; i < dropData.size(); ++i)
|
||||||
{
|
{
|
||||||
std::pair<int, int> coordinates(getCoordinatesFromId(data[i].getId()));
|
std::pair<int, int> coordinates(getCoordinatesFromId(dropData[i].getId()));
|
||||||
if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second)))
|
if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second)))
|
||||||
{
|
{
|
||||||
selectionChanged = true;
|
selectionChanged = true;
|
||||||
|
@ -74,6 +74,8 @@ namespace CSVRender
|
|||||||
/// \todo make shortcut configurable
|
/// \todo make shortcut configurable
|
||||||
QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut);
|
QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut);
|
||||||
connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest()));
|
connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest()));
|
||||||
|
|
||||||
|
updateOgreWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent)
|
CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent)
|
||||||
|
@ -105,7 +105,7 @@ TextOverlay::TextOverlay(const Ogre::MovableObject* obj, const Ogre::Camera* cam
|
|||||||
"TransOverlayMaterial");
|
"TransOverlayMaterial");
|
||||||
if(mQuadMaterial.isNull())
|
if(mQuadMaterial.isNull())
|
||||||
{
|
{
|
||||||
Ogre::MaterialPtr mQuadMaterial = Ogre::MaterialManager::getSingleton().create(
|
mQuadMaterial = Ogre::MaterialManager::getSingleton().create(
|
||||||
"TransOverlayMaterial",
|
"TransOverlayMaterial",
|
||||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true );
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true );
|
||||||
Ogre::Pass *pass = mQuadMaterial->getTechnique( 0 )->getPass( 0 );
|
Ogre::Pass *pass = mQuadMaterial->getTechnique( 0 )->getPass( 0 );
|
||||||
|
@ -84,21 +84,21 @@ void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelI
|
|||||||
emit closeRequest();
|
emit closeRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& data, DropType type)
|
bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& dropData, DropType type)
|
||||||
{
|
{
|
||||||
if (WorldspaceWidget::handleDrop (data, type))
|
if (WorldspaceWidget::handleDrop (dropData, type))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (type!=Type_CellsInterior)
|
if (type!=Type_CellsInterior)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mCellId = data.begin()->getId();
|
mCellId = dropData.begin()->getId();
|
||||||
Cell *cell = new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics());
|
Cell *cell = new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics());
|
||||||
connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot()));
|
connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot()));
|
||||||
mCell.reset (cell);
|
mCell.reset (cell);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
emit cellChanged(*data.begin());
|
emit cellChanged(*dropData.begin());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -233,15 +233,15 @@ CSVRender::WorldspaceWidget::dropRequirments
|
|||||||
return ignored;
|
return ignored;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSVRender::WorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& data,
|
bool CSVRender::WorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& dropData,
|
||||||
DropType type)
|
DropType type)
|
||||||
{
|
{
|
||||||
if (type==Type_DebugProfile)
|
if (type==Type_DebugProfile)
|
||||||
{
|
{
|
||||||
if (mRun)
|
if (mRun)
|
||||||
{
|
{
|
||||||
for (std::vector<CSMWorld::UniversalId>::const_iterator iter (data.begin());
|
for (std::vector<CSMWorld::UniversalId>::const_iterator iter (dropData.begin());
|
||||||
iter!=data.end(); ++iter)
|
iter!=dropData.end(); ++iter)
|
||||||
mRun->addProfile (iter->getId());
|
mRun->addProfile (iter->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,14 +667,14 @@ void CSVWorld::EditWidget::remake(int row)
|
|||||||
int displayRole = tree->nestedHeaderData (i, col,
|
int displayRole = tree->nestedHeaderData (i, col,
|
||||||
Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt();
|
Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt();
|
||||||
|
|
||||||
CSMWorld::ColumnBase::Display display =
|
CSMWorld::ColumnBase::Display display2 =
|
||||||
static_cast<CSMWorld::ColumnBase::Display> (displayRole);
|
static_cast<CSMWorld::ColumnBase::Display> (displayRole);
|
||||||
|
|
||||||
mNestedTableDispatcher->makeDelegate (display);
|
mNestedTableDispatcher->makeDelegate (display2);
|
||||||
|
|
||||||
// FIXME: assumed all columns are editable
|
// FIXME: assumed all columns are editable
|
||||||
QWidget* editor =
|
QWidget* editor =
|
||||||
mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i)));
|
mNestedTableDispatcher->makeEditor (display2, tree->index (0, col, tree->index(row, i)));
|
||||||
if (editor)
|
if (editor)
|
||||||
{
|
{
|
||||||
mNestedTableMapper->addMapping (editor, col);
|
mNestedTableMapper->addMapping (editor, col);
|
||||||
@ -699,7 +699,7 @@ void CSVWorld::EditWidget::remake(int row)
|
|||||||
label->setEnabled(false);
|
label->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
createEditorContextMenu(editor, display, row);
|
createEditorContextMenu(editor, display2, row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i)));
|
mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i)));
|
||||||
|
@ -67,8 +67,8 @@ void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event)
|
|||||||
CSMWorld::ColumnBase::Display display = getIndexDisplayType(index);
|
CSMWorld::ColumnBase::Display display = getIndexDisplayType(index);
|
||||||
if (CSVWorld::DragDropUtils::canAcceptData(*event, display))
|
if (CSVWorld::DragDropUtils::canAcceptData(*event, display))
|
||||||
{
|
{
|
||||||
const CSMWorld::TableMimeData *data = CSVWorld::DragDropUtils::getTableMimeData(*event);
|
const CSMWorld::TableMimeData *mimeData = CSVWorld::DragDropUtils::getTableMimeData(*event);
|
||||||
if (data->fromDocument(mDocument))
|
if (mimeData->fromDocument(mDocument))
|
||||||
{
|
{
|
||||||
CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, display);
|
CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, display);
|
||||||
QVariant newIndexData = QString::fromUtf8(id.getId().c_str());
|
QVariant newIndexData = QString::fromUtf8(id.getId().c_str());
|
||||||
|
@ -26,10 +26,10 @@ CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUnd
|
|||||||
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
|
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
|
||||||
iter!=types.end(); ++iter)
|
iter!=types.end(); ++iter)
|
||||||
{
|
{
|
||||||
CSMWorld::UniversalId id (*iter, "");
|
CSMWorld::UniversalId id2 (*iter, "");
|
||||||
|
|
||||||
mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(),
|
mType->addItem (QIcon (id2.getIcon().c_str()), id2.getTypeName().c_str(),
|
||||||
static_cast<int> (id.getType()));
|
static_cast<int> (id2.getType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
insertBeforeButtons (mType, false);
|
insertBeforeButtons (mType, false);
|
||||||
|
@ -187,18 +187,18 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection
|
|||||||
emit updateTitle();
|
emit updateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& data)
|
void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& dropData)
|
||||||
{
|
{
|
||||||
CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL;
|
CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL;
|
||||||
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL;
|
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL;
|
||||||
CSVWidget::SceneToolbar* toolbar = NULL;
|
CSVWidget::SceneToolbar* toolbar = NULL;
|
||||||
|
|
||||||
CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (data);
|
CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (dropData);
|
||||||
|
|
||||||
switch (mScene->getDropRequirements (type))
|
switch (mScene->getDropRequirements (type))
|
||||||
{
|
{
|
||||||
case CSVRender::WorldspaceWidget::canHandle:
|
case CSVRender::WorldspaceWidget::canHandle:
|
||||||
mScene->handleDrop (data, type);
|
mScene->handleDrop (dropData, type);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CSVRender::WorldspaceWidget::needPaged:
|
case CSVRender::WorldspaceWidget::needPaged:
|
||||||
@ -206,15 +206,15 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI
|
|||||||
toolbar = makeToolbar(pagedNewWidget, widget_Paged);
|
toolbar = makeToolbar(pagedNewWidget, widget_Paged);
|
||||||
makeConnections(pagedNewWidget);
|
makeConnections(pagedNewWidget);
|
||||||
replaceToolbarAndWorldspace(pagedNewWidget, toolbar);
|
replaceToolbarAndWorldspace(pagedNewWidget, toolbar);
|
||||||
mScene->handleDrop (data, type);
|
mScene->handleDrop (dropData, type);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CSVRender::WorldspaceWidget::needUnpaged:
|
case CSVRender::WorldspaceWidget::needUnpaged:
|
||||||
unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(data.begin()->getId(), mDocument, this);
|
unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(dropData.begin()->getId(), mDocument, this);
|
||||||
toolbar = makeToolbar(unPagedNewWidget, widget_Unpaged);
|
toolbar = makeToolbar(unPagedNewWidget, widget_Unpaged);
|
||||||
makeConnections(unPagedNewWidget);
|
makeConnections(unPagedNewWidget);
|
||||||
replaceToolbarAndWorldspace(unPagedNewWidget, toolbar);
|
replaceToolbarAndWorldspace(unPagedNewWidget, toolbar);
|
||||||
cellSelectionChanged(*(data.begin()));
|
cellSelectionChanged(*(dropData.begin()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CSVRender::WorldspaceWidget::ignored:
|
case CSVRender::WorldspaceWidget::ignored:
|
||||||
|
@ -280,12 +280,6 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
|||||||
setSelectionBehavior (QAbstractItemView::SelectRows);
|
setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||||
setSelectionMode (QAbstractItemView::ExtendedSelection);
|
setSelectionMode (QAbstractItemView::ExtendedSelection);
|
||||||
|
|
||||||
setSortingEnabled (sorting);
|
|
||||||
if (sorting)
|
|
||||||
{
|
|
||||||
sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
int columns = mModel->columnCount();
|
int columns = mModel->columnCount();
|
||||||
for (int i=0; i<columns; ++i)
|
for (int i=0; i<columns; ++i)
|
||||||
{
|
{
|
||||||
@ -306,6 +300,13 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
|||||||
hideColumn (i);
|
hideColumn (i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sorting)
|
||||||
|
{
|
||||||
|
// FIXME: some tables (e.g. CellRef) have this column hidden, which makes it confusing
|
||||||
|
sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder);
|
||||||
|
}
|
||||||
|
setSortingEnabled (sorting);
|
||||||
|
|
||||||
mEditAction = new QAction (tr ("Edit Record"), this);
|
mEditAction = new QAction (tr ("Edit Record"), this);
|
||||||
connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));
|
connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));
|
||||||
addAction (mEditAction);
|
addAction (mEditAction);
|
||||||
@ -429,7 +430,7 @@ std::vector<std::string> CSVWorld::Table::getSelectedIds() const
|
|||||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||||
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
|
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
|
||||||
|
|
||||||
for (QModelIndexList::const_iterator iter (selectedRows.begin());
|
for (QModelIndexList::const_iterator iter (selectedRows.begin());
|
||||||
iter != selectedRows.end();
|
iter != selectedRows.end();
|
||||||
++iter)
|
++iter)
|
||||||
{
|
{
|
||||||
|
@ -203,14 +203,14 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event)
|
|||||||
{
|
{
|
||||||
if (QDropEvent* drop = dynamic_cast<QDropEvent*>(event))
|
if (QDropEvent* drop = dynamic_cast<QDropEvent*>(event))
|
||||||
{
|
{
|
||||||
const CSMWorld::TableMimeData* data = dynamic_cast<const CSMWorld::TableMimeData*>(drop->mimeData());
|
const CSMWorld::TableMimeData* mimeData = dynamic_cast<const CSMWorld::TableMimeData*>(drop->mimeData());
|
||||||
if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped
|
if (!mimeData) // May happen when non-records (e.g. plain text) are dragged and dropped
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter);
|
bool handled = mimeData->holdsType(CSMWorld::UniversalId::Type_Filter);
|
||||||
if (handled)
|
if (handled)
|
||||||
{
|
{
|
||||||
mFilterBox->setRecordFilter(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId());
|
mFilterBox->setRecordFilter(mimeData->returnMatching(CSMWorld::UniversalId::Type_Filter).getId());
|
||||||
}
|
}
|
||||||
return handled;
|
return handled;
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,8 @@ using namespace Ogre;
|
|||||||
|
|
||||||
static bool fsstrict = false;
|
static bool fsstrict = false;
|
||||||
|
|
||||||
|
static const std::ctype<char>& facet = std::use_facet<std::ctype<char> >(std::locale::classic());
|
||||||
|
|
||||||
static char strict_normalize_char(char ch)
|
static char strict_normalize_char(char ch)
|
||||||
{
|
{
|
||||||
return ch == '\\' ? '/' : ch;
|
return ch == '\\' ? '/' : ch;
|
||||||
@ -56,7 +58,7 @@ static char strict_normalize_char(char ch)
|
|||||||
|
|
||||||
static char nonstrict_normalize_char(char ch)
|
static char nonstrict_normalize_char(char ch)
|
||||||
{
|
{
|
||||||
return ch == '\\' ? '/' : std::tolower(ch,std::locale::classic());
|
return ch == '\\' ? '/' : facet.tolower(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
template<typename T1, typename T2>
|
||||||
@ -245,7 +247,7 @@ public:
|
|||||||
|
|
||||||
time_t getModifiedTime(const String&) { return 0; }
|
time_t getModifiedTime(const String&) { return 0; }
|
||||||
|
|
||||||
// This is never called as far as I can see.
|
// This is never called as far as I can see. (actually called from CSMWorld::Resources ctor)
|
||||||
StringVectorPtr list(bool recursive = true, bool dirs = false)
|
StringVectorPtr list(bool recursive = true, bool dirs = false)
|
||||||
{
|
{
|
||||||
return find ("*", recursive, dirs);
|
return find ("*", recursive, dirs);
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
void ESM::RefNum::load (ESMReader& esm, bool wide)
|
void ESM::RefNum::load (ESMReader& esm, bool wide)
|
||||||
{
|
{
|
||||||
if (wide)
|
if (wide)
|
||||||
esm.getHNT (*this, "FRMR", 8);
|
esm.getHNT (*this, SREC_FRMR, 8);
|
||||||
else
|
else
|
||||||
esm.getHNT (mIndex, "FRMR");
|
esm.getHNT (mIndex, SREC_FRMR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const
|
void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const
|
||||||
@ -36,14 +36,14 @@ void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum)
|
|||||||
// the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system.
|
// the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system.
|
||||||
// Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway,
|
// Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway,
|
||||||
// because any item can theoretically be moved by a script.
|
// because any item can theoretically be moved by a script.
|
||||||
if (esm.isNextSub ("NAM0"))
|
if (esm.isNextSub (SREC_NAM0))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
blank();
|
blank();
|
||||||
|
|
||||||
mRefNum.load (esm, wideRefNum);
|
mRefNum.load (esm, wideRefNum);
|
||||||
|
|
||||||
mRefID = esm.getHNString ("NAME");
|
esm.getHNString (SREC_NAME, mRefID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted)
|
void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted)
|
||||||
@ -180,7 +180,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool
|
|||||||
void ESM::CellRef::blank()
|
void ESM::CellRef::blank()
|
||||||
{
|
{
|
||||||
mRefNum.unset();
|
mRefNum.unset();
|
||||||
mRefID.clear();
|
mRefID.clear();
|
||||||
mScale = 1;
|
mScale = 1;
|
||||||
mOwner.clear();
|
mOwner.clear();
|
||||||
mGlobalVariable.clear();
|
mGlobalVariable.clear();
|
||||||
@ -196,7 +196,7 @@ void ESM::CellRef::blank()
|
|||||||
mTrap.clear();
|
mTrap.clear();
|
||||||
mReferenceBlocked = -1;
|
mReferenceBlocked = -1;
|
||||||
mTeleport = false;
|
mTeleport = false;
|
||||||
|
|
||||||
for (int i=0; i<3; ++i)
|
for (int i=0; i<3; ++i)
|
||||||
{
|
{
|
||||||
mDoorDest.pos[i] = 0;
|
mDoorDest.pos[i] = 0;
|
||||||
@ -206,6 +206,67 @@ void ESM::CellRef::blank()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ESM::CellRef::CellRef ()
|
||||||
|
{
|
||||||
|
blank();
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::CellRef::CellRef (const CellRef& other)
|
||||||
|
: mRefNum(other.mRefNum)
|
||||||
|
, mRefID(other.mRefID)
|
||||||
|
, mScale(other.mScale)
|
||||||
|
, mOwner(other.mOwner)
|
||||||
|
, mGlobalVariable(other.mGlobalVariable)
|
||||||
|
, mSoul(other.mSoul)
|
||||||
|
, mFaction(other.mFaction)
|
||||||
|
, mFactionRank(other.mFactionRank)
|
||||||
|
, mChargeInt(other.mChargeInt)
|
||||||
|
, mEnchantmentCharge(other.mEnchantmentCharge)
|
||||||
|
, mGoldValue(other.mGoldValue)
|
||||||
|
, mTeleport(other.mTeleport)
|
||||||
|
, mDoorDest(other.mDoorDest)
|
||||||
|
, mDestCell(other.mDestCell)
|
||||||
|
, mLockLevel(other.mLockLevel)
|
||||||
|
, mKey(other.mKey)
|
||||||
|
, mTrap(other.mTrap)
|
||||||
|
, mReferenceBlocked(other.mReferenceBlocked)
|
||||||
|
, mPos(other.mPos)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::CellRef::CellRef (CellRef&& other)
|
||||||
|
{
|
||||||
|
*this = std::move(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::CellRef& ESM::CellRef::operator= (CellRef&& other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
mRefNum = other.mRefNum; // RefNum
|
||||||
|
mRefID = std::move(other.mRefID);
|
||||||
|
mScale = other.mScale;
|
||||||
|
mOwner = std::move(other.mOwner);
|
||||||
|
mGlobalVariable = std::move(other.mGlobalVariable);
|
||||||
|
mSoul = std::move(other.mSoul);
|
||||||
|
mFaction = std::move(other.mFaction);
|
||||||
|
mFactionRank = other.mFactionRank;
|
||||||
|
mChargeInt = other.mChargeInt;
|
||||||
|
mEnchantmentCharge = other.mEnchantmentCharge;
|
||||||
|
mGoldValue = other.mGoldValue;
|
||||||
|
mTeleport = other.mTeleport;
|
||||||
|
mDoorDest = other.mDoorDest; // Position
|
||||||
|
mDestCell = std::move(other.mDestCell);
|
||||||
|
mLockLevel = other.mLockLevel;
|
||||||
|
mKey = std::move(other.mKey);
|
||||||
|
mTrap = std::move(other.mTrap);
|
||||||
|
mReferenceBlocked = other.mReferenceBlocked;
|
||||||
|
mPos = other.mPos; // Position
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
bool ESM::operator== (const RefNum& left, const RefNum& right)
|
bool ESM::operator== (const RefNum& left, const RefNum& right)
|
||||||
{
|
{
|
||||||
return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile;
|
return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile;
|
||||||
|
@ -109,6 +109,15 @@ namespace ESM
|
|||||||
void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const;
|
void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
|
|
||||||
|
CellRef();
|
||||||
|
~CellRef() = default;
|
||||||
|
|
||||||
|
CellRef(const CellRef&);
|
||||||
|
CellRef& operator=(const CellRef&) = default;
|
||||||
|
|
||||||
|
CellRef (CellRef&& other);
|
||||||
|
CellRef& operator=(CellRef&& other);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool operator== (const RefNum& left, const RefNum& right);
|
bool operator== (const RefNum& left, const RefNum& right);
|
||||||
|
@ -128,7 +128,9 @@ enum RecNameInts
|
|||||||
enum SubRecNameInts
|
enum SubRecNameInts
|
||||||
{
|
{
|
||||||
SREC_DELE = ESM::FourCC<'D','E','L','E'>::value,
|
SREC_DELE = ESM::FourCC<'D','E','L','E'>::value,
|
||||||
SREC_NAME = ESM::FourCC<'N','A','M','E'>::value
|
SREC_NAME = ESM::FourCC<'N','A','M','E'>::value,
|
||||||
|
SREC_NAM0 = ESM::FourCC<'N','A','M','0'>::value,
|
||||||
|
SREC_FRMR = ESM::FourCC<'F','R','M','R'>::value
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,12 @@ std::string ESMReader::getHNString(const char* name)
|
|||||||
return getHString();
|
return getHString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESMReader::getHNString(const int name, std::string& str)
|
||||||
|
{
|
||||||
|
getSubNameIs(name);
|
||||||
|
getHString(str);
|
||||||
|
}
|
||||||
|
|
||||||
std::string ESMReader::getHString()
|
std::string ESMReader::getHString()
|
||||||
{
|
{
|
||||||
getSubHeader();
|
getSubHeader();
|
||||||
@ -130,6 +136,28 @@ std::string ESMReader::getHString()
|
|||||||
return getString(mCtx.leftSub);
|
return getString(mCtx.leftSub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESMReader::getHString(std::string& str)
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
|
||||||
|
// Hack to make MultiMark.esp load. Zero-length strings do not
|
||||||
|
// occur in any of the official mods, but MultiMark makes use of
|
||||||
|
// them. For some reason, they break the rules, and contain a byte
|
||||||
|
// (value 0) even if the header says there is no data. If
|
||||||
|
// Morrowind accepts it, so should we.
|
||||||
|
if (mCtx.leftSub == 0)
|
||||||
|
{
|
||||||
|
// Skip the following zero byte
|
||||||
|
mCtx.leftRec--;
|
||||||
|
char c;
|
||||||
|
getExact(&c, 1);
|
||||||
|
str = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getString(str, mCtx.leftSub);
|
||||||
|
}
|
||||||
|
|
||||||
void ESMReader::getHExact(void*p, int size)
|
void ESMReader::getHExact(void*p, int size)
|
||||||
{
|
{
|
||||||
getSubHeader();
|
getSubHeader();
|
||||||
@ -159,6 +187,24 @@ void ESMReader::getSubNameIs(const char* name)
|
|||||||
+ mCtx.subName.toString());
|
+ mCtx.subName.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESMReader::getSubNameIs(const int name)
|
||||||
|
{
|
||||||
|
getSubName();
|
||||||
|
if (mCtx.subName != name)
|
||||||
|
{
|
||||||
|
unsigned char typeName[4];
|
||||||
|
typeName[0] = name & 0xff;
|
||||||
|
typeName[1] = (name >> 8) & 0xff;
|
||||||
|
typeName[2] = (name >> 16) & 0xff;
|
||||||
|
typeName[3] = (name >> 24) & 0xff;
|
||||||
|
|
||||||
|
std::string subName = std::string((char*)typeName, 4);
|
||||||
|
|
||||||
|
fail("Expected subrecord " + subName + " but got "
|
||||||
|
+ mCtx.subName.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ESMReader::isNextSub(const char* name)
|
bool ESMReader::isNextSub(const char* name)
|
||||||
{
|
{
|
||||||
if (!mCtx.leftRec)
|
if (!mCtx.leftRec)
|
||||||
@ -174,6 +220,21 @@ bool ESMReader::isNextSub(const char* name)
|
|||||||
return !mCtx.subCached;
|
return !mCtx.subCached;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ESMReader::isNextSub(const int name)
|
||||||
|
{
|
||||||
|
if (!mCtx.leftRec)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
getSubName();
|
||||||
|
|
||||||
|
// If the name didn't match, then mark the it as 'cached' so it's
|
||||||
|
// available for the next call to getSubName.
|
||||||
|
mCtx.subCached = (mCtx.subName != name);
|
||||||
|
|
||||||
|
// If subCached is false, then subName == name.
|
||||||
|
return !mCtx.subCached;
|
||||||
|
}
|
||||||
|
|
||||||
bool ESMReader::peekNextSub(const char *name)
|
bool ESMReader::peekNextSub(const char *name)
|
||||||
{
|
{
|
||||||
if (!mCtx.leftRec)
|
if (!mCtx.leftRec)
|
||||||
@ -347,6 +408,26 @@ std::string ESMReader::getString(int size)
|
|||||||
return std::string (ptr, size);
|
return std::string (ptr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESMReader::getString(std::string& str, int size)
|
||||||
|
{
|
||||||
|
size_t s = size;
|
||||||
|
if (mBuffer.size() <= s)
|
||||||
|
// Add some extra padding to reduce the chance of having to resize again later.
|
||||||
|
mBuffer.resize(3*s);
|
||||||
|
|
||||||
|
mBuffer[s] = 0; // And make sure the string is zero terminated
|
||||||
|
|
||||||
|
char *ptr = &mBuffer[0];
|
||||||
|
getExact(ptr, size); // read ESM data
|
||||||
|
|
||||||
|
size = static_cast<int>(strnlen(ptr, size));
|
||||||
|
|
||||||
|
if (mEncoder)
|
||||||
|
str = mEncoder->getUtf8(ptr, size); // Convert to UTF8 and return
|
||||||
|
else
|
||||||
|
str = std::string (ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
void ESMReader::fail(const std::string &msg)
|
void ESMReader::fail(const std::string &msg)
|
||||||
{
|
{
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -103,6 +103,13 @@ public:
|
|||||||
getHT(x);
|
getHT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
void getHNT(X &x, const int name)
|
||||||
|
{
|
||||||
|
getSubNameIs(name);
|
||||||
|
getHT(x);
|
||||||
|
}
|
||||||
|
|
||||||
// Optional version of getHNT
|
// Optional version of getHNT
|
||||||
template <typename X>
|
template <typename X>
|
||||||
void getHNOT(X &x, const char* name)
|
void getHNOT(X &x, const char* name)
|
||||||
@ -121,6 +128,14 @@ public:
|
|||||||
getHT(x);
|
getHT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
void getHNT(X &x, const int name, int size)
|
||||||
|
{
|
||||||
|
assert(sizeof(X) == size);
|
||||||
|
getSubNameIs(name);
|
||||||
|
getHT(x);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename X>
|
template <typename X>
|
||||||
void getHNOT(X &x, const char* name, int size)
|
void getHNOT(X &x, const char* name, int size)
|
||||||
{
|
{
|
||||||
@ -159,9 +174,11 @@ public:
|
|||||||
|
|
||||||
// Read a string with the given sub-record name
|
// Read a string with the given sub-record name
|
||||||
std::string getHNString(const char* name);
|
std::string getHNString(const char* name);
|
||||||
|
void getHNString(const int name, std::string& str);
|
||||||
|
|
||||||
// Read a string, including the sub-record header (but not the name)
|
// Read a string, including the sub-record header (but not the name)
|
||||||
std::string getHString();
|
std::string getHString();
|
||||||
|
void getHString(std::string& str);
|
||||||
|
|
||||||
// Read the given number of bytes from a subrecord
|
// Read the given number of bytes from a subrecord
|
||||||
void getHExact(void*p, int size);
|
void getHExact(void*p, int size);
|
||||||
@ -177,6 +194,7 @@ public:
|
|||||||
|
|
||||||
// Get the next subrecord name and check if it matches the parameter
|
// Get the next subrecord name and check if it matches the parameter
|
||||||
void getSubNameIs(const char* name);
|
void getSubNameIs(const char* name);
|
||||||
|
void getSubNameIs(const int name);
|
||||||
|
|
||||||
/** Checks if the next sub record name matches the parameter. If it
|
/** Checks if the next sub record name matches the parameter. If it
|
||||||
does, it is read into 'subName' just as if getSubName() was
|
does, it is read into 'subName' just as if getSubName() was
|
||||||
@ -184,6 +202,7 @@ public:
|
|||||||
calls to getSubName(), isNextSub() and getSubNameIs().
|
calls to getSubName(), isNextSub() and getSubNameIs().
|
||||||
*/
|
*/
|
||||||
bool isNextSub(const char* name);
|
bool isNextSub(const char* name);
|
||||||
|
bool isNextSub(const int name);
|
||||||
|
|
||||||
bool peekNextSub(const char* name);
|
bool peekNextSub(const char* name);
|
||||||
|
|
||||||
@ -256,6 +275,7 @@ public:
|
|||||||
// Read the next 'size' bytes and return them as a string. Converts
|
// Read the next 'size' bytes and return them as a string. Converts
|
||||||
// them from native encoding to UTF8 in the process.
|
// them from native encoding to UTF8 in the process.
|
||||||
std::string getString(int size);
|
std::string getString(int size);
|
||||||
|
void getString(std::string& str, int size);
|
||||||
|
|
||||||
void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
|
void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
|
||||||
uint64_t getOffset() { return mEsm->tell(); }
|
uint64_t getOffset() { return mEsm->tell(); }
|
||||||
|
@ -35,6 +35,20 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool cEqual(const std::string &x, const std::string &y) {
|
||||||
|
if (x.size() != y.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string::const_iterator xit = x.begin();
|
||||||
|
std::string::const_iterator yit = y.begin();
|
||||||
|
for (; xit != x.end(); ++xit, ++yit) {
|
||||||
|
if (*xit != *yit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int ciCompareLen(const std::string &x, const std::string &y, size_t len)
|
static int ciCompareLen(const std::string &x, const std::string &y, size_t len)
|
||||||
{
|
{
|
||||||
std::string::const_iterator xit = x.begin();
|
std::string::const_iterator xit = x.begin();
|
||||||
|
17
extern/murmurhash/CMakeLists.txt
vendored
Normal file
17
extern/murmurhash/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
|
# This is NOT intended as a stand-alone build system! Instead, you should include this from the main CMakeLists of your project.
|
||||||
|
|
||||||
|
set(MURMURHASH_LIBRARY "murmurhash")
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
set(SOURCE_FILES
|
||||||
|
MurmurHash2.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(${MURMURHASH_LIBRARY} STATIC ${SOURCE_FILES})
|
||||||
|
|
||||||
|
set(MURMURHASH_LIBRARIES ${MURMURHASH_LIBRARY})
|
||||||
|
|
||||||
|
link_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set(MURMURHASH_LIBRARIES ${MURMURHASH_LIBRARIES} PARENT_SCOPE)
|
522
extern/murmurhash/MurmurHash2.cpp
vendored
Normal file
522
extern/murmurhash/MurmurHash2.cpp
vendored
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MurmurHash2 was written by Austin Appleby, and is placed in the public
|
||||||
|
// domain. The author hereby disclaims copyright to this source code.
|
||||||
|
|
||||||
|
// Note - This code makes a few assumptions about how your machine behaves -
|
||||||
|
|
||||||
|
// 1. We can read a 4-byte value from any address without crashing
|
||||||
|
// 2. sizeof(int) == 4
|
||||||
|
|
||||||
|
// And it has a few limitations -
|
||||||
|
|
||||||
|
// 1. It will not work incrementally.
|
||||||
|
// 2. It will not produce the same results on little-endian and big-endian
|
||||||
|
// machines.
|
||||||
|
|
||||||
|
#include "MurmurHash2.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Platform-specific functions and macros
|
||||||
|
|
||||||
|
// Microsoft Visual Studio
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
|
||||||
|
#define BIG_CONSTANT(x) (x)
|
||||||
|
|
||||||
|
// Other compilers
|
||||||
|
|
||||||
|
#else // defined(_MSC_VER)
|
||||||
|
|
||||||
|
#define BIG_CONSTANT(x) (x##LLU)
|
||||||
|
|
||||||
|
#endif // !defined(_MSC_VER)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
|
||||||
|
{
|
||||||
|
// 'm' and 'r' are mixing constants generated offline.
|
||||||
|
// They're not really 'magic', they just happen to work well.
|
||||||
|
|
||||||
|
const uint32_t m = 0x5bd1e995;
|
||||||
|
const int r = 24;
|
||||||
|
|
||||||
|
// Initialize the hash to a 'random' value
|
||||||
|
|
||||||
|
uint32_t h = seed ^ len;
|
||||||
|
|
||||||
|
// Mix 4 bytes at a time into the hash
|
||||||
|
|
||||||
|
const unsigned char * data = (const unsigned char *)key;
|
||||||
|
|
||||||
|
while(len >= 4)
|
||||||
|
{
|
||||||
|
uint32_t k = *(uint32_t*)data;
|
||||||
|
|
||||||
|
k *= m;
|
||||||
|
k ^= k >> r;
|
||||||
|
k *= m;
|
||||||
|
|
||||||
|
h *= m;
|
||||||
|
h ^= k;
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the last few bytes of the input array
|
||||||
|
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 3: h ^= data[2] << 16;
|
||||||
|
case 2: h ^= data[1] << 8;
|
||||||
|
case 1: h ^= data[0];
|
||||||
|
h *= m;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do a few final mixes of the hash to ensure the last few
|
||||||
|
// bytes are well-incorporated.
|
||||||
|
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> 15;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MurmurHash2, 64-bit versions, by Austin Appleby
|
||||||
|
|
||||||
|
// The same caveats as 32-bit MurmurHash2 apply here - beware of alignment
|
||||||
|
// and endian-ness issues if used across multiple platforms.
|
||||||
|
|
||||||
|
// 64-bit hash for 64-bit platforms
|
||||||
|
|
||||||
|
uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed )
|
||||||
|
{
|
||||||
|
const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
|
||||||
|
const int r = 47;
|
||||||
|
|
||||||
|
uint64_t h = seed ^ (len * m);
|
||||||
|
|
||||||
|
const uint64_t * data = (const uint64_t *)key;
|
||||||
|
const uint64_t * end = data + (len/8);
|
||||||
|
|
||||||
|
while(data != end)
|
||||||
|
{
|
||||||
|
uint64_t k = *data++;
|
||||||
|
|
||||||
|
k *= m;
|
||||||
|
k ^= k >> r;
|
||||||
|
k *= m;
|
||||||
|
|
||||||
|
h ^= k;
|
||||||
|
h *= m;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned char * data2 = (const unsigned char*)data;
|
||||||
|
|
||||||
|
switch(len & 7)
|
||||||
|
{
|
||||||
|
case 7: h ^= uint64_t(data2[6]) << 48;
|
||||||
|
case 6: h ^= uint64_t(data2[5]) << 40;
|
||||||
|
case 5: h ^= uint64_t(data2[4]) << 32;
|
||||||
|
case 4: h ^= uint64_t(data2[3]) << 24;
|
||||||
|
case 3: h ^= uint64_t(data2[2]) << 16;
|
||||||
|
case 2: h ^= uint64_t(data2[1]) << 8;
|
||||||
|
case 1: h ^= uint64_t(data2[0]);
|
||||||
|
h *= m;
|
||||||
|
};
|
||||||
|
|
||||||
|
h ^= h >> r;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> r;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 64-bit hash for 32-bit platforms
|
||||||
|
|
||||||
|
uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed )
|
||||||
|
{
|
||||||
|
const uint32_t m = 0x5bd1e995;
|
||||||
|
const int r = 24;
|
||||||
|
|
||||||
|
uint32_t h1 = uint32_t(seed) ^ len;
|
||||||
|
uint32_t h2 = uint32_t(seed >> 32);
|
||||||
|
|
||||||
|
const uint32_t * data = (const uint32_t *)key;
|
||||||
|
|
||||||
|
while(len >= 8)
|
||||||
|
{
|
||||||
|
uint32_t k1 = *data++;
|
||||||
|
k1 *= m; k1 ^= k1 >> r; k1 *= m;
|
||||||
|
h1 *= m; h1 ^= k1;
|
||||||
|
len -= 4;
|
||||||
|
|
||||||
|
uint32_t k2 = *data++;
|
||||||
|
k2 *= m; k2 ^= k2 >> r; k2 *= m;
|
||||||
|
h2 *= m; h2 ^= k2;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(len >= 4)
|
||||||
|
{
|
||||||
|
uint32_t k1 = *data++;
|
||||||
|
k1 *= m; k1 ^= k1 >> r; k1 *= m;
|
||||||
|
h1 *= m; h1 ^= k1;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 3: h2 ^= ((unsigned char*)data)[2] << 16;
|
||||||
|
case 2: h2 ^= ((unsigned char*)data)[1] << 8;
|
||||||
|
case 1: h2 ^= ((unsigned char*)data)[0];
|
||||||
|
h2 *= m;
|
||||||
|
};
|
||||||
|
|
||||||
|
h1 ^= h2 >> 18; h1 *= m;
|
||||||
|
h2 ^= h1 >> 22; h2 *= m;
|
||||||
|
h1 ^= h2 >> 17; h1 *= m;
|
||||||
|
h2 ^= h1 >> 19; h2 *= m;
|
||||||
|
|
||||||
|
uint64_t h = h1;
|
||||||
|
|
||||||
|
h = (h << 32) | h2;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MurmurHash2A, by Austin Appleby
|
||||||
|
|
||||||
|
// This is a variant of MurmurHash2 modified to use the Merkle-Damgard
|
||||||
|
// construction. Bulk speed should be identical to Murmur2, small-key speed
|
||||||
|
// will be 10%-20% slower due to the added overhead at the end of the hash.
|
||||||
|
|
||||||
|
// This variant fixes a minor issue where null keys were more likely to
|
||||||
|
// collide with each other than expected, and also makes the function
|
||||||
|
// more amenable to incremental implementations.
|
||||||
|
|
||||||
|
#define mmix(h,k) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
|
||||||
|
|
||||||
|
uint32_t MurmurHash2A ( const void * key, int len, uint32_t seed )
|
||||||
|
{
|
||||||
|
const uint32_t m = 0x5bd1e995;
|
||||||
|
const int r = 24;
|
||||||
|
uint32_t l = len;
|
||||||
|
|
||||||
|
const unsigned char * data = (const unsigned char *)key;
|
||||||
|
|
||||||
|
uint32_t h = seed;
|
||||||
|
|
||||||
|
while(len >= 4)
|
||||||
|
{
|
||||||
|
uint32_t k = *(uint32_t*)data;
|
||||||
|
|
||||||
|
mmix(h,k);
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t t = 0;
|
||||||
|
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 3: t ^= data[2] << 16;
|
||||||
|
case 2: t ^= data[1] << 8;
|
||||||
|
case 1: t ^= data[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
mmix(h,t);
|
||||||
|
mmix(h,l);
|
||||||
|
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> 15;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// CMurmurHash2A, by Austin Appleby
|
||||||
|
|
||||||
|
// This is a sample implementation of MurmurHash2A designed to work
|
||||||
|
// incrementally.
|
||||||
|
|
||||||
|
// Usage -
|
||||||
|
|
||||||
|
// CMurmurHash2A hasher
|
||||||
|
// hasher.Begin(seed);
|
||||||
|
// hasher.Add(data1,size1);
|
||||||
|
// hasher.Add(data2,size2);
|
||||||
|
// ...
|
||||||
|
// hasher.Add(dataN,sizeN);
|
||||||
|
// uint32_t hash = hasher.End()
|
||||||
|
|
||||||
|
class CMurmurHash2A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void Begin ( uint32_t seed = 0 )
|
||||||
|
{
|
||||||
|
m_hash = seed;
|
||||||
|
m_tail = 0;
|
||||||
|
m_count = 0;
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add ( const unsigned char * data, int len )
|
||||||
|
{
|
||||||
|
m_size += len;
|
||||||
|
|
||||||
|
MixTail(data,len);
|
||||||
|
|
||||||
|
while(len >= 4)
|
||||||
|
{
|
||||||
|
uint32_t k = *(uint32_t*)data;
|
||||||
|
|
||||||
|
mmix(m_hash,k);
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
MixTail(data,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t End ( void )
|
||||||
|
{
|
||||||
|
mmix(m_hash,m_tail);
|
||||||
|
mmix(m_hash,m_size);
|
||||||
|
|
||||||
|
m_hash ^= m_hash >> 13;
|
||||||
|
m_hash *= m;
|
||||||
|
m_hash ^= m_hash >> 15;
|
||||||
|
|
||||||
|
return m_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static const uint32_t m = 0x5bd1e995;
|
||||||
|
static const int r = 24;
|
||||||
|
|
||||||
|
void MixTail ( const unsigned char * & data, int & len )
|
||||||
|
{
|
||||||
|
while( len && ((len<4) || m_count) )
|
||||||
|
{
|
||||||
|
m_tail |= (*data++) << (m_count * 8);
|
||||||
|
|
||||||
|
m_count++;
|
||||||
|
len--;
|
||||||
|
|
||||||
|
if(m_count == 4)
|
||||||
|
{
|
||||||
|
mmix(m_hash,m_tail);
|
||||||
|
m_tail = 0;
|
||||||
|
m_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t m_hash;
|
||||||
|
uint32_t m_tail;
|
||||||
|
uint32_t m_count;
|
||||||
|
uint32_t m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MurmurHashNeutral2, by Austin Appleby
|
||||||
|
|
||||||
|
// Same as MurmurHash2, but endian- and alignment-neutral.
|
||||||
|
// Half the speed though, alas.
|
||||||
|
|
||||||
|
uint32_t MurmurHashNeutral2 ( const void * key, int len, uint32_t seed )
|
||||||
|
{
|
||||||
|
const uint32_t m = 0x5bd1e995;
|
||||||
|
const int r = 24;
|
||||||
|
|
||||||
|
uint32_t h = seed ^ len;
|
||||||
|
|
||||||
|
const unsigned char * data = (const unsigned char *)key;
|
||||||
|
|
||||||
|
while(len >= 4)
|
||||||
|
{
|
||||||
|
uint32_t k;
|
||||||
|
|
||||||
|
k = data[0];
|
||||||
|
k |= data[1] << 8;
|
||||||
|
k |= data[2] << 16;
|
||||||
|
k |= data[3] << 24;
|
||||||
|
|
||||||
|
k *= m;
|
||||||
|
k ^= k >> r;
|
||||||
|
k *= m;
|
||||||
|
|
||||||
|
h *= m;
|
||||||
|
h ^= k;
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 3: h ^= data[2] << 16;
|
||||||
|
case 2: h ^= data[1] << 8;
|
||||||
|
case 1: h ^= data[0];
|
||||||
|
h *= m;
|
||||||
|
};
|
||||||
|
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> 15;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MurmurHashAligned2, by Austin Appleby
|
||||||
|
|
||||||
|
// Same algorithm as MurmurHash2, but only does aligned reads - should be safer
|
||||||
|
// on certain platforms.
|
||||||
|
|
||||||
|
// Performance will be lower than MurmurHash2
|
||||||
|
|
||||||
|
#define MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t MurmurHashAligned2 ( const void * key, int len, uint32_t seed )
|
||||||
|
{
|
||||||
|
const uint32_t m = 0x5bd1e995;
|
||||||
|
const int r = 24;
|
||||||
|
|
||||||
|
const unsigned char * data = (const unsigned char *)key;
|
||||||
|
|
||||||
|
uint32_t h = seed ^ len;
|
||||||
|
|
||||||
|
int align = (uint64_t)data & 3;
|
||||||
|
|
||||||
|
if(align && (len >= 4))
|
||||||
|
{
|
||||||
|
// Pre-load the temp registers
|
||||||
|
|
||||||
|
uint32_t t = 0, d = 0;
|
||||||
|
|
||||||
|
switch(align)
|
||||||
|
{
|
||||||
|
case 1: t |= data[2] << 16;
|
||||||
|
case 2: t |= data[1] << 8;
|
||||||
|
case 3: t |= data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
t <<= (8 * align);
|
||||||
|
|
||||||
|
data += 4-align;
|
||||||
|
len -= 4-align;
|
||||||
|
|
||||||
|
int sl = 8 * (4-align);
|
||||||
|
int sr = 8 * align;
|
||||||
|
|
||||||
|
// Mix
|
||||||
|
|
||||||
|
while(len >= 4)
|
||||||
|
{
|
||||||
|
d = *(uint32_t *)data;
|
||||||
|
t = (t >> sr) | (d << sl);
|
||||||
|
|
||||||
|
uint32_t k = t;
|
||||||
|
|
||||||
|
MIX(h,k,m);
|
||||||
|
|
||||||
|
t = d;
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle leftover data in temp registers
|
||||||
|
|
||||||
|
d = 0;
|
||||||
|
|
||||||
|
if(len >= align)
|
||||||
|
{
|
||||||
|
switch(align)
|
||||||
|
{
|
||||||
|
case 3: d |= data[2] << 16;
|
||||||
|
case 2: d |= data[1] << 8;
|
||||||
|
case 1: d |= data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t k = (t >> sr) | (d << sl);
|
||||||
|
MIX(h,k,m);
|
||||||
|
|
||||||
|
data += align;
|
||||||
|
len -= align;
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// Handle tail bytes
|
||||||
|
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 3: h ^= data[2] << 16;
|
||||||
|
case 2: h ^= data[1] << 8;
|
||||||
|
case 1: h ^= data[0];
|
||||||
|
h *= m;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 3: d |= data[2] << 16;
|
||||||
|
case 2: d |= data[1] << 8;
|
||||||
|
case 1: d |= data[0];
|
||||||
|
case 0: h ^= (t >> sr) | (d << sl);
|
||||||
|
h *= m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> 15;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while(len >= 4)
|
||||||
|
{
|
||||||
|
uint32_t k = *(uint32_t *)data;
|
||||||
|
|
||||||
|
MIX(h,k,m);
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// Handle tail bytes
|
||||||
|
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 3: h ^= data[2] << 16;
|
||||||
|
case 2: h ^= data[1] << 8;
|
||||||
|
case 1: h ^= data[0];
|
||||||
|
h *= m;
|
||||||
|
};
|
||||||
|
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> 15;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
38
extern/murmurhash/MurmurHash2.h
vendored
Normal file
38
extern/murmurhash/MurmurHash2.h
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MurmurHash2 was written by Austin Appleby, and is placed in the public
|
||||||
|
// domain. The author hereby disclaims copyright to this source code.
|
||||||
|
|
||||||
|
#ifndef _MURMURHASH2_H_
|
||||||
|
#define _MURMURHASH2_H_
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Platform-specific functions and macros
|
||||||
|
|
||||||
|
// Microsoft Visual Studio
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||||
|
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
|
||||||
|
// Other compilers
|
||||||
|
|
||||||
|
#else // defined(_MSC_VER)
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#endif // !defined(_MSC_VER)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed );
|
||||||
|
uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed );
|
||||||
|
uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed );
|
||||||
|
uint32_t MurmurHash2A ( const void * key, int len, uint32_t seed );
|
||||||
|
uint32_t MurmurHashNeutral2 ( const void * key, int len, uint32_t seed );
|
||||||
|
uint32_t MurmurHashAligned2 ( const void * key, int len, uint32_t seed );
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // _MURMURHASH2_H_
|
@ -10,11 +10,38 @@
|
|||||||
# pragma warning(disable: 4996)
|
# pragma warning(disable: 4996)
|
||||||
# define strcasecmp stricmp
|
# define strcasecmp stricmp
|
||||||
# if (_MSC_VER < 1900)
|
# if (_MSC_VER < 1900)
|
||||||
# define snprintf _snprintf
|
# include <stdio.h>
|
||||||
|
# include <stdarg.h>
|
||||||
|
# define snprintf c99_snprintf
|
||||||
|
# define vsnprintf c99_vsnprintf
|
||||||
|
/* see http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */
|
||||||
|
inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
|
||||||
|
{
|
||||||
|
int count = -1;
|
||||||
|
|
||||||
|
if (size != 0)
|
||||||
|
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
|
||||||
|
if (count == -1)
|
||||||
|
count = _vscprintf(format, ap);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
count = c99_vsnprintf(outBuf, size, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# warning "Unable to determine your compiler, you should probably take a look here."
|
# warning "Unable to determine your compiler, you should probably take a look here."
|
||||||
# include <strings.h> // Just take a guess
|
# include <strings.h> // Just take a guess
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _STRINGS_WRAPPER_H */
|
#endif /* _STRINGS_WRAPPER_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user