1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-12-13 04:43:06 +00:00

Merge from upstream master

This commit is contained in:
Andrei Kortunov 2017-11-21 09:39:54 +04:00
commit f0ca3eabd3
461 changed files with 12819 additions and 5534 deletions

11
.editorconfig Normal file
View file

@ -0,0 +1,11 @@
root = true
[*.cpp]
indent_style = space
indent_size = 4
insert_final_newline = true
[*.hpp]
indent_style = space
indent_size = 4
insert_final_newline = true

2
.gitignore vendored
View file

@ -26,6 +26,8 @@ Doxygen
.settings .settings
.directory .directory
.idea .idea
cmake-build-*
files/windows/*.aps
## qt-creator ## qt-creator
CMakeLists.txt.user* CMakeLists.txt.user*

View file

@ -41,8 +41,8 @@ addons:
project: project:
name: "OpenMW/openmw" name: "OpenMW/openmw"
description: "<Your project description here>" description: "<Your project description here>"
notification_email: scrawl@baseoftrash.de notification_email: 720642+scrawl@users.noreply.github.com
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE" build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE"
build_command: "make -j3" build_command: "make -j3"
branch_pattern: coverity_scan branch_pattern: coverity_scan
matrix: matrix:

View file

@ -34,11 +34,13 @@ Programmers
Ben Shealy (bentsherman) Ben Shealy (bentsherman)
Bret Curtis (psi29a) Bret Curtis (psi29a)
Britt Mathis (galdor557) Britt Mathis (galdor557)
Capostrophic
cc9cii cc9cii
Chris Boyce (slothlife) Chris Boyce (slothlife)
Chris Robinson (KittyCat) Chris Robinson (KittyCat)
Cory F. Cohen (cfcohen) Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam) Cris Mihalache (Mirceam)
crussell187
darkf darkf
devnexen devnexen
Dieho Dieho
@ -50,6 +52,7 @@ Programmers
Edmondo Tommasina (edmondo) Edmondo Tommasina (edmondo)
Eduard Cot (trombonecot) Eduard Cot (trombonecot)
Eli2 Eli2
elsid
Emanuel Guével (potatoesmaster) Emanuel Guével (potatoesmaster)
eroen eroen
escondida escondida
@ -59,10 +62,11 @@ Programmers
Gašper Sedej Gašper Sedej
gugus/gus gugus/gus
Hallfaer Tuilinn Hallfaer Tuilinn
Haoda Wang (h313)
hristoast hristoast
Internecine Internecine
Jacob Essex (Yacoby) Jacob Essex (Yacoby)
Jannik Heller (scrawl) Jake Westrip (16bitint)
Jason Hooks (jhooks) Jason Hooks (jhooks)
jeaye jeaye
Jeffrey Haines (Jyby) Jeffrey Haines (Jyby)
@ -84,6 +88,7 @@ Programmers
lazydev lazydev
Leon Krieg (lkrieg) Leon Krieg (lkrieg)
Leon Saunders (emoose) Leon Saunders (emoose)
Łukasz Gołębiewski (lukago)
logzero logzero
lohikaarme lohikaarme
Lukasz Gromanowski (lgro) Lukasz Gromanowski (lgro)
@ -102,6 +107,7 @@ Programmers
Michael Papageorgiou (werdanith) Michael Papageorgiou (werdanith)
Michał Bień (Glorf) Michał Bień (Glorf)
Michał Moroz (dragonee) Michał Moroz (dragonee)
Miloslav Číž (drummyfish)
Miroslav Puda (pakanek) Miroslav Puda (pakanek)
MiroslavR MiroslavR
Mitchell Schwitzer (schwitzerm) Mitchell Schwitzer (schwitzerm)
@ -123,6 +129,7 @@ Programmers
Radu-Marius Popovici (rpopovici) Radu-Marius Popovici (rpopovici)
Rafael Moura (dhustkoder) Rafael Moura (dhustkoder)
rdimesio rdimesio
rexelion
riothamus riothamus
Rob Cutmore (rcutmore) Rob Cutmore (rcutmore)
Robert MacGregor (Ragora) Robert MacGregor (Ragora)
@ -131,12 +138,14 @@ Programmers
Roman Proskuryakov (kpp) Roman Proskuryakov (kpp)
Sandy Carter (bwrsandman) Sandy Carter (bwrsandman)
Scott Howard Scott Howard
scrawl
Sebastian Wick (swick) Sebastian Wick (swick)
Sergey Shambir Sergey Shambir
ShadowRadiance ShadowRadiance
Siimacore Siimacore
sir_herrbatka sir_herrbatka
smbas smbas
spycrab
Stefan Galowicz (bogglez) Stefan Galowicz (bogglez)
Stanislav Bobrov (Jiub) Stanislav Bobrov (Jiub)
stil-t stil-t
@ -145,6 +154,7 @@ Programmers
t6 t6
terrorfisch terrorfisch
Thomas Luppi (Digmaster) Thomas Luppi (Digmaster)
Will Herrmann (Thunderforge)
Tom Mason (wheybags) Tom Mason (wheybags)
Torben Leif Carrington (TorbenC) Torben Leif Carrington (TorbenC)
viadanna viadanna

View file

@ -1,3 +1,82 @@
0.42.0
------
Bug #1956: Duplicate objects after loading the game, when a mod was edited
Bug #2100: Falling leaves in Vurt's Leafy West Gash II not rendered correctly
Bug #2116: Cant fit through some doorways pressed against staircases
Bug #2289: Some modal dialogs are not centered on the screen when the window resizes
Bug #2409: Softlock when pressing weapon/magic switch keys during chargen, afterwards switches weapons even though a text field is selected
Bug #2483: Previous/Next Weapon hotkeys triggered while typing the name of game save
Bug #2629: centeroncell, coc causes death / fall damage time to time when teleporting from high
Bug #2645: Cycling weapons is possible while console/pause menu is open
Bug #2678: Combat with water creatures do not end upon exiting water
Bug #2759: Light Problems in Therana's Chamber in Tel Branora
Bug #2771: unhandled sdl event of type 0x302
Bug #2777: (constant/on cast) disintegrate armor/weapon on self is seemingly not working
Bug #2838: Editor: '.' in a record name should be allowed
Bug #2909: NPCs appear floating when standing on a slope
Bug #3093: Controller movement cannot be used while mouse is moving
Bug #3134: Crash possible when using console with open container
Bug #3254: AI enemies hit between them.
Bug #3344: Editor: Verification results sorting by Type is not alphabetical.
Bug #3345: Editor: Cloned and added pathgrids are lost after reopen of saved omwgame file
Bug #3355: [MGSO] Physics maxing out in south cornerclub Balmora
Bug #3484: Editor: camera position is not set when changing cell via drag&drop
Bug #3508: Slowfall kills Jump momentum
Bug #3580: Crash: Error ElementBufferObject::remove BufferData<0> out of range
Bug #3581: NPCs wander too much
Bug #3601: Menu Titles not centered vertically
Bug #3607: [Mac OS] Beginning of NPC speech cut off (same issue as closed bug #3453)
Bug #3613: Can not map "next weapon" or "next spell" to controller
Bug #3617: Enchanted arrows don't explode when hitting the ground
Bug #3645: Unable to use steps in Vivec, Palace of Vivec
Bug #3650: Tamriel Rebuilt 16.09.1 Hist Cuirass GND nif is rendered inside a Pink Box
Bug #3652: Item icon shadows get stuck in the alchemy GUI
Bug #3653: Incorrect swish sounds
Bug #3666: NPC collision should not be disabled until death animation has finished
Bug #3669: Editor: Text field was missing from book object editing dialogue
Bug #3670: Unhandled SDL event of type 0x304
Bug #3671: Incorrect local variable value after picking up bittercup
Bug #3686: Travelling followers doesn't increase travel fee
Bug #3689: Problematic greetings from Antares Big Mod that override the appropriate ones.
Bug #3690: Certain summoned creatures do not engage in combat with underwater creatures
Bug #3691: Enemies do not initiate combat with player followers on sight
Bug #3695: [Regression] Dispel does not always dispel spell effects in 0.41
Bug #3699: Crash on MWWorld::ProjectileManager::moveMagicBolts
Bug #3700: Climbing on rocks and mountains
Bug #3704: Creatures don't auto-equip their shields on creation
Bug #3705: AI combat engagement logic differs from vanilla
Bug #3707: Animation playing does some very odd things if pc comes in contact with the animated mesh
Bug #3712: [Mod] Freeze upon entering Adanumuran with mod Adanumuran Reclaimed
Bug #3713: [Regression] Cancelling dialogue or using travel with creatures throws a (possibly game-breaking) exception
Bug #3719: Dropped identification papers can't be picked up again
Bug #3722: Command spell doesn't bring enemies out of combat
Bug #3727: Using "Activate" mid-script-execution invalidates interpreter context
Bug #3746: Editor: Book records show attribute IDs instead of skill IDs for teached skills entry.
Bug #3755: Followers stop following after loading from savegame
Bug #3772: ModStat lowers attribute to 100 if it was greater
Bug #3781: Guns in Clean Hunter Rifles mod use crossbow sounds
Bug #3797: NPC and creature names don't show up in combat when RMB windows are displayed
Bug #3800: Wrong tooltip maximum width
Bug #3801: Drowning widget is bugged
Bug #3802: BarterOffer shouldn't limit pcMercantile
Bug #3813: Some fatal error
Bug #3816: Expression parser thinks the -> token is unexpected when a given explicit refID clashes with a journal ID
Bug #3822: Custom added creatures are not animated
Feature #451: Water sounds
Feature #2691: Light particles sometimes not shown in inventory character preview
Feature #3523: Light source on magic projectiles
Feature #3644: Nif NiSphericalCollider Unknown Record Type
Feature #3675: ess-Importer: convert mark location
Feature #3693: ess-Importer: convert last known exterior cell
Feature #3748: Editor: Replace "Scroll" check box in Book records with "Book Type" combo box.
Feature #3751: Editor: Replace "Xyz Blood" check boxes in NPC and Creature records with "Blood Type" combo box
Feature #3752: Editor: Replace emitter check boxes in Light records with "Emitter Type" combo box
Feature #3756: Editor: Replace "Female" check box in NPC records with "Gender" combo box
Feature #3757: Editor: Replace "Female" check box in BodyPart records with "Gender" combo box
Task #3092: const version of ContainerStoreIterator
Task #3795: /deps folder not in .gitignore
0.41.0 0.41.0
------ ------

View file

@ -6,5 +6,5 @@ brew outdated cmake || brew upgrade cmake
brew outdated pkgconfig || brew upgrade pkgconfig brew outdated pkgconfig || brew upgrade pkgconfig
brew install $macos_qt_formula brew install $macos_qt_formula
curl https://downloads.openmw.org/osx/dependencies/openmw-deps-5e144e2.zip -o ~/openmw-deps.zip curl https://downloads.openmw.org/osx/dependencies/openmw-deps-c40905f.zip -o ~/openmw-deps.zip
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

View file

@ -330,9 +330,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
"OpenAL-Soft-1.17.2.zip" "OpenAL-Soft-1.17.2.zip"
# OSG # OSG
download "OpenSceneGraph 3.4.0-scrawl" \ download "OpenSceneGraph 3.4.1-scrawl" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \ "http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \
"OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z"
# Qt # Qt
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
@ -527,20 +527,20 @@ cd $DEPS
echo echo
# OSG # OSG
printf "OSG 3.4.0-scrawl... " printf "OSG 3.4.1-scrawl... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d OSG ] && \ if [ -d OSG ] && \
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_PATCH_VERSION 0" OSG/include/osg/Version > /dev/null grep "OPENSCENEGRAPH_PATCH_VERSION 1" OSG/include/osg/Version > /dev/null
then then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf OSG rm -rf OSG
eval 7z x -y "${DEPS}/OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
fi fi
OSG_SDK="$(real_pwd)/OSG" OSG_SDK="$(real_pwd)/OSG"
@ -556,8 +556,8 @@ printf "OSG 3.4.0-scrawl... "
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \ add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll "$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.0/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.0/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll
echo Done. echo Done.
} }
@ -717,10 +717,10 @@ if [ -z $CI ]; then
echo echo
echo "- OSG Plugin DLLs..." echo "- OSG Plugin DLLs..."
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.0 mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
for DLL in $OSG_PLUGINS; do for DLL in $OSG_PLUGINS; do
echo " $(basename $DLL)." echo " $(basename $DLL)."
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.0 cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
done done
echo echo
@ -750,19 +750,4 @@ if [ -z $VERBOSE ]; then
fi fi
fi fi
if [ -z $CI ]; then
echo "- Copying Runtime Resources/Config Files"
echo " gamecontrollerdb.txt"
cp gamecontrollerdb.txt $BUILD_CONFIG/gamecontrollerdb.txt
echo " openmw.cfg"
cp openmw.cfg.install $BUILD_CONFIG/openmw.cfg
echo " openmw-cs.cfg"
cp openmw-cs.cfg $BUILD_CONFIG/openmw-cs.cfg
echo " settings-default.cfg"
cp settings-default.cfg $BUILD_CONFIG/settings-default.cfg
echo " resources/"
cp -r resources $BUILD_CONFIG/resources
echo
fi
exit $RET exit $RET

View file

@ -6,4 +6,4 @@ DATE=`date +'%d%m%Y'`
SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}` SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}`
TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg" TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg"
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "ftp://s3.mydevil.net:21/nightly/${TARGET_FILENAME}" curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}${TARGET_FILENAME}"

View file

@ -1,3 +1,32 @@
# Apps and tools
option(BUILD_OPENMW "build OpenMW" ON)
option(BUILD_BSATOOL "build BSA extractor" ON)
option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON)
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
option(BUILD_WIZARD "build Installation Wizard" ON)
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
option(BUILD_NIFTEST "build nif file tester" OFF)
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
option(BUILD_DOCS "build documentation." OFF )
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE)
else()
set(USE_QT TRUE)
endif()
if (USE_QT)
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
endif()
# set the minimum required version across the board
cmake_minimum_required(VERSION 3.1.0)
project(OpenMW) project(OpenMW)
# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them. # If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.
@ -25,7 +54,7 @@ endif()
message(STATUS "Configuring OpenMW...") message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 41) set(OPENMW_VERSION_MINOR 42)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
@ -59,21 +88,6 @@ option(QT_STATIC "Link static build of QT into the binaries" FALSE)
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE) option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
# Apps and tools
option(BUILD_OPENMW "build OpenMW" ON)
option(BUILD_BSATOOL "build BSA extractor" ON)
option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON)
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
option(BUILD_WIZARD "build Installation Wizard" ON)
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
option(BUILD_NIFTEST "build nif file tester" OFF)
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
option(BUILD_DOCS "build documentation." OFF )
# what is necessary to build documentation # what is necessary to build documentation
IF( BUILD_DOCS ) IF( BUILD_DOCS )
# Builds the documentation. # Builds the documentation.
@ -120,16 +134,8 @@ if (WIN32)
option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON) option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON)
endif() endif()
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE)
else()
set(USE_QT TRUE)
endif()
# Dependencies # Dependencies
if (USE_QT) if (USE_QT)
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
message(STATUS "Using Qt${DESIRED_QT_VERSION}") message(STATUS "Using Qt${DESIRED_QT_VERSION}")
if (DESIRED_QT_VERSION MATCHES 4) if (DESIRED_QT_VERSION MATCHES 4)
@ -144,17 +150,6 @@ if (USE_QT)
endif() endif()
endif() endif()
if (APPLE)
# OS X build process relies on this fix: https://github.com/Kitware/CMake/commit/3df5147043d83aa09acd5c9ce31d5c602efb99db
cmake_minimum_required(VERSION 3.1.0)
elseif (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows.
cmake_minimum_required(VERSION 2.8.11)
else()
# We probably support older versions than this.
cmake_minimum_required(VERSION 2.6)
endif()
# Sound setup # Sound setup
find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE) find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE)
# Required for building the FFmpeg headers # Required for building the FFmpeg headers
@ -207,7 +202,7 @@ if(NOT HAVE_STDINT_H)
endif() endif()
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgUtil osgFX) find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
set(USED_OSG_PLUGINS set(USED_OSG_PLUGINS
@ -310,27 +305,28 @@ endif (APPLE)
# Other files # Other files
configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg configure_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
"${OpenMW_BINARY_DIR}/settings-default.cfg") "${OpenMW_BINARY_DIR}" "settings-default.cfg")
if (NOT APPLE) if (NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${OpenMW_BINARY_DIR}/openmw.cfg") "${OpenMW_BINARY_DIR}" "openmw.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install") "${OpenMW_BINARY_DIR}" "openmw.cfg.install")
else () else ()
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg") "${OpenMW_BINARY_DIR}/openmw.cfg")
endif () endif ()
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
"${OpenMW_BINARY_DIR}/openmw-cs.cfg") "${OpenMW_BINARY_DIR}" "openmw-cs.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters # Needs the copy version because the configure version assumes the end of the file has been reached when a null character is reached and there are no CMake expressions to evaluate.
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) copy_resource_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
"${OpenMW_BINARY_DIR}" "resources/defaultfilters")
configure_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt configure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt
"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt") "${OpenMW_BINARY_DIR}" "gamecontrollerdb.txt")
if (NOT WIN32 AND NOT APPLE) if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
@ -408,7 +404,7 @@ IF(NOT WIN32 AND NOT APPLE)
#ENDIF(BUILD_MYGUI_PLUGIN) #ENDIF(BUILD_MYGUI_PLUGIN)
# Install licenses # Install licenses
INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "files/mygui/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
# Install icon and desktop file # Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
@ -442,9 +438,9 @@ if(WIN32)
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/LICENSE" DESTINATION "." RENAME "LICENSE.txt")
INSTALL(FILES INSTALL(FILES
"${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt" "${OpenMW_SOURCE_DIR}/files/mygui/DejaVu Font License.txt"
"${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt"
DESTINATION ".") DESTINATION ".")
@ -494,6 +490,7 @@ if(WIN32)
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe") SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
SET(CPACK_NSIS_MUI_FINISHPAGE_RUN "openmw-launcher.exe")
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico") SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico") SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp") SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")

View file

@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> {one line to give the program's name and a brief idea of what it does.}
Copyright (C) <year> <name of author> Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode: notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author> {project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.

View file

@ -7,13 +7,13 @@ OpenMW is a recreation of the engine for the popular role-playing game Morrowind
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set. OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
* Version: 0.41.0 * Version: 0.42.0
* License: GPLv3 (see [docs/license/GPL3.txt](https://github.com/OpenMW/openmw/blob/master/docs/license/GPL3.txt) for more information) * License: GPLv3 (see [LICENSE](https://github.com/OpenMW/openmw/blob/master/LICENSE) for more information)
* Website: http://www.openmw.org * Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net * IRC: #openmw on irc.freenode.net
Font Licenses: Font Licenses:
* DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information) * DejaVuLGCSansMono.ttf: custom (see [files/mygui/DejaVu Font License.txt](https://github.com/OpenMW/openmw/blob/master/files/mygui/DejaVu%20Font%20License.txt) for more information)
Current Status Current Status
-------------- --------------

View file

@ -4,7 +4,7 @@ set(BSATOOL
source_group(apps\\bsatool FILES ${BSATOOL}) source_group(apps\\bsatool FILES ${BSATOOL})
# Main executable # Main executable
add_executable(bsatool openmw_add_executable(bsatool
${BSATOOL} ${BSATOOL}
) )

View file

@ -8,7 +8,7 @@ set(ESMTOOL
source_group(apps\\esmtool FILES ${ESMTOOL}) source_group(apps\\esmtool FILES ${ESMTOOL})
# Main executable # Main executable
add_executable(esmtool openmw_add_executable(esmtool
${ESMTOOL} ${ESMTOOL}
) )

View file

@ -16,6 +16,8 @@ set(ESSIMPORTER_FILES
importjour.cpp importjour.cpp
importscri.cpp importscri.cpp
importscpt.cpp importscpt.cpp
importproj.cpp
importsplm.cpp
importercontext.cpp importercontext.cpp
converter.cpp converter.cpp
convertacdt.cpp convertacdt.cpp
@ -28,7 +30,7 @@ set(ESSIMPORTER_FILES
convertplayer.cpp convertplayer.cpp
) )
add_executable(openmw-essimporter openmw_add_executable(openmw-essimporter
${ESSIMPORTER_FILES} ${ESSIMPORTER_FILES}
) )

View file

@ -1,6 +1,7 @@
#include "converter.hpp" #include "converter.hpp"
#include <stdexcept> #include <stdexcept>
#include <algorithm>
#include <osgDB/WriteFile> #include <osgDB/WriteFile>
@ -53,6 +54,36 @@ namespace
return true; return true;
return false; return false;
} }
void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId)
{
std::stringstream stream;
stream << std::hex << indexedRefId.substr(indexedRefId.size()-8,8);
stream >> refIndex;
refId = indexedRefId.substr(0,indexedRefId.size()-8);
}
int convertActorId(const std::string& indexedRefId, ESSImport::Context& context)
{
if (isIndexedRefId(indexedRefId))
{
int refIndex;
std::string refId;
splitIndexedRefId(indexedRefId, refIndex, refId);
auto it = context.mActorIdMap.find(std::make_pair(refIndex, refId));
if (it == context.mActorIdMap.end())
return -1;
return it->second;
}
else if (indexedRefId == "PlayerSaveGame")
{
return context.mPlayer.mObject.mCreatureStats.mActorId;
}
return -1;
}
} }
namespace ESSImport namespace ESSImport
@ -322,12 +353,9 @@ namespace ESSImport
} }
else else
{ {
std::stringstream stream;
stream << std::hex << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8);
int refIndex; int refIndex;
stream >> refIndex; splitIndexedRefId(cellref.mIndexedRefId, refIndex, out.mRefID);
out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8);
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID); std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
@ -347,6 +375,10 @@ namespace ESSImport
convertNpcData(cellref, objstate.mNpcStats); convertNpcData(cellref, objstate.mNpcStats);
convertNPCC(npccIt->second, objstate); convertNPCC(npccIt->second, objstate);
convertCellRef(cellref, objstate); convertCellRef(cellref, objstate);
objstate.mCreatureStats.mActorId = mContext->generateActorId();
mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId));
esm.writeHNT ("OBJE", ESM::REC_NPC_); esm.writeHNT ("OBJE", ESM::REC_NPC_);
objstate.save(esm); objstate.save(esm);
continue; continue;
@ -383,6 +415,10 @@ namespace ESSImport
convertACSC(cellref.mACSC, objstate.mCreatureStats); convertACSC(cellref.mACSC, objstate.mCreatureStats);
convertCREC(crecIt->second, objstate); convertCREC(crecIt->second, objstate);
convertCellRef(cellref, objstate); convertCellRef(cellref, objstate);
objstate.mCreatureStats.mActorId = mContext->generateActorId();
mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId));
esm.writeHNT ("OBJE", ESM::REC_CREA); esm.writeHNT ("OBJE", ESM::REC_CREA);
objstate.save(esm); objstate.save(esm);
continue; continue;
@ -413,4 +449,73 @@ namespace ESSImport
} }
} }
void ConvertPROJ::read(ESM::ESMReader& esm)
{
mProj.load(esm);
}
void ConvertPROJ::write(ESM::ESMWriter& esm)
{
for (const PROJ::PNAM& pnam : mProj.mProjectiles)
{
if (!pnam.isMagic())
{
ESM::ProjectileState out;
convertBaseState(out, pnam);
out.mBowId = pnam.mBowId.toString();
out.mVelocity = pnam.mVelocity;
out.mAttackStrength = pnam.mAttackStrength;
esm.startRecord(ESM::REC_PROJ);
out.save(esm);
esm.endRecord(ESM::REC_PROJ);
}
else
{
ESM::MagicBoltState out;
convertBaseState(out, pnam);
auto it = std::find_if(mContext->mActiveSpells.begin(), mContext->mActiveSpells.end(),
[&pnam](const SPLM::ActiveSpell& spell) -> bool { return spell.mIndex == pnam.mSplmIndex; });
if (it == mContext->mActiveSpells.end())
{
std::cerr << "Warning: Skipped conversion for magic projectile \"" << pnam.mArrowId.toString() << "\" (invalid spell link)" << std::endl;
continue;
}
out.mSpellId = it->mSPDT.mId.toString();
out.mSpeed = pnam.mSpeed * 0.001f; // not sure where this factor comes from
esm.startRecord(ESM::REC_MPRJ);
out.save(esm);
esm.endRecord(ESM::REC_MPRJ);
}
}
}
void ConvertPROJ::convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam)
{
base.mId = pnam.mArrowId.toString();
base.mPosition = pnam.mPosition;
osg::Quat orient;
orient.makeRotate(osg::Vec3f(0,1,0), pnam.mVelocity);
base.mOrientation = orient;
base.mActorId = convertActorId(pnam.mActorId.toString(), *mContext);
}
void ConvertSPLM::read(ESM::ESMReader& esm)
{
mSPLM.load(esm);
mContext->mActiveSpells = mSPLM.mActiveSpells;
}
void ConvertSPLM::write(ESM::ESMWriter& esm)
{
std::cerr << "Warning: Skipped active spell conversion (not implemented)" << std::endl;
}
} }

View file

@ -22,6 +22,7 @@
#include <components/esm/globalscript.hpp> #include <components/esm/globalscript.hpp>
#include <components/esm/queststate.hpp> #include <components/esm/queststate.hpp>
#include <components/esm/stolenitems.hpp> #include <components/esm/stolenitems.hpp>
#include <components/esm/projectilestate.hpp>
#include "importcrec.hpp" #include "importcrec.hpp"
#include "importcntc.hpp" #include "importcntc.hpp"
@ -35,6 +36,8 @@
#include "importques.hpp" #include "importques.hpp"
#include "importjour.hpp" #include "importjour.hpp"
#include "importscpt.hpp" #include "importscpt.hpp"
#include "importproj.h"
#include "importsplm.h"
#include "convertacdt.hpp" #include "convertacdt.hpp"
#include "convertnpcc.hpp" #include "convertnpcc.hpp"
@ -593,6 +596,27 @@ private:
std::vector<ESM::GlobalScript> mScripts; std::vector<ESM::GlobalScript> mScripts;
}; };
/// Projectile converter
class ConvertPROJ : public Converter
{
public:
virtual int getStage() override { return 2; }
virtual void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm) override;
private:
void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);
PROJ mProj;
};
class ConvertSPLM : public Converter
{
public:
virtual void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm) override;
private:
SPLM mSPLM;
};
} }
#endif #endif

View file

@ -271,6 +271,7 @@ namespace ESSImport
const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value; const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value;
const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value; const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value;
const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value; const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value;
const unsigned int recSPLM = ESM::FourCC<'S','P','L','M'>::value;
std::map<unsigned int, std::shared_ptr<Converter> > converters; std::map<unsigned int, std::shared_ptr<Converter> > converters;
converters[ESM::REC_GLOB] = std::shared_ptr<Converter>(new ConvertGlobal()); converters[ESM::REC_GLOB] = std::shared_ptr<Converter>(new ConvertGlobal());
@ -303,12 +304,13 @@ namespace ESSImport
converters[ESM::REC_QUES] = std::shared_ptr<Converter>(new ConvertQUES()); converters[ESM::REC_QUES] = std::shared_ptr<Converter>(new ConvertQUES());
converters[recJOUR ] = std::shared_ptr<Converter>(new ConvertJOUR()); converters[recJOUR ] = std::shared_ptr<Converter>(new ConvertJOUR());
converters[ESM::REC_SCPT] = std::shared_ptr<Converter>(new ConvertSCPT()); converters[ESM::REC_SCPT] = std::shared_ptr<Converter>(new ConvertSCPT());
converters[ESM::REC_PROJ] = std::shared_ptr<Converter>(new ConvertPROJ());
converters[recSPLM] = std::shared_ptr<Converter>(new ConvertSPLM());
// TODO: // TODO:
// - REGN (weather in certain regions?) // - REGN (weather in certain regions?)
// - VFXM // - VFXM
// - SPLM (active spell effects) // - SPLM (active spell effects)
// - PROJ (magic projectiles in air)
std::set<unsigned int> unknownRecords; std::set<unsigned int> unknownRecords;
@ -420,6 +422,19 @@ namespace ESSImport
context.mPlayer.save(writer); context.mPlayer.save(writer);
writer.endRecord(ESM::REC_PLAY); writer.endRecord(ESM::REC_PLAY);
writer.startRecord(ESM::REC_ACTC);
writer.writeHNT("COUN", context.mNextActorId);
writer.endRecord(ESM::REC_ACTC);
// Stage 2 requires cell references to be written / actors IDs assigned
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
it != converters.end(); ++it)
{
if (it->second->getStage() != 2)
continue;
it->second->write(writer);
}
writer.startRecord (ESM::REC_DIAS); writer.startRecord (ESM::REC_DIAS);
context.mDialogueState.save(writer); context.mDialogueState.save(writer);
writer.endRecord(ESM::REC_DIAS); writer.endRecord(ESM::REC_DIAS);

View file

@ -15,7 +15,7 @@
#include "importcrec.hpp" #include "importcrec.hpp"
#include "importcntc.hpp" #include "importcntc.hpp"
#include "importplayer.hpp" #include "importplayer.hpp"
#include "importsplm.h"
@ -48,14 +48,20 @@ namespace ESSImport
std::map<std::pair<int, std::string>, NPCC> mNpcChanges; std::map<std::pair<int, std::string>, NPCC> mNpcChanges;
std::map<std::pair<int, std::string>, CNTC> mContainerChanges; std::map<std::pair<int, std::string>, CNTC> mContainerChanges;
std::map<std::pair<int, std::string>, int> mActorIdMap;
int mNextActorId;
std::map<std::string, ESM::Creature> mCreatures; std::map<std::string, ESM::Creature> mCreatures;
std::map<std::string, ESM::NPC> mNpcs; std::map<std::string, ESM::NPC> mNpcs;
std::vector<SPLM::ActiveSpell> mActiveSpells;
Context() Context()
: mDay(0) : mDay(0)
, mMonth(0) , mMonth(0)
, mYear(0) , mYear(0)
, mHour(0.f) , mHour(0.f)
, mNextActorId(0)
{ {
mPlayer.mAutoMove = 0; mPlayer.mAutoMove = 0;
ESM::CellId playerCellId; ESM::CellId playerCellId;
@ -67,16 +73,23 @@ namespace ESSImport
= mPlayer.mLastKnownExteriorPosition[2] = mPlayer.mLastKnownExteriorPosition[2]
= 0.0f; = 0.0f;
mPlayer.mHasMark = 0; mPlayer.mHasMark = 0;
mPlayer.mCurrentCrimeId = 0; // TODO mPlayer.mCurrentCrimeId = -1; // TODO
mPlayer.mPaidCrimeId = -1;
mPlayer.mObject.blank(); mPlayer.mObject.blank();
mPlayer.mObject.mEnabled = true; mPlayer.mObject.mEnabled = true;
mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame
mPlayer.mObject.mCreatureStats.mActorId = generateActorId();
mGlobalMapState.mBounds.mMinX = 0; mGlobalMapState.mBounds.mMinX = 0;
mGlobalMapState.mBounds.mMaxX = 0; mGlobalMapState.mBounds.mMaxX = 0;
mGlobalMapState.mBounds.mMinY = 0; mGlobalMapState.mBounds.mMinY = 0;
mGlobalMapState.mBounds.mMaxY = 0; mGlobalMapState.mBounds.mMaxY = 0;
} }
int generateActorId()
{
return mNextActorId++;
}
}; };
} }

View file

@ -0,0 +1,18 @@
#include "importproj.h"
#include <components/esm/esmreader.hpp>
namespace ESSImport
{
void ESSImport::PROJ::load(ESM::ESMReader& esm)
{
while (esm.isNextSub("PNAM"))
{
PNAM pnam;
esm.getHT(pnam);
mProjectiles.push_back(pnam);
}
}
}

View file

@ -0,0 +1,47 @@
#ifndef OPENMW_ESSIMPORT_IMPORTPROJ_H
#define OPENMW_ESSIMPORT_IMPORTPROJ_H
#include <vector>
#include <components/esm/esmcommon.hpp>
#include <components/esm/util.hpp>
namespace ESM
{
class ESMReader;
}
namespace ESSImport
{
struct PROJ
{
#pragma pack(push)
#pragma pack(1)
struct PNAM // 184 bytes
{
float mAttackStrength;
float mSpeed;
unsigned char mUnknown[4*2];
float mFlightTime;
int mSplmIndex; // reference to a SPLM record (0 for ballistic projectiles)
unsigned char mUnknown2[4];
ESM::Vector3 mVelocity;
ESM::Vector3 mPosition;
unsigned char mUnknown3[4*9];
ESM::NAME32 mActorId; // indexed refID (with the exception of "PlayerSaveGame")
ESM::NAME32 mArrowId;
ESM::NAME32 mBowId;
bool isMagic() const { return mSplmIndex != 0; }
};
#pragma pack(pop)
std::vector<PNAM> mProjectiles;
void load(ESM::ESMReader& esm);
};
}
#endif

View file

@ -0,0 +1,43 @@
#include "importsplm.h"
#include <components/esm/esmreader.hpp>
namespace ESSImport
{
void SPLM::load(ESM::ESMReader& esm)
{
while (esm.isNextSub("NAME"))
{
ActiveSpell spell;
esm.getHT(spell.mIndex);
esm.getHNT(spell.mSPDT, "SPDT");
spell.mTarget = esm.getHNOString("TNAM");
while (esm.isNextSub("NPDT"))
{
ActiveEffect effect;
esm.getHT(effect.mNPDT);
// Effect-specific subrecords can follow:
// - INAM for disintegration and bound effects
// - CNAM for summoning and command effects
// - VNAM for vampirism
// NOTE: There can be multiple INAMs per effect.
// TODO: Needs more research.
esm.skipHSubUntil("NAM0"); // sentinel
esm.getSubName();
esm.skipHSub();
spell.mActiveEffects.push_back(effect);
}
unsigned char xnam; // sentinel
esm.getHNT(xnam, "XNAM");
mActiveSpells.push_back(spell);
}
}
}

View file

@ -0,0 +1,81 @@
#ifndef OPENMW_ESSIMPORT_IMPORTSPLM_H
#define OPENMW_ESSIMPORT_IMPORTSPLM_H
#include <vector>
#include <components/esm/esmcommon.hpp>
#include <components/esm/util.hpp>
namespace ESM
{
class ESMReader;
}
namespace ESSImport
{
struct SPLM
{
#pragma pack(push)
#pragma pack(1)
struct SPDT // 160 bytes
{
int mType; // 1 = spell, 2 = enchantment, 3 = potion
ESM::NAME32 mId; // base ID of a spell/enchantment/potion
unsigned char mUnknown[4*4];
ESM::NAME32 mCasterId;
ESM::NAME32 mSourceId; // empty for spells
unsigned char mUnknown2[4*11];
};
struct NPDT // 56 bytes
{
ESM::NAME32 mAffectedActorId;
unsigned char mUnknown[4*2];
int mMagnitude;
float mSecondsActive;
unsigned char mUnknown2[4*2];
};
struct INAM // 40 bytes
{
int mUnknown;
unsigned char mUnknown2;
ESM::FIXED_STRING<35> mItemId; // disintegrated item / bound item / item to re-equip after expiration
};
struct CNAM // 36 bytes
{
int mUnknown; // seems to always be 0
ESM::NAME32 mSummonedOrCommandedActor[32];
};
struct VNAM // 4 bytes
{
int mUnknown;
};
#pragma pack(pop)
struct ActiveEffect
{
NPDT mNPDT;
};
struct ActiveSpell
{
int mIndex;
SPDT mSPDT;
std::string mTarget;
std::vector<ActiveEffect> mActiveEffects;
};
std::vector<ActiveSpell> mActiveSpells;
void load(ESM::ESMReader& esm);
};
}
#endif

View file

@ -78,7 +78,7 @@ if(NOT WIN32)
endif(NOT WIN32) endif(NOT WIN32)
# Main executable # Main executable
add_executable(openmw-launcher openmw_add_executable(openmw-launcher
${GUI_TYPE} ${GUI_TYPE}
${LAUNCHER} ${LAUNCHER}
${LAUNCHER_HEADER} ${LAUNCHER_HEADER}

View file

@ -172,7 +172,10 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
} }
} }
return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure; if (!setup() || !setupGameData()) {
return FirstRunDialogResultFailure;
}
return FirstRunDialogResultContinue;
} }
void Launcher::MainDialog::setVersionLabel() void Launcher::MainDialog::setVersionLabel()
@ -344,6 +347,11 @@ bool Launcher::MainDialog::setupGameSettings()
file.close(); file.close();
} }
return true;
}
bool Launcher::MainDialog::setupGameData()
{
QStringList dataDirs; QStringList dataDirs;
// Check if the paths actually contain data files // Check if the paths actually contain data files

View file

@ -72,6 +72,7 @@ namespace Launcher
bool setupLauncherSettings(); bool setupLauncherSettings();
bool setupGameSettings(); bool setupGameSettings();
bool setupGraphicsSettings(); bool setupGraphicsSettings();
bool setupGameData();
void setVersionLabel(); void setVersionLabel();

View file

@ -9,7 +9,7 @@ set(MWINIIMPORT_HEADER
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER}) source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
add_executable(openmw-iniimporter openmw_add_executable(openmw-iniimporter
${MWINIIMPORT} ${MWINIIMPORT}
) )

View file

@ -4,7 +4,7 @@ set(NIFTEST
source_group(components\\nif\\tests FILES ${NIFTEST}) source_group(components\\nif\\tests FILES ${NIFTEST})
# Main executable # Main executable
add_executable(niftest openmw_add_executable(niftest
${NIFTEST} ${NIFTEST}
) )

View file

@ -18,7 +18,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 landtexturetableproxymodel
) )
@ -70,7 +70,7 @@ opencs_units (view/world
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
bodypartcreator bodypartcreator landtexturecreator landcreator
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world
@ -174,7 +174,7 @@ else()
set (OPENCS_OPENMW_CFG "") set (OPENCS_OPENMW_CFG "")
endif(APPLE) endif(APPLE)
add_executable(openmw-cs openmw_add_executable(openmw-cs
MACOSX_BUNDLE MACOSX_BUNDLE
${OPENCS_SRC} ${OPENCS_SRC}
${OPENCS_UI_HDR} ${OPENCS_UI_HDR}
@ -199,7 +199,7 @@ if(APPLE)
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
OUTPUT_NAME ${OPENCS_BUNDLE_NAME} OUTPUT_NAME ${OPENCS_BUNDLE_NAME}
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns" MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" MACOSX_BUNDLE_BUNDLE_NAME "OpenMW-CS"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION} MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION}

View file

@ -5,9 +5,6 @@
#include <QLocalSocket> #include <QLocalSocket>
#include <QMessageBox> #include <QMessageBox>
#include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp>
#include <components/fallback/validate.hpp> #include <components/fallback/validate.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
@ -33,11 +30,7 @@ CS::Editor::Editor ()
NifOsg::Loader::setShowMarkers(true); NifOsg::Loader::setShowMarkers(true);
mVFS.reset(new VFS::Manager(mFsStrict)); mDocumentManager.setFileData(mFsStrict, config.first, config.second);
VFS::registerArchives(mVFS.get(), Files::Collections(config.first, !mFsStrict), config.second, true);
mDocumentManager.setVFS(mVFS.get());
mNewGame.setLocalData (mLocal); mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal); mFileDialog.setLocalData (mLocal);
@ -97,16 +90,16 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options"); boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options");
desc.add_options() desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken()->composing()) ("data", boost::program_options::value<Files::EscapePathContainer>()->default_value(Files::EscapePathContainer(), "data")->multitoken()->composing())
("data-local", boost::program_options::value<std::string>()->default_value("")) ("data-local", boost::program_options::value<Files::EscapeHashString>()->default_value(""))
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false)) ("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
("encoding", boost::program_options::value<std::string>()->default_value("win1252")) ("encoding", boost::program_options::value<Files::EscapeHashString>()->default_value("win1252"))
("resources", boost::program_options::value<std::string>()->default_value("resources")) ("resources", boost::program_options::value<Files::EscapeHashString>()->default_value("resources"))
("fallback-archive", boost::program_options::value<std::vector<std::string> >()-> ("fallback-archive", boost::program_options::value<Files::EscapeStringVector>()->
default_value(std::vector<std::string>(), "fallback-archive")->multitoken()) default_value(Files::EscapeStringVector(), "fallback-archive")->multitoken())
("fallback", boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "") ("fallback", boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "")
->multitoken()->composing(), "fallback values") ->multitoken()->composing(), "fallback values")
("script-blacklist", boost::program_options::value<std::vector<std::string> >()->default_value(std::vector<std::string>(), "") ("script-blacklist", boost::program_options::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), "")
->multitoken(), "exclude specified script from the verifier (if the use of the blacklist is enabled)") ->multitoken(), "exclude specified script from the verifier (if the use of the blacklist is enabled)")
("script-blacklist-use", boost::program_options::value<bool>()->implicit_value(true) ("script-blacklist-use", boost::program_options::value<bool>()->implicit_value(true)
->default_value(true), "enable script blacklisting"); ->default_value(true), "enable script blacklisting");
@ -116,25 +109,29 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mCfgMgr.readConfiguration(variables, desc, quiet); mCfgMgr.readConfiguration(variables, desc, quiet);
mDocumentManager.setEncoding ( mDocumentManager.setEncoding (
ToUTF8::calculateEncoding (variables["encoding"].as<std::string>())); ToUTF8::calculateEncoding (variables["encoding"].as<Files::EscapeHashString>().toStdString()));
mDocumentManager.setResourceDir (mResources = variables["resources"].as<std::string>()); mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::EscapeHashString>().toStdString());
mDocumentManager.setFallbackMap (variables["fallback"].as<FallbackMap>().mMap); mDocumentManager.setFallbackMap (variables["fallback"].as<FallbackMap>().mMap);
if (variables["script-blacklist-use"].as<bool>()) if (variables["script-blacklist-use"].as<bool>())
mDocumentManager.setBlacklistedScripts ( mDocumentManager.setBlacklistedScripts (
variables["script-blacklist"].as<std::vector<std::string> >()); variables["script-blacklist"].as<Files::EscapeStringVector>().toStdStringVector());
mFsStrict = variables["fs-strict"].as<bool>(); mFsStrict = variables["fs-strict"].as<bool>();
Files::PathContainer dataDirs, dataLocal; Files::PathContainer dataDirs, dataLocal;
if (!variables["data"].empty()) { if (!variables["data"].empty()) {
dataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>()); dataDirs = Files::PathContainer(Files::EscapePath::toPathContainer(variables["data"].as<Files::EscapePathContainer>()));
} }
std::string local = variables["data-local"].as<std::string>(); std::string local = variables["data-local"].as<Files::EscapeHashString>().toStdString();
if (!local.empty()) { if (!local.empty())
{
if (local.front() == '\"')
local = local.substr(1, local.length() - 2);
dataLocal.push_back(Files::PathContainer::value_type(local)); dataLocal.push_back(Files::PathContainer::value_type(local));
} }
@ -164,7 +161,7 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mFileDialog.addFiles(path); mFileDialog.addFiles(path);
} }
return std::make_pair (dataDirs, variables["fallback-archive"].as<std::vector<std::string> >()); return std::make_pair (dataDirs, variables["fallback-archive"].as<Files::EscapeStringVector>().toStdStringVector());
} }
void CS::Editor::createGame() void CS::Editor::createGame()

View file

@ -1,8 +1,6 @@
#ifndef CS_EDITOR_H #ifndef CS_EDITOR_H
#define CS_EDITOR_H #define CS_EDITOR_H
#include <memory>
#include <boost/interprocess/sync/file_lock.hpp> #include <boost/interprocess/sync/file_lock.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
@ -30,11 +28,6 @@
#include "view/tools/merge.hpp" #include "view/tools/merge.hpp"
namespace VFS
{
class Manager;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document; class Document;
@ -46,9 +39,6 @@ namespace CS
{ {
Q_OBJECT Q_OBJECT
// FIXME: should be moved to document, so we can have different resources for each opened project
std::unique_ptr<VFS::Manager> mVFS;
Files::ConfigurationManager mCfgMgr; Files::ConfigurationManager mCfgMgr;
CSMPrefs::State mSettingsState; CSMPrefs::State mSettingsState;
CSMDoc::DocumentManager mDocumentManager; CSMDoc::DocumentManager mDocumentManager;

View file

@ -269,13 +269,14 @@ void CSMDoc::Document::createBase()
} }
} }
CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration, CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_, const std::vector< boost::filesystem::path >& files,bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
const Fallback::Map* fallback, const Fallback::Map* fallback,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, ToUTF8::FromType encoding,
const std::vector<std::string>& blacklistedScripts) const std::vector<std::string>& blacklistedScripts,
: mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager, fallback, resDir), bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, fallback, resDir),
mTools (*this, encoding), mTools (*this, encoding),
mProjectPath ((configuration.getUserDataPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().string() + ".project")),
@ -287,7 +288,7 @@ CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationM
if (mContentFiles.empty()) if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence"); throw std::runtime_error ("Empty content file sequence");
if (!boost::filesystem::exists (mProjectPath)) if (mNew || !boost::filesystem::exists (mProjectPath))
{ {
boost::filesystem::path customFiltersPath (configuration.getUserDataPath()); boost::filesystem::path customFiltersPath (configuration.getUserDataPath());
customFiltersPath /= "defaultfilters"; customFiltersPath /= "defaultfilters";
@ -337,11 +338,6 @@ CSMDoc::Document::~Document()
{ {
} }
const VFS::Manager *CSMDoc::Document::getVFS() const
{
return mVFS;
}
QUndoStack& CSMDoc::Document::getUndoStack() QUndoStack& CSMDoc::Document::getUndoStack()
{ {
return mUndoStack; return mUndoStack;

View file

@ -9,6 +9,7 @@
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <components/files/multidircollection.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "../world/data.hpp" #include "../world/data.hpp"
@ -59,7 +60,6 @@ namespace CSMDoc
private: private:
const VFS::Manager* mVFS;
boost::filesystem::path mSavePath; boost::filesystem::path mSavePath;
std::vector<boost::filesystem::path> mContentFiles; std::vector<boost::filesystem::path> mContentFiles;
bool mNew; bool mNew;
@ -102,17 +102,15 @@ namespace CSMDoc
public: public:
Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration, Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_, const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
const Fallback::Map* fallback, const Fallback::Map* fallback, ToUTF8::FromType encoding,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, const std::vector<std::string>& blacklistedScripts,
const std::vector<std::string>& blacklistedScripts); bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);
~Document(); ~Document();
const VFS::Manager* getVFS() const;
QUndoStack& getUndoStack(); QUndoStack& getUndoStack();
int getState() const; int getState() const;

View file

@ -9,7 +9,7 @@
#include "document.hpp" #include "document.hpp"
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mVFS(NULL) : mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252)
{ {
boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects"; boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
@ -62,7 +62,7 @@ CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files, const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_) const boost::filesystem::path& savePath, bool new_)
{ {
return new Document (mVFS, mConfiguration, files, new_, savePath, mResDir, &mFallbackMap, mEncoding, mResourcesManager, mBlacklistedScripts); return new Document (mConfiguration, files, new_, savePath, mResDir, &mFallbackMap, mEncoding, mBlacklistedScripts, mFsStrict, mDataPaths, mArchives);
} }
void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document) void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)
@ -127,8 +127,9 @@ void CSMDoc::DocumentManager::documentNotLoaded (Document *document, const std::
removeDocument (document); removeDocument (document);
} }
void CSMDoc::DocumentManager::setVFS(const VFS::Manager *vfs) void CSMDoc::DocumentManager::setFileData(bool strict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
{ {
mResourcesManager.setVFS(vfs); mFsStrict = strict;
mVFS = vfs; mDataPaths = dataPaths;
mArchives = archives;
} }

View file

@ -11,8 +11,7 @@
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
#include <components/files/multidircollection.hpp>
#include "../world/resourcesmanager.hpp"
#include "loader.hpp" #include "loader.hpp"
@ -39,9 +38,14 @@ namespace CSMDoc
QThread mLoaderThread; QThread mLoaderThread;
Loader mLoader; Loader mLoader;
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;
CSMWorld::ResourcesManager mResourcesManager;
std::vector<std::string> mBlacklistedScripts; std::vector<std::string> mBlacklistedScripts;
const VFS::Manager* mVFS;
boost::filesystem::path mResDir;
Fallback::Map mFallbackMap;
bool mFsStrict;
Files::PathContainer mDataPaths;
std::vector<std::string> mArchives;
DocumentManager (const DocumentManager&); DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&); DocumentManager& operator= (const DocumentManager&);
@ -74,15 +78,11 @@ namespace CSMDoc
void setBlacklistedScripts (const std::vector<std::string>& scriptIds); void setBlacklistedScripts (const std::vector<std::string>& scriptIds);
void setVFS(const VFS::Manager* vfs); /// Sets the file data that gets passed to newly created documents.
void setFileData(bool strict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);
bool isEmpty(); bool isEmpty();
private:
boost::filesystem::path mResDir;
Fallback::Map mFallbackMap;
private slots: private slots:
void documentLoaded (Document *document); void documentLoaded (Document *document);

View file

@ -132,7 +132,7 @@ namespace CSMPrefs
if (mods && i == 0) if (mods && i == 0)
{ {
if (mods & Qt::ControlModifier) if (mods & Qt::ControlModifier)
result.append("Ctl+"); result.append("Ctrl+");
if (mods & Qt::ShiftModifier) if (mods & Qt::ShiftModifier)
result.append("Shift+"); result.append("Shift+");
if (mods & Qt::AltModifier) if (mods & Qt::AltModifier)
@ -196,7 +196,7 @@ namespace CSMPrefs
std::string name = value.substr(start, end - start); std::string name = value.substr(start, end - start);
if (name == "Ctl") if (name == "Ctrl")
{ {
mods |= Qt::ControlModifier; mods |= Qt::ControlModifier;
} }

View file

@ -240,6 +240,8 @@ void CSMPrefs::State::declare()
declareShortcut ("document-world-cells", "Open Cell List", QKeySequence()); declareShortcut ("document-world-cells", "Open Cell List", QKeySequence());
declareShortcut ("document-world-referencables", "Open Object List", QKeySequence()); declareShortcut ("document-world-referencables", "Open Object List", QKeySequence());
declareShortcut ("document-world-references", "Open Instance List", QKeySequence()); declareShortcut ("document-world-references", "Open Instance List", QKeySequence());
declareShortcut ("document-world-lands", "Open Lands List", QKeySequence());
declareShortcut ("document-world-landtextures", "Open Land Textures List", QKeySequence());
declareShortcut ("document-world-pathgrid", "Open Pathgrid List", QKeySequence()); declareShortcut ("document-world-pathgrid", "Open Pathgrid List", QKeySequence());
declareShortcut ("document-world-regionmap", "Open Region Map", QKeySequence()); declareShortcut ("document-world-regionmap", "Open Region Map", QKeySequence());
declareShortcut ("document-mechanics-globals", "Open Global List", QKeySequence()); declareShortcut ("document-mechanics-globals", "Open Global List", QKeySequence());
@ -259,6 +261,7 @@ void CSMPrefs::State::declare()
declareShortcut ("document-character-topicinfos", "Open Topic Info List", QKeySequence()); declareShortcut ("document-character-topicinfos", "Open Topic Info List", QKeySequence());
declareShortcut ("document-character-journalinfos", "Open Journal Info List", QKeySequence()); declareShortcut ("document-character-journalinfos", "Open Journal Info List", QKeySequence());
declareShortcut ("document-character-bodyparts", "Open Body Part List", QKeySequence()); declareShortcut ("document-character-bodyparts", "Open Body Part List", QKeySequence());
declareShortcut ("document-assets-reload", "Reload Assets", QKeySequence(Qt::Key_F5));
declareShortcut ("document-assets-sounds", "Open Sound Asset List", QKeySequence()); declareShortcut ("document-assets-sounds", "Open Sound Asset List", QKeySequence());
declareShortcut ("document-assets-soundgens", "Open Sound Generator List", QKeySequence()); declareShortcut ("document-assets-soundgens", "Open Sound Generator List", QKeySequence());
declareShortcut ("document-assets-meshes", "Open Mesh Asset List", QKeySequence()); declareShortcut ("document-assets-meshes", "Open Mesh Asset List", QKeySequence());
@ -275,6 +278,7 @@ void CSMPrefs::State::declare()
declareShortcut ("table-edit", "Edit Record", QKeySequence()); declareShortcut ("table-edit", "Edit Record", QKeySequence());
declareShortcut ("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A)); declareShortcut ("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A));
declareShortcut ("table-clone", "Clone Record", QKeySequence(Qt::ShiftModifier | Qt::Key_D)); declareShortcut ("table-clone", "Clone Record", QKeySequence(Qt::ShiftModifier | Qt::Key_D));
declareShortcut ("touch-record", "Touch Record", QKeySequence());
declareShortcut ("table-revert", "Revert Record", QKeySequence()); declareShortcut ("table-revert", "Revert Record", QKeySequence());
declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete)); declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete));
declareShortcut ("table-moveup", "Move Record Up", QKeySequence()); declareShortcut ("table-moveup", "Move Record Up", QKeySequence());

View file

@ -38,9 +38,10 @@ CSMTools::MergeOperation::MergeOperation (CSMDoc::Document& document, ToUTF8::Fr
appendStage (new MergeRefIdsStage (mState)); appendStage (new MergeRefIdsStage (mState));
appendStage (new MergeReferencesStage (mState)); appendStage (new MergeReferencesStage (mState));
appendStage (new MergeReferencesStage (mState)); appendStage (new MergeReferencesStage (mState));
appendStage (new ListLandTexturesMergeStage (mState)); appendStage (new PopulateLandTexturesMergeStage (mState));
appendStage (new MergeLandTexturesStage (mState));
appendStage (new MergeLandStage (mState)); appendStage (new MergeLandStage (mState));
appendStage (new FixLandsAndLandTexturesMergeStage (mState));
appendStage (new CleanupLandTexturesMergeStage (mState));
appendStage (new FinishMergedDocumentStage (mState, encoding)); appendStage (new FinishMergedDocumentStage (mState, encoding));
} }

View file

@ -8,7 +8,9 @@
#include "mergestate.hpp" #include "mergestate.hpp"
#include "../doc/document.hpp" #include "../doc/document.hpp"
#include "../world/commands.hpp"
#include "../world/data.hpp" #include "../world/data.hpp"
#include "../world/idtable.hpp"
CSMTools::StartMergeStage::StartMergeStage (MergeState& state) CSMTools::StartMergeStage::StartMergeStage (MergeState& state)
@ -109,102 +111,32 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa
} }
CSMTools::ListLandTexturesMergeStage::ListLandTexturesMergeStage (MergeState& state) CSMTools::PopulateLandTexturesMergeStage::PopulateLandTexturesMergeStage (MergeState& state)
: mState (state) : mState (state)
{}
int CSMTools::ListLandTexturesMergeStage::setup()
{ {
return mState.mSource.getData().getLand().getSize();
} }
void CSMTools::ListLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages) int CSMTools::PopulateLandTexturesMergeStage::setup()
{ {
const CSMWorld::Record<CSMWorld::Land>& record =
mState.mSource.getData().getLand().getRecord (stage);
if (!record.isDeleted())
{
const CSMWorld::Land& land = record.get();
// make sure record is loaded
land.loadData (ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX);
if (const ESM::Land::LandData *data = land.getLandData (ESM::Land::DATA_VTEX))
{
// list texture indices
std::pair<uint16_t, int> key;
key.second = land.mPlugin;
for (int i=0; i<ESM::Land::LAND_NUM_TEXTURES; ++i)
{
key.first = data->mTextures[i];
mState.mTextureIndices[key] = -1;
}
}
}
}
CSMTools::MergeLandTexturesStage::MergeLandTexturesStage (MergeState& state)
: mState (state), mNext (mState.mTextureIndices.end())
{}
int CSMTools::MergeLandTexturesStage::setup()
{
// Should use the size of mState.mTextureIndices instead, but that is not available at this
// point. Unless there are any errors in the land and land texture records this will not
// make a difference.
return mState.mSource.getData().getLandTextures().getSize(); return mState.mSource.getData().getLandTextures().getSize();
} }
void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& messages) void CSMTools::PopulateLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)
{ {
if (stage==0) const CSMWorld::Record<CSMWorld::LandTexture>& record =
mNext = mState.mTextureIndices.begin(); mState.mSource.getData().getLandTextures().getRecord (stage);
bool found = false; if (!record.isDeleted())
do
{ {
if (mNext==mState.mTextureIndices.end()) mState.mTarget->getData().getLandTextures().appendRecord(record);
return;
mNext->second = stage+1;
std::ostringstream stream;
stream << mNext->first.first-1 << "_" << mNext->first.second;
int index = mState.mSource.getData().getLandTextures().searchId (stream.str());
if (index!=-1)
{
CSMWorld::LandTexture texture =
mState.mSource.getData().getLandTextures().getRecord (index).get();
stream.clear();
stream << mNext->second-1 << "_0";
texture.mIndex = mNext->second-1;
texture.mId = stream.str();
CSMWorld::Record<CSMWorld::LandTexture> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture);
mState.mTarget->getData().getLandTextures().appendRecord (newRecord);
found = true;
} }
++mNext;
}
while (!found);
} }
CSMTools::MergeLandStage::MergeLandStage (MergeState& state) : mState (state) {} CSMTools::MergeLandStage::MergeLandStage (MergeState& state)
: mState (state)
{
}
int CSMTools::MergeLandStage::setup() int CSMTools::MergeLandStage::setup()
{ {
@ -218,40 +150,66 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
if (!record.isDeleted()) if (!record.isDeleted())
{ {
const CSMWorld::Land& land = record.get(); mState.mTarget->getData().getLand().appendRecord (record);
}
land.loadData (ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | }
ESM::Land::DATA_VTEX);
CSMWorld::Land newLand (land); CSMTools::FixLandsAndLandTexturesMergeStage::FixLandsAndLandTexturesMergeStage (MergeState& state)
: mState (state)
newLand.mPlugin = 0; {
}
if (land.mDataTypes & ESM::Land::DATA_VTEX)
{ int CSMTools::FixLandsAndLandTexturesMergeStage::setup()
// adjust land texture references {
if (ESM::Land::LandData *data = newLand.getLandData()) // We will have no more than the source
{ return mState.mSource.getData().getLand().getSize();
std::pair<uint16_t, int> key; }
key.second = land.mPlugin;
void CSMTools::FixLandsAndLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)
for (int i=0; i<ESM::Land::LAND_NUM_TEXTURES; ++i) {
{ if (stage < mState.mTarget->getData().getLand().getSize())
key.first = data->mTextures[i]; {
std::map<std::pair<uint16_t, int>, int>::const_iterator iter = CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&>(
mState.mTextureIndices.find (key); *mState.mTarget->getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
if (iter!=mState.mTextureIndices.end()) CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&>(
data->mTextures[i] = iter->second; *mState.mTarget->getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));
else
data->mTextures[i] = 0; std::string id = mState.mTarget->getData().getLand().getId(stage);
}
} CSMWorld::TouchLandCommand cmd(landTable, ltexTable, id);
} cmd.redo();
CSMWorld::Record<CSMWorld::Land> newRecord ( // Get rid of base data
CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand); const CSMWorld::Record<CSMWorld::Land>& oldRecord =
mState.mTarget->getData().getLand().getRecord (stage);
mState.mTarget->getData().getLand().appendRecord (newRecord);
CSMWorld::Record<CSMWorld::Land> newRecord(CSMWorld::RecordBase::State_ModifiedOnly,
nullptr, &oldRecord.get());
mState.mTarget->getData().getLand().setRecord(stage, newRecord);
}
}
CSMTools::CleanupLandTexturesMergeStage::CleanupLandTexturesMergeStage (MergeState& state)
: mState (state)
{
}
int CSMTools::CleanupLandTexturesMergeStage::setup()
{
return 1;
}
void CSMTools::CleanupLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)
{
auto& landTextures = mState.mTarget->getData().getLandTextures();
for (int i = 0; i < landTextures.getSize(); )
{
if (!landTextures.getRecord(i).isModified())
landTextures.removeRows(i, 1);
else
++i;
} }
} }

View file

@ -116,29 +116,14 @@ namespace CSMTools
///< Messages resulting from this stage will be appended to \a messages. ///< Messages resulting from this stage will be appended to \a messages.
}; };
class ListLandTexturesMergeStage : public CSMDoc::Stage /// Adds all land texture records that could potentially be referenced when merging
class PopulateLandTexturesMergeStage : public CSMDoc::Stage
{ {
MergeState& mState; MergeState& mState;
public: public:
ListLandTexturesMergeStage (MergeState& state); PopulateLandTexturesMergeStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class MergeLandTexturesStage : public CSMDoc::Stage
{
MergeState& mState;
std::map<std::pair<uint16_t, int>, int>::iterator mNext;
public:
MergeLandTexturesStage (MergeState& state);
virtual int setup(); virtual int setup();
///< \return number of steps ///< \return number of steps
@ -161,6 +146,40 @@ namespace CSMTools
virtual void perform (int stage, CSMDoc::Messages& messages); virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages. ///< Messages resulting from this stage will be appended to \a messages.
}; };
/// During this stage, the complex process of combining LandTextures from
/// potentially multiple plugins is undertaken.
class FixLandsAndLandTexturesMergeStage : public CSMDoc::Stage
{
MergeState& mState;
public:
FixLandsAndLandTexturesMergeStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
/// Removes base LandTexture records. This gets rid of the base records previously
/// needed in FixLandsAndLandTexturesMergeStage.
class CleanupLandTexturesMergeStage : public CSMDoc::Stage
{
MergeState& mState;
public:
CleanupLandTexturesMergeStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
} }
#endif #endif

View file

@ -6,6 +6,7 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <functional> #include <functional>
#include <QVariant> #include <QVariant>
@ -13,8 +14,9 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "columnbase.hpp" #include "columnbase.hpp"
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "land.hpp"
#include "landtexture.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -22,15 +24,14 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct IdAccessor struct IdAccessor
{ {
std::string& getId (ESXRecordT& record); void setId(ESXRecordT& record, const std::string& id) const;
const std::string getId (const ESXRecordT& record) const; const std::string getId (const ESXRecordT& record) const;
}; };
template<typename ESXRecordT> template<typename ESXRecordT>
std::string& IdAccessor<ESXRecordT>::getId (ESXRecordT& record) void IdAccessor<ESXRecordT>::setId(ESXRecordT& record, const std::string& id) const
{ {
return record.mId; record.mId = id;
} }
template<typename ESXRecordT> template<typename ESXRecordT>
@ -39,6 +40,39 @@ namespace CSMWorld
return record.mId; return record.mId;
} }
template<>
inline void IdAccessor<Land>::setId (Land& record, const std::string& id) const
{
int x=0, y=0;
Land::parseUniqueRecordId(id, x, y);
record.mX = x;
record.mY = y;
}
template<>
inline void IdAccessor<LandTexture>::setId (LandTexture& record, const std::string& id) const
{
int plugin = 0;
int index = 0;
LandTexture::parseUniqueRecordId(id, plugin, index);
record.mPluginIndex = plugin;
record.mIndex = index;
}
template<>
inline const std::string IdAccessor<Land>::getId (const Land& record) const
{
return Land::createUniqueRecordId(record.mX, record.mY);
}
template<>
inline const std::string IdAccessor<LandTexture>::getId (const LandTexture& record) const
{
return LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex);
}
/// \brief Single-type record collection /// \brief Single-type record collection
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> > template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class Collection : public CollectionBase class Collection : public CollectionBase
@ -69,6 +103,13 @@ namespace CSMWorld
/// ///
/// \return Success? /// \return Success?
int cloneRecordImp (const std::string& origin, const std::string& dest,
UniversalId::Type type);
///< Returns the index of the clone.
int touchRecordImp (const std::string& id);
///< Returns the index of the record on success, -1 on failure.
public: public:
Collection(); Collection();
@ -108,6 +149,10 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type); const UniversalId::Type type);
virtual bool touchRecord(const std::string& id);
///< Change the state of a record from base to modified, if it is not already.
/// \return True if the record was changed.
virtual int searchId (const std::string& id) const; virtual int searchId (const std::string& id) const;
////< 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)
@ -206,16 +251,71 @@ namespace CSMWorld
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin, int Collection<ESXRecordT, IdAccessorT>::cloneRecordImp(const std::string& origin,
const std::string& destination, const std::string& destination, UniversalId::Type type)
const UniversalId::Type type)
{ {
Record<ESXRecordT> copy; Record<ESXRecordT> copy;
copy.mModified = getRecord(origin).get(); copy.mModified = getRecord(origin).get();
copy.mState = RecordBase::State_ModifiedOnly; copy.mState = RecordBase::State_ModifiedOnly;
copy.get().mId = destination; IdAccessorT().setId(copy.get(), destination);
int index = getAppendIndex(destination, type);
insertRecord(copy, getAppendIndex(destination, type)); insertRecord(copy, getAppendIndex(destination, type));
return index;
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::touchRecordImp(const std::string& id)
{
int index = getIndex(id);
Record<ESXRecordT>& record = mRecords.at(index);
if (record.isDeleted())
{
throw std::runtime_error("attempt to touch deleted record");
}
if (!record.isModified())
{
record.setModified(record.get());
return index;
}
return -1;
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,
const std::string& destination, const UniversalId::Type type)
{
cloneRecordImp(origin, destination, type);
}
template<>
inline void Collection<Land, IdAccessor<Land> >::cloneRecord(const std::string& origin,
const std::string& destination, const UniversalId::Type type)
{
int index = cloneRecordImp(origin, destination, type);
mRecords.at(index).get().mPlugin = 0;
}
template<typename ESXRecordT, typename IdAccessorT>
bool Collection<ESXRecordT, IdAccessorT>::touchRecord(const std::string& id)
{
return touchRecordImp(id) != -1;
}
template<>
inline bool Collection<Land, IdAccessor<Land> >::touchRecord(const std::string& id)
{
int index = touchRecordImp(id);
if (index >= 0)
{
mRecords.at(index).get().mPlugin = 0;
return true;
}
return false;
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -366,7 +466,7 @@ namespace CSMWorld
UniversalId::Type type) UniversalId::Type type)
{ {
ESXRecordT record; ESXRecordT record;
IdAccessorT().getId (record) = id; IdAccessorT().setId(record, id);
record.blank(); record.blank();
Record<ESXRecordT> record2; Record<ESXRecordT> record2;

View file

@ -78,6 +78,8 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type) = 0; const UniversalId::Type type) = 0;
virtual bool touchRecord(const std::string& id) = 0;
virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0; virtual const RecordBase& getRecord (int index) const = 0;

View file

@ -1,28 +1,341 @@
#include "columnimp.hpp" #include "columnimp.hpp"
CSMWorld::BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType) #include <stdexcept>
: mMeshType(meshType) #include <QVector>
{}
QVariant CSMWorld::BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const namespace CSMWorld
{ {
/* LandTextureNicknameColumn */
LandTextureNicknameColumn::LandTextureNicknameColumn()
: Column<LandTexture>(Columns::ColumnId_TextureNickname, ColumnBase::Display_String)
{
}
QVariant LandTextureNicknameColumn::get(const Record<LandTexture>& record) const
{
return QString::fromUtf8(record.get().mId.c_str());
}
void LandTextureNicknameColumn::set(Record<LandTexture>& record, const QVariant& data)
{
LandTexture copy = record.get();
copy.mId = data.toString().toUtf8().constData();
record.setModified(copy);
}
bool LandTextureNicknameColumn::isEditable() const
{
return true;
}
/* LandTextureIndexColumn */
LandTextureIndexColumn::LandTextureIndexColumn()
: Column<LandTexture>(Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer)
{
}
QVariant LandTextureIndexColumn::get(const Record<LandTexture>& record) const
{
return record.get().mIndex;
}
bool LandTextureIndexColumn::isEditable() const
{
return false;
}
/* LandPluginIndexColumn */
LandPluginIndexColumn::LandPluginIndexColumn()
: Column<Land>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)
{
}
QVariant LandPluginIndexColumn::get(const Record<Land>& record) const
{
return record.get().mPlugin;
}
bool LandPluginIndexColumn::isEditable() const
{
return false;
}
/* LandTexturePluginIndexColumn */
LandTexturePluginIndexColumn::LandTexturePluginIndexColumn()
: Column<LandTexture>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)
{
}
QVariant LandTexturePluginIndexColumn::get(const Record<LandTexture>& record) const
{
return record.get().mPluginIndex;
}
bool LandTexturePluginIndexColumn::isEditable() const
{
return false;
}
/* LandMapLodColumn */
LandMapLodColumn::LandMapLodColumn()
: Column<Land>(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandMapLodColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_GLOBAL_MAP_LOD_SIZE;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_WNAM))
{
for (int i = 0; i < Size; ++i)
values[i] = land.mWnam[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandMapLodColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_GLOBAL_MAP_LOD_SIZE)
throw std::runtime_error("invalid land map LOD data");
Land copy = record.get();
copy.add(Land::DATA_WNAM);
for (int i = 0; i < values.size(); ++i)
{
copy.mWnam[i] = values[i];
}
record.setModified(copy);
}
bool LandMapLodColumn::isEditable() const
{
return true;
}
/* LandNormalsColumn */
LandNormalsColumn::LandNormalsColumn()
: Column<Land>(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandNormalsColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_NUM_VERTS * 3;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_VNML))
{
for (int i = 0; i < Size; ++i)
values[i] = land.getLandData()->mNormals[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandNormalsColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_NUM_VERTS * 3)
throw std::runtime_error("invalid land normals data");
Land copy = record.get();
copy.add(Land::DATA_VNML);
for (int i = 0; i < values.size(); ++i)
{
copy.getLandData()->mNormals[i] = values[i];
}
record.setModified(copy);
}
bool LandNormalsColumn::isEditable() const
{
return true;
}
/* LandHeightsColumn */
LandHeightsColumn::LandHeightsColumn()
: Column<Land>(Columns::ColumnId_LandHeightsIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandHeightsColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_NUM_VERTS;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_VHGT))
{
for (int i = 0; i < Size; ++i)
values[i] = land.getLandData()->mHeights[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandHeightsColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_NUM_VERTS)
throw std::runtime_error("invalid land heights data");
Land copy = record.get();
copy.add(Land::DATA_VHGT);
for (int i = 0; i < values.size(); ++i)
{
copy.getLandData()->mHeights[i] = values[i];
}
record.setModified(copy);
}
bool LandHeightsColumn::isEditable() const
{
return true;
}
/* LandColoursColumn */
LandColoursColumn::LandColoursColumn()
: Column<Land>(Columns::ColumnId_LandColoursIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandColoursColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_NUM_VERTS * 3;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_VCLR))
{
for (int i = 0; i < Size; ++i)
values[i] = land.getLandData()->mColours[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandColoursColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_NUM_VERTS * 3)
throw std::runtime_error("invalid land colours data");
Land copy = record.get();
copy.add(Land::DATA_VCLR);
for (int i = 0; i < values.size(); ++i)
{
copy.getLandData()->mColours[i] = values[i];
}
record.setModified(copy);
}
bool LandColoursColumn::isEditable() const
{
return true;
}
/* LandTexturesColumn */
LandTexturesColumn::LandTexturesColumn()
: Column<Land>(Columns::ColumnId_LandTexturesIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandTexturesColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_NUM_TEXTURES;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_VTEX))
{
for (int i = 0; i < Size; ++i)
values[i] = land.getLandData()->mTextures[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandTexturesColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_NUM_TEXTURES)
throw std::runtime_error("invalid land textures data");
Land copy = record.get();
copy.add(Land::DATA_VTEX);
for (int i = 0; i < values.size(); ++i)
{
copy.getLandData()->mTextures[i] = values[i];
}
record.setModified(copy);
}
bool LandTexturesColumn::isEditable() const
{
return true;
}
/* BodyPartRaceColumn */
BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType)
: mMeshType(meshType)
{}
QVariant BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const
{
if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin)
{ {
return QString::fromUtf8(record.get().mRace.c_str()); return QString::fromUtf8(record.get().mRace.c_str());
} }
return QVariant(QVariant::UserType); return QVariant(QVariant::UserType);
} }
void CSMWorld::BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVariant &data) void BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVariant &data)
{ {
ESM::BodyPart record2 = record.get(); ESM::BodyPart record2 = record.get();
record2.mRace = data.toString().toUtf8().constData(); record2.mRace = data.toString().toUtf8().constData();
record.setModified(record2); record.setModified(record2);
} }
bool CSMWorld::BodyPartRaceColumn::isEditable() const bool BodyPartRaceColumn::isEditable() const
{ {
return true; return true;
}
} }

View file

@ -2,10 +2,12 @@
#define CSM_WOLRD_COLUMNIMP_H #define CSM_WOLRD_COLUMNIMP_H
#include <cassert> #include <cassert>
#include <cstdint>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <QColor> #include <QColor>
#include <QVector>
#include <components/esm/loadbody.hpp> #include <components/esm/loadbody.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
@ -15,6 +17,9 @@
#include "columns.hpp" #include "columns.hpp"
#include "info.hpp" #include "info.hpp"
#include "land.hpp"
#include "landtexture.hpp"
namespace CSMWorld namespace CSMWorld
{ {
/// \note Shares ID with VarValueColumn. A table can not have both. /// \note Shares ID with VarValueColumn. A table can not have both.
@ -60,6 +65,20 @@ namespace CSMWorld
} }
}; };
template<>
inline QVariant StringIdColumn<Land>::get(const Record<Land>& record) const
{
const Land& land = record.get();
return QString::fromUtf8(Land::createUniqueRecordId(land.mX, land.mY).c_str());
}
template<>
inline QVariant StringIdColumn<LandTexture>::get(const Record<LandTexture>& record) const
{
const LandTexture& ltex = record.get();
return QString::fromUtf8(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str());
}
template<typename ESXRecordT> template<typename ESXRecordT>
struct RecordStateColumn : public Column<ESXRecordT> struct RecordStateColumn : public Column<ESXRecordT>
{ {
@ -673,32 +692,23 @@ namespace CSMWorld
} }
}; };
/// \todo QColor is a GUI class and should not be in model. Need to think of an alternative
/// solution.
template<typename ESXRecordT> template<typename ESXRecordT>
struct MapColourColumn : public Column<ESXRecordT> struct MapColourColumn : public Column<ESXRecordT>
{ {
/// \todo Replace Display_Integer with something that displays the colour value more directly.
MapColourColumn() MapColourColumn()
: Column<ESXRecordT> (Columns::ColumnId_MapColour, ColumnBase::Display_Colour) : Column<ESXRecordT> (Columns::ColumnId_MapColour, ColumnBase::Display_Colour)
{} {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {
int colour = record.get().mMapColor; return record.get().mMapColor;
return QColor (colour & 0xff, (colour>>8) & 0xff, (colour>>16) & 0xff);
} }
virtual void set (Record<ESXRecordT>& record, const QVariant& data) virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{ {
ESXRecordT record2 = record.get(); ESXRecordT copy = record.get();
copy.mMapColor = data.toInt();
QColor colour = data.value<QColor>(); record.setModified (copy);
record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red();
record.setModified (record2);
} }
virtual bool isEditable() const virtual bool isEditable() const
@ -2418,6 +2428,94 @@ namespace CSMWorld
} }
}; };
struct LandTextureNicknameColumn : public Column<LandTexture>
{
LandTextureNicknameColumn();
QVariant get(const Record<LandTexture>& record) const override;
void set(Record<LandTexture>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandTextureIndexColumn : public Column<LandTexture>
{
LandTextureIndexColumn();
QVariant get(const Record<LandTexture>& record) const override;
bool isEditable() const override;
};
struct LandPluginIndexColumn : public Column<Land>
{
LandPluginIndexColumn();
QVariant get(const Record<Land>& record) const override;
bool isEditable() const override;
};
struct LandTexturePluginIndexColumn : public Column<LandTexture>
{
LandTexturePluginIndexColumn();
QVariant get(const Record<LandTexture>& record) const override;
bool isEditable() const override;
};
struct LandMapLodColumn : public Column<Land>
{
using DataType = QVector<signed char>;
LandMapLodColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandNormalsColumn : public Column<Land>
{
using DataType = QVector<signed char>;
LandNormalsColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandHeightsColumn : public Column<Land>
{
using DataType = QVector<float>;
LandHeightsColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandColoursColumn : public Column<Land>
{
using DataType = QVector<unsigned char>;
LandColoursColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandTexturesColumn : public Column<Land>
{
using DataType = QVector<uint16_t>;
LandTexturesColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct BodyPartRaceColumn : public RaceColumn<ESM::BodyPart> struct BodyPartRaceColumn : public RaceColumn<ESM::BodyPart>
{ {
const MeshTypeColumn<ESM::BodyPart> *mMeshType; const MeshTypeColumn<ESM::BodyPart> *mMeshType;
@ -2430,4 +2528,11 @@ namespace CSMWorld
}; };
} }
// This is required to access the type as a QVariant.
Q_DECLARE_METATYPE(CSMWorld::LandMapLodColumn::DataType)
//Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType) // Same as LandMapLodColumn::DataType
Q_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType)
Q_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType)
Q_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType)
#endif #endif

View file

@ -330,6 +330,14 @@ namespace CSMWorld
{ ColumnId_WeatherChance, "Percent Chance" }, { ColumnId_WeatherChance, "Percent Chance" },
{ ColumnId_Text, "Text" }, { ColumnId_Text, "Text" },
{ ColumnId_TextureNickname, "Texture Nickname" },
{ ColumnId_PluginIndex, "Plugin Index" },
{ ColumnId_TextureIndex, "Texture Index" },
{ ColumnId_LandMapLodIndex, "Land map height LOD" },
{ ColumnId_LandNormalsIndex, "Land normals" },
{ ColumnId_LandHeightsIndex, "Land heights" },
{ ColumnId_LandColoursIndex, "Land colors" },
{ ColumnId_LandTexturesIndex, "Land textures" },
{ ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },

View file

@ -329,6 +329,15 @@ namespace CSMWorld
ColumnId_Text = 297, ColumnId_Text = 297,
ColumnId_TextureNickname = 298,
ColumnId_PluginIndex = 299,
ColumnId_TextureIndex = 300,
ColumnId_LandMapLodIndex = 301,
ColumnId_LandNormalsIndex = 302,
ColumnId_LandHeightsIndex = 303,
ColumnId_LandColoursIndex = 304,
ColumnId_LandTexturesIndex = 305,
// Allocated to a separate value range, so we don't get a collision should we ever need // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.
ColumnId_UseValue1 = 0x10000, ColumnId_UseValue1 = 0x10000,

View file

@ -2,6 +2,7 @@
#include <cmath> #include <cmath>
#include <sstream> #include <sstream>
#include <unordered_set>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -15,6 +16,175 @@
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
CSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUndoCommand* parent)
: QUndoCommand(parent)
, mTable(table)
, mId(id)
, mOld(nullptr)
, mChanged(false)
{
setText(("Touch " + mId).c_str());
mOld.reset(mTable.getRecord(mId).clone());
}
void CSMWorld::TouchCommand::redo()
{
mChanged = mTable.touchRecord(mId);
}
void CSMWorld::TouchCommand::undo()
{
if (mChanged)
{
mTable.setRecord(mId, *mOld);
mChanged = false;
}
}
CSMWorld::ImportLandTexturesCommand::ImportLandTexturesCommand(IdTable& landTable,
IdTable& ltexTable, QUndoCommand* parent)
: QUndoCommand(parent)
, mLands(landTable)
, mLtexs(ltexTable)
, mOldState(0)
{
setText("Copy land textures to current plugin");
}
void CSMWorld::ImportLandTexturesCommand::redo()
{
int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex);
int oldPlugin = mLands.data(mLands.getModelIndex(getOriginId(), pluginColumn)).toInt();
// Original data
int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);
mOld = mLands.data(mLands.getModelIndex(getOriginId(), textureColumn)).value<DataType>();
// Need to make a copy so the old values can be looked up
DataType copy(mOld);
// Perform touch/copy/etc...
onRedo();
// Find all indices used
std::unordered_set<int> texIndices;
for (int i = 0; i < mOld.size(); ++i)
{
// All indices are offset by 1 for a default texture
if (mOld[i] > 0)
texIndices.insert(mOld[i] - 1);
}
std::vector<std::string> oldTextures;
for (int index : texIndices)
{
oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));
}
// Import the textures, replace old values
LandTextureIdTable::ImportResults results = dynamic_cast<LandTextureIdTable&>(mLtexs).importTextures(oldTextures);
mCreatedTextures = std::move(results.createdRecords);
for (const auto& it : results.recordMapping)
{
int plugin = 0, newIndex = 0, oldIndex = 0;
LandTexture::parseUniqueRecordId(it.first, plugin, oldIndex);
LandTexture::parseUniqueRecordId(it.second, plugin, newIndex);
if (newIndex != oldIndex)
{
for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i)
{
// All indices are offset by 1 for a default texture
if (mOld[i] == oldIndex + 1)
copy[i] = newIndex + 1;
}
}
}
// Apply modification
int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification);
mOldState = mLands.data(mLands.getModelIndex(getDestinationId(), stateColumn)).toInt();
QVariant variant;
variant.setValue(copy);
mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant);
}
void CSMWorld::ImportLandTexturesCommand::undo()
{
// Restore to previous
int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);
QVariant variant;
variant.setValue(mOld);
mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant);
int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification);
mLands.setData(mLands.getModelIndex(getDestinationId(), stateColumn), mOldState);
// Undo copy/touch/etc...
onUndo();
for (const std::string& id : mCreatedTextures)
{
int row = mLtexs.getModelIndex(id, 0).row();
mLtexs.removeRows(row, 1);
}
mCreatedTextures.clear();
}
CSMWorld::CopyLandTexturesCommand::CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable,
const std::string& origin, const std::string& dest, QUndoCommand* parent)
: ImportLandTexturesCommand(landTable, ltexTable, parent)
, mOriginId(origin)
, mDestId(dest)
{
}
const std::string& CSMWorld::CopyLandTexturesCommand::getOriginId() const
{
return mOriginId;
}
const std::string& CSMWorld::CopyLandTexturesCommand::getDestinationId() const
{
return mDestId;
}
CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTable,
const std::string& id, QUndoCommand* parent)
: ImportLandTexturesCommand(landTable, ltexTable, parent)
, mId(id)
, mOld(nullptr)
, mChanged(false)
{
setText(("Touch " + mId).c_str());
mOld.reset(mLands.getRecord(mId).clone());
}
const std::string& CSMWorld::TouchLandCommand::getOriginId() const
{
return mId;
}
const std::string& CSMWorld::TouchLandCommand::getDestinationId() const
{
return mId;
}
void CSMWorld::TouchLandCommand::onRedo()
{
mChanged = mLands.touchRecord(mId);
}
void CSMWorld::TouchLandCommand::onUndo()
{
if (mChanged)
{
mLands.setRecord(mId, *mOld);
mChanged = false;
}
}
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand* parent) const QVariant& new_, QUndoCommand* parent)
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)

View file

@ -5,12 +5,14 @@
#include <string> #include <string>
#include <map> #include <map>
#include <memory>
#include <vector> #include <vector>
#include <QVariant> #include <QVariant>
#include <QUndoCommand> #include <QUndoCommand>
#include <QModelIndex> #include <QModelIndex>
#include "columnimp.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
@ -24,6 +26,106 @@ namespace CSMWorld
struct RecordBase; struct RecordBase;
struct NestedTableWrapperBase; struct NestedTableWrapperBase;
class TouchCommand : public QUndoCommand
{
public:
TouchCommand(IdTable& model, const std::string& id, QUndoCommand* parent=nullptr);
void redo() override;
void undo() override;
private:
IdTable& mTable;
std::string mId;
std::unique_ptr<RecordBase> mOld;
bool mChanged;
};
/// \brief Adds LandTexture records and modifies texture indices as needed.
///
/// LandTexture records are different from other types of records, because
/// they only effect the current plugin. Thus, when modifying or copying
/// a Land record, all of the LandTexture records referenced need to be
/// added to the current plugin. Since these newly added LandTextures could
/// have indices that conflict with pre-existing LandTextures in the current
/// plugin, the indices might have to be changed, both for the newly added
/// LandRecord and within the Land record.
class ImportLandTexturesCommand : public QUndoCommand
{
public:
ImportLandTexturesCommand(IdTable& landTable, IdTable& ltexTable,
QUndoCommand* parent);
void redo() override;
void undo() override;
protected:
using DataType = LandTexturesColumn::DataType;
virtual const std::string& getOriginId() const = 0;
virtual const std::string& getDestinationId() const = 0;
virtual void onRedo() = 0;
virtual void onUndo() = 0;
IdTable& mLands;
IdTable& mLtexs;
DataType mOld;
int mOldState;
std::vector<std::string> mCreatedTextures;
};
/// \brief This command is used to fix LandTexture records and texture
/// indices after cloning a Land. See ImportLandTexturesCommand for
/// details.
class CopyLandTexturesCommand : public ImportLandTexturesCommand
{
public:
CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, const std::string& origin,
const std::string& dest, QUndoCommand* parent = nullptr);
private:
const std::string& getOriginId() const override;
const std::string& getDestinationId() const override;
void onRedo() override {}
void onUndo() override {}
std::string mOriginId;
std::string mDestId;
};
/// \brief This command brings a land record into the current plugin, adding
/// LandTexture records and modifying texture indices as needed.
/// \note See ImportLandTextures for more details.
class TouchLandCommand : public ImportLandTexturesCommand
{
public:
TouchLandCommand(IdTable& landTable, IdTable& ltexTable,
const std::string& id, QUndoCommand* parent = nullptr);
private:
const std::string& getOriginId() const override;
const std::string& getDestinationId() const override;
void onRedo() override;
void onUndo() override;
std::string mId;
std::unique_ptr<RecordBase> mOld;
bool mChanged;
};
class ModifyCommand : public QUndoCommand class ModifyCommand : public QUndoCommand
{ {
QAbstractItemModel *mModel; QAbstractItemModel *mModel;

View file

@ -11,6 +11,8 @@
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp>
#include "idtable.hpp" #include "idtable.hpp"
#include "idtree.hpp" #include "idtree.hpp"
@ -61,11 +63,18 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
return number; return number;
} }
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir) CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
const std::vector<std::string>& archives, const Fallback::Map* fallback, const boost::filesystem::path& resDir)
: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells), : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
mResourcesManager (resourcesManager), mFallbackMap(fallback), mFallbackMap(fallback), mReader (0), mDialogue (0), mReaderIndex(1),
mReader (0), mDialogue (0), mReaderIndex(1), mResourceSystem(new Resource::ResourceSystem(resourcesManager.getVFS())) mFsStrict(fsStrict), mDataPaths(dataPaths), mArchives(archives)
{ {
mVFS.reset(new VFS::Manager(mFsStrict));
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths, !mFsStrict), mArchives, true);
mResourcesManager.setVFS(mVFS.get());
mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));
mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string()); mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string());
int index = 0; int index = 0;
@ -403,6 +412,24 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight)); Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight));
mMagicEffects.addColumn (new DescriptionColumn<ESM::MagicEffect>); mMagicEffects.addColumn (new DescriptionColumn<ESM::MagicEffect>);
mLand.addColumn (new StringIdColumn<Land>);
mLand.addColumn (new RecordStateColumn<Land>);
mLand.addColumn (new FixedRecordTypeColumn<Land>(UniversalId::Type_Land));
mLand.addColumn (new LandPluginIndexColumn);
mLand.addColumn (new LandMapLodColumn);
mLand.addColumn (new LandNormalsColumn);
mLand.addColumn (new LandHeightsColumn);
mLand.addColumn (new LandColoursColumn);
mLand.addColumn (new LandTexturesColumn);
mLandTextures.addColumn (new StringIdColumn<LandTexture>(true));
mLandTextures.addColumn (new RecordStateColumn<LandTexture>);
mLandTextures.addColumn (new FixedRecordTypeColumn<LandTexture>(UniversalId::Type_LandTexture));
mLandTextures.addColumn (new LandTextureNicknameColumn);
mLandTextures.addColumn (new LandTexturePluginIndexColumn);
mLandTextures.addColumn (new LandTextureIndexColumn);
mLandTextures.addColumn (new TextureColumn<LandTexture>);
mPathgrids.addColumn (new StringIdColumn<Pathgrid>); mPathgrids.addColumn (new StringIdColumn<Pathgrid>);
mPathgrids.addColumn (new RecordStateColumn<Pathgrid>); mPathgrids.addColumn (new RecordStateColumn<Pathgrid>);
mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (UniversalId::Type_Pathgrid)); mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (UniversalId::Type_Pathgrid));
@ -522,6 +549,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart);
addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);
addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect);
addModel (new IdTable (&mLand, IdTable::Feature_AllowTouch), UniversalId::Type_Land);
addModel (new LandTextureIdTable (&mLandTextures), UniversalId::Type_LandTexture);
addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid); addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid);
addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript);
addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview),
@ -984,19 +1013,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break; case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break;
case ESM::REC_LAND: case ESM::REC_LAND: mLand.load(*mReader, mBase); break;
{
int index = mLand.load(*mReader, mBase);
// Load all land data for now. A future optimisation may only load non-base data
// if a suitable mechanism for avoiding race conditions can be established.
if (index!=-1/* && !mBase*/)
mLand.getRecord (index).get().loadData (
ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR |
ESM::Land::DATA_VTEX);
break;
}
case ESM::REC_CELL: case ESM::REC_CELL:
{ {
@ -1215,6 +1232,43 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
return ids; return ids;
} }
void CSMWorld::Data::assetsChanged()
{
mVFS.get()->reset();
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths, !mFsStrict), mArchives, true);
const UniversalId assetTableIds[] = {
UniversalId::Type_Meshes,
UniversalId::Type_Icons,
UniversalId::Type_Musics,
UniversalId::Type_SoundsRes,
UniversalId::Type_Textures,
UniversalId::Type_Videos
};
size_t numAssetTables = sizeof(assetTableIds) / sizeof(UniversalId);
for (size_t i = 0; i < numAssetTables; ++i)
{
ResourceTable* table = static_cast<ResourceTable*>(getTableModel(assetTableIds[i]));
table->beginReset();
}
// Trigger recreation
mResourcesManager.recreateResources();
for (size_t i = 0; i < numAssetTables; ++i)
{
ResourceTable* table = static_cast<ResourceTable*>(getTableModel(assetTableIds[i]));
table->endReset();
}
// Get rid of potentially old cached assets
mResourceSystem->clearCache();
emit assetTablesChanged();
}
void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{ {
if (topLeft.column()<=0) if (topLeft.column()<=0)
@ -1228,7 +1282,7 @@ void CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end)
const VFS::Manager* CSMWorld::Data::getVFS() const const VFS::Manager* CSMWorld::Data::getVFS() const
{ {
return mResourcesManager.getVFS(); return mVFS.get();
} }
const Fallback::Map* CSMWorld::Data::getFallbackMap() const const Fallback::Map* CSMWorld::Data::getFallbackMap() const

View file

@ -31,6 +31,7 @@
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/files/multidircollection.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
@ -46,6 +47,7 @@
#include "infocollection.hpp" #include "infocollection.hpp"
#include "nestedinfocollection.hpp" #include "nestedinfocollection.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "resourcesmanager.hpp"
#include "metadata.hpp" #include "metadata.hpp"
#ifndef Q_MOC_RUN #ifndef Q_MOC_RUN
#include "subcellcollection.hpp" #include "subcellcollection.hpp"
@ -108,7 +110,6 @@ namespace CSMWorld
RefCollection mRefs; RefCollection mRefs;
IdCollection<ESM::Filter> mFilters; IdCollection<ESM::Filter> mFilters;
Collection<MetaData> mMetaData; Collection<MetaData> mMetaData;
const ResourcesManager& mResourcesManager;
const Fallback::Map* mFallbackMap; const Fallback::Map* mFallbackMap;
std::vector<QAbstractItemModel *> mModels; std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex; std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
@ -119,6 +120,11 @@ namespace CSMWorld
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache; std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
int mReaderIndex; int mReaderIndex;
bool mFsStrict;
Files::PathContainer mDataPaths;
std::vector<std::string> mArchives;
std::unique_ptr<VFS::Manager> mVFS;
ResourcesManager mResourcesManager;
std::shared_ptr<Resource::ResourceSystem> mResourceSystem; std::shared_ptr<Resource::ResourceSystem> mResourceSystem;
std::vector<std::shared_ptr<ESM::ESMReader> > mReaders; std::vector<std::shared_ptr<ESM::ESMReader> > mReaders;
@ -140,7 +146,9 @@ namespace CSMWorld
public: public:
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir); Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
const std::vector<std::string>& archives, const Fallback::Map* fallback,
const boost::filesystem::path& resDir);
virtual ~Data(); virtual ~Data();
@ -304,8 +312,12 @@ namespace CSMWorld
void idListChanged(); void idListChanged();
void assetTablesChanged();
private slots: private slots:
void assetsChanged();
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);

View file

@ -4,6 +4,7 @@
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include "collection.hpp" #include "collection.hpp"
#include "land.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -39,6 +40,22 @@ namespace CSMWorld
record.load (reader, isDeleted); record.load (reader, isDeleted);
} }
template<>
inline void IdCollection<Land, IdAccessor<Land> >::loadRecord (Land& record,
ESM::ESMReader& reader, bool& isDeleted)
{
record.load (reader, isDeleted);
// Load all land data for now. A future optimisation may only load non-base data
// if a suitable mechanism for avoiding race conditions can be established.
int flags = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;
record.loadData (flags);
// Prevent data from being reloaded.
record.mContext.filename.clear();
}
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base) int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
{ {

View file

@ -1,11 +1,17 @@
#include "idtable.hpp" #include "idtable.hpp"
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <limits>
#include <map>
#include <stdexcept> #include <stdexcept>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
#include "landtexture.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features) CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)
: IdTableBase (features), mIdCollection (idCollection) : IdTableBase (features), mIdCollection (idCollection)
@ -179,6 +185,26 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin,
endInsertRows(); endInsertRows();
} }
bool CSMWorld::IdTable::touchRecord(const std::string& id)
{
bool changed = mIdCollection->touchRecord(id);
int row = mIdCollection->getIndex(id);
int column = mIdCollection->searchColumnIndex(Columns::ColumnId_RecordType);
if (changed && column != -1)
{
QModelIndex modelIndex = index(row, column);
emit dataChanged(modelIndex, modelIndex);
}
return changed;
}
std::string CSMWorld::IdTable::getId(int row) const
{
return mIdCollection->getId(row);
}
///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
{ {
@ -281,3 +307,72 @@ CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const
{ {
return mIdCollection; return mIdCollection;
} }
CSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, unsigned int features)
: IdTable(idCollection, features)
{
}
CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(const std::vector<std::string>& ids)
{
ImportResults results;
// Map existing textures to ids
std::map<std::string, std::string> reverseLookupMap;
for (int i = 0; i < idCollection()->getSize(); ++i)
{
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(i));
std::string texture = record.get().mTexture;
std::transform(texture.begin(), texture.end(), texture.begin(), tolower);
if (record.isModified())
reverseLookupMap.emplace(texture, idCollection()->getId(i));
}
for (const std::string& id : ids)
{
int plugin, index;
LandTexture::parseUniqueRecordId(id, plugin, index);
int oldRow = idCollection()->searchId(id);
// If it does not exist or it is in the current plugin, it can be skipped.
if (oldRow < 0 || plugin == 0)
{
results.recordMapping.push_back(std::make_pair(id, id));
continue;
}
// Look for a pre-existing record
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(oldRow));
std::string texture = record.get().mTexture;
std::transform(texture.begin(), texture.end(), texture.begin(), tolower);
auto searchIt = reverseLookupMap.find(texture);
if (searchIt != reverseLookupMap.end())
{
results.recordMapping.push_back(std::make_pair(id, searchIt->second));
continue;
}
// Iterate until an unused index or found, or the index has completely wrapped around.
int startIndex = index;
do {
std::string newId = LandTexture::createUniqueRecordId(0, index);
int newRow = idCollection()->searchId(newId);
if (newRow < 0)
{
// Id not taken, clone it
cloneRecord(id, newId, UniversalId::Type_LandTexture);
results.createdRecords.push_back(newId);
results.recordMapping.push_back(std::make_pair(id, newId));
reverseLookupMap.emplace(texture, newId);
break;
}
const size_t MaxIndex = std::numeric_limits<uint16_t>::max() - 1;
index = (index + 1) % MaxIndex;
} while (index != startIndex);
}
return results;
}

View file

@ -61,6 +61,11 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);
bool touchRecord(const std::string& id);
///< Will change the record state to modified, if it is not already.
std::string getId(int row) const;
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, const RecordBase& record,
@ -93,6 +98,29 @@ namespace CSMWorld
virtual CollectionBase *idCollection() const; virtual CollectionBase *idCollection() const;
}; };
/// An IdTable customized to handle the more unique needs of LandTextureId's which behave
/// differently from other records. The major difference is that base records cannot be
/// modified.
class LandTextureIdTable : public IdTable
{
public:
struct ImportResults
{
using StringPair = std::pair<std::string,std::string>;
/// The newly added records
std::vector<std::string> createdRecords;
/// The 1st string is the original id, the 2nd is the mapped id
std::vector<StringPair> recordMapping;
};
LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0);
/// Finds and maps/recreates the specified ids.
ImportResults importTextures(const std::vector<std::string>& ids);
};
} }
#endif #endif

View file

@ -32,7 +32,9 @@ namespace CSMWorld
Feature_Preview = 8, Feature_Preview = 8,
/// Table can not be modified through ordinary means. /// Table can not be modified through ordinary means.
Feature_Constant = 16 Feature_Constant = 16,
Feature_AllowTouch = 32
}; };
private: private:

View file

@ -1,15 +1,30 @@
#include "land.hpp" #include "land.hpp"
#include <sstream> #include <sstream>
#include <stdexcept>
namespace CSMWorld namespace CSMWorld
{ {
void Land::load(ESM::ESMReader &esm, bool &isDeleted) void Land::load(ESM::ESMReader &esm, bool &isDeleted)
{ {
ESM::Land::load(esm, isDeleted); ESM::Land::load(esm, isDeleted);
}
std::string Land::createUniqueRecordId(int x, int y)
{
std::ostringstream stream; std::ostringstream stream;
stream << "#" << mX << " " << mY; stream << "#" << x << " " << y;
mId = stream.str(); return stream.str();
}
void Land::parseUniqueRecordId(const std::string& id, int& x, int& y)
{
size_t mid = id.find(' ');
if (mid == std::string::npos || id[0] != '#')
throw std::runtime_error("Invalid Land ID");
x = std::stoi(id.substr(1, mid - 1));
y = std::stoi(id.substr(mid + 1));
} }
} }

View file

@ -12,10 +12,11 @@ namespace CSMWorld
/// \todo Add worldspace support to the Land record. /// \todo Add worldspace support to the Land record.
struct Land : public ESM::Land struct Land : public ESM::Land
{ {
std::string mId;
/// Loads the metadata and ID /// Loads the metadata and ID
void load (ESM::ESMReader &esm, bool &isDeleted); void load (ESM::ESMReader &esm, bool &isDeleted);
static std::string createUniqueRecordId(int x, int y);
static void parseUniqueRecordId(const std::string& id, int& x, int& y);
}; };
} }

View file

@ -1,5 +1,8 @@
#include "landtexture.hpp" #include "landtexture.hpp"
#include <sstream>
#include <stdexcept>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
namespace CSMWorld namespace CSMWorld
@ -11,4 +14,21 @@ namespace CSMWorld
mPluginIndex = esm.getIndex(); mPluginIndex = esm.getIndex();
} }
std::string LandTexture::createUniqueRecordId(int plugin, int index)
{
std::stringstream ss;
ss << 'L' << plugin << '#' << index;
return ss.str();
}
void LandTexture::parseUniqueRecordId(const std::string& id, int& plugin, int& index)
{
size_t middle = id.find('#');
if (middle == std::string::npos || id[0] != 'L')
throw std::runtime_error("Invalid LandTexture ID");
plugin = std::stoi(id.substr(1,middle-1));
index = std::stoi(id.substr(middle+1));
}
} }

View file

@ -13,6 +13,11 @@ namespace CSMWorld
int mPluginIndex; int mPluginIndex;
void load (ESM::ESMReader &esm, bool &isDeleted); void load (ESM::ESMReader &esm, bool &isDeleted);
/// Returns a string identifier that will be unique to any LandTexture.
static std::string createUniqueRecordId(int plugin, int index);
/// Deconstructs a unique string identifier into plugin and index.
static void parseUniqueRecordId(const std::string& id, int& plugin, int& index);
}; };
} }

View file

@ -0,0 +1,21 @@
#include "landtexturetableproxymodel.hpp"
#include "idtable.hpp"
namespace CSMWorld
{
LandTextureTableProxyModel::LandTextureTableProxyModel(QObject* parent)
: IdTableProxyModel(parent)
{
}
bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const
{
int columnIndex = mSourceModel->findColumnIndex(Columns::ColumnId_Modification);
QModelIndex index = mSourceModel->index(sourceRow, columnIndex);
if (mSourceModel->data(index).toInt() != RecordBase::State_ModifiedOnly)
return false;
return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
}

View file

@ -0,0 +1,22 @@
#ifndef CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H
#define CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H
#include "idtableproxymodel.hpp"
namespace CSMWorld
{
/// \brief Removes base records from filtered results.
class LandTextureTableProxyModel : public IdTableProxyModel
{
Q_OBJECT
public:
LandTextureTableProxyModel(QObject* parent = nullptr);
protected:
bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const override;
};
}
#endif

View file

@ -813,6 +813,12 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
mData.insertRecord(*newRecord, type, destination); mData.insertRecord(*newRecord, type, destination);
} }
bool CSMWorld::RefIdCollection::touchRecord(const std::string& id)
{
throw std::runtime_error("RefIdCollection::touchRecord is unimplemented");
return false;
}
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
UniversalId::Type type) UniversalId::Type type)
{ {

View file

@ -80,6 +80,8 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type); const UniversalId::Type type);
virtual bool touchRecord(const std::string& id);
virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); virtual void appendBlankRecord (const std::string& id, UniversalId::Type type);
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types

View file

@ -12,6 +12,14 @@ CSMWorld::Resources::Resources (const VFS::Manager* vfs, const std::string& base
const char * const *extensions) const char * const *extensions)
: mBaseDirectory (baseDirectory), mType (type) : mBaseDirectory (baseDirectory), mType (type)
{ {
recreate(vfs, extensions);
}
void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char * const *extensions)
{
mFiles.clear();
mIndex.clear();
int baseSize = mBaseDirectory.size(); int baseSize = mBaseDirectory.size();
const std::map<std::string, VFS::File*>& index = vfs->getIndex(); const std::map<std::string, VFS::File*>& index = vfs->getIndex();

View file

@ -27,6 +27,8 @@ namespace CSMWorld
Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type, Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type,
const char * const *extensions = 0); const char * const *extensions = 0);
void recreate(const VFS::Manager* vfs, const char * const *extensions = 0);
int getSize() const; int getSize() const;
std::string getId (int index) const; std::string getId (int index) const;

View file

@ -14,21 +14,24 @@ void CSMWorld::ResourcesManager::addResources (const Resources& resources)
resources)); resources));
} }
const char * const * CSMWorld::ResourcesManager::getMeshExtensions()
{
// maybe we could go over the osgDB::Registry to list all supported node formats
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 };
return sMeshTypes;
}
void CSMWorld::ResourcesManager::setVFS(const VFS::Manager *vfs) void CSMWorld::ResourcesManager::setVFS(const VFS::Manager *vfs)
{ {
mVFS = vfs; mVFS = vfs;
mResources.clear(); mResources.clear();
// maybe we could go over the osgDB::Registry to list all supported node formats addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, getMeshExtensions()));
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 };
addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, sMeshTypes));
addResources (Resources (vfs, "icons", UniversalId::Type_Icon)); addResources (Resources (vfs, "icons", UniversalId::Type_Icon));
addResources (Resources (vfs, "music", UniversalId::Type_Music)); addResources (Resources (vfs, "music", UniversalId::Type_Music));
addResources (Resources (vfs, "sound", UniversalId::Type_SoundRes)); addResources (Resources (vfs, "sound", UniversalId::Type_SoundRes));
addResources (Resources (vfs, "textures", UniversalId::Type_Texture)); addResources (Resources (vfs, "textures", UniversalId::Type_Texture));
addResources (Resources (vfs, "videos", UniversalId::Type_Video)); addResources (Resources (vfs, "video", UniversalId::Type_Video));
} }
const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const
@ -36,6 +39,18 @@ const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const
return mVFS; return mVFS;
} }
void CSMWorld::ResourcesManager::recreateResources()
{
std::map<UniversalId::Type, Resources>::iterator it = mResources.begin();
for ( ; it != mResources.end(); ++it)
{
if (it->first == UniversalId::Type_Mesh)
it->second.recreate(mVFS, getMeshExtensions());
else
it->second.recreate(mVFS);
}
}
const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const
{ {
std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type); std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type);

View file

@ -22,6 +22,8 @@ namespace CSMWorld
void addResources (const Resources& resources); void addResources (const Resources& resources);
const char * const * getMeshExtensions();
public: public:
ResourcesManager(); ResourcesManager();
@ -30,6 +32,8 @@ namespace CSMWorld
void setVFS(const VFS::Manager* vfs); void setVFS(const VFS::Manager* vfs);
void recreateResources();
const Resources& get (UniversalId::Type type) const; const Resources& get (UniversalId::Type type) const;
}; };
} }

View file

@ -154,3 +154,13 @@ int CSMWorld::ResourceTable::getColumnId (int column) const
return -1; return -1;
} }
void CSMWorld::ResourceTable::beginReset()
{
beginResetModel();
}
void CSMWorld::ResourceTable::endReset()
{
endResetModel();
}

View file

@ -53,6 +53,11 @@ namespace CSMWorld
virtual bool isDeleted (const std::string& id) const; virtual bool isDeleted (const std::string& id) const;
virtual int getColumnId (int column) const; virtual int getColumnId (int column) const;
/// Signal Qt that the data is about to change.
void beginReset();
/// Signal Qt that the data has been changed.
void endReset();
}; };
} }

View file

@ -53,6 +53,8 @@ namespace
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 },
@ -118,6 +120,8 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 },

View file

@ -126,6 +126,10 @@ namespace CSMWorld
Type_SoundGen, Type_SoundGen,
Type_MagicEffects, Type_MagicEffects,
Type_MagicEffect, Type_MagicEffect,
Type_Lands,
Type_Land,
Type_LandTextures,
Type_LandTexture,
Type_Pathgrids, Type_Pathgrids,
Type_Pathgrid, Type_Pathgrid,
Type_StartScripts, Type_StartScripts,

View file

@ -172,6 +172,16 @@ void CSVDoc::View::setupWorldMenu()
setupShortcut("document-world-references", references); setupShortcut("document-world-references", references);
world->addAction (references); world->addAction (references);
QAction *lands = new QAction (tr ("Lands"), this);
connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView()));
setupShortcut("document-world-lands", lands);
world->addAction (lands);
QAction *landTextures = new QAction (tr ("Land Textures"), this);
connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView()));
setupShortcut("document-world-landtextures", landTextures);
world->addAction (landTextures);
QAction *grid = new QAction (tr ("Pathgrid"), this); QAction *grid = new QAction (tr ("Pathgrid"), this);
connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView()));
setupShortcut("document-world-pathgrid", grid); setupShortcut("document-world-pathgrid", grid);
@ -284,6 +294,13 @@ void CSVDoc::View::setupAssetsMenu()
{ {
QMenu *assets = menuBar()->addMenu (tr ("Assets")); QMenu *assets = menuBar()->addMenu (tr ("Assets"));
QAction *reload = new QAction (tr ("Reload"), this);
connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged()));
setupShortcut("document-assets-reload", reload);
assets->addAction (reload);
assets->addSeparator();
QAction *sounds = new QAction (tr ("Sounds"), this); QAction *sounds = new QAction (tr ("Sounds"), this);
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
setupShortcut("document-assets-sounds", sounds); setupShortcut("document-assets-sounds", sounds);
@ -869,6 +886,16 @@ void CSVDoc::View::addRunLogSubView()
addSubView (CSMWorld::UniversalId::Type_RunLog); addSubView (CSMWorld::UniversalId::Type_RunLog);
} }
void CSVDoc::View::addLandsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Lands);
}
void CSVDoc::View::addLandTexturesSubView()
{
addSubView (CSMWorld::UniversalId::Type_LandTextures);
}
void CSVDoc::View::addPathgridSubView() void CSVDoc::View::addPathgridSubView()
{ {
addSubView (CSMWorld::UniversalId::Type_Pathgrids); addSubView (CSMWorld::UniversalId::Type_Pathgrids);

View file

@ -224,6 +224,10 @@ namespace CSVDoc
void addRunLogSubView(); void addRunLogSubView();
void addLandsSubView();
void addLandTexturesSubView();
void addPathgridSubView(); void addPathgridSubView();
void addStartScriptsSubView(); void addStartScriptsSubView();

View file

@ -24,7 +24,7 @@ void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main)
main->addWidget (list); main->addWidget (list);
QFontMetrics metrics (QApplication::font()); QFontMetrics metrics (QApplication::font(list));
int maxWidth = 1; int maxWidth = 1;

View file

@ -26,6 +26,33 @@
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "object.hpp" #include "object.hpp"
namespace CSVRender
{
class CellNodeContainer : public osg::Referenced
{
public:
CellNodeContainer(Cell* cell) : mCell(cell) {}
Cell* getCell(){ return mCell; }
private:
Cell* mCell;
};
class CellNodeCallback : public osg::NodeCallback
{
public:
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
CellNodeContainer* container = static_cast<CellNodeContainer*>(node->getUserData());
container->getCell()->updateLand();
}
};
}
bool CSVRender::Cell::removeObject (const std::string& id) bool CSVRender::Cell::removeObject (const std::string& id)
{ {
std::map<std::string, Object *>::iterator iter = std::map<std::string, Object *>::iterator iter =
@ -75,10 +102,69 @@ bool CSVRender::Cell::addObjects (int start, int end)
return modified; return modified;
} }
void CSVRender::Cell::updateLand()
{
if (!mUpdateLand || mLandDeleted)
return;
mUpdateLand = false;
// Cell is deleted
if (mDeleted)
{
unloadLand();
return;
}
// Setup land if available
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
int landIndex = land.searchId(mId);
if (landIndex != -1 && !land.getRecord(mId).isDeleted())
{
const ESM::Land& esmLand = land.getRecord(mId).get();
if (esmLand.getLandData (ESM::Land::DATA_VHGT))
{
if (mTerrain)
{
mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());
mTerrain->clearAssociatedCaches();
}
else
{
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode,
mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain));
}
mTerrain->loadCell(esmLand.mX, esmLand.mY);
if (!mCellBorder)
mCellBorder.reset(new CellBorder(mCellNode, mCoordinates));
mCellBorder->buildShape(esmLand);
return;
}
}
// No land data
mLandDeleted = true;
unloadLand();
}
void CSVRender::Cell::unloadLand()
{
if (mTerrain)
mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());
if (mCellBorder)
mCellBorder.reset();
}
CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,
bool deleted) bool deleted)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0), : mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0),
mSubModeElementMask (0) mSubModeElementMask (0), mUpdateLand(true), mLandDeleted(false)
{ {
std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id); std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id);
@ -86,6 +172,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
mCoordinates = result.first; mCoordinates = result.first;
mCellNode = new osg::Group; mCellNode = new osg::Group;
mCellNode->setUserData(new CellNodeContainer(this));
mCellNode->setUpdateCallback(new CellNodeCallback);
rootNode->addChild(mCellNode); rootNode->addChild(mCellNode);
setCellMarker(); setCellMarker();
@ -99,22 +187,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
addObjects (0, rows-1); addObjects (0, rows-1);
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand(); updateLand();
int landIndex = land.searchId(mId);
if (landIndex != -1)
{
const ESM::Land& esmLand = land.getRecord(mId).get();
if (esmLand.getLandData (ESM::Land::DATA_VHGT))
{
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, data.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain));
mTerrain->loadCell(esmLand.mX,
esmLand.mY);
mCellBorder.reset(new CellBorder(mCellNode, mCoordinates));
mCellBorder->buildShape(esmLand);
}
}
mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates));
mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates)); mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates));
@ -275,14 +348,66 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
void CSVRender::Cell::pathgridModified() void CSVRender::Cell::pathgridModified()
{ {
if (mPathgrid)
mPathgrid->recreateGeometry(); mPathgrid->recreateGeometry();
} }
void CSVRender::Cell::pathgridRemoved() void CSVRender::Cell::pathgridRemoved()
{ {
if (mPathgrid)
mPathgrid->removeGeometry(); mPathgrid->removeGeometry();
} }
void CSVRender::Cell::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
mUpdateLand = true;
}
void CSVRender::Cell::landAboutToBeRemoved (const QModelIndex& parent, int start, int end)
{
mLandDeleted = true;
unloadLand();
}
void CSVRender::Cell::landAdded (const QModelIndex& parent, int start, int end)
{
mUpdateLand = true;
mLandDeleted = false;
}
void CSVRender::Cell::landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
mUpdateLand = true;
}
void CSVRender::Cell::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end)
{
mUpdateLand = true;
}
void CSVRender::Cell::landTextureAdded (const QModelIndex& parent, int start, int end)
{
mUpdateLand = true;
}
void CSVRender::Cell::reloadAssets()
{
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter != mObjects.end(); ++iter)
{
iter->second->reloadAssets();
}
if (mTerrain)
{
mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());
mTerrain->loadCell(mCoordinates.getX(), mCoordinates.getY());
}
if (mCellWater)
mCellWater->reloadAssets();
}
void CSVRender::Cell::setSelection (int elementMask, Selection mode) void CSVRender::Cell::setSelection (int elementMask, Selection mode)
{ {
if (elementMask & Mask_Reference) if (elementMask & Mask_Reference)
@ -302,7 +427,7 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode)
iter->second->setSelected (selected); iter->second->setSelected (selected);
} }
} }
if (elementMask & Mask_Pathgrid) if (mPathgrid && elementMask & Mask_Pathgrid)
{ {
// Only one pathgrid may be selected, so some operations will only have an effect // Only one pathgrid may be selected, so some operations will only have an effect
// if the pathgrid is already focused // if the pathgrid is already focused
@ -402,7 +527,7 @@ std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getSelection (un
iter!=mObjects.end(); ++iter) iter!=mObjects.end(); ++iter)
if (iter->second->getSelected()) if (iter->second->getSelected())
result.push_back (iter->second->getTag()); result.push_back (iter->second->getTag());
if (elementMask & Mask_Pathgrid) if (mPathgrid && elementMask & Mask_Pathgrid)
if (mPathgrid->isSelected()) if (mPathgrid->isSelected())
result.push_back(mPathgrid->getTag()); result.push_back(mPathgrid->getTag());
@ -439,6 +564,6 @@ void CSVRender::Cell::reset (unsigned int elementMask)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin()); for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter) iter!=mObjects.end(); ++iter)
iter->second->reset(); iter->second->reset();
if (elementMask & Mask_Pathgrid) if (mPathgrid && elementMask & Mask_Pathgrid)
mPathgrid->resetIndicators(); mPathgrid->resetIndicators();
} }

View file

@ -57,6 +57,7 @@ namespace CSVRender
bool mDeleted; bool mDeleted;
int mSubMode; int mSubMode;
unsigned int mSubModeElementMask; unsigned int mSubModeElementMask;
bool mUpdateLand, mLandDeleted;
/// Ignored if cell does not have an object with the given ID. /// Ignored if cell does not have an object with the given ID.
/// ///
@ -72,6 +73,9 @@ namespace CSVRender
/// \return Have any objects been added? /// \return Have any objects been added?
bool addObjects (int start, int end); bool addObjects (int start, int end);
void updateLand();
void unloadLand();
public: public:
enum Selection enum Selection
@ -118,6 +122,20 @@ namespace CSVRender
void pathgridRemoved(); void pathgridRemoved();
void landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void landAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void landAdded (const QModelIndex& parent, int start, int end);
void landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void landTextureAdded (const QModelIndex& parent, int start, int end);
void reloadAssets();
void setSelection (int elementMask, Selection mode); void setSelection (int elementMask, Selection mode);
// Select everything that references the same ID as at least one of the elements // Select everything that references the same ID as at least one of the elements
@ -143,6 +161,8 @@ namespace CSVRender
/// Erase all overrides and restore the visual representation of the cell to its /// Erase all overrides and restore the visual representation of the cell to its
/// true state. /// true state.
void reset (unsigned int elementMask); void reset (unsigned int elementMask);
friend class CellNodeCallback;
}; };
} }

View file

@ -92,6 +92,11 @@ namespace CSVRender
} }
} }
void CellWater::reloadAssets()
{
recreate();
}
void CellWater::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) void CellWater::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
{ {
const CSMWorld::Collection<CSMWorld::Cell>& cells = mData.getCells(); const CSMWorld::Collection<CSMWorld::Cell>& cells = mData.getCells();

View file

@ -42,6 +42,8 @@ namespace CSVRender
void updateCellData(const CSMWorld::Record<CSMWorld::Cell>& cellRecord); void updateCellData(const CSMWorld::Record<CSMWorld::Cell>& cellRecord);
void reloadAssets();
private slots: private slots:
void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);

View file

@ -532,6 +532,12 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
return false; return false;
} }
void CSVRender::Object::reloadAssets()
{
update();
updateMarker();
}
std::string CSVRender::Object::getReferenceId() const std::string CSVRender::Object::getReferenceId() const
{ {
return mReferenceId; return mReferenceId;

View file

@ -151,6 +151,9 @@ namespace CSVRender
/// this object? /// this object?
bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
/// Reloads the underlying asset
void reloadAssets();
/// Returns an empty string if this is a refereceable-type object. /// Returns an empty string if this is a refereceable-type object.
std::string getReferenceId() const; std::string getReferenceId() const;

View file

@ -351,6 +351,72 @@ void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent,
} }
} }
void CSVRender::PagedWorldspaceWidget::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
for (int r = topLeft.row(); r <= bottomRight.row(); ++r)
{
std::string id = mDocument.getData().getLand().getId(r);
auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);
if (cellIt != mCells.end())
{
cellIt->second->landDataChanged(topLeft, bottomRight);
flagAsModified();
}
}
}
void CSVRender::PagedWorldspaceWidget::landAboutToBeRemoved (const QModelIndex& parent, int start, int end)
{
for (int r = start; r <= end; ++r)
{
std::string id = mDocument.getData().getLand().getId(r);
auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);
if (cellIt != mCells.end())
{
cellIt->second->landAboutToBeRemoved(parent, start, end);
flagAsModified();
}
}
}
void CSVRender::PagedWorldspaceWidget::landAdded (const QModelIndex& parent, int start, int end)
{
for (int r = start; r <= end; ++r)
{
std::string id = mDocument.getData().getLand().getId(r);
auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);
if (cellIt != mCells.end())
{
cellIt->second->landAdded(parent, start, end);
flagAsModified();
}
}
}
void CSVRender::PagedWorldspaceWidget::landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
for (auto cellIt : mCells)
cellIt.second->landTextureChanged(topLeft, bottomRight);
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end)
{
for (auto cellIt : mCells)
cellIt.second->landTextureAboutToBeRemoved(parent, start, end);
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::landTextureAdded (const QModelIndex& parent, int start, int end)
{
for (auto cellIt : mCells)
cellIt.second->landTextureAdded(parent, start, end);
flagAsModified();
}
std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction() std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()
{ {
@ -472,6 +538,27 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc
connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (cellAdded (const QModelIndex&, int, int))); this, SLOT (cellAdded (const QModelIndex&, int, int)));
connect (&document.getData(), SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
QAbstractItemModel *lands = document.getData().getTableModel (CSMWorld::UniversalId::Type_Lands);
connect (lands, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (landDataChanged (const QModelIndex&, const QModelIndex&)));
connect (lands, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (landAboutToBeRemoved (const QModelIndex&, int, int)));
connect (lands, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (landAdded (const QModelIndex&, int, int)));
QAbstractItemModel *ltexs = document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures);
connect (ltexs, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (landTextureDataChanged (const QModelIndex&, const QModelIndex&)));
connect (ltexs, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (landTextureAboutToBeRemoved (const QModelIndex&, int, int)));
connect (ltexs, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (landTextureAdded (const QModelIndex&, int, int)));
// Shortcuts // Shortcuts
CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this); CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this);
connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell())); connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell()));
@ -763,6 +850,15 @@ void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int
flagAsModified(); flagAsModified();
} }
void CSVRender::PagedWorldspaceWidget::assetTablesChanged()
{
std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();
for ( ; iter != mCells.end(); ++iter)
{
iter->second->reloadAssets();
}
}
void CSVRender::PagedWorldspaceWidget::loadCameraCell() void CSVRender::PagedWorldspaceWidget::loadCameraCell()
{ {
addCellToSceneFromCamera(0, 0); addCellToSceneFromCamera(0, 0);

View file

@ -155,6 +155,16 @@ namespace CSVRender
virtual void cellAdded (const QModelIndex& index, int start, int end); virtual void cellAdded (const QModelIndex& index, int start, int end);
virtual void landDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight);
virtual void landAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void landAdded (const QModelIndex& parent, int start, int end);
virtual void landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight);
virtual void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void landTextureAdded (const QModelIndex& parent, int start, int end);
void assetTablesChanged ();
void loadCameraCell(); void loadCameraCell();
void loadEastCell(); void loadEastCell();

View file

@ -71,6 +71,8 @@ namespace CSVRender
primarySelectPressed(hitResult); primarySelectPressed(hitResult);
} }
else if (Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos)) else if (Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos))
{
if (cell->getPathgrid())
{ {
// Add node // Add node
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
@ -80,6 +82,7 @@ namespace CSVRender
cell->getPathgrid()->applyPoint(macro, hitResult.worldPos); cell->getPathgrid()->applyPoint(macro, hitResult.worldPos);
} }
} }
}
void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit) void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit)
{ {
@ -205,7 +208,7 @@ namespace CSVRender
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
Cell* cell = getWorldspaceWidget().getCell(hit.worldPos); Cell* cell = getWorldspaceWidget().getCell(hit.worldPos);
if (cell) if (cell && cell->getPathgrid())
{ {
PathgridTag* tag = 0; PathgridTag* tag = 0;
if (hit.tag && (tag = dynamic_cast<PathgridTag*>(hit.tag.get())) && tag->getPathgrid()->getId() == mEdgeId) if (hit.tag && (tag = dynamic_cast<PathgridTag*>(hit.tag.get())) && tag->getPathgrid()->getId() == mEdgeId)

View file

@ -17,6 +17,9 @@ CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data,
connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int))); this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int)));
connect (&mData, SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
if (!referenceable) if (!referenceable)
{ {
QAbstractItemModel *references = QAbstractItemModel *references =
@ -119,3 +122,8 @@ void CSVRender::PreviewWidget::referenceAboutToBeRemoved (const QModelIndex& par
if (index.row()>=start && index.row()<=end) if (index.row()>=start && index.row()<=end)
emit closeRequest(); emit closeRequest();
} }
void CSVRender::PreviewWidget::assetTablesChanged ()
{
mObject.reloadAssets();
}

View file

@ -47,6 +47,8 @@ namespace CSVRender
void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void assetTablesChanged ();
}; };
} }

View file

@ -221,8 +221,8 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
SceneWidget::~SceneWidget() SceneWidget::~SceneWidget()
{ {
// Since we're holding on to the scene templates past the existence of this graphics context, we'll need to manually release the created objects // Since we're holding on to the resources past the existence of this graphics context, we'll need to manually release the created objects
mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState()); mResourceSystem->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
} }
void SceneWidget::setLighting(Lighting *lighting) void SceneWidget::setLighting(Lighting *lighting)
@ -393,6 +393,7 @@ void SceneWidget::selectNavigationMode (const std::string& mode)
mCurrentCamControl->setCamera(NULL); mCurrentCamControl->setCamera(NULL);
mCurrentCamControl = mOrbitCamControl; mCurrentCamControl = mOrbitCamControl;
mOrbitCamControl->setCamera(getCamera()); mOrbitCamControl->setCamera(getCamera());
mOrbitCamControl->reset();
} }
} }

View file

@ -1,5 +1,8 @@
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "../../model/world/land.hpp"
#include "../../model/world/landtexture.hpp"
namespace CSVRender namespace CSVRender
{ {
@ -11,12 +14,9 @@ namespace CSVRender
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY) osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)
{ {
std::ostringstream stream;
stream << "#" << cellX << " " << cellY;
// The cell isn't guaranteed to have Land. This is because the terrain implementation // The cell isn't guaranteed to have Land. This is because the terrain implementation
// has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
int index = mData.getLand().searchId(stream.str()); int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY));
if (index == -1) if (index == -1)
return NULL; return NULL;
@ -26,16 +26,11 @@ namespace CSVRender
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
{ {
int numRecords = mData.getLandTextures().getSize(); int row = mData.getLandTextures().searchId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index));
if (row == -1)
return nullptr;
for (int i=0; i<numRecords; ++i) return &mData.getLandTextures().getRecord(row).get();
{
const CSMWorld::LandTexture* ltex = &mData.getLandTextures().getRecord(i).get();
if (ltex->mIndex == index && ltex->mPluginIndex == plugin)
return ltex;
}
return NULL;
} }
void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY)

View file

@ -47,6 +47,9 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string&
connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int))); this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int)));
connect (&document.getData(), SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
update(); update();
mCell.reset (new Cell (document.getData(), mRootNode, mCellId)); mCell.reset (new Cell (document.getData(), mRootNode, mCellId));
@ -82,6 +85,12 @@ void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelI
emit closeRequest(); emit closeRequest();
} }
void CSVRender::UnpagedWorldspaceWidget::assetTablesChanged()
{
if (mCell)
mCell->reloadAssets();
}
bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& universalIdData, DropType type) bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& universalIdData, DropType type)
{ {
if (WorldspaceWidget::handleDrop (universalIdData, type)) if (WorldspaceWidget::handleDrop (universalIdData, type))

View file

@ -108,6 +108,8 @@ namespace CSVRender
void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void assetTablesChanged ();
signals: signals:
void cellChanged(const CSMWorld::UniversalId& id); void cellChanged(const CSMWorld::UniversalId& id);

View file

@ -15,12 +15,15 @@ void CSVWorld::ColorDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
int colorInt = index.data().toInt();
QColor color(colorInt & 0xff, (colorInt >> 8) & 0xff, (colorInt >> 16) & 0xff);
QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0), QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0),
option.rect.y() + qRound(option.rect.height() / 4.0), option.rect.y() + qRound(option.rect.height() / 4.0),
option.rect.width() / 2, option.rect.width() / 2,
option.rect.height() / 2); option.rect.height() / 2);
painter->save(); painter->save();
painter->fillRect(coloredRect, index.data().value<QColor>()); painter->fillRect(coloredRect, color);
painter->setPen(Qt::black); painter->setPen(Qt::black);
painter->drawRect(coloredRect); painter->drawRect(coloredRect);
painter->restore(); painter->restore();

View file

@ -31,6 +31,9 @@ namespace CSVWorld
virtual void cloneMode(const std::string& originId, virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type) = 0; const CSMWorld::UniversalId::Type type) = 0;
/// Touches a record, if the creator supports it.
virtual void touch(const std::vector<CSMWorld::UniversalId>& ids) = 0;
virtual void setEditLock (bool locked) = 0; virtual void setEditLock (bool locked) = 0;
virtual void toggleWidgets(bool active = true) = 0; virtual void toggleWidgets(bool active = true) = 0;

View file

@ -51,6 +51,11 @@ std::string CSVWorld::GenericCreator::getId() const
return mId->text().toUtf8().constData(); return mId->text().toUtf8().constData();
} }
std::string CSVWorld::GenericCreator::getClonedId() const
{
return mClonedId;
}
std::string CSVWorld::GenericCreator::getIdValidatorResult() const std::string CSVWorld::GenericCreator::getIdValidatorResult() const
{ {
std::string errors; std::string errors;
@ -254,6 +259,22 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originId,
mClonedType = type; mClonedType = type;
} }
void CSVWorld::GenericCreator::touch(const std::vector<CSMWorld::UniversalId>& ids)
{
// Combine multiple touch commands into one "macro" command
mUndoStack.beginMacro("Touch Records");
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*mData.getTableModel(mListId));
for (const CSMWorld::UniversalId& uid : ids)
{
CSMWorld::TouchCommand* touchCmd = new CSMWorld::TouchCommand(table, uid.getId());
mUndoStack.push(touchCmd);
}
// Execute
mUndoStack.endMacro();
}
void CSVWorld::GenericCreator::toggleWidgets(bool active) void CSVWorld::GenericCreator::toggleWidgets(bool active)
{ {
} }

View file

@ -64,6 +64,8 @@ namespace CSVWorld
virtual std::string getId() const; virtual std::string getId() const;
std::string getClonedId() const;
virtual std::string getIdValidatorResult() const; virtual std::string getIdValidatorResult() const;
/// Allow subclasses to add additional data to \a command. /// Allow subclasses to add additional data to \a command.
@ -103,6 +105,8 @@ namespace CSVWorld
virtual void cloneMode(const std::string& originId, virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type); const CSMWorld::UniversalId::Type type);
virtual void touch(const std::vector<CSMWorld::UniversalId>& ids);
virtual std::string getErrors() const; virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty ///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error. /// string is returned, there is no error.

View file

@ -32,13 +32,19 @@ std::string CSVWorld::InfoCreator::getId() const
void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{ {
int index = CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId()));
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
findColumnIndex (
getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ?
CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal);
command.addValue (index, mTopic->text()); if (getCollectionId() == CSMWorld::UniversalId::Type_TopicInfos)
{
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Topic), mTopic->text());
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Rank), -1);
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Gender), -1);
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_PcRank), -1);
}
else
{
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Journal), mTopic->text());
}
} }
CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack, CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack,

View file

@ -0,0 +1,120 @@
#include "landcreator.hpp"
#include <limits>
#include <QLabel>
#include <QSpinBox>
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/land.hpp"
namespace CSVWorld
{
LandCreator::LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id)
: GenericCreator(data, undoStack, id)
, mXLabel(nullptr)
, mYLabel(nullptr)
, mX(nullptr)
, mY(nullptr)
{
const int MaxInt = std::numeric_limits<int>::max();
const int MinInt = std::numeric_limits<int>::min();
setManualEditing(false);
mXLabel = new QLabel("X: ");
mX = new QSpinBox();
mX->setMinimum(MinInt);
mX->setMaximum(MaxInt);
insertBeforeButtons(mXLabel, false);
insertBeforeButtons(mX, true);
mYLabel = new QLabel("Y: ");
mY = new QSpinBox();
mY->setMinimum(MinInt);
mY->setMaximum(MaxInt);
insertBeforeButtons(mYLabel, false);
insertBeforeButtons(mY, true);
connect (mX, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int)));
connect (mY, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int)));
}
void LandCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type)
{
GenericCreator::cloneMode(originId, type);
int x = 0, y = 0;
CSMWorld::Land::parseUniqueRecordId(originId, x, y);
mX->setValue(x);
mY->setValue(y);
}
void LandCreator::touch(const std::vector<CSMWorld::UniversalId>& ids)
{
// Combine multiple touch commands into one "macro" command
getUndoStack().beginMacro("Touch records");
CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));
for (const CSMWorld::UniversalId& uid : ids)
{
CSMWorld::TouchLandCommand* touchCmd = new CSMWorld::TouchLandCommand(lands, ltexs, uid.getId());
getUndoStack().push(touchCmd);
}
// Execute
getUndoStack().endMacro();
}
void LandCreator::focus()
{
mX->setFocus();
}
void LandCreator::reset()
{
GenericCreator::reset();
mX->setValue(0);
mY->setValue(0);
}
std::string LandCreator::getErrors() const
{
if (getData().getLand().searchId(getId()) >= 0)
return "A land with that name already exists.";
return "";
}
std::string LandCreator::getId() const
{
return CSMWorld::Land::createUniqueRecordId(mX->value(), mY->value());
}
void LandCreator::pushCommand(std::unique_ptr<CSMWorld::CreateCommand> command, const std::string& id)
{
if (mCloneMode)
{
CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));
getUndoStack().beginMacro(("Clone " + id).c_str());
getUndoStack().push(command.release());
CSMWorld::CopyLandTexturesCommand* ltexCopy = new CSMWorld::CopyLandTexturesCommand(lands, ltexs, getClonedId(), getId());
getUndoStack().push(ltexCopy);
getUndoStack().endMacro();
}
else
getUndoStack().push (command.release());
}
void LandCreator::coordChanged(int value)
{
update();
}
}

View file

@ -0,0 +1,47 @@
#ifndef CSV_WORLD_LANDCREATOR_H
#define CSV_WORLD_LANDCREATOR_H
#include "genericcreator.hpp"
class QLabel;
class QSpinBox;
namespace CSVWorld
{
class LandCreator : public GenericCreator
{
Q_OBJECT
QLabel* mXLabel;
QLabel* mYLabel;
QSpinBox* mX;
QSpinBox* mY;
public:
LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;
void touch(const std::vector<CSMWorld::UniversalId>& ids) override;
void focus() override;
void reset() override;
std::string getErrors() const override;
protected:
std::string getId() const override;
void pushCommand(std::unique_ptr<CSMWorld::CreateCommand> command,
const std::string& id) override;
private slots:
void coordChanged(int value);
};
}
#endif

View file

@ -0,0 +1,101 @@
#include "landtexturecreator.hpp"
#include <cstdint>
#include <limits>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexture.hpp"
namespace CSVWorld
{
LandTextureCreator::LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id)
: GenericCreator(data, undoStack, id)
{
// One index is reserved for a default texture
const size_t MaxIndex = std::numeric_limits<uint16_t>::max() - 1;
setManualEditing(false);
QLabel* nameLabel = new QLabel("Name");
insertBeforeButtons(nameLabel, false);
mNameEdit = new QLineEdit(this);
insertBeforeButtons(mNameEdit, true);
QLabel* indexLabel = new QLabel("Index");
insertBeforeButtons(indexLabel, false);
mIndexBox = new QSpinBox(this);
mIndexBox->setMinimum(0);
mIndexBox->setMaximum(MaxIndex);
insertBeforeButtons(mIndexBox, true);
connect(mNameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(nameChanged(const QString&)));
connect(mIndexBox, SIGNAL(valueChanged(int)), this, SLOT(indexChanged(int)));
}
void LandTextureCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type)
{
GenericCreator::cloneMode(originId, type);
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(getCollectionId()));
int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname);
mNameEdit->setText((table.data(table.getModelIndex(originId, column)).toString()));
column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureIndex);
mIndexBox->setValue((table.data(table.getModelIndex(originId, column)).toInt()));
}
void LandTextureCreator::focus()
{
mIndexBox->setFocus();
}
void LandTextureCreator::reset()
{
GenericCreator::reset();
mNameEdit->setText("");
mIndexBox->setValue(0);
}
std::string LandTextureCreator::getErrors() const
{
if (getData().getLandTextures().searchId(getId()) >= 0)
{
return "Index is already in use";
}
return "";
}
void LandTextureCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const
{
GenericCreator::configureCreateCommand(command);
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(getCollectionId()));
int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname);
command.addValue(column, mName.c_str());
}
std::string LandTextureCreator::getId() const
{
return CSMWorld::LandTexture::createUniqueRecordId(0, mIndexBox->value());
}
void LandTextureCreator::nameChanged(const QString& value)
{
mName = value.toUtf8().constData();
update();
}
void LandTextureCreator::indexChanged(int value)
{
update();
}
}

View file

@ -0,0 +1,49 @@
#ifndef CSV_WORLD_LANDTEXTURECREATOR_H
#define CSV_WORLD_LANDTEXTURECREATOR_H
#include <string>
#include "genericcreator.hpp"
class QLineEdit;
class QSpinBox;
namespace CSVWorld
{
class LandTextureCreator : public GenericCreator
{
Q_OBJECT
public:
LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;
void focus() override;
void reset() override;
std::string getErrors() const override;
protected:
void configureCreateCommand(CSMWorld::CreateCommand& command) const override;
std::string getId() const override;
private slots:
void nameChanged(const QString& val);
void indexChanged(int val);
private:
QLineEdit* mNameEdit;
QSpinBox* mIndexBox;
std::string mName;
};
}
#endif

View file

@ -18,6 +18,8 @@
#include "pathgridcreator.hpp" #include "pathgridcreator.hpp"
#include "previewsubview.hpp" #include "previewsubview.hpp"
#include "bodypartcreator.hpp" #include "bodypartcreator.hpp"
#include "landcreator.hpp"
#include "landtexturecreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{ {
@ -81,6 +83,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Pathgrids, manager.add (CSMWorld::UniversalId::Type_Pathgrids,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, PathgridCreatorFactory>); new CSVDoc::SubViewFactoryWithCreator<TableSubView, PathgridCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Lands,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandCreator> >);
manager.add (CSMWorld::UniversalId::Type_LandTextures,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandTextureCreator> >);
manager.add (CSMWorld::UniversalId::Type_Globals, manager.add (CSMWorld::UniversalId::Type_Globals,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GlobalCreator> >); new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GlobalCreator> >);
@ -181,6 +189,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Pathgrid, manager.add (CSMWorld::UniversalId::Type_Pathgrid,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, PathgridCreatorFactory> (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, PathgridCreatorFactory> (false));
manager.add (CSMWorld::UniversalId::Type_Land,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandCreator> >(false));
manager.add (CSMWorld::UniversalId::Type_LandTexture,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandTextureCreator> >(false));
manager.add (CSMWorld::UniversalId::Type_DebugProfile, manager.add (CSMWorld::UniversalId::Type_DebugProfile,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> > (false));

View file

@ -16,6 +16,7 @@
#include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtablebase.hpp" #include "../../model/world/idtablebase.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/landtexturetableproxymodel.hpp"
#include "../../model/world/record.hpp" #include "../../model/world/record.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/commanddispatcher.hpp" #include "../../model/world/commanddispatcher.hpp"
@ -60,6 +61,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction(mCloneAction); menu.addAction(mCloneAction);
} }
if (mTouchAction)
menu.addAction (mTouchAction);
if (mCreateAction) if (mCreateAction)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
@ -226,17 +230,22 @@ void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event)
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
bool createAndDelete, bool sorting, CSMDoc::Document& document) bool createAndDelete, bool sorting, CSMDoc::Document& document)
: DragRecordTable(document), mCreateAction (0), : DragRecordTable(document), mCreateAction (nullptr), mCloneAction(nullptr), mTouchAction(nullptr),
mCloneAction(0), mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false) mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false)
{ {
mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id)); mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id));
bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos ||
id.getType() == CSMWorld::UniversalId::Type_JournalInfos; id.getType() == CSMWorld::UniversalId::Type_JournalInfos;
bool isLtexTable = (id.getType() == CSMWorld::UniversalId::Type_LandTextures);
if (isInfoTable) if (isInfoTable)
{ {
mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this); mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this);
} }
else if (isLtexTable)
{
mProxyModel = new CSMWorld::LandTextureTableProxyModel (this);
}
else else
{ {
mProxyModel = new CSMWorld::IdTableProxyModel (this); mProxyModel = new CSMWorld::IdTableProxyModel (this);
@ -302,6 +311,15 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
cloneShortcut->associateAction(mCloneAction); cloneShortcut->associateAction(mCloneAction);
} }
if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch)
{
mTouchAction = new QAction(tr("Touch Record"), this);
connect(mTouchAction, SIGNAL(triggered()), this, SLOT(touchRecord()));
addAction(mTouchAction);
CSMPrefs::Shortcut* touchShortcut = new CSMPrefs::Shortcut("table-touch", this);
touchShortcut->associateAction(mTouchAction);
}
mRevertAction = new QAction (tr ("Revert Record"), this); mRevertAction = new QAction (tr ("Revert Record"), this);
connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert())); connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert()));
addAction (mRevertAction); addAction (mRevertAction);
@ -442,6 +460,22 @@ void CSVWorld::Table::cloneRecord()
} }
} }
void CSVWorld::Table::touchRecord()
{
if (!mEditLock && mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch)
{
std::vector<CSMWorld::UniversalId> touchIds;
QModelIndexList selectedRows = selectionModel()->selectedRows();
for (auto it = selectedRows.begin(); it != selectedRows.end(); ++it)
{
touchIds.push_back(getUniversalId(it->row()));
}
emit touchRequest(touchIds);
}
}
void CSVWorld::Table::moveUpRecord() void CSVWorld::Table::moveUpRecord()
{ {
if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))

View file

@ -56,6 +56,7 @@ namespace CSVWorld
QAction *mEditAction; QAction *mEditAction;
QAction *mCreateAction; QAction *mCreateAction;
QAction *mCloneAction; QAction *mCloneAction;
QAction *mTouchAction;
QAction *mRevertAction; QAction *mRevertAction;
QAction *mDeleteAction; QAction *mDeleteAction;
QAction *mMoveUpAction; QAction *mMoveUpAction;
@ -115,6 +116,8 @@ namespace CSVWorld
void cloneRequest(const CSMWorld::UniversalId&); void cloneRequest(const CSMWorld::UniversalId&);
void touchRequest(const std::vector<CSMWorld::UniversalId>& ids);
void closeRequest(); void closeRequest();
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds); void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
@ -129,6 +132,8 @@ namespace CSVWorld
void cloneRecord(); void cloneRecord();
void touchRecord();
void moveUpRecord(); void moveUpRecord();
void moveDownRecord(); void moveDownRecord();

View file

@ -249,6 +249,11 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
mCreator->focus(); mCreator->focus();
} }
void CSVWorld::TableBottomBox::touchRequest(const std::vector<CSMWorld::UniversalId>& ids)
{
mCreator->touch(ids);
}
void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds) void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds)
{ {
extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds); extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds);

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