Merge remote-tracking branch 'refs/remotes/origin/master' into pathfinding

# Conflicts:
#	apps/openmw/mwmechanics/aiactivate.cpp
#	apps/openmw/mwmechanics/aicombat.cpp
#	apps/openmw/mwmechanics/aicombat.hpp
#	apps/openmw/mwmechanics/aifollow.cpp
#	apps/openmw/mwmechanics/aipackage.cpp
#	apps/openmw/mwmechanics/aipackage.hpp
#	apps/openmw/mwmechanics/aiwander.cpp
#	apps/openmw/mwmechanics/aiwander.hpp
coverity_scan^2
mrcheko 9 years ago
commit d2fe6fe857

28
.gitignore vendored

@ -10,6 +10,7 @@ prebuilt
## doxygen ## doxygen
Doxygen Doxygen
!docs/cs-manual/Makefile
## ides/editors ## ides/editors
*~ *~
@ -21,6 +22,7 @@ Doxygen
.project .project
.settings .settings
.directory .directory
.idea
## qt-creator ## qt-creator
CMakeLists.txt.user* CMakeLists.txt.user*
@ -38,10 +40,33 @@ resources
/openmw /openmw
/opencs /opencs
/niftest /niftest
bsatool
openmw-cs
openmw-essimporter
openmw-iniimporter
openmw-launcher
openmw-wizard
## generated objects ## generated objects
apps/openmw/config.hpp apps/openmw/config.hpp
apps/launcher/ui_contentselector.h
apps/launcher/ui_settingspage.h
apps/opencs/ui_contentselector.h
apps/opencs/ui_filedialog.h
apps/wizard/qrc_wizard.cxx
apps/wizard/ui_componentselectionpage.h
apps/wizard/ui_conclusionpage.h
apps/wizard/ui_existinginstallationpage.h
apps/wizard/ui_importpage.h
apps/wizard/ui_installationpage.h
apps/wizard/ui_installationtargetpage.h
apps/wizard/ui_intropage.h
apps/wizard/ui_languageselectionpage.h
apps/wizard/ui_methodselectionpage.h
components/ui_contentselector.h
docs/mainpage.hpp docs/mainpage.hpp
docs/Doxyfile
docs/DoxyfilePages
moc_*.cxx moc_*.cxx
*.cxx_parameters *.cxx_parameters
*qrc_launcher.cxx *qrc_launcher.cxx
@ -53,3 +78,6 @@ moc_*.cxx
*ui_playpage.h *ui_playpage.h
*.[ao] *.[ao]
*.so *.so
gamecontrollerdb.txt
openmw.appdata.xml

@ -1,7 +1,10 @@
os: os:
- linux - linux
# - osx - osx
osx_image: xcode7.2
language: cpp language: cpp
sudo: required
dist: trusty
branches: branches:
only: only:
- master - master
@ -18,8 +21,8 @@ addons:
name: "OpenMW/openmw" name: "OpenMW/openmw"
description: "<Your project description here>" description: "<Your project description here>"
notification_email: scrawl@baseoftrash.de notification_email: scrawl@baseoftrash.de
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE" build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE"
build_command: "make" build_command: "make -j2"
branch_pattern: coverity_scan branch_pattern: coverity_scan
matrix: matrix:
include: include:
@ -38,7 +41,7 @@ before_script:
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
script: script:
- cd ./build - cd ./build
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j2; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi

@ -14,15 +14,20 @@ Programmers
Adam Hogan (aurix) Adam Hogan (aurix)
Aesylwinn Aesylwinn
aegis
Aleksandar Jovanov Aleksandar Jovanov
Alex Haddad (rainChu) Alex Haddad (rainChu)
Alex McKibben (WeirdSexy) Alex McKibben (WeirdSexy)
alexanderkjall
Alexander Nadeau (wareya) Alexander Nadeau (wareya)
Alexander Olofsson (Ace) Alexander Olofsson (Ace)
Allofich
Austin Salgat (Salgat)
Artem Kotsynyak (greye) Artem Kotsynyak (greye)
artemutin artemutin
Arthur Moore (EmperorArthur) Arthur Moore (EmperorArthur)
athile athile
Ben Shealy (bentsherman)
Bret Curtis (psi29a) Bret Curtis (psi29a)
Britt Mathis (galdor557) Britt Mathis (galdor557)
cc9cii cc9cii
@ -48,6 +53,7 @@ Programmers
Gašper Sedej Gašper Sedej
gugus/gus gugus/gus
Hallfaer Tuilinn Hallfaer Tuilinn
hristoast
Internecine Internecine
Jacob Essex (Yacoby) Jacob Essex (Yacoby)
Jannik Heller (scrawl) Jannik Heller (scrawl)
@ -56,6 +62,7 @@ Programmers
Jeffrey Haines (Jyby) Jeffrey Haines (Jyby)
Jengerer Jengerer
Jiří Kuneš (kunesj) Jiří Kuneš (kunesj)
Joe Wilkerson (neuralroberts)
Joel Graff (graffy) Joel Graff (graffy)
John Blomberg (fstp) John Blomberg (fstp)
Jordan Ayers Jordan Ayers
@ -81,6 +88,7 @@ Programmers
Michael Mc Donnell Michael Mc Donnell
Michael Papageorgiou (werdanith) Michael Papageorgiou (werdanith)
Michał Bień (Glorf) Michał Bień (Glorf)
Michał Moroz (dragonee)
Miroslav Puda (pakanek) Miroslav Puda (pakanek)
MiroslavR MiroslavR
naclander naclander
@ -92,15 +100,17 @@ Programmers
Nolan Poe (nopoe) Nolan Poe (nopoe)
Paul Cercueil (pcercuei) Paul Cercueil (pcercuei)
Paul McElroy (Greendogo) Paul McElroy (Greendogo)
Pi03k
Pieter van der Kloet (pvdk) Pieter van der Kloet (pvdk)
pkubik pkubik
Radu-Marius Popovici (rpopovici) Radu-Marius Popovici (rpopovici)
rdimesio rdimesio
riothamus riothamus
Rob Cutmore (rcutmore)
Robert MacGregor (Ragora) Robert MacGregor (Ragora)
Rohit Nirmal Rohit Nirmal
Roman Melnik (Kromgart) Roman Melnik (Kromgart)
Roman Proskuryakov (humbug) Roman Proskuryakov (kpp)
Sandy Carter (bwrsandman) Sandy Carter (bwrsandman)
Scott Howard Scott Howard
Sebastian Wick (swick) Sebastian Wick (swick)
@ -109,7 +119,9 @@ Programmers
smbas smbas
Stefan Galowicz (bogglez) Stefan Galowicz (bogglez)
Stanislav Bobrov (Jiub) Stanislav Bobrov (Jiub)
svaante
Sylvain Thesnieres (Garvek) Sylvain Thesnieres (Garvek)
t6
terrorfisch terrorfisch
Thomas Luppi (Digmaster) Thomas Luppi (Digmaster)
Tom Mason (wheybags) Tom Mason (wheybags)
@ -124,6 +136,7 @@ Manual
Bodillium Bodillium
Cramal Cramal
Alejandro Sanchez (HiPhish)
sir_herrbatka sir_herrbatka
Packagers Packagers

@ -1,3 +1,162 @@
0.39.0
------
Bug #1384: Dark Brotherhood Assassin (and other scripted NPCs?) spawns beneath/inside solid objects
Bug #1544: "Drop" drops equipped item in a separate stack
Bug #1587: Collision detection glitches
Bug #1629: Container UI locks up in Vivec at Jeanne's
Bug #1771: Dark Brotherhood Assassin oddity in Eight Plates
Bug #1827: Unhandled NiTextureEffect in ex_dwrv_ruin30.nif
Bug #2089: When saving while swimming in water in an interior cell, you will be spawned under water on loading
Bug #2295: Internal texture not showing, nipixeldata
Bug #2363: Corpses don't disappear
Bug #2369: Respawns should be timed individually
Bug #2393: Сharacter is stuck in the tree
Bug #2444: [Mod] NPCs from Animated Morrowind appears not using proper animations
Bug #2467: Creatures do not respawn
Bug #2515: Ghosts in Ibar-Dad spawn stuck in walls
Bug #2610: FixMe script still needs to be implemented
Bug #2689: Riekling raider pig constantly screams while running
Bug #2719: Vivec don't put their hands on the knees with this replacer (Psymoniser Vivec God Replacement NPC Edition v1.0
Bug #2737: Camera shaking when side stepping around object
Bug #2760: AI Combat Priority Problem - Use of restoration spell instead of attacking
Bug #2806: Stack overflow in LocalScripts::getNext
Bug #2807: Collision detection allows player to become stuck inside objects
Bug #2814: Stairs to Marandus have improper collision
Bug #2925: Ranes Ienith will not appear, breaking the Morag Tong and Thieves Guid questlines
Bug #3024: Editor: Creator bar in startscript subview does not accept script ID drops
Bug #3046: Sleep creature: Velk is spawned half-underground in the Thirr River Valley
Bug #3080: Calling aifollow without operant in local script every frame causes mechanics to overheat + log
Bug #3101: Regression: White guar does not move
Bug #3108: Game Freeze after Killing Diseased Rat in Foreign Quarter Tomb
Bug #3124: Bloodmoon Quest - Rite of the Wolf Giver (BM_WolfGiver) Innocent victim won't turn werewolf
Bug #3125: Improper dialogue window behavior when talking to creatures
Bug #3130: Some wandering NPCs disappearing, cannot finish quests
Bug #3132: Editor: GMST ID named sMake Enchantment is instead named sMake when making new game from scratch
Bug #3133: OpenMW and the OpenCS are writting warnings about scripts that use the function GetDisabled.
Bug #3135: Journal entry for The Pigrim's Path missing name
Bug #3136: Dropped bow is displaced
Bug #3140: Editor: OpenMW-CS fails to open newly converted and saved omwaddon file.
Bug #3142: Duplicate Resist Magic message
Bug #3143: Azura missing her head
Bug #3146: Potion effect showing when ingredient effects are not known
Bug #3155: When executing chop attack with a spear, hands turn partly invisible
Bug #3161: Fast travel from Silt Strider or Boat Ride will break save files made afterwards
Bug #3163: Editor: Objects dropped to scene do not always save
Bug #3173: Game Crashes After Casting Recall Spell
Bug #3174: Constant effect enchantments play spell animation on dead bodies
Bug #3175: Spell effects do not wear down when caster dies
Bug #3176: NPCs appearing randomly far away from towns
Bug #3177: Submerged corpse floats ontop of water when it shouldn't (Widow Vabdas' Deed quest)
Bug #3184: Bacola Closcius in Balmora, South Wall Cornerclub spams magic effects if attacked
Bug #3207: Editor: New objects do not render
Bug #3212: Arrow of Ranged Silence
Bug #3213: Looking at Floor After Magical Transport
Bug #3220: The number of remaining ingredients in the alchemy window doesn't go down when failing to brew a potion
Bug #3222: Falling through the water in Vivec
Bug #3223: Crash at the beginning with MOD (The Symphony)
Bug #3228: Purple screen when leveling up.
Bug #3233: Infinite disposition via MWDialogue::Filter::testDisposition() glitch
Bug #3234: Armor mesh stuck on body in inventory menu
Bug #3235: Unlike vanilla, OpenMW don't allow statics and activators cast effects on the player.
Bug #3238: Not loading cells when using Poorly Placed Object Fix.esm
Bug #3248: Editor: Using the "Next Script" and "Previous Script" buttons changes the record status to "Modified"
Bug #3258: Woman biped skeleton
Bug #3259: No alternating punches
Bug #3262: Crash in class selection menu
Bug #3279: Load menu: Deleting a savegame makes scroll bar jump to the top
Bug #3326: Starting a new game, getting to class selection, then starting another new game temporarily assigns Acrobat class
Bug #3327: Stuck in table after loading when character was sneaking when quicksave
Feature #652: Editor: GMST verifier
Feature #929: Editor: Info record verifier
Feature #1279: Editor: Render cell border markers
Feature #2482: Background cell loading and caching of loaded cells
Feature #2484: Editor: point lighting
Feature #2801: Support NIF bump map textures in osg
Feature #2926: Editor: Optional line wrap in script editor wrap lines
Feature #3000: Editor: Reimplement 3D scene camera system
Feature #3035: Editor: Make scenes a drop target for referenceables
Feature #3043: Editor: Render cell markers v2
Feature #3164: Editor: Instance Selection Menu
Feature #3165: Editor: Instance editing mode - move sub mode
Feature #3244: Allow changing water Level of Interiors behaving like exteriors
Feature #3250: Editor: Use "Enter" key instead of clicking "Create" button to confirm ID input in Creator Bar
Support #3179: Fatal error on startup
0.38.0
------
Bug #1699: Guard will continuously run into mudcrab
Bug #1934: Saw in Dome of Kasia doesnt harm the player
Bug #1962: Rat floats when killed near the door
Bug #1963: Kwama eggsacks pulse too fast
Bug #2198: NPC voice sound source should be placed at their head
Bug #2210: OpenMW installation wizard crashes...
Bug #2211: Editor: handle DELE subrecord at the end of a record
Bug #2413: ESM error Unknown subrecord in Grandmaster of Hlaalu
Bug #2537: Bloodmoon quest Ristaag: Sattir not consistently dying, plot fails to advance; same with Grerid
Bug #2697: "The Swimmer" moves away after leading you to underwater cave
Bug #2724: Loading previous save duplicates containers and harvestables
Bug #2769: Inventory doll - Cursor not respecting order of clothes
Bug #2865: Scripts silently fail when moving NPCs between cells.
Bug #2873: Starting a new game leads to CTD / Fatal Error
Bug #2918: Editor: it's not possible to create an omwaddon containing a dot in the file name
Bug #2933: Dialog box can't disable a npc if it is in another cell. (Rescue Madura Seran).
Bug #2942: atronach sign behavior (spell absorption) changes when trying to receive a blessing at "shrine of tribunal"
Bug #2952: Enchantment Merchant Items reshuffled EVERY time 'barter' is clicked
Bug #2961: ESM Error: Unknown subrecord if Deus Ex Machina mod is loaded
Bug #2972: Resurrecting the player via console does not work when health was 0
Bug #2986: Projectile weapons work underwater
Bug #2988: "Expected subrecord" bugs showing up.
Bug #2991: Can't use keywords in strings for MessageBox
Bug #2993: Tribunal:The Shrine of the Dead Urvel Dulni can't stop to follow the player.
Bug #3008: NIFFile Error while loading meshes with a NiLODNode
Bug #3010: Engine: items should sink to the ground when dropped under water
Bug #3011: NIFFile Error while loading meshes with a NiPointLight
Bug #3016: Engine: something wrong with scripting - crash / fatal error
Bug #3020: Editor: verify does not check if given "item ID" (as content) for a "container" exists
Bug #3026: [MOD: Julan Ashlander Companion] Dialogue not triggering correctly
Bug #3028: Tooltips for Health, Magicka and Fatigue show in Options menu even when bars aren't visible
Bug #3034: Item count check dialogue option doesn't work (Guards accept gold even if you don't have enough)
Bug #3036: Owned tooltip color affects spell tooltips incorrrectly
Bug #3037: Fatal error loading old ES_Landscape.esp in Store<ESM::LandTexture>::search
Bug #3038: Player sounds come from underneath
Bug #3040: Execution of script failed: There is a message box already
Bug #3047: [MOD: Julan Ashlander Companion] Scripts KS_Bedscript or KS_JulanNight not working as intended
Bug #3048: Fatal Error
Bug #3051: High field of view results in first person rendering glitches
Bug #3053: Crash on new game at character class selection
Bug #3058: Physiched sleeves aren't rendered correctly.
Bug #3060: NPCs use wrong landing sound
Bug #3062: Mod support regression: Andromeda's fast travel.
Bug #3063: Missing Journal Textures without Tribunal and Bloodmoon installed
Bug #3077: repeated aifollow causes the distance to stack
Bug #3078: Creature Dialogues not showing when certain Function/Conditions are required.
Bug #3082: Crash when entering Holamayan Monastery with mesh replacer installed
Bug #3086: Party at Boro's House Creature with Class don't talk under OpenMW
Bug #3089: Dreamers spawn too soon
Bug #3100: Certain controls erroneously work as a werewolf
Bug #3102: Multiple unique soultrap spell sources clone souls.
Bug #3105: Summoned creatures and objects disappear at midnight
Bug #3112: gamecontrollerdb file creation with wrong extension
Bug #3116: Dialogue Function "Same Race" is avoided
Bug #3117: Dialogue Bug: Choice conditions are tested when not in a choice
Bug #3118: Body Parts are not rendered when used in a pose.
Bug #3122: NPC direction is reversed during sneak awareness check
Feature #776: Sound effects from one direction don't necessarily affect both speakers in stereo
Feature #858: Different fov settings for hands and the game world
Feature #1176: Handle movement of objects between cells
Feature #2507: Editor: choosing colors for syntax highlighting
Feature #2867: Editor: hide script error list when there are no errors
Feature #2885: Accept a file format other than nif
Feature #2982: player->SetDelete 1 results in: PC can't move, menu can be opened
Feature #2996: Editor: make it possible to preset the height of the script check area in a script view
Feature #3014: Editor: Tooltips in 3D scene
Feature #3064: Werewolf field of view
Feature #3074: Quicksave indicator
Task #287: const version of Ptr
Task #2542: Editor: redo user settings system
0.37.0 0.37.0
------ ------

@ -1,22 +1,17 @@
#!/bin/sh #!/bin/sh
if [ "${ANALYZE}" ]; then if [ "${ANALYZE}" ]; then
if [ $(lsb_release -sc) = "precise" ]; then
echo "yes" | sudo apt-add-repository ppa:ubuntu-toolchain-r/test
fi
echo "yes" | sudo add-apt-repository "deb http://llvm.org/apt/`lsb_release -sc`/ llvm-toolchain-`lsb_release -sc`-3.6 main" echo "yes" | sudo add-apt-repository "deb http://llvm.org/apt/`lsb_release -sc`/ llvm-toolchain-`lsb_release -sc`-3.6 main"
wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add - wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
fi fi
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
echo "yes" | sudo apt-add-repository ppa:openmw/openmw echo "yes" | sudo apt-add-repository ppa:openmw/openmw
echo "yes" | sudo apt-add-repository ppa:boost-latest/ppa
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -qq libgtest-dev google-mock sudo apt-get install -qq libgtest-dev google-mock
sudo apt-get install -qq libboost-filesystem1.55-dev libboost-program-options1.55-dev libboost-system1.55-dev libboost-thread1.55-dev sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev
sudo apt-get install -qq ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
sudo apt-get install -qq cmake-data #workaround for broken osgqt cmake script in ubuntu 12.04
if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi
sudo mkdir /usr/src/gtest/build sudo mkdir /usr/src/gtest/build
cd /usr/src/gtest/build cd /usr/src/gtest/build

@ -1,9 +1,10 @@
#!/bin/sh #!/bin/sh
export CXX=clang++
export CC=clang
brew tap openmw/openmw
brew update brew update
brew unlink boost brew rm cmake || true
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg openmw/openmw/qt unshield brew rm pkgconfig || true
brew rm qt5 || true
brew install cmake pkgconfig qt5
curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

@ -1,7 +1,8 @@
#!/bin/sh #!/bin/sh
free -m
mkdir build mkdir build
cd build cd build
export CODE_COVERAGE=1 export CODE_COVERAGE=1
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE ${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE

@ -0,0 +1,671 @@
#!/bin/bash
while [ $# -gt 0 ]; do
ARGSTR=$1
shift
if [ ${ARGSTR:0:1} != "-" ]; then
echo "Unknown argument $ARGSTR"
echo "Try '$0 -h'"
exit 1
fi
for (( i=1; i<${#ARGSTR}; i++ )); do
ARG=${ARGSTR:$i:1}
case $ARG in
V )
VERBOSE=true ;;
v )
VS_VERSION=$1
shift ;;
d )
SKIP_DOWNLOAD=true ;;
e )
SKIP_EXTRACT=true ;;
k )
KEEP=true ;;
u )
UNITY_BUILD=true ;;
p )
PLATFORM=$1
shift ;;
c )
CONFIGURATION=$1
shift ;;
h )
cat <<EOF
Usage: $0 [-cdehkpuvV]
Options:
-c <Release/Debug>
Set the configuration, can also be set with environment variable CONFIGURATION.
-d
Skip checking the downloads.
-e
Skip extracting dependencies.
-h
Show this message.
-k
Keep the old build directory, default is to delete it.
-p <Win32/Win64>
Set the build platform, can also be set with environment variable PLATFORM.
-u
Configure for unity builds.
-v <2013/2015>
Choose the Visual Studio version to use.
-V
Run verbosely
EOF
exit 0
;;
* )
echo "Unknown argument $ARG."
echo "Try '$0 -h'"
exit 1 ;;
esac
done
done
if [ -z $VERBOSE ]; then
STRIP="> /dev/null 2>&1"
fi
if [ -z $VS_VERSION ]; then
VS_VERSION="2013"
fi
if [ -z $APPVEYOR ]; then
echo "Running prebuild outside of Appveyor."
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
cd $(dirname "$DIR")/..
else
echo "Running prebuild in Appveyor."
cd $APPVEYOR_BUILD_FOLDER
VERSION="$(cat README.md | grep Version: | awk '{ print $3; }')-$(git rev-parse --short HEAD)"
appveyor UpdateBuild -Version "$VERSION" > /dev/null &
fi
run_cmd() {
CMD="$1"
shift
if [ -z $VERBOSE ]; then
eval $CMD $@ > output.log 2>&1
RET=$?
if [ $RET -ne 0 ]; then
if [ -z $APPVEYOR ]; then
echo "Command $CMD failed, output can be found in `real_pwd`/output.log"
else
echo
echo "Command $CMD failed;"
cat output.log
fi
else
rm output.log
fi
return $RET
else
eval $CMD $@
return $?
fi
}
download() {
if [ $# -lt 3 ]; then
echo "Invalid parameters to download."
return 1
fi
NAME=$1
shift
echo "$NAME..."
while [ $# -gt 1 ]; do
URL=$1
FILE=$2
shift
shift
if ! [ -f $FILE ]; then
printf " Downloading $FILE... "
if [ -z $VERBOSE ]; then
curl --silent --retry 10 -kLy 5 -o $FILE $URL
RET=$?
else
curl --retry 10 -kLy 5 -o $FILE $URL
RET=$?
fi
if [ $RET -ne 0 ]; then
echo "Failed!"
else
echo "Done."
fi
else
echo " $FILE exists, skipping."
fi
done
if [ $# -ne 0 ]; then
echo "Missing parameter."
fi
}
real_pwd() {
pwd | sed "s,/\(.\),\1:,"
}
CMAKE_OPTS=""
add_cmake_opts() {
CMAKE_OPTS="$CMAKE_OPTS $@"
}
RUNTIME_DLLS=""
add_runtime_dlls() {
RUNTIME_DLLS="$RUNTIME_DLLS $@"
}
OSG_PLUGINS=""
add_osg_dlls() {
OSG_PLUGINS="$OSG_PLUGINS $@"
}
if [ -z $PLATFORM ]; then
PLATFORM=`uname -m`
fi
if [ -z $CONFIGURATION ]; then
CONFIGURATION="Debug"
fi
case $VS_VERSION in
14|2015 )
GENERATOR="Visual Studio 14 2015"
XP_TOOLSET="v140_xp"
;;
# 12|2013|
* )
GENERATOR="Visual Studio 12 2013"
XP_TOOLSET="v120_xp"
;;
esac
case $PLATFORM in
x64|x86_64|x86-64|win64|Win64 )
ARCHNAME=x86-64
ARCHSUFFIX=64
BITS=64
BASE_OPTS="-G\"$GENERATOR Win64\""
add_cmake_opts "-G\"$GENERATOR Win64\""
;;
x32|x86|i686|i386|win32|Win32 )
ARCHNAME=x86
ARCHSUFFIX=86
BITS=32
BASE_OPTS="-G\"$GENERATOR\" -T$XP_TOOLSET"
add_cmake_opts "-G\"$GENERATOR\"" -T$XP_TOOLSET
;;
* )
echo "Unknown platform $PLATFORM."
exit 1
;;
esac
if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi
case $CONFIGURATION in
debug|Debug|DEBUG )
CONFIGURATION=Debug
;;
release|Release|RELEASE )
CONFIGURATION=Release
;;
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
CONFIGURATION=RelWithDebInfo
;;
esac
echo
echo "=========================="
echo "Starting prebuild on win$BITS"
echo "=========================="
echo
# cd OpenMW/AppVeyor-test
mkdir -p deps
cd deps
DEPS="`pwd`"
if [ -z $SKIP_DOWNLOAD ]; then
echo "Downloading dependency packages."
echo
# Boost
if [ -z $APPVEYOR ]; then
download "Boost 1.58.0" \
http://sourceforge.net/projects/boost/files/boost-binaries/1.58.0/boost_1_58_0-msvc-12.0-$BITS.exe \
boost-1.58.0-win$BITS.exe
fi
# Bullet
download "Bullet 2.83.5" \
http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.83.5-win$BITS.7z \
Bullet-2.83.5-win$BITS.7z
# FFmpeg
download "FFmpeg 2.5.2" \
http://ffmpeg.zeranoe.com/builds/win$BITS/shared/ffmpeg-2.5.2-win$BITS-shared.7z \
ffmpeg$BITS-2.5.2.7z \
http://ffmpeg.zeranoe.com/builds/win$BITS/dev/ffmpeg-2.5.2-win$BITS-dev.7z \
ffmpeg$BITS-2.5.2-dev.7z
# MyGUI
download "MyGUI 3.2.2" \
http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-win$BITS.7z \
MyGUI-3.2.2-win$BITS.7z
# OpenAL
download "OpenAL-Soft 1.16.0" \
http://kcat.strangesoft.net/openal-binaries/openal-soft-1.16.0-bin.zip \
OpenAL-Soft-1.16.0.zip
# OSG
download "OpenSceneGraph 3.3.8" \
http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.3.8-win$BITS.7z \
OSG-3.3.8-win$BITS.7z
# Qt
if [ -z $APPVEYOR ]; then
download "Qt 4.8.6" \
http://sourceforge.net/projects/qt64ng/files/qt/$ARCHNAME/4.8.6/msvc2013/qt-4.8.6-x$ARCHSUFFIX-msvc2013.7z \
qt$BITS-4.8.6.7z
fi
# SDL2
download "SDL 2.0.3" \
https://www.libsdl.org/release/SDL2-devel-2.0.3-VC.zip \
SDL2-2.0.3.zip
fi
cd .. #/..
# Set up dependencies
if [ -z $KEEP ]; then
echo
printf "Preparing build directory... "
rm -rf Build_$BITS
mkdir -p Build_$BITS/deps
echo Done.
fi
mkdir -p Build_$BITS/deps
cd Build_$BITS/deps
DEPS_INSTALL=`pwd`
cd $DEPS
echo
echo "Extracting dependencies..."
# Boost
printf "Boost 1.58.0... "
{
if [ -z $APPVEYOR ]; then
cd $DEPS_INSTALL
BOOST_SDK="`real_pwd`/Boost"
if [ -d Boost ] && grep "BOOST_VERSION 105800" Boost/boost/version.hpp > /dev/null; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf Boost
$DEPS/boost-1.58.0-win$BITS.exe //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent
fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
echo Done.
else
# Appveyor unstable has all the boost we need already
BOOST_SDK="c:/Libraries/boost"
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
echo AppVeyor.
fi
}
cd $DEPS
# Bullet
printf "Bullet 2.83.5... "
{
cd $DEPS_INSTALL
if [ -d Bullet ]; then
printf "Exists. (No version checking) "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf Bullet
eval 7z x -y $DEPS/Bullet-2.83.5-win$BITS.7z $STRIP
mv Bullet-2.83.5-win$BITS Bullet
fi
export BULLET_ROOT="`real_pwd`/Bullet"
echo Done.
}
cd $DEPS
# FFmpeg
printf "FFmpeg 2.5.2... "
{
cd $DEPS_INSTALL
if [ -d FFmpeg ] && grep "FFmpeg version: 2.5.2" FFmpeg/README.txt > /dev/null; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf FFmpeg
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2.7z $STRIP
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2-dev.7z $STRIP
mv ffmpeg-2.5.2-win$BITS-shared FFmpeg
cp -r ffmpeg-2.5.2-win$BITS-dev/* FFmpeg/
rm -rf ffmpeg-2.5.2-win$BITS-dev
fi
export FFMPEG_HOME="`real_pwd`/FFmpeg"
add_runtime_dlls `pwd`/FFmpeg/bin/{avcodec-56,avformat-56,avutil-54,swresample-1,swscale-3}.dll
if [ $BITS -eq 32 ]; then
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
fi
echo Done.
}
cd $DEPS
# MyGUI
printf "MyGUI 3.2.2... "
{
cd $DEPS_INSTALL
if [ -d MyGUI ] && \
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf MyGUI
eval 7z x -y $DEPS/MyGUI-3.2.2-win$BITS.7z $STRIP
mv MyGUI-3.2.2-win$BITS MyGUI
fi
MYGUI_SDK="`real_pwd`/MyGUI"
add_cmake_opts -DMYGUISDK="$MYGUI_SDK" \
-DMYGUI_INCLUDE_DIRS="$MYGUI_SDK/include/MYGUI" \
-DMYGUI_PREQUEST_FILE="$MYGUI_SDK/include/MYGUI/MyGUI_Prerequest.h"
if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="_d"
else
SUFFIX=""
fi
add_runtime_dlls `pwd`/MyGUI/bin/$CONFIGURATION/MyGUIEngine$SUFFIX.dll
echo Done.
}
cd $DEPS
# OpenAL
printf "OpenAL-Soft 1.16.0... "
{
if [ -d openal-soft-1.16.0-bin ]; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf openal-soft-1.16.0-bin
eval 7z x -y OpenAL-Soft-1.16.0.zip $STRIP
fi
OPENAL_SDK="`real_pwd`/openal-soft-1.16.0-bin"
add_cmake_opts -DOPENAL_INCLUDE_DIR="$OPENAL_SDK/include/AL" \
-DOPENAL_LIBRARY="$OPENAL_SDK/libs/Win$BITS/OpenAL32.lib"
echo Done.
}
cd $DEPS
# OSG
printf "OSG 3.3.8... "
{
cd $DEPS_INSTALL
if [ -d OSG ] && \
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_MINOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_PATCH_VERSION 8" OSG/include/osg/Version > /dev/null
then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf OSG
eval 7z x -y $DEPS/OSG-3.3.8-win$BITS.7z $STRIP
mv OSG-3.3.8-win$BITS OSG
fi
OSG_SDK="`real_pwd`/OSG"
add_cmake_opts -DOSG_DIR="$OSG_SDK"
if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d"
else
SUFFIX=""
fi
add_runtime_dlls `pwd`/OSG/bin/{OpenThreads,zlib}$SUFFIX.dll \
`pwd`/OSG/bin/osg{,Animation,DB,FX,GA,Particle,Qt,Text,Util,Viewer}$SUFFIX.dll
add_osg_dlls `pwd`/OSG/bin/osgPlugins-3.3.8/osgdb_{bmp,dds,gif,jpeg,png,tga}$SUFFIX.dll
echo Done.
}
cd $DEPS
# Qt
if [ -z $APPVEYOR ]; then
printf "Qt 4.8.6... "
else
printf "Qt 5.4... "
fi
{
if [ -z $APPVEYOR ]; then
cd $DEPS_INSTALL
QT_SDK="`real_pwd`/Qt"
if [ -d Qt ] && head -n2 Qt/BUILDINFO.txt | grep "4.8.6" > /dev/null; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf Qt
eval 7z x -y $DEPS/qt$BITS-4.8.6.7z $STRIP
mv qt-4.8.6-* Qt
cd Qt
eval ./qtbinpatcher.exe $STRIP
fi
cd $QT_SDK
add_cmake_opts -DDESIRED_QT_VERSION=4 \
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe"
if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d4"
else
SUFFIX="4"
fi
add_runtime_dlls `pwd`/bin/Qt{Core,Gui,Network,OpenGL}$SUFFIX.dll
echo Done.
else
if [ $BITS -eq 32 ]; then
QT_SDK="C:/Qt/5.4/msvc2013_opengl"
else
QT_SDK="C:/Qt/5.4/msvc2013_64_opengl"
fi
add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK"
echo AppVeyor.
fi
}
cd $DEPS
# SDL2
printf "SDL 2.0.3... "
{
if [ -d SDL2-2.0.3 ]; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf SDL2-2.0.3
eval 7z x -y SDL2-2.0.3.zip $STRIP
fi
SDL_SDK="`real_pwd`/SDL2-2.0.3"
add_cmake_opts -DSDL2_INCLUDE_DIR="$SDL_SDK/include" \
-DSDL2MAIN_LIBRARY="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2main.lib" \
-DSDL2_LIBRARY_PATH="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2.lib"
add_runtime_dlls `pwd`/SDL2-2.0.3/lib/x$ARCHSUFFIX/SDL2.dll
echo Done.
}
cd $DEPS_INSTALL/..
echo
echo "Setting up OpenMW build..."
add_cmake_opts -DBUILD_BSATOOL=no \
-DBUILD_ESMTOOL=no \
-DBUILD_MYGUI_PLUGIN=no \
-DOPENMW_MP_BUILD=on
if [ -z $CI ]; then
echo " (Outside of CI, doing full build.)"
else
case $STEP in
components )
echo " Subproject: Components."
add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \
-DBUILD_OPENCS=no \
-DBUILD_OPENMW=no \
-DBUILD_WIZARD=no
;;
openmw )
echo " Subproject: OpenMW."
add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \
-DBUILD_OPENCS=no \
-DBUILD_WIZARD=no
;;
opencs )
echo " Subproject: OpenCS."
add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \
-DBUILD_OPENMW=no \
-DBUILD_WIZARD=no
;;
misc )
echo " Subproject: Misc."
add_cmake_opts -DBUILD_OPENCS=no \
-DBUILD_OPENMW=no
;;
* )
echo " Building everything."
;;
esac
fi
if [ -z $VERBOSE ]; then
printf " Configuring... "
else
echo " cmake .. $CMAKE_OPTS"
fi
run_cmd cmake .. $CMAKE_OPTS
RET=$?
if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then echo Done.
else echo Failed.; fi
fi
echo
# NOTE: Disable this when/if we want to run test cases
if [ -z $CI ]; then
echo "Copying Runtime DLLs..."
mkdir -p $CONFIGURATION
for DLL in $RUNTIME_DLLS; do
echo " `basename $DLL`."
cp "$DLL" $CONFIGURATION/
done
echo "OSG Plugin DLLs..."
mkdir -p $CONFIGURATION/osgPlugins-3.3.8
for DLL in $OSG_PLUGINS; do
echo " `basename $DLL`."
cp "$DLL" $CONFIGURATION/osgPlugins-3.3.8
done
echo
echo "Copying Runtime Resources/Config Files"
echo " gamecontrollerdb.txt"
cp $CONFIGURATION/../gamecontrollerdb.txt $CONFIGURATION/gamecontrollerdb.txt
echo " openmw.cfg"
cp $CONFIGURATION/../openmw.cfg.install $CONFIGURATION/openmw.cfg
echo " openmw-cs.cfg"
cp $CONFIGURATION/../openmw-cs.cfg $CONFIGURATION/openmw-cs.cfg
echo " settings-default.cfg"
cp $CONFIGURATION/../settings-default.cfg $CONFIGURATION/settings-default.cfg
echo " resources/"
cp -r $CONFIGURATION/../resources $CONFIGURATION/resources
fi
exit $RET

@ -1,5 +1,25 @@
#!/bin/sh #!/bin/sh
export CXX=clang++
export CC=clang
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
QT_PATH="/usr/local/opt/qt5"
mkdir build mkdir build
cd build cd build
cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" ..
cmake \
-D PKG_CONFIG_USE_CMAKE_PREFIX_PATH=ON \
-D CMAKE_EXE_LINKER_FLAGS="-lz" \
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
-D CMAKE_OSX_SYSROOT="macosx10.11" \
-D CMAKE_BUILD_TYPE=Debug \
-D OPENMW_OSX_DEPLOYMENT=TRUE \
-D DESIRED_QT_VERSION=5 \
-D OSG_PLUGIN_LIB_SEARCH_PATH="$DEPENDENCIES_ROOT/lib/osgPlugins-3.4.0" \
-D BUILD_ESMTOOL=FALSE \
-D BUILD_MYGUI_PLUGIN=FALSE \
-G"Unix Makefiles" \
..

@ -0,0 +1,59 @@
#!/bin/bash
if [ -z $PLATFORM ]; then
PLATFORM=`uname -m`
fi
if [ -z $CONFIGURATION ]; then
CONFIGURATION="Debug"
fi
case $PLATFORM in
x32|x86|i686|i386|win32|Win32 )
BITS=32
PLATFORM=Win32
;;
x64|x86_64|x86-64|win64|Win64 )
BITS=64
PLATFORM=x64
;;
* )
echo "Unknown platform $PLATFORM."
exit 1 ;;
esac
if [ -z $APPVEYOR ]; then
echo "Running $BITS-bit $CONFIGURATION build outside of Appveyor."
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
cd $(dirname "$DIR")/..
else
echo "Running $BITS-bit $CONFIGURATION build in Appveyor."
cd $APPVEYOR_BUILD_FOLDER
fi
cd build_$BITS
which msbuild > /dev/null
if [ $? -ne 0 ]; then
msbuild() {
/c/Program\ Files\ \(x86\)/MSBuild/12.0/Bin/MSBuild.exe "$@"
}
fi
if [ -z $APPVEYOR ]; then
msbuild OpenMW.sln //t:Build //m:8
else
msbuild OpenMW.sln //t:Build //m:8 //logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
fi
RET=$?
if [ $RET -eq 0 ] && [ ! -z $PACKAGE ]; then
msbuild PACKAGE.vcxproj //t:Build //m:8
RET=$?
fi
exit $RET

@ -16,11 +16,16 @@ endif (APPLE)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
if (ANDROID)
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
set (OSG_PLUGINS_DIR CACHE STRING "")
endif()
# Version # Version
message(STATUS "Configuring OpenMW...") message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 37) set(OPENMW_VERSION_MINOR 39)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
@ -111,35 +116,49 @@ 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()
# We probably support older versions than this. if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
cmake_minimum_required(VERSION 2.6) set(USE_QT FALSE)
else()
# Sound setup set(USE_QT TRUE)
unset(FFMPEG_LIBRARIES CACHE) endif()
find_package(FFmpeg REQUIRED) # Dependencies
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}")
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARIES}) if (DESIRED_QT_VERSION MATCHES 4)
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL)
else()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
# Instruct CMake to run moc automatically when needed.
#set(CMAKE_AUTOMOC ON)
endif()
endif()
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND OR NOT SWRESAMPLE_FOUND) if (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
message(FATAL_ERROR "FFmpeg component required, but not found!") # 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() endif()
# Sound setup
find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE)
# Required for building the FFmpeg headers # Required for building the FFmpeg headers
add_definitions(-D__STDC_CONSTANT_MACROS) add_definitions(-D__STDC_CONSTANT_MACROS)
# TinyXML # TinyXML
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
if(USE_SYSTEM_TINYXML) if (USE_SYSTEM_TINYXML)
find_library(TINYXML_LIBRARIES tinyxml) find_package(TinyXML REQUIRED)
find_path(TINYXML_INCLUDE_DIR tinyxml.h)
message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}")
add_definitions (-DTIXML_USE_STL) add_definitions (-DTIXML_USE_STL)
if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR) include_directories(SYSTEM ${TinyXML_INCLUDE_DIRS})
include_directories(${TINYXML_INCLUDE_DIR})
message(STATUS "Using system TinyXML library.")
else()
message(FATAL_ERROR "Detection of system TinyXML incomplete.")
endif()
endif() endif()
# Platform specific # Platform specific
@ -156,10 +175,10 @@ if (WIN32)
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
endif() endif()
if (ANDROID) if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}") find_package(LIBUNSHIELD REQUIRED) # required only for non win32 when building openmw-wizard
set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE) set(OPENMW_USE_UNSHIELD TRUE)
endif (ANDROID) endif()
option(OPENGL_ES "enable opengl es support" FALSE ) option(OPENGL_ES "enable opengl es support" FALSE )
@ -167,30 +186,6 @@ if (OPENGL_ES)
add_definitions(-DOPENGL_ES) add_definitions(-DOPENGL_ES)
endif(OPENGL_ES) endif(OPENGL_ES)
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE)
else()
set(USE_QT TRUE)
endif()
# Dependencies
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}")
if (DESIRED_QT_VERSION MATCHES 4)
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL)
else()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
# Instruct CMake to run moc automatically when needed.
#set(CMAKE_AUTOMOC ON)
endif()
endif()
# Fix for not visible pthreads functions for linker with glibc 2.15 # Fix for not visible pthreads functions for linker with glibc 2.15
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
find_package (Threads) find_package (Threads)
@ -204,12 +199,6 @@ if(NOT HAVE_STDINT_H)
message(FATAL_ERROR "stdint.h was not found" ) message(FATAL_ERROR "stdint.h was not found" )
endif() endif()
include (CheckIncludeFileCXX)
check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP)
if (HAVE_UNORDERED_MAP)
add_definitions(-DHAVE_UNORDERED_MAP)
endif ()
set(BOOST_COMPONENTS system filesystem program_options thread) set(BOOST_COMPONENTS system filesystem program_options thread)
if(WIN32) if(WIN32)
@ -220,11 +209,7 @@ IF(BOOST_STATIC)
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
endif() endif()
if (USE_QT) find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgUtil osgFX)
set (OSG_QT osgQt)
endif()
find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle ${OSG_QT} osgUtil osgFX)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
@ -234,6 +219,7 @@ if(OSG_STATIC)
# For now, users wishing to do a static build will need to pass the path to where the plugins reside # For now, users wishing to do a static build will need to pass the path to where the plugins reside
# More clever logic would need to deduce the path, probably installed under <OpenSceneGraph>/lib/osgPlugins-<X.X.X> # More clever logic would need to deduce the path, probably installed under <OpenSceneGraph>/lib/osgPlugins-<X.X.X>
include(FindPkgMacros)
find_library(${PLUGIN_NAME}_LIBRARY_REL NAMES ${PLUGIN_NAME} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH}) find_library(${PLUGIN_NAME}_LIBRARY_REL NAMES ${PLUGIN_NAME} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH})
find_library(${PLUGIN_NAME}_LIBRARY_DBG NAMES ${PLUGIN_NAME_DBG} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH}) find_library(${PLUGIN_NAME}_LIBRARY_DBG NAMES ${PLUGIN_NAME_DBG} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH})
make_library_set(${PLUGIN_NAME}_LIBRARY) make_library_set(${PLUGIN_NAME}_LIBRARY)
@ -245,12 +231,6 @@ if(OSG_STATIC)
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${PLUGIN_NAME}_LIBRARY}) set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${PLUGIN_NAME}_LIBRARY})
endmacro() endmacro()
macro(use_static_osg_plugin_dep DEPENDENCY)
find_package(${DEPENDENCY} REQUIRED)
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${DEPENDENCY}_LIBRARIES})
endmacro()
add_definitions(-DOSG_LIBRARY_STATIC) add_definitions(-DOSG_LIBRARY_STATIC)
set(PLUGIN_LIST set(PLUGIN_LIST
@ -271,6 +251,11 @@ if(OSG_STATIC)
JPEG # needed by osgdb_jpeg JPEG # needed by osgdb_jpeg
) )
macro(use_static_osg_plugin_dep DEPENDENCY)
find_package(${DEPENDENCY} REQUIRED)
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${DEPENDENCY}_LIBRARIES})
endmacro()
foreach(DEPENDENCY ${PLUGIN_DEPS_LIST}) foreach(DEPENDENCY ${PLUGIN_DEPS_LIST})
use_static_osg_plugin_dep(${DEPENDENCY}) use_static_osg_plugin_dep(${DEPENDENCY})
endforeach() endforeach()
@ -294,7 +279,7 @@ endif()
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED) find_package(OpenAL REQUIRED)
find_package(Bullet REQUIRED) find_package(Bullet 283 REQUIRED COMPONENTS BulletCollision LinearMath)
include_directories("." include_directories("."
SYSTEM SYSTEM
@ -302,7 +287,7 @@ include_directories("."
${Boost_INCLUDE_DIR} ${Boost_INCLUDE_DIR}
${MYGUI_INCLUDE_DIRS} ${MYGUI_INCLUDE_DIRS}
${OPENAL_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR}
${BULLET_INCLUDE_DIRS} ${Bullet_INCLUDE_DIRS}
) )
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${MYGUI_LIB_DIR}) link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${MYGUI_LIB_DIR})
@ -369,21 +354,17 @@ endif()
# CXX Compiler settings # CXX Compiler settings
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long")
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION) if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
string(REGEX REPLACE ".*version ([0-9\\.]*).*" "\\1" CLANG_VERSION ${CLANG_VERSION})
if ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression")
endif ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6) endif ()
endif(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) endif()
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.6)
OUTPUT_VARIABLE GCC_VERSION)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
endif(CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) endif()
elseif (MSVC) elseif (MSVC)
# Enable link-time code generation globally for all linking # Enable link-time code generation globally for all linking
if (OPENMW_LTO_BUILD) if (OPENMW_LTO_BUILD)
@ -558,6 +539,9 @@ endif(WIN32)
# Extern # Extern
add_subdirectory (extern/osg-ffmpeg-videoplayer) add_subdirectory (extern/osg-ffmpeg-videoplayer)
add_subdirectory (extern/oics) add_subdirectory (extern/oics)
if (USE_QT)
add_subdirectory (extern/osgQt)
endif()
# Components # Components
add_subdirectory (components) add_subdirectory (components)
@ -685,8 +669,6 @@ if (WIN32)
endforeach(d) endforeach(d)
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
# oics uses tinyxml, which has an initialized but unused variable
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${WARNINGS} /wd4189 ${MT_BUILD}")
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_BSATOOL) if (BUILD_BSATOOL)

@ -1,13 +1,13 @@
OpenMW OpenMW
====== ======
[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) [![Build Status](https://api.travis-ci.org/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Build status](https://ci.appveyor.com/api/projects/status/e6bqw8oouy8ufd46?svg=true)](https://ci.appveyor.com/project/scrawl/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
OpenMW is a recreation of the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. OpenMW is a recreation of the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
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.37.0 * Version: 0.39.0
* License: GPL (see docs/license/GPL3.txt for more information) * License: GPL (see docs/license/GPL3.txt 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

@ -5,6 +5,7 @@
#include <map> #include <map>
#include <set> #include <set>
#include <fstream> #include <fstream>
#include <cmath>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
@ -354,12 +355,12 @@ int load(Arguments& info)
esm.getRecHeader(flags); esm.getRecHeader(flags);
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n); EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
if (record == 0) if (record == 0)
{ {
if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end()) if (std::find(skipped.begin(), skipped.end(), n.intval) == skipped.end())
{ {
std::cout << "Skipping " << n.toString() << " records." << std::endl; std::cout << "Skipping " << n.toString() << " records." << std::endl;
skipped.push_back(n.val); skipped.push_back(n.intval);
} }
esm.skipRecord(); esm.skipRecord();
@ -391,20 +392,20 @@ int load(Arguments& info)
record->print(); record->print();
} }
if (record->getType().val == ESM::REC_CELL && loadCells && interested) if (record->getType().intval == ESM::REC_CELL && loadCells && interested)
{ {
loadCell(record->cast<ESM::Cell>()->get(), esm, info); loadCell(record->cast<ESM::Cell>()->get(), esm, info);
} }
if (save) if (save)
{ {
info.data.mRecords.push_back(record); info.data.mRecords.push_back(record);
} }
else else
{ {
delete record; delete record;
} }
++info.data.mRecordStats[n.val]; ++info.data.mRecordStats[n.intval];
} }
} catch(std::exception &e) { } catch(std::exception &e) {
@ -442,23 +443,18 @@ int clone(Arguments& info)
size_t recordCount = info.data.mRecords.size(); size_t recordCount = info.data.mRecords.size();
int digitCount = 1; // For a nicer output int digitCount = 1; // For a nicer output
if (recordCount > 9) ++digitCount; if (recordCount > 0)
if (recordCount > 99) ++digitCount; digitCount = (int)std::log10(recordCount) + 1;
if (recordCount > 999) ++digitCount;
if (recordCount > 9999) ++digitCount;
if (recordCount > 99999) ++digitCount;
if (recordCount > 999999) ++digitCount;
std::cout << "Loaded " << recordCount << " records:" << std::endl << std::endl; std::cout << "Loaded " << recordCount << " records:" << std::endl << std::endl;
ESM::NAME name;
int i = 0; int i = 0;
typedef std::map<int, int> Stats; typedef std::map<int, int> Stats;
Stats &stats = info.data.mRecordStats; Stats &stats = info.data.mRecordStats;
for (Stats::iterator it = stats.begin(); it != stats.end(); ++it) for (Stats::iterator it = stats.begin(); it != stats.end(); ++it)
{ {
name.val = it->first; ESM::NAME name;
name.intval = it->first;
int amount = it->second; int amount = it->second;
std::cout << std::setw(digitCount) << amount << " " << name.toString() << " "; std::cout << std::setw(digitCount) << amount << " " << name.toString() << " ";
@ -491,12 +487,12 @@ int clone(Arguments& info)
for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it) for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it)
{ {
EsmTool::RecordBase *record = *it; EsmTool::RecordBase *record = *it;
name.val = record->getType().val; const ESM::NAME& typeName = record->getType();
esm.startRecord(name.toString(), record->getFlags()); esm.startRecord(typeName.toString(), record->getFlags());
record->save(esm); record->save(esm);
if (name.val == ESM::REC_CELL) { if (typeName.intval == ESM::REC_CELL) {
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get(); ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
if (!info.data.mCellRefs[ptr].empty()) { if (!info.data.mCellRefs[ptr].empty()) {
typedef std::deque<std::pair<ESM::CellRef, bool> > RefList; typedef std::deque<std::pair<ESM::CellRef, bool> > RefList;
@ -508,7 +504,7 @@ int clone(Arguments& info)
} }
} }
esm.endRecord(name.toString()); esm.endRecord(typeName.toString());
saved++; saved++;
int perc = (int)((saved / (float)recordCount)*100); int perc = (int)((saved / (float)recordCount)*100);

@ -179,7 +179,7 @@ RecordBase::create(ESM::NAME type)
{ {
RecordBase *record = 0; RecordBase *record = 0;
switch (type.val) { switch (type.intval) {
case ESM::REC_ACTI: case ESM::REC_ACTI:
{ {
record = new EsmTool::Record<ESM::Activator>; record = new EsmTool::Record<ESM::Activator>;

@ -121,7 +121,7 @@ public:
{ {
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
mContext->mPlayerBase = npc; mContext->mPlayerBase = npc;
std::map<const int, float> empty; ESM::SpellState::SpellParams empty;
// FIXME: player start spells and birthsign spells aren't listed here, // FIXME: player start spells and birthsign spells aren't listed here,
// need to fix openmw to account for this // need to fix openmw to account for this
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)

@ -1,6 +1,7 @@
#include "convertinventory.hpp" #include "convertinventory.hpp"
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <cstdlib>
namespace ESSImport namespace ESSImport
{ {

@ -17,6 +17,8 @@ namespace ESSImport
faction.mReputation = it->mReputation; faction.mReputation = it->mReputation;
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction; out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
} }
for (int i=0; i<3; ++i)
out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
for (int i=0; i<27; ++i) for (int i=0; i<27; ++i)
@ -28,7 +30,7 @@ namespace ESSImport
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell) if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell)
out.mObject.mCreatureStats.mDrawState = 2; out.mObject.mCreatureStats.mDrawState = 2;
firstPersonCam = (pcdt.mPNAM.mCameraState == PCDT::CameraState_FirstPerson); firstPersonCam = !(pcdt.mPNAM.mCameraFlags & PCDT::CameraFlag_ThirdPerson);
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin(); for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
it != pcdt.mKnownDialogueTopics.end(); ++it) it != pcdt.mKnownDialogueTopics.end(); ++it)

@ -75,7 +75,7 @@ namespace ESSImport
// unsure at which point between TGTN and CRED // unsure at which point between TGTN and CRED
if (esm.isNextSub("AADT")) if (esm.isNextSub("AADT"))
{ {
// occured when a creature was in the middle of its attack, 44 bytes // occurred when a creature was in the middle of its attack, 44 bytes
esm.skipHSub(); esm.skipHSub();
} }

@ -54,7 +54,7 @@ namespace
*(image->data(x,y)+2) = *it++; *(image->data(x,y)+2) = *it++;
*(image->data(x,y)+1) = *it++; *(image->data(x,y)+1) = *it++;
*image->data(x,y) = *it++; *image->data(x,y) = *it++;
it++; // skip alpha ++it; // skip alpha
} }
} }
@ -322,14 +322,14 @@ namespace ESSImport
ESM::NAME n = esm.getRecName(); ESM::NAME n = esm.getRecName();
esm.getRecHeader(); esm.getRecHeader();
std::map<unsigned int, boost::shared_ptr<Converter> >::iterator it = converters.find(n.val); std::map<unsigned int, boost::shared_ptr<Converter> >::iterator it = converters.find(n.intval);
if (it != converters.end()) if (it != converters.end())
{ {
it->second->read(esm); it->second->read(esm);
} }
else else
{ {
if (unknownRecords.insert(n.val).second) if (unknownRecords.insert(n.intval).second)
{ {
std::ios::fmtflags f(std::cerr.flags()); std::ios::fmtflags f(std::cerr.flags());
std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl; std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;

@ -21,13 +21,18 @@ namespace ESSImport
item.mCount = contItem.mCount; item.mCount = contItem.mCount;
item.mRelativeEquipmentSlot = -1; item.mRelativeEquipmentSlot = -1;
// seems that a stack of items can have a set of subrecords for each item? rings0000.ess unsigned int itemCount = std::abs(item.mCount);
// doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place? bool separateStacks = false;
// I guess we should double check the stacking logic in OpenMW for (unsigned int i=0;i<itemCount;++i)
for (int i=0;i<std::abs(item.mCount);++i)
{ {
if (esm.isNextSub("XIDX")) // index in the stack? bool newStack = esm.isNextSub("XIDX");
esm.skipHSub(); if (newStack)
{
unsigned int idx;
esm.getHT(idx);
separateStacks = true;
item.mCount = 1;
}
item.mSCRI.load(esm); item.mSCRI.load(esm);
@ -38,9 +43,13 @@ namespace ESSImport
int charge=-1; int charge=-1;
esm.getHNOT(charge, "XHLT"); esm.getHNOT(charge, "XHLT");
item.mChargeInt = charge; item.mChargeInt = charge;
if (newStack)
mItems.push_back(item);
} }
mItems.push_back(item); if (!separateStacks)
mItems.push_back(item);
} }
// equipped items // equipped items

@ -43,10 +43,9 @@ struct PCDT
DrawState_Weapon = 0x80, DrawState_Weapon = 0x80,
DrawState_Spell = 0x100 DrawState_Spell = 0x100
}; };
enum CameraState enum CameraFlags
{ {
CameraState_FirstPerson = 0x8, CameraFlag_ThirdPerson = 0x2
CameraState_ThirdPerson = 0xa
}; };
#pragma pack(push) #pragma pack(push)
@ -64,11 +63,13 @@ struct PCDT
struct PNAM struct PNAM
{ {
short mDrawState; // DrawState short mDrawState; // DrawState
short mCameraState; // CameraState short mCameraFlags; // CameraFlags
unsigned int mLevelProgress; unsigned int mLevelProgress;
float mSkillProgress[27]; // skill progress, non-uniform scaled float mSkillProgress[27]; // skill progress, non-uniform scaled
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
unsigned char mUnknown3[88]; unsigned char mUnknown3[84];
unsigned char mSpecIncreases[3]; // number of skill increases for each specialization
unsigned char mUnknown4;
}; };
#pragma pack(pop) #pragma pack(pop)

@ -99,9 +99,6 @@ if (DESIRED_QT_VERSION MATCHES 4)
endif(WIN32) endif(WIN32)
else() else()
qt5_use_modules(openmw-launcher Widgets Core) qt5_use_modules(openmw-launcher Widgets Core)
if (WIN32)
target_link_libraries(Qt5::WinMain)
endif()
endif() endif()
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)

@ -51,10 +51,6 @@ int main(int argc, char *argv[])
if (result == Launcher::FirstRunDialogResultFailure) if (result == Launcher::FirstRunDialogResultFailure)
return 0; return 0;
// if (!mainWin.setup()) {
// return 0;
// }
if (result == Launcher::FirstRunDialogResultContinue) if (result == Launcher::FirstRunDialogResultContinue)
mainWin.show(); mainWin.show();

@ -50,7 +50,6 @@ namespace Launcher
explicit MainDialog(QWidget *parent = 0); explicit MainDialog(QWidget *parent = 0);
~MainDialog(); ~MainDialog();
bool setup();
FirstRunDialogResult showFirstRunDialog(); FirstRunDialogResult showFirstRunDialog();
bool reloadSettings(); bool reloadSettings();
@ -65,6 +64,8 @@ namespace Launcher
void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus); void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus);
private: private:
bool setup();
void createIcons(); void createIcons();
void createPages(); void createPages();

@ -1,5 +1,6 @@
#include "importer.hpp" #include "importer.hpp"
#include <ctime>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <map> #include <map>
@ -897,8 +898,13 @@ std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename
boost::filesystem::path resolved = filename; boost::filesystem::path resolved = filename;
#endif #endif
writeTime = boost::filesystem::last_write_time(resolved); writeTime = boost::filesystem::last_write_time(resolved);
std::cout << "content file: " << resolved << " timestamp = (" << writeTime <<
") " << asctime(localtime(&writeTime)) << std::endl; // print timestamp
const int size=1024;
char timeStrBuffer[size];
if (std::strftime(timeStrBuffer, size, "%x %X", localtime(&writeTime)) > 0)
std::cout << "content file: " << resolved << " timestamp = (" << writeTime <<
") " << timeStrBuffer << std::endl;
} }
else else
{ {

@ -26,7 +26,7 @@ opencs_units_noqt (model/world
universalid record commands columnbase columnimp scriptcontext cell refidcollection universalid record commands columnbase columnimp scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager metadata idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro
) )
opencs_hdrs_noqt (model/world opencs_hdrs_noqt (model/world
@ -35,14 +35,14 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools opencs_units (model/tools
tools reportmodel mergeoperation tools reportmodel mergeoperation
) )
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages mergestages gmstcheck topicinfocheck journalcheck
) )
opencs_hdrs_noqt (model/tools opencs_hdrs_noqt (model/tools
@ -67,7 +67,7 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator 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
) )
@ -85,16 +85,17 @@ opencs_units (view/widget
opencs_units (view/render opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render
lighting lightingday lightingnight lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase
lightingbright object cell terrainstorage tagbase cellarrow cellarrow cellmarker cellborder cameracontroller pathgrid
) )
opencs_hdrs_noqt (view/render opencs_hdrs_noqt (view/render
elements mask
) )
@ -192,11 +193,12 @@ endif(APPLE)
target_link_libraries(openmw-cs target_link_libraries(openmw-cs
${OSG_LIBRARIES} ${OSG_LIBRARIES}
${OPENTHREADS_LIBRARIES} ${OPENTHREADS_LIBRARIES}
${OSGTEXT_LIBRARIES}
${OSGUTIL_LIBRARIES} ${OSGUTIL_LIBRARIES}
${OSGVIEWER_LIBRARIES} ${OSGVIEWER_LIBRARIES}
${OSGGA_LIBRARIES} ${OSGGA_LIBRARIES}
${OSGFX_LIBRARIES} ${OSGFX_LIBRARIES}
${OSGQT_LIBRARIES} ${EXTERN_OSGQT_LIBRARY}
${Boost_SYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}
@ -215,9 +217,6 @@ if (DESIRED_QT_VERSION MATCHES 4)
endif() endif()
else() else()
qt5_use_modules(openmw-cs Widgets Core Network OpenGL) qt5_use_modules(openmw-cs Widgets Core Network OpenGL)
if (WIN32)
target_link_libraries(Qt5::WinMain)
endif()
endif() endif()
if (WIN32) if (WIN32)

@ -8,6 +8,8 @@
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp> #include <components/vfs/registerarchives.hpp>
#include <components/fallback/validate.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
#include "model/doc/document.hpp" #include "model/doc/document.hpp"
@ -17,6 +19,8 @@
#include <Windows.h> #include <Windows.h>
#endif #endif
using namespace Fallback;
CS::Editor::Editor () CS::Editor::Editor ()
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), : mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""), mViewManager (mDocumentManager), mPid(""),
@ -100,6 +104,8 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
("resources", boost::program_options::value<std::string>()->default_value("resources")) ("resources", boost::program_options::value<std::string>()->default_value("resources"))
("fallback-archive", boost::program_options::value<std::vector<std::string> >()-> ("fallback-archive", boost::program_options::value<std::vector<std::string> >()->
default_value(std::vector<std::string>(), "fallback-archive")->multitoken()) default_value(std::vector<std::string>(), "fallback-archive")->multitoken())
("fallback", boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "")
->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<std::vector<std::string> >()->default_value(std::vector<std::string>(), "")
->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)
@ -114,6 +120,8 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mDocumentManager.setResourceDir (mResources = variables["resources"].as<std::string>()); mDocumentManager.setResourceDir (mResources = variables["resources"].as<std::string>());
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<std::vector<std::string> >());

File diff suppressed because it is too large Load Diff

@ -25,9 +25,13 @@
class QAbstractItemModel; class QAbstractItemModel;
namespace VFS namespace Fallback
{ {
class Map;
}
namespace VFS
{
class Manager; class Manager;
} }
@ -66,6 +70,7 @@ namespace CSMDoc
Saving mSavingOperation; Saving mSavingOperation;
OperationHolder mSaving; OperationHolder mSaving;
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
const Fallback::Map* mFallbackMap;
Blacklist mBlacklist; Blacklist mBlacklist;
Runner mRunner; Runner mRunner;
bool mDirty; bool mDirty;
@ -101,6 +106,7 @@ namespace CSMDoc
Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration, Document (const VFS::Manager* vfs, 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,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts); const std::vector<std::string>& blacklistedScripts);

@ -41,6 +41,7 @@ CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& con
CSMDoc::DocumentManager::~DocumentManager() CSMDoc::DocumentManager::~DocumentManager()
{ {
mLoaderThread.quit(); mLoaderThread.quit();
mLoader.stop();
mLoader.hasThingsToDo().wakeAll(); mLoader.hasThingsToDo().wakeAll();
mLoaderThread.wait(); mLoaderThread.wait();
@ -64,7 +65,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, mEncoding, mResourcesManager, mBlacklistedScripts); return new Document (mVFS, mConfiguration, files, new_, savePath, mResDir, &mFallbackMap, mEncoding, mResourcesManager, mBlacklistedScripts);
} }
void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document) void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)
@ -100,6 +101,11 @@ void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& par
mResDir = boost::filesystem::system_complete(parResDir); mResDir = boost::filesystem::system_complete(parResDir);
} }
void CSMDoc::DocumentManager::setFallbackMap(const std::map<std::string, std::string>& fallbackMap)
{
mFallbackMap = Fallback::Map(fallbackMap);
}
void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding) void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding)
{ {
mEncoding = encoding; mEncoding = encoding;

@ -10,6 +10,7 @@
#include <QThread> #include <QThread>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include <components/fallback/fallback.hpp>
#include "../world/resourcesmanager.hpp" #include "../world/resourcesmanager.hpp"
@ -67,6 +68,8 @@ namespace CSMDoc
void setResourceDir (const boost::filesystem::path& parResDir); void setResourceDir (const boost::filesystem::path& parResDir);
void setFallbackMap (const std::map<std::string, std::string>& fallbackMap);
void setEncoding (ToUTF8::FromType encoding); void setEncoding (ToUTF8::FromType encoding);
void setBlacklistedScripts (const std::vector<std::string>& scriptIds); void setBlacklistedScripts (const std::vector<std::string>& scriptIds);
@ -78,6 +81,7 @@ namespace CSMDoc
private: private:
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
Fallback::Map mFallbackMap;
private slots: private slots:

@ -1,6 +1,6 @@
#include "loader.hpp" #include "loader.hpp"
#include <QTimer> #include <iostream>
#include "../tools/reportmodel.hpp" #include "../tools/reportmodel.hpp"
@ -11,11 +11,12 @@ CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLoaded (0), mRecordsLeft (fa
CSMDoc::Loader::Loader() CSMDoc::Loader::Loader()
: mShouldStop(false)
{ {
QTimer *timer = new QTimer (this); mTimer = new QTimer (this);
connect (timer, SIGNAL (timeout()), this, SLOT (load())); connect (mTimer, SIGNAL (timeout()), this, SLOT (load()));
timer->start(); mTimer->start();
} }
QWaitCondition& CSMDoc::Loader::hasThingsToDo() QWaitCondition& CSMDoc::Loader::hasThingsToDo()
@ -23,6 +24,11 @@ QWaitCondition& CSMDoc::Loader::hasThingsToDo()
return mThingsToDo; return mThingsToDo;
} }
void CSMDoc::Loader::stop()
{
mShouldStop = true;
}
void CSMDoc::Loader::load() void CSMDoc::Loader::load()
{ {
if (mDocuments.empty()) if (mDocuments.empty())
@ -30,6 +36,10 @@ void CSMDoc::Loader::load()
mMutex.lock(); mMutex.lock();
mThingsToDo.wait (&mMutex); mThingsToDo.wait (&mMutex);
mMutex.unlock(); mMutex.unlock();
if (mShouldStop)
mTimer->stop();
return; return;
} }
@ -45,13 +55,12 @@ void CSMDoc::Loader::load()
bool done = false; bool done = false;
const int batchingSize = 50;
try try
{ {
if (iter->second.mRecordsLeft) if (iter->second.mRecordsLeft)
{ {
Messages messages (Message::Severity_Error); Messages messages (Message::Severity_Error);
const int batchingSize = 50;
for (int i=0; i<batchingSize; ++i) // do not flood the system with update signals for (int i=0; i<batchingSize; ++i) // do not flood the system with update signals
if (document->getData().continueLoading (messages)) if (document->getData().continueLoading (messages))
{ {

@ -5,6 +5,7 @@
#include <QObject> #include <QObject>
#include <QMutex> #include <QMutex>
#include <QTimer>
#include <QWaitCondition> #include <QWaitCondition>
namespace CSMDoc namespace CSMDoc
@ -28,12 +29,17 @@ namespace CSMDoc
QWaitCondition mThingsToDo; QWaitCondition mThingsToDo;
std::vector<std::pair<Document *, Stage> > mDocuments; std::vector<std::pair<Document *, Stage> > mDocuments;
QTimer* mTimer;
bool mShouldStop;
public: public:
Loader(); Loader();
QWaitCondition& hasThingsToDo(); QWaitCondition& hasThingsToDo();
void stop();
private slots: private slots:
void load(); void load();

@ -1,6 +1,6 @@
#include "messages.hpp" #include "messages.hpp"
CSMDoc::Message::Message() {} CSMDoc::Message::Message() : mSeverity(Severity_Default){}
CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& message, CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, Severity severity) const std::string& hint, Severity severity)

@ -39,9 +39,6 @@ namespace CSMDoc
{ {
public: public:
// \deprecated Use CSMDoc::Message directly instead.
typedef CSMDoc::Message Message;
typedef std::vector<Message> Collection; typedef std::vector<Message> Collection;
typedef Collection::const_iterator Iterator; typedef Collection::const_iterator Iterator;

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

@ -11,6 +11,7 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "../world/infocollection.hpp" #include "../world/infocollection.hpp"
#include "../world/cellcoordinates.hpp"
#include "document.hpp" #include "document.hpp"
#include "savingstate.hpp" #include "savingstate.hpp"
@ -238,7 +239,7 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
// An empty mOriginalCell is meant to indicate that it is the same as // An empty mOriginalCell is meant to indicate that it is the same as
// the current cell. It is possible that a moved ref is moved again. // the current cell. It is possible that a moved ref is moved again.
if ((record.get().mOriginalCell.empty() ? if ((record.get().mOriginalCell.empty() ?
record.get().mCell : record.get().mOriginalCell) != stream.str() && !interior) record.get().mCell : record.get().mOriginalCell) != stream.str() && !interior && record.mState!=CSMWorld::RecordBase::State_ModifiedOnly && !record.get().mNew)
indices.push_back (i); indices.push_back (i);
else else
indices.push_front (i); indices.push_front (i);
@ -265,13 +266,32 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
std::map<std::string, std::deque<int> >::const_iterator references = std::map<std::string, std::deque<int> >::const_iterator references =
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId)); mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
if (cell.isModified() || if (cell.isModified() ||
cell.mState == CSMWorld::RecordBase::State_Deleted || cell.mState == CSMWorld::RecordBase::State_Deleted ||
references!=mState.getSubRecords().end()) references!=mState.getSubRecords().end())
{ {
CSMWorld::Cell cellRecord = cell.get(); CSMWorld::Cell cellRecord = cell.get();
bool interior = cellRecord.mId.substr (0, 1)!="#"; bool interior = cellRecord.mId.substr (0, 1)!="#";
// count new references and adjust RefNumCount accordingsly
int newRefNum = cellRecord.mRefNumCounter;
if (references!=mState.getSubRecords().end())
{
for (std::deque<int>::const_iterator iter (references->second.begin());
iter!=references->second.end(); ++iter)
{
const CSMWorld::Record<CSMWorld::CellRef>& ref =
mDocument.getData().getReferences().getRecord (*iter);
if (ref.get().mNew ||
(!interior && ref.mState==CSMWorld::RecordBase::State_ModifiedOnly &&
/// \todo consider worldspace
CSMWorld::CellCoordinates (ref.get().getCellIndex()).getId("")!=ref.get().mCell))
++cellRecord.mRefNumCounter;
}
}
// write cell data // write cell data
writer.startRecord (cellRecord.sRecordId); writer.startRecord (cellRecord.sRecordId);
@ -309,11 +329,18 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
stream << "#" << index.first << " " << index.second; stream << "#" << index.first << " " << index.second;
} }
// An empty mOriginalCell is meant to indicate that it is the same as if (refRecord.mNew ||
// the current cell. It is possible that a moved ref is moved again. (!interior && ref.mState==CSMWorld::RecordBase::State_ModifiedOnly &&
if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) refRecord.mCell!=stream.str()))
{
refRecord.mRefNum.mIndex = newRefNum++;
}
else if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell)
!= stream.str() && !interior) != stream.str() && !interior)
{ {
// An empty mOriginalCell is meant to indicate that it is the same as
// the current cell. It is possible that a moved ref is moved again.
ESM::MovedCellRef moved; ESM::MovedCellRef moved;
moved.mRefNum = refRecord.mRefNum; moved.mRefNum = refRecord.mRefNum;
@ -350,7 +377,7 @@ int CSMDoc::WritePathgridCollectionStage::setup()
void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages) void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages)
{ {
ESM::ESMWriter& writer = mState.getWriter(); ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid = const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
mDocument.getData().getPathgrids().getRecord (stage); mDocument.getData().getPathgrids().getRecord (stage);
if (pathgrid.isModified() || pathgrid.mState == CSMWorld::RecordBase::State_Deleted) if (pathgrid.isModified() || pathgrid.mState == CSMWorld::RecordBase::State_Deleted)
@ -386,7 +413,7 @@ int CSMDoc::WriteLandCollectionStage::setup()
void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages) void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)
{ {
ESM::ESMWriter& writer = mState.getWriter(); ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::Land>& land = const CSMWorld::Record<CSMWorld::Land>& land =
mDocument.getData().getLand().getRecord (stage); mDocument.getData().getLand().getRecord (stage);
if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted) if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted)
@ -394,10 +421,6 @@ void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)
CSMWorld::Land record = land.get(); CSMWorld::Land record = land.get();
writer.startRecord (record.sRecordId); writer.startRecord (record.sRecordId);
record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted); record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted);
if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes))
data->save (mState.getWriter());
writer.endRecord (record.sRecordId); writer.endRecord (record.sRecordId);
} }
} }
@ -416,7 +439,7 @@ int CSMDoc::WriteLandTextureCollectionStage::setup()
void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages) void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages)
{ {
ESM::ESMWriter& writer = mState.getWriter(); ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture = const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
mDocument.getData().getLandTextures().getRecord (stage); mDocument.getData().getLandTextures().getRecord (stage);
if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted) if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted)

@ -15,10 +15,16 @@
CSMPrefs::DoubleSetting::DoubleSetting (Category *parent, Settings::Manager *values, CSMPrefs::DoubleSetting::DoubleSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, double default_) QMutex *mutex, const std::string& key, const std::string& label, double default_)
: Setting (parent, values, mutex, key, label), : Setting (parent, values, mutex, key, label),
mMin (0), mMax (std::numeric_limits<double>::max()), mPrecision(2), mMin (0), mMax (std::numeric_limits<double>::max()),
mDefault (default_) mDefault (default_)
{} {}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setPrecision(int precision)
{
mPrecision = precision;
return *this;
}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setRange (double min, double max) CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setRange (double min, double max)
{ {
mMin = min; mMin = min;
@ -49,6 +55,7 @@ std::pair<QWidget *, QWidget *> CSMPrefs::DoubleSetting::makeWidgets (QWidget *p
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
QDoubleSpinBox *widget = new QDoubleSpinBox (parent); QDoubleSpinBox *widget = new QDoubleSpinBox (parent);
widget->setDecimals(mPrecision);
widget->setRange (mMin, mMax); widget->setRange (mMin, mMax);
widget->setValue (mDefault); widget->setValue (mDefault);

@ -9,6 +9,7 @@ namespace CSMPrefs
{ {
Q_OBJECT Q_OBJECT
int mPrecision;
double mMin; double mMin;
double mMax; double mMax;
std::string mTooltip; std::string mTooltip;
@ -20,6 +21,8 @@ namespace CSMPrefs
QMutex *mutex, const std::string& key, const std::string& label, QMutex *mutex, const std::string& key, const std::string& label,
double default_); double default_);
DoubleSetting& setPrecision (int precision);
// defaults to [0, std::numeric_limits<double>::max()] // defaults to [0, std::numeric_limits<double>::max()]
DoubleSetting& setRange (double min, double max); DoubleSetting& setRange (double min, double max);

@ -133,12 +133,17 @@ void CSMPrefs::State::declare()
declareBool ("show-linenum", "Show Line Numbers", true). declareBool ("show-linenum", "Show Line Numbers", true).
setTooltip ("Show line numbers to the left of the script editor window." setTooltip ("Show line numbers to the left of the script editor window."
"The current row and column numbers of the text cursor are shown at the bottom."); "The current row and column numbers of the text cursor are shown at the bottom.");
declareBool ("wrap-lines", "Wrap Lines", false).
setTooltip ("Wrap lines longer than width of script editor.");
declareBool ("mono-font", "Use monospace font", true); declareBool ("mono-font", "Use monospace font", true);
declareInt ("tab-width", "Tab Width", 4).
setTooltip ("Number of characters for tab width").
setRange (1, 10);
EnumValue warningsNormal ("Normal", "Report warnings as warning"); EnumValue warningsNormal ("Normal", "Report warnings as warning");
declareEnum ("warnings", "Warning Mode", warningsNormal). declareEnum ("warnings", "Warning Mode", warningsNormal).
addValue ("Ignore", "Do not report warning"). addValue ("Ignore", "Do not report warning").
addValue (warningsNormal). addValue (warningsNormal).
addValue ("Strcit", "Promote warning to an error"); addValue ("Strict", "Promote warning to an error");
declareBool ("toolbar", "Show toolbar", true); declareBool ("toolbar", "Show toolbar", true);
declareInt ("compile-delay", "Delay between updating of source errors", 100). declareInt ("compile-delay", "Delay between updating of source errors", 100).
setTooltip ("Delay in milliseconds"). setTooltip ("Delay in milliseconds").
@ -170,6 +175,17 @@ void CSMPrefs::State::declare()
inputButtons.add (left).add (cLeft).add (right).add (cRight).add (middle).add (cMiddle); inputButtons.add (left).add (cLeft).add (right).add (cRight).add (middle).add (cMiddle);
declareEnum ("p-navi", "Primary Camera Navigation Button", left).addValues (inputButtons); declareEnum ("p-navi", "Primary Camera Navigation Button", left).addValues (inputButtons);
declareEnum ("s-navi", "Secondary Camera Navigation Button", cLeft).addValues (inputButtons); declareEnum ("s-navi", "Secondary Camera Navigation Button", cLeft).addValues (inputButtons);
declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false);
declareDouble ("p-navi-orbit-sensitivity", "Orbit Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-orbit-invert", "Invert Orbit Camera Mouse Input", false);
declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0);
declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);
declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0);
declareDouble ("navi-orbit-rot-speed", "Orbital Camera Rotational Speed", 3.14 / 4).setRange(0.001, 6.28);
declareDouble ("navi-orbit-speed-mult", "Orbital Camera Speed Multiplier (from Modifier)", 4).setRange(0.001, 1000.0);
declareEnum ("p-edit", "Primary Editing Button", right).addValues (inputButtons); declareEnum ("p-edit", "Primary Editing Button", right).addValues (inputButtons);
declareEnum ("s-edit", "Secondary Editing Button", cRight).addValues (inputButtons); declareEnum ("s-edit", "Secondary Editing Button", cRight).addValues (inputButtons);
declareEnum ("p-select", "Primary Selection Button", middle).addValues (inputButtons); declareEnum ("p-select", "Primary Selection Button", middle).addValues (inputButtons);
@ -190,6 +206,24 @@ void CSMPrefs::State::declare()
declareBool ("scene-hide-basic", "Hide basic 3D scenes tooltips", false); declareBool ("scene-hide-basic", "Hide basic 3D scenes tooltips", false);
declareInt ("scene-delay", "Tooltip delay in milliseconds", 500). declareInt ("scene-delay", "Tooltip delay in milliseconds", 500).
setMin (1); setMin (1);
EnumValue createAndInsert ("Create cell and insert");
EnumValue showAndInsert ("Show cell and insert");
EnumValue dontInsert ("Discard");
EnumValue insertAnyway ("Insert anyway");
EnumValues insertOutsideCell;
insertOutsideCell.add (createAndInsert).add (dontInsert).add (insertAnyway);
EnumValues insertOutsideVisibleCell;
insertOutsideVisibleCell.add (showAndInsert).add (dontInsert).add (insertAnyway);
declareCategory ("Scene Drops");
declareInt ("distance", "Drop Distance", 50).
setTooltip ("If an instance drop can not be placed against another object at the "
"insert point, it will be placed by this distance from the insert point instead");
declareEnum ("outside-drop", "Handling drops outside of cells", createAndInsert).
addValues (insertOutsideCell);
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
addValues (insertOutsideVisibleCell);
} }
void CSMPrefs::State::declareCategory (const std::string& key) void CSMPrefs::State::declareCategory (const std::string& key)

@ -1,4 +1,4 @@
#ifndef CSV_PREFS_STATE_H #ifndef CSM_PREFS_STATE_H
#define CSM_PREFS_STATE_H #define CSM_PREFS_STATE_H
#include <map> #include <map>

@ -0,0 +1,123 @@
#include "gmstcheck.hpp"
#include <sstream>
#include "../world/defaultgmsts.hpp"
CSMTools::GmstCheckStage::GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings)
: mGameSettings(gameSettings)
{}
int CSMTools::GmstCheckStage::setup()
{
return mGameSettings.getSize();
}
void CSMTools::GmstCheckStage::perform(int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::GameSetting>& record = mGameSettings.getRecord (stage);
if (record.isDeleted())
return;
const ESM::GameSetting& gmst = record.get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Gmst, gmst.mId);
// Test for empty string
if (gmst.mValue.getType() == ESM::VT_String && gmst.mValue.getString().empty())
messages.add(id, gmst.mId + " is an empty string", "", CSMDoc::Message::Severity_Warning);
// Checking type and limits
// optimization - compare it to lists based on naming convention (f-float,i-int,s-string)
if (gmst.mId[0] == 'f')
{
for (size_t i = 0; i < CSMWorld::DefaultGmsts::FloatCount; ++i)
{
if (gmst.mId == CSMWorld::DefaultGmsts::Floats[i])
{
if (gmst.mValue.getType() != ESM::VT_Float)
{
std::ostringstream stream;
stream << "Expected float type for " << gmst.mId << " but found "
<< varTypeToString(gmst.mValue.getType()) << " type";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
if (gmst.mValue.getFloat() < CSMWorld::DefaultGmsts::FloatLimits[i*2])
messages.add(id, gmst.mId + " is less than the suggested range", "",
CSMDoc::Message::Severity_Warning);
if (gmst.mValue.getFloat() > CSMWorld::DefaultGmsts::FloatLimits[i*2+1])
messages.add(id, gmst.mId + " is more than the suggested range", "",
CSMDoc::Message::Severity_Warning);
break; // for loop
}
}
}
else if (gmst.mId[0] == 'i')
{
for (size_t i = 0; i < CSMWorld::DefaultGmsts::IntCount; ++i)
{
if (gmst.mId == CSMWorld::DefaultGmsts::Ints[i])
{
if (gmst.mValue.getType() != ESM::VT_Int)
{
std::ostringstream stream;
stream << "Expected int type for " << gmst.mId << " but found "
<< varTypeToString(gmst.mValue.getType()) << " type";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
if (gmst.mValue.getInteger() < CSMWorld::DefaultGmsts::IntLimits[i*2])
messages.add(id, gmst.mId + " is less than the suggested range", "",
CSMDoc::Message::Severity_Warning);
if (gmst.mValue.getInteger() > CSMWorld::DefaultGmsts::IntLimits[i*2+1])
messages.add(id, gmst.mId + " is more than the suggested range", "",
CSMDoc::Message::Severity_Warning);
break; // for loop
}
}
}
else if (gmst.mId[0] == 's')
{
for (size_t i = 0; i < CSMWorld::DefaultGmsts::StringCount; ++i)
{
if (gmst.mId == CSMWorld::DefaultGmsts::Strings[i])
{
ESM::VarType type = gmst.mValue.getType();
if (type != ESM::VT_String && type != ESM::VT_None)
{
std::ostringstream stream;
stream << "Expected string or none type for " << gmst.mId << " but found "
<< varTypeToString(gmst.mValue.getType()) << " type";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
break; // for loop
}
}
}
}
std::string CSMTools::GmstCheckStage::varTypeToString(ESM::VarType type)
{
switch (type)
{
case ESM::VT_Unknown: return "unknown";
case ESM::VT_None: return "none";
case ESM::VT_Short: return "short";
case ESM::VT_Int: return "int";
case ESM::VT_Long: return "long";
case ESM::VT_Float: return "float";
case ESM::VT_String: return "string";
default: return "unhandled";
}
}

@ -0,0 +1,34 @@
#ifndef CSM_TOOLS_GMSTCHECK_H
#define CSM_TOOLS_GMSTCHECK_H
#include <components/esm/loadgmst.hpp>
#include "../world/idcollection.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that GMSTs are alright
class GmstCheckStage : public CSMDoc::Stage
{
public:
GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings);
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
private:
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
std::string varTypeToString(ESM::VarType);
};
}
#endif

@ -0,0 +1,79 @@
#include "journalcheck.hpp"
#include <set>
#include <sstream>
CSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals,
const CSMWorld::InfoCollection& journalInfos)
: mJournals(journals), mJournalInfos(journalInfos)
{}
int CSMTools::JournalCheckStage::setup()
{
return mJournals.getSize();
}
void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage);
if (journalRecord.isDeleted())
return;
const ESM::Dialogue &journal = journalRecord.get();
int statusNamedCount = 0;
int totalInfoCount = 0;
std::set<int> questIndices;
CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId);
for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it)
{
const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it);
if (infoRecord.isDeleted())
continue;
const CSMWorld::Info& journalInfo = infoRecord.get();
totalInfoCount += 1;
if (journalInfo.mQuestStatus == ESM::DialInfo::QS_Name)
{
statusNamedCount += 1;
}
if (journalInfo.mResponse.empty())
{
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning);
}
std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);
// Duplicate index
if (result.second == false)
{
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
std::ostringstream stream;
stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex;
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
}
if (totalInfoCount == 0)
{
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning);
}
else if (statusNamedCount > 1)
{
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error);
}
}

@ -0,0 +1,35 @@
#ifndef CSM_TOOLS_JOURNALCHECK_H
#define CSM_TOOLS_JOURNALCHECK_H
#include <components/esm/loaddial.hpp>
#include "../world/idcollection.hpp"
#include "../world/infocollection.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that journal infos are good
class JournalCheckStage : public CSMDoc::Stage
{
public:
JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue>& journals,
const CSMWorld::InfoCollection& journalInfos);
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
private:
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
const CSMWorld::InfoCollection& mJournalInfos;
};
}
#endif

@ -7,7 +7,7 @@
namespace namespace
{ {
void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string text) void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text)
{ {
if (!text.empty()) if (!text.empty())
{ {

@ -99,6 +99,7 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++; ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
ref.mRefNum.mContentFile = 0; ref.mRefNum.mContentFile = 0;
ref.mNew = false;
CSMWorld::Record<CSMWorld::CellRef> newRecord ( CSMWorld::Record<CSMWorld::CellRef> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref); CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref);
@ -224,8 +225,6 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::Land newLand (land); CSMWorld::Land newLand (land);
newLand.mEsm = 0; // avoid potential dangling pointer (ESMReader isn't needed anyway,
// because record is already fully loaded)
newLand.mPlugin = 0; newLand.mPlugin = 0;
if (land.mDataTypes & ESM::Land::DATA_VTEX) if (land.mDataTypes & ESM::Land::DATA_VTEX)

@ -70,20 +70,6 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i) for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i)
{ {
// check the connection number for each point matches the edge connections
if (pathgrid.mPoints[i].mConnectionNum > pointList[i].mConnectionNum)
{
std::ostringstream ss;
ss << " has has less edges than expected for point " << i;
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
}
else if (pathgrid.mPoints[i].mConnectionNum < pointList[i].mConnectionNum)
{
std::ostringstream ss;
ss << " has has more edges than expected for point " << i;
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
}
// check that edges are bidirectional // check that edges are bidirectional
bool foundReverse = false; bool foundReverse = false;
for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j) for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j)

@ -29,9 +29,16 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
// test for empty name // test for empty name
if (region.mName.empty()) if (region.mName.empty())
messages.push_back (std::make_pair (id, region.mId + " has an empty name")); messages.add(id, region.mId + " has an empty name", "", CSMDoc::Message::Severity_Error);
/// \todo test that the ID in mSleeplist exists /// \todo test that the ID in mSleeplist exists
// test that chances add up to 100
int chances = region.mData.mClear + region.mData.mCloudy + region.mData.mFoggy + region.mData.mOvercast +
region.mData.mRain + region.mData.mThunder + region.mData.mAsh + region.mData.mBlight +
region.mData.mA + region.mData.mB;
if (chances != 100)
messages.add(id, "Weather chances do not add up to 100", "", CSMDoc::Message::Severity_Error);
/// \todo check data members that can't be edited in the table view /// \todo check data members that can't be edited in the table view
} }

@ -182,7 +182,7 @@ int CSMTools::ReportModel::countErrors() const
{ {
int count = 0; int count = 0;
for (std::vector<CSMDoc::Messages::Message>::const_iterator iter (mRows.begin()); for (std::vector<CSMDoc::Message>::const_iterator iter (mRows.begin());
iter!=mRows.end(); ++iter) iter!=mRows.end(); ++iter)
if (iter->mSeverity==CSMDoc::Message::Severity_Error || if (iter->mSeverity==CSMDoc::Message::Severity_Error ||
iter->mSeverity==CSMDoc::Message::Severity_SeriousError) iter->mSeverity==CSMDoc::Message::Severity_SeriousError)

@ -16,7 +16,7 @@ namespace CSMTools
{ {
Q_OBJECT Q_OBJECT
std::vector<CSMDoc::Messages::Message> mRows; std::vector<CSMDoc::Message> mRows;
// Fixed columns // Fixed columns
enum Columns enum Columns

@ -120,24 +120,25 @@ QString CSMTools::Search::flatten (const QString& text) const
return flat; return flat;
} }
CSMTools::Search::Search() : mType (Type_None), mPaddingBefore (10), mPaddingAfter (10) {} CSMTools::Search::Search() : mType (Type_None), mValue (0), mIdColumn (0), mTypeColumn (0),
mPaddingBefore (10), mPaddingAfter (10) {}
CSMTools::Search::Search (Type type, const std::string& value) CSMTools::Search::Search (Type type, const std::string& value)
: mType (type), mText (value), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mText (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
if (type!=Type_Text && type!=Type_Id) if (type!=Type_Text && type!=Type_Id)
throw std::logic_error ("Invalid search parameter (string)"); throw std::logic_error ("Invalid search parameter (string)");
} }
CSMTools::Search::Search (Type type, const QRegExp& value) CSMTools::Search::Search (Type type, const QRegExp& value)
: mType (type), mRegExp (value), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mRegExp (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
if (type!=Type_TextRegEx && type!=Type_IdRegEx) if (type!=Type_TextRegEx && type!=Type_IdRegEx)
throw std::logic_error ("Invalid search parameter (RegExp)"); throw std::logic_error ("Invalid search parameter (RegExp)");
} }
CSMTools::Search::Search (Type type, int value) CSMTools::Search::Search (Type type, int value)
: mType (type), mValue (value), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mValue (value), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
if (type!=Type_RecordState) if (type!=Type_RecordState)
throw std::logic_error ("invalid search parameter (int)"); throw std::logic_error ("invalid search parameter (int)");

@ -26,7 +26,7 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages
return; return;
} }
const ESM::SoundGenerator soundGen = record.get(); const ESM::SoundGenerator& soundGen = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId);
if (!soundGen.mCreature.empty()) if (!soundGen.mCreature.empty())

@ -29,6 +29,9 @@
#include "soundgencheck.hpp" #include "soundgencheck.hpp"
#include "magiceffectcheck.hpp" #include "magiceffectcheck.hpp"
#include "mergeoperation.hpp" #include "mergeoperation.hpp"
#include "gmstcheck.hpp"
#include "topicinfocheck.hpp"
#include "journalcheck.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type) CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{ {
@ -111,6 +114,23 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
mData.getResources (CSMWorld::UniversalId::Type_Icons), mData.getResources (CSMWorld::UniversalId::Type_Icons),
mData.getResources (CSMWorld::UniversalId::Type_Textures))); mData.getResources (CSMWorld::UniversalId::Type_Textures)));
mVerifierOperation->appendStage (new GmstCheckStage (mData.getGmsts()));
mVerifierOperation->appendStage (new TopicInfoCheckStage (mData.getTopicInfos(),
mData.getCells(),
mData.getClasses(),
mData.getFactions(),
mData.getGmsts(),
mData.getGlobals(),
mData.getJournals(),
mData.getRaces(),
mData.getRegions(),
mData.getTopics(),
mData.getReferenceables().getDataSet(),
mData.getResources (CSMWorld::UniversalId::Type_SoundsRes)));
mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos()));
mVerifier.setOperation (mVerifierOperation); mVerifier.setOperation (mVerifierOperation);
} }

@ -0,0 +1,441 @@
#include "topicinfocheck.hpp"
#include <sstream>
#include "../world/infoselectwrapper.hpp"
CSMTools::TopicInfoCheckStage::TopicInfoCheckStage(
const CSMWorld::InfoCollection& topicInfos,
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions,
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
const CSMWorld::IdCollection<ESM::Global>& globals,
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Region>& regions,
const CSMWorld::IdCollection<ESM::Dialogue> &topics,
const CSMWorld::RefIdData& referencables,
const CSMWorld::Resources& soundFiles)
: mTopicInfos(topicInfos),
mCells(cells),
mClasses(classes),
mFactions(factions),
mGameSettings(gmsts),
mGlobals(globals),
mJournals(journals),
mRaces(races),
mRegions(regions),
mTopics(topics),
mReferencables(referencables),
mSoundFiles(soundFiles)
{}
int CSMTools::TopicInfoCheckStage::setup()
{
// Generate list of cell names for reference checking
mCellNames.clear();
for (int i = 0; i < mCells.getSize(); ++i)
{
const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mCells.getRecord(i);
if (cellRecord.isDeleted())
continue;
mCellNames.insert(cellRecord.get().mName);
}
// Cell names can also include region names
for (int i = 0; i < mRegions.getSize(); ++i)
{
const CSMWorld::Record<ESM::Region>& regionRecord = mRegions.getRecord(i);
if (regionRecord.isDeleted())
continue;
mCellNames.insert(regionRecord.get().mName);
}
// Default cell name
int index = mGameSettings.searchId("sDefaultCellname");
if (index != -1)
{
const CSMWorld::Record<ESM::GameSetting>& gmstRecord = mGameSettings.getRecord(index);
if (!gmstRecord.isDeleted() && gmstRecord.get().mValue.getType() == ESM::VT_String)
{
mCellNames.insert(gmstRecord.get().mValue.getString());
}
}
return mTopicInfos.getSize();
}
void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage);
if (infoRecord.isDeleted())
return;
const CSMWorld::Info& topicInfo = infoRecord.get();
// There should always be a topic that matches
int topicIndex = mTopics.searchId(topicInfo.mTopicId);
const CSMWorld::Record<ESM::Dialogue>& topicRecord = mTopics.getRecord(topicIndex);
if (topicRecord.isDeleted())
return;
const ESM::Dialogue& topic = topicRecord.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_TopicInfo, topicInfo.mId);
// Check fields
if (!topicInfo.mActor.empty())
{
verifyActor(topicInfo.mActor, id, messages);
}
if (!topicInfo.mClass.empty())
{
verifyId(topicInfo.mClass, mClasses, id, messages);
}
if (!topicInfo.mCell.empty())
{
verifyCell(topicInfo.mCell, id, messages);
}
if (!topicInfo.mFaction.empty())
{
if (verifyId(topicInfo.mFaction, mFactions, id, messages))
{
verifyFactionRank(topicInfo.mFaction, topicInfo.mData.mRank, id, messages);
}
}
if (!topicInfo.mPcFaction.empty())
{
if (verifyId(topicInfo.mPcFaction, mFactions, id, messages))
{
verifyFactionRank(topicInfo.mPcFaction, topicInfo.mData.mPCrank, id, messages);
}
}
if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)
{
std::ostringstream stream;
messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error);
}
if (!topicInfo.mRace.empty())
{
verifyId(topicInfo.mRace, mRaces, id, messages);
}
if (!topicInfo.mSound.empty())
{
verifySound(topicInfo.mSound, id, messages);
}
if (topicInfo.mResponse.empty() && topic.mType != ESM::Dialogue::Voice)
{
messages.add(id, "Response is empty", "", CSMDoc::Message::Severity_Warning);
}
// Check info conditions
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator it = topicInfo.mSelects.begin();
it != topicInfo.mSelects.end(); ++it)
{
verifySelectStruct((*it), id, messages);
}
}
// Verification functions
bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
const std::string specifier = "Actor";
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);
if (index.first == -1)
{
writeMissingIdError(specifier, actor, id, messages);
return false;
}
else if (mReferencables.getRecord(index).isDeleted())
{
writeDeletedRecordError(specifier, actor, id, messages);
return false;
}
else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature)
{
writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages);
return false;
}
return true;
}
bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
const std::string specifier = "Cell";
if (mCellNames.find(cell) == mCellNames.end())
{
writeMissingIdError(specifier, cell, id, messages);
return false;
}
return true;
}
bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& factionName, int rank, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
if (rank < -1)
{
std::ostringstream stream;
stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false;
}
int index = mFactions.searchId(factionName);
const ESM::Faction &faction = mFactions.getRecord(index).get();
int limit = 0;
for (; limit < 10; ++limit)
{
if (faction.mRanks[limit].empty())
break;
}
if (rank >= limit)
{
std::ostringstream stream;
stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1
<< " for the " << factionName << " faction";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false;
}
return true;
}
bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
const std::string specifier = "Item";
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);
if (index.first == -1)
{
writeMissingIdError(specifier, item, id, messages);
return false;
}
else if (mReferencables.getRecord(index).isDeleted())
{
writeDeletedRecordError(specifier, item, id, messages);
return false;
}
else
{
switch (index.second)
{
case CSMWorld::UniversalId::Type_Potion:
case CSMWorld::UniversalId::Type_Apparatus:
case CSMWorld::UniversalId::Type_Armor:
case CSMWorld::UniversalId::Type_Book:
case CSMWorld::UniversalId::Type_Clothing:
case CSMWorld::UniversalId::Type_Ingredient:
case CSMWorld::UniversalId::Type_Light:
case CSMWorld::UniversalId::Type_Lockpick:
case CSMWorld::UniversalId::Type_Miscellaneous:
case CSMWorld::UniversalId::Type_Probe:
case CSMWorld::UniversalId::Type_Repair:
case CSMWorld::UniversalId::Type_Weapon:
case CSMWorld::UniversalId::Type_ItemLevelledList:
break;
default:
writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages);
return false;
}
}
return true;
}
bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::SelectStruct& select,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
CSMWorld::ConstInfoSelectWrapper infoCondition(select);
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)
{
messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error);
return false;
}
else if (!infoCondition.variantTypeIsValid())
{
std::ostringstream stream;
stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of ";
switch (select.mValue.getType())
{
case ESM::VT_None: stream << "None"; break;
case ESM::VT_Short: stream << "Short"; break;
case ESM::VT_Int: stream << "Int"; break;
case ESM::VT_Long: stream << "Long"; break;
case ESM::VT_Float: stream << "Float"; break;
case ESM::VT_String: stream << "String"; break;
default: stream << "Unknown"; break;
}
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false;
}
else if (infoCondition.conditionIsAlwaysTrue())
{
std::ostringstream stream;
stream << "Info Condition: " << infoCondition.toString() << " is always true";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
return false;
}
else if (infoCondition.conditionIsNeverTrue())
{
std::ostringstream stream;
stream << "Info Condition: " << infoCondition.toString() << " is never true";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
return false;
}
// Id checks
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global &&
!verifyId(infoCondition.getVariableName(), mGlobals, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal &&
!verifyId(infoCondition.getVariableName(), mJournals, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item &&
!verifyItem(infoCondition.getVariableName(), id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead &&
!verifyActor(infoCondition.getVariableName(), id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId &&
!verifyActor(infoCondition.getVariableName(), id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction &&
!verifyId(infoCondition.getVariableName(), mFactions, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass &&
!verifyId(infoCondition.getVariableName(), mClasses, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace &&
!verifyId(infoCondition.getVariableName(), mRaces, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell &&
!verifyCell(infoCondition.getVariableName(), id, messages))
{
return false;
}
return true;
}
bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
const std::string specifier = "Sound File";
if (mSoundFiles.searchId(sound) == -1)
{
writeMissingIdError(specifier, sound, id, messages);
return false;
}
return true;
}
template <typename T>
bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
int index = collection.searchId(name);
if (index == -1)
{
writeMissingIdError(T::getRecordType(), name, id, messages);
return false;
}
else if (collection.getRecord(index).isDeleted())
{
writeDeletedRecordError(T::getRecordType(), name, id, messages);
return false;
}
return true;
}
// Error functions
void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
std::ostringstream stream;
stream << specifier << ": ID or name \"" << missingId << "\" could not be found";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
std::ostringstream stream;
stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
CSMWorld::UniversalId tempId(invalidType, invalidId);
std::ostringstream stream;
stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \""
<< invalidId << "\" (can be of type " << expectedType << ")";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}

@ -0,0 +1,95 @@
#ifndef CSM_TOOLS_TOPICINFOCHECK_HPP
#define CSM_TOOLS_TOPICINFOCHECK_HPP
#include <set>
#include <components/esm/loadclas.hpp>
#include <components/esm/loaddial.hpp>
#include <components/esm/loadfact.hpp>
#include <components/esm/loadglob.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm/loadrace.hpp>
#include <components/esm/loadregn.hpp>
#include "../world/cell.hpp"
#include "../world/idcollection.hpp"
#include "../world/infocollection.hpp"
#include "../world/refiddata.hpp"
#include "../world/resources.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: check topics
class TopicInfoCheckStage : public CSMDoc::Stage
{
public:
TopicInfoCheckStage(
const CSMWorld::InfoCollection& topicInfos,
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions,
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
const CSMWorld::IdCollection<ESM::Global>& globals,
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Region>& regions,
const CSMWorld::IdCollection<ESM::Dialogue>& topics,
const CSMWorld::RefIdData& referencables,
const CSMWorld::Resources& soundFiles);
virtual int setup();
///< \return number of steps
virtual void perform(int step, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages
private:
const CSMWorld::InfoCollection& mTopicInfos;
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
const CSMWorld::IdCollection<ESM::Global>& mGlobals;
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
const CSMWorld::IdCollection<ESM::Race>& mRaces;
const CSMWorld::IdCollection<ESM::Region>& mRegions;
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
const CSMWorld::RefIdData& mReferencables;
const CSMWorld::Resources& mSoundFiles;
std::set<std::string> mCellNames;
// These return false when not successful and write an error
bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifyFactionRank(const std::string& name, int rank, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages);
bool verifyItem(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifySelectStruct(const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages);
bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
template <typename T>
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
// Common error messages
void writeMissingIdError(const std::string& specifier, const std::string& missingId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
void writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
};
}
#endif

@ -1,5 +1,7 @@
#include "cellcoordinates.hpp" #include "cellcoordinates.hpp"
#include <cmath>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
@ -7,6 +9,9 @@ CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {}
CSMWorld::CellCoordinates::CellCoordinates (const std::pair<int, int>& coordinates)
: mX (coordinates.first), mY (coordinates.second) {}
int CSMWorld::CellCoordinates::getX() const int CSMWorld::CellCoordinates::getX() const
{ {
return mX; return mX;
@ -49,6 +54,13 @@ std::pair<CSMWorld::CellCoordinates, bool> CSMWorld::CellCoordinates::fromId (
return std::make_pair (CellCoordinates(), false); return std::make_pair (CellCoordinates(), false);
} }
std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y)
{
const int cellSize = 8192;
return std::make_pair (std::floor (x/cellSize), std::floor (y/cellSize));
}
bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)
{ {
return left.getX()==right.getX() && left.getY()==right.getY(); return left.getX()==right.getX() && left.getY()==right.getY();

@ -3,6 +3,7 @@
#include <iosfwd> #include <iosfwd>
#include <string> #include <string>
#include <utility>
#include <QMetaType> #include <QMetaType>
@ -19,6 +20,8 @@ namespace CSMWorld
CellCoordinates (int x, int y); CellCoordinates (int x, int y);
CellCoordinates (const std::pair<int, int>& coordinates);
int getX() const; int getX() const;
int getY() const; int getY() const;
@ -34,6 +37,8 @@ namespace CSMWorld
/// ///
/// \note The worldspace part of \a id is ignored /// \note The worldspace part of \a id is ignored
static std::pair<CellCoordinates, bool> fromId (const std::string& id); static std::pair<CellCoordinates, bool> fromId (const std::string& id);
static std::pair<int, int> coordinatesToCellIndex (float x, float y);
}; };
bool operator== (const CellCoordinates& left, const CellCoordinates& right); bool operator== (const CellCoordinates& left, const CellCoordinates& right);

@ -85,6 +85,7 @@ namespace CSMWorld
Display_Enchantment, Display_Enchantment,
//CONCRETE TYPES ENDS HERE //CONCRETE TYPES ENDS HERE
Display_UnsignedInteger8,
Display_Integer, Display_Integer,
Display_Float, Display_Float,
Display_Var, Display_Var,

@ -3,6 +3,7 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "universalid.hpp" #include "universalid.hpp"
#include "infoselectwrapper.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -273,8 +274,8 @@ namespace CSMWorld
{ ColumnId_InfoList, "Info List" }, { ColumnId_InfoList, "Info List" },
{ ColumnId_InfoCondition, "Info Conditions" }, { ColumnId_InfoCondition, "Info Conditions" },
{ ColumnId_InfoCondFunc, "Function" }, { ColumnId_InfoCondFunc, "Function" },
{ ColumnId_InfoCondVar, "Func/Variable" }, { ColumnId_InfoCondVar, "Variable/Object" },
{ ColumnId_InfoCondComp, "Comp" }, { ColumnId_InfoCondComp, "Relation" },
{ ColumnId_InfoCondValue, "Values" }, { ColumnId_InfoCondValue, "Values" },
{ ColumnId_OriginalCell, "Original Cell" }, { ColumnId_OriginalCell, "Original Cell" },
@ -325,6 +326,10 @@ namespace CSMWorld
{ ColumnId_Idle7, "Idle 7" }, { ColumnId_Idle7, "Idle 7" },
{ ColumnId_Idle8, "Idle 8" }, { ColumnId_Idle8, "Idle 8" },
{ ColumnId_RegionWeather, "Weather" },
{ ColumnId_WeatherName, "Type" },
{ ColumnId_WeatherChance, "Percent Chance" },
{ ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },
{ ColumnId_UseValue3, "Use value 3" }, { ColumnId_UseValue3, "Use value 3" },
@ -546,18 +551,6 @@ namespace
"AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0 "AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0
}; };
static const char *sInfoCondFunc[] =
{
" ", "Function", "Global", "Local", "Journal",
"Item", "Dead", "Not ID", "Not Faction", "Not Class",
"Not Race", "Not Cell", "Not Local", 0
};
static const char *sInfoCondComp[] =
{
"!=", "<", "<=", "=", ">", ">=", 0
};
const char **getEnumNames (CSMWorld::Columns::ColumnId column) const char **getEnumNames (CSMWorld::Columns::ColumnId column)
{ {
switch (column) switch (column)
@ -585,10 +578,8 @@ namespace
case CSMWorld::Columns::ColumnId_EffectId: return sEffectId; case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType; case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType; case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc; case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;
// FIXME: don't have dynamic value enum delegate, use Display_String for now case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
//case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond;
case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp;
default: return 0; default: return 0;
} }

@ -325,6 +325,10 @@ namespace CSMWorld
ColumnId_Idle7 = 292, ColumnId_Idle7 = 292,
ColumnId_Idle8 = 293, ColumnId_Idle8 = 293,
ColumnId_RegionWeather = 294,
ColumnId_WeatherName = 295,
ColumnId_WeatherChance = 296,
// 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,

@ -11,6 +11,7 @@
#include "record.hpp" #include "record.hpp"
#include "commands.hpp" #include "commands.hpp"
#include "idtableproxymodel.hpp" #include "idtableproxymodel.hpp"
#include "commandmacro.hpp"
std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const
{ {
@ -171,10 +172,9 @@ void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, cons
if (modifyCell.get()) if (modifyCell.get())
{ {
mDocument.getUndoStack().beginMacro (modifyData->text()); CommandMacro macro (mDocument.getUndoStack());
mDocument.getUndoStack().push (modifyData.release()); macro.push (modifyData.release());
mDocument.getUndoStack().push (modifyCell.release()); macro.push (modifyCell.release());
mDocument.getUndoStack().endMacro();
} }
else else
mDocument.getUndoStack().push (modifyData.release()); mDocument.getUndoStack().push (modifyData.release());
@ -194,9 +194,7 @@ void CSMWorld::CommandDispatcher::executeDelete()
int columnIndex = model.findColumnIndex (Columns::ColumnId_Id); int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);
if (rows.size()>1) CommandMacro macro (mDocument.getUndoStack(), rows.size()>1 ? "Delete multiple records" : "");
mDocument.getUndoStack().beginMacro (tr ("Delete multiple records"));
for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
{ {
std::string id = model.data (model.getModelIndex (*iter, columnIndex)). std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
@ -204,7 +202,7 @@ void CSMWorld::CommandDispatcher::executeDelete()
if (mId.getType() == UniversalId::Type_Referenceables) if (mId.getType() == UniversalId::Type_Referenceables)
{ {
mDocument.getUndoStack().push ( new CSMWorld::DeleteCommand (model, id, macro.push (new CSMWorld::DeleteCommand (model, id,
static_cast<CSMWorld::UniversalId::Type>(model.data (model.index ( static_cast<CSMWorld::UniversalId::Type>(model.data (model.index (
model.getModelIndex (id, columnIndex).row(), model.getModelIndex (id, columnIndex).row(),
model.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType))).toInt()))); model.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType))).toInt())));
@ -212,9 +210,6 @@ void CSMWorld::CommandDispatcher::executeDelete()
else else
mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id)); mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id));
} }
if (rows.size()>1)
mDocument.getUndoStack().endMacro();
} }
void CSMWorld::CommandDispatcher::executeRevert() void CSMWorld::CommandDispatcher::executeRevert()
@ -231,25 +226,19 @@ void CSMWorld::CommandDispatcher::executeRevert()
int columnIndex = model.findColumnIndex (Columns::ColumnId_Id); int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);
if (rows.size()>1) CommandMacro macro (mDocument.getUndoStack(), rows.size()>1 ? "Revert multiple records" : "");
mDocument.getUndoStack().beginMacro (tr ("Revert multiple records"));
for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
{ {
std::string id = model.data (model.getModelIndex (*iter, columnIndex)). std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
toString().toUtf8().constData(); toString().toUtf8().constData();
mDocument.getUndoStack().push (new CSMWorld::RevertCommand (model, id)); macro.push (new CSMWorld::RevertCommand (model, id));
} }
if (rows.size()>1)
mDocument.getUndoStack().endMacro();
} }
void CSMWorld::CommandDispatcher::executeExtendedDelete() void CSMWorld::CommandDispatcher::executeExtendedDelete()
{ {
if (mExtendedTypes.size()>1) CommandMacro macro (mDocument.getUndoStack(), mExtendedTypes.size()>1 ? tr ("Extended delete of multiple records") : "");
mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records"));
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin()); for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
iter!=mExtendedTypes.end(); ++iter) iter!=mExtendedTypes.end(); ++iter)
@ -276,20 +265,15 @@ void CSMWorld::CommandDispatcher::executeExtendedDelete()
Misc::StringUtils::lowerCase (record.get().mCell))) Misc::StringUtils::lowerCase (record.get().mCell)))
continue; continue;
mDocument.getUndoStack().push ( macro.push (new CSMWorld::DeleteCommand (model, record.get().mId));
new CSMWorld::DeleteCommand (model, record.get().mId));
} }
} }
} }
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().endMacro();
} }
void CSMWorld::CommandDispatcher::executeExtendedRevert() void CSMWorld::CommandDispatcher::executeExtendedRevert()
{ {
if (mExtendedTypes.size()>1) CommandMacro macro (mDocument.getUndoStack(), mExtendedTypes.size()>1 ? tr ("Extended revert of multiple records") : "");
mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records"));
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin()); for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
iter!=mExtendedTypes.end(); ++iter) iter!=mExtendedTypes.end(); ++iter)
@ -313,12 +297,8 @@ void CSMWorld::CommandDispatcher::executeExtendedRevert()
Misc::StringUtils::lowerCase (record.get().mCell))) Misc::StringUtils::lowerCase (record.get().mCell)))
continue; continue;
mDocument.getUndoStack().push ( macro.push (new CSMWorld::RevertCommand (model, record.get().mId));
new CSMWorld::RevertCommand (model, record.get().mId));
} }
} }
} }
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().endMacro();
} }

@ -0,0 +1,26 @@
#include "commandmacro.hpp"
#include <QUndoStack>
#include <QUndoCommand>
CSMWorld::CommandMacro::CommandMacro (QUndoStack& undoStack, const QString& description)
: mUndoStack (undoStack), mDescription (description), mStarted (false)
{}
CSMWorld::CommandMacro::~CommandMacro()
{
if (mStarted)
mUndoStack.endMacro();
}
void CSMWorld::CommandMacro::push (QUndoCommand *command)
{
if (!mStarted)
{
mUndoStack.beginMacro (mDescription.isEmpty() ? command->text() : mDescription);
mStarted = true;
}
mUndoStack.push (command);
}

@ -0,0 +1,34 @@
#ifndef CSM_WOLRD_COMMANDMACRO_H
#define CSM_WOLRD_COMMANDMACRO_H
class QUndoStack;
class QUndoCommand;
#include <QString>
namespace CSMWorld
{
class CommandMacro
{
QUndoStack& mUndoStack;
QString mDescription;
bool mStarted;
/// not implemented
CommandMacro (const CommandMacro&);
/// not implemented
CommandMacro& operator= (const CommandMacro&);
public:
/// If \a description is empty, the description of the first command is used.
CommandMacro (QUndoStack& undoStack, const QString& description = "");
~CommandMacro();
void push (QUndoCommand *command);
};
}
#endif

@ -8,13 +8,16 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QAbstractProxyModel> #include <QAbstractProxyModel>
#include "cellcoordinates.hpp"
#include "idcollection.hpp"
#include "idtable.hpp" #include "idtable.hpp"
#include "idtree.hpp" #include "idtree.hpp"
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
#include "pathgrid.hpp"
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) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)
{ {
if (QAbstractProxyModel *proxy = dynamic_cast<QAbstractProxyModel *> (&model)) if (QAbstractProxyModel *proxy = dynamic_cast<QAbstractProxyModel *> (&model))
{ {
@ -68,9 +71,6 @@ void CSMWorld::ModifyCommand::undo()
void CSMWorld::CreateCommand::applyModifications() void CSMWorld::CreateCommand::applyModifications()
{ {
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second);
if (!mNestedValues.empty()) if (!mNestedValues.empty())
{ {
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel); CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
@ -114,7 +114,7 @@ void CSMWorld::CreateCommand::setType (UniversalId::Type type)
void CSMWorld::CreateCommand::redo() void CSMWorld::CreateCommand::redo()
{ {
mModel.addRecord (mId, mType); mModel.addRecordWithData (mId, mValues, mType);
applyModifications(); applyModifications();
} }
@ -238,6 +238,29 @@ void CSMWorld::CloneCommand::undo()
mModel.removeRow (mModel.getModelIndex (mId, 0).row()); mModel.removeRow (mModel.getModelIndex (mId, 0).row());
} }
CSMWorld::CreatePathgridCommand::CreatePathgridCommand(IdTable& model, const std::string& id, QUndoCommand *parent)
: CreateCommand(model, id, parent)
{
setType(UniversalId::Type_Pathgrid);
}
void CSMWorld::CreatePathgridCommand::redo()
{
CreateCommand::redo();
Record<Pathgrid> record = static_cast<const Record<Pathgrid>& >(mModel.getRecord(mId));
record.get().blank();
record.get().mCell = mId;
std::pair<CellCoordinates, bool> coords = CellCoordinates::fromId(mId);
if (coords.second)
{
record.get().mData.mX = coords.first.getX();
record.get().mData.mY = coords.first.getY();
}
mModel.setRecord(mId, record, mType);
}
CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent) CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mRow (row) : QUndoCommand (parent), mModel (model), mRow (row)

@ -153,6 +153,15 @@ namespace CSMWorld
virtual void undo(); virtual void undo();
}; };
class CreatePathgridCommand : public CreateCommand
{
public:
CreatePathgridCommand(IdTable& model, const std::string& id, QUndoCommand *parent = 0);
virtual void redo();
};
/// \brief Update cell ID according to x/y-coordinates /// \brief Update cell ID according to x/y-coordinates
/// ///
/// \note The new value will be calculated in the first call to redo instead of the /// \note The new value will be calculated in the first call to redo instead of the

@ -10,6 +10,8 @@
#include <components/esm/loadglob.hpp> #include <components/esm/loadglob.hpp>
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
#include <components/resource/scenemanager.hpp>
#include "idtable.hpp" #include "idtable.hpp"
#include "idtree.hpp" #include "idtree.hpp"
#include "columnimp.hpp" #include "columnimp.hpp"
@ -59,10 +61,13 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
return number; return number;
} }
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager) CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir)
: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells), : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0), mResourceSystem(new Resource::ResourceSystem(resourcesManager.getVFS())) mResourcesManager (resourcesManager), mFallbackMap(fallback),
mReader (0), mDialogue (0), mReaderIndex(0), mResourceSystem(new Resource::ResourceSystem(resourcesManager.getVFS()))
{ {
mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string());
int index = 0; int index = 0;
mGlobals.addColumn (new StringIdColumn<ESM::Global>); mGlobals.addColumn (new StringIdColumn<ESM::Global>);
@ -176,6 +181,14 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRegions.addColumn (new NameColumn<ESM::Region>); mRegions.addColumn (new NameColumn<ESM::Region>);
mRegions.addColumn (new MapColourColumn<ESM::Region>); mRegions.addColumn (new MapColourColumn<ESM::Region>);
mRegions.addColumn (new SleepListColumn<ESM::Region>); mRegions.addColumn (new SleepListColumn<ESM::Region>);
// Region Weather
mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionWeather));
index = mRegions.getColumns()-1;
mRegions.addAdapter (std::make_pair(&mRegions.getColumn(index), new RegionWeatherAdapter ()));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_WeatherName, ColumnBase::Display_String, false));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_WeatherChance, ColumnBase::Display_UnsignedInteger8));
// Region Sounds // Region Sounds
mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionSounds)); mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionSounds));
index = mRegions.getColumns()-1; index = mRegions.getColumns()-1;
@ -270,7 +283,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc)); new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc));
// FIXME: don't have dynamic value enum delegate, use Display_String for now // FIXME: don't have dynamic value enum delegate, use Display_String for now
mTopicInfos.getNestableColumn(index)->addColumn( mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String)); new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_InfoCondVar));
mTopicInfos.getNestableColumn(index)->addColumn( mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp)); new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));
mTopicInfos.getNestableColumn(index)->addColumn( mTopicInfos.getNestableColumn(index)->addColumn(
@ -933,7 +946,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
bool unhandledRecord = false; bool unhandledRecord = false;
switch (n.val) switch (n.intval)
{ {
case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break; case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break;
case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break; case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break;
@ -1045,7 +1058,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
else else
{ {
mTopics.load (record, mBase); mTopics.load (record, mBase);
mDialogue = &mTopics.getRecord (record.mId).get(); mDialogue = &mTopics.getRecord (record.mId).get();
} }
} }
@ -1201,3 +1214,8 @@ const VFS::Manager* CSMWorld::Data::getVFS() const
{ {
return mResourcesManager.getVFS(); return mResourcesManager.getVFS();
} }
const Fallback::Map* CSMWorld::Data::getFallbackMap() const
{
return mFallbackMap;
}

@ -58,6 +58,11 @@ namespace VFS
class Manager; class Manager;
} }
namespace Fallback
{
class Map;
}
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
@ -104,6 +109,7 @@ namespace CSMWorld
IdCollection<ESM::Filter> mFilters; IdCollection<ESM::Filter> mFilters;
Collection<MetaData> mMetaData; Collection<MetaData> mMetaData;
const ResourcesManager& mResourcesManager; const ResourcesManager& mResourcesManager;
const Fallback::Map* mFallbackMap;
std::vector<QAbstractItemModel *> mModels; std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex; std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
ESM::ESMReader *mReader; ESM::ESMReader *mReader;
@ -132,12 +138,14 @@ namespace CSMWorld
public: public:
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager); Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir);
virtual ~Data(); virtual ~Data();
const VFS::Manager* getVFS() const; const VFS::Manager* getVFS() const;
const Fallback::Map* getFallbackMap() const;
boost::shared_ptr<Resource::ResourceSystem> getResourceSystem(); boost::shared_ptr<Resource::ResourceSystem> getResourceSystem();
boost::shared_ptr<const Resource::ResourceSystem> getResourceSystem() const; boost::shared_ptr<const Resource::ResourceSystem> getResourceSystem() const;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,34 @@
#ifndef CSM_WORLD_DEFAULTGMSTS_H
#define CSM_WORLD_DEFAULTGMSTS_H
#include <cstddef>
namespace CSMWorld {
namespace DefaultGmsts {
const size_t FloatCount = 258;
const size_t IntCount = 89;
const size_t StringCount = 1174;
const size_t OptionalFloatCount = 42;
const size_t OptionalIntCount = 4;
const size_t OptionalStringCount = 26;
extern const char* Floats[];
extern const char * Ints[];
extern const char * Strings[];
extern const char * OptionalFloats[];
extern const char * OptionalInts[];
extern const char * OptionalStrings[];
extern const float FloatsDefaultValues[];
extern const int IntsDefaultValues[];
extern const float FloatLimits[];
extern const int IntLimits[];
}
}
#endif

@ -60,6 +60,10 @@ std::vector<CSMWorld::ColumnBase::Display> CSMWorld::IdCompletionManager::getDis
{ {
types.push_back(current->first); types.push_back(current->first);
} }
// Hack for Display_InfoCondVar
types.push_back(CSMWorld::ColumnBase::Display_InfoCondVar);
return types; return types;
} }
@ -104,7 +108,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data)
QAbstractItemView *popup = new CSVWidget::CompleterPopup(); QAbstractItemView *popup = new CSVWidget::CompleterPopup();
completer->setPopup(popup); // The completer takes ownership of the popup completer->setPopup(popup); // The completer takes ownership of the popup
completer->setMaxVisibleItems(10); completer->setMaxVisibleItems(10);
mCompleters[current->first] = completer; mCompleters[current->first] = completer;
} }
} }

@ -2,6 +2,8 @@
#include <stdexcept> #include <stdexcept>
#include <components/esm/cellid.hpp>
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
@ -149,6 +151,23 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type
endInsertRows(); endInsertRows();
} }
void CSMWorld::IdTable::addRecordWithData (const std::string& id,
const std::map<int, QVariant>& data, UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex (id, type);
beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendBlankRecord (id, type);
for (std::map<int, QVariant>::const_iterator iter (data.begin()); iter!=data.end(); ++iter)
{
mIdCollection->setData(index, iter->first, iter->second);
}
endInsertRows();
}
void CSMWorld::IdTable::cloneRecord(const std::string& origin, void CSMWorld::IdTable::cloneRecord(const std::string& origin,
const std::string& destination, const std::string& destination,
CSMWorld::UniversalId::Type type) CSMWorld::UniversalId::Type type)
@ -242,7 +261,7 @@ std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row)
return std::make_pair (UniversalId::Type_None, ""); return std::make_pair (UniversalId::Type_None, "");
if (id[0]=='#') if (id[0]=='#')
id = "sys::default"; id = ESM::CellId::sDefaultWorldspace;
return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint); return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint);
} }

@ -53,6 +53,10 @@ namespace CSMWorld
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
void addRecordWithData (const std::string& id, const std::map<int, QVariant>& data,
UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
void cloneRecord(const std::string& origin, void cloneRecord(const std::string& origin,
const std::string& destination, const std::string& destination,
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);

@ -264,7 +264,7 @@ void CSMWorld::IdTree::setNestedTable(const QModelIndex& index, const CSMWorld::
CSMWorld::NestedTableWrapperBase* CSMWorld::IdTree::nestedTable(const QModelIndex& index) const CSMWorld::NestedTableWrapperBase* CSMWorld::IdTree::nestedTable(const QModelIndex& index) const
{ {
if (!hasChildren(index)) if (!hasChildren(index))
throw std::logic_error("Tried to retrive nested table, but index has no children"); throw std::logic_error("Tried to retrieve nested table, but index has no children");
return mNestedCollection->nestedTable(index.row(), index.column()); return mNestedCollection->nestedTable(index.row(), index.column());
} }

@ -0,0 +1,893 @@
#include "infoselectwrapper.hpp"
#include <limits>
#include <sstream>
#include <stdexcept>
const size_t CSMWorld::ConstInfoSelectWrapper::RuleMinSize = 5;
const size_t CSMWorld::ConstInfoSelectWrapper::FunctionPrefixOffset = 1;
const size_t CSMWorld::ConstInfoSelectWrapper::FunctionIndexOffset = 2;
const size_t CSMWorld::ConstInfoSelectWrapper::RelationIndexOffset = 4;
const size_t CSMWorld::ConstInfoSelectWrapper::VarNameOffset = 5;
const char* CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[] =
{
"Rank Low",
"Rank High",
"Rank Requirement",
"Reputation",
"Health Percent",
"PC Reputation",
"PC Level",
"PC Health Percent",
"PC Magicka",
"PC Fatigue",
"PC Strength",
"PC Block",
"PC Armorer",
"PC Medium Armor",
"PC Heavy Armor",
"PC Blunt Weapon",
"PC Long Blade",
"PC Axe",
"PC Spear",
"PC Athletics",
"PC Enchant",
"PC Detruction",
"PC Alteration",
"PC Illusion",
"PC Conjuration",
"PC Mysticism",
"PC Restoration",
"PC Alchemy",
"PC Unarmored",
"PC Security",
"PC Sneak",
"PC Acrobatics",
"PC Light Armor",
"PC Short Blade",
"PC Marksman",
"PC Merchantile",
"PC Speechcraft",
"PC Hand to Hand",
"PC Sex",
"PC Expelled",
"PC Common Disease",
"PC Blight Disease",
"PC Clothing Modifier",
"PC Crime Level",
"Same Sex",
"Same Race",
"Same Faction",
"Faction Rank Difference",
"Detected",
"Alarmed",
"Choice",
"PC Intelligence",
"PC Willpower",
"PC Agility",
"PC Speed",
"PC Endurance",
"PC Personality",
"PC Luck",
"PC Corpus",
"Weather",
"PC Vampire",
"Level",
"Attacked",
"Talked to PC",
"PC Health",
"Creature Target",
"Friend Hit",
"Fight",
"Hello",
"Alarm",
"Flee",
"Should Attack",
"Werewolf",
"PC Werewolf Kills",
"Global",
"Local",
"Journal",
"Item",
"Dead",
"Not Id",
"Not Faction",
"Not Class",
"Not Race",
"Not Cell",
"Not Local",
0
};
const char* CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings[] =
{
"=",
"!=",
">",
">=",
"<",
"<=",
0
};
const char* CSMWorld::ConstInfoSelectWrapper::ComparisonEnumStrings[] =
{
"Boolean",
"Integer",
"Numeric",
0
};
// static functions
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(FunctionName name)
{
if (name < Function_None)
return FunctionEnumStrings[name];
else
return "(Invalid Data: Function)";
}
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(RelationType type)
{
if (type < Relation_None)
return RelationEnumStrings[type];
else
return "(Invalid Data: Relation)";
}
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(ComparisonType type)
{
if (type < Comparison_None)
return ComparisonEnumStrings[type];
else
return "(Invalid Data: Comparison)";
}
// ConstInfoSelectWrapper
CSMWorld::ConstInfoSelectWrapper::ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select)
: mConstSelect(select)
{
readRule();
}
CSMWorld::ConstInfoSelectWrapper::FunctionName CSMWorld::ConstInfoSelectWrapper::getFunctionName() const
{
return mFunctionName;
}
CSMWorld::ConstInfoSelectWrapper::RelationType CSMWorld::ConstInfoSelectWrapper::getRelationType() const
{
return mRelationType;
}
CSMWorld::ConstInfoSelectWrapper::ComparisonType CSMWorld::ConstInfoSelectWrapper::getComparisonType() const
{
return mComparisonType;
}
bool CSMWorld::ConstInfoSelectWrapper::hasVariable() const
{
return mHasVariable;
}
const std::string& CSMWorld::ConstInfoSelectWrapper::getVariableName() const
{
return mVariableName;
}
bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue() const
{
if (!variantTypeIsValid())
return false;
if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer)
{
if (mConstSelect.mValue.getType() == ESM::VT_Float)
return conditionIsAlwaysTrue(getConditionFloatRange(), getValidIntRange());
else
return conditionIsAlwaysTrue(getConditionIntRange(), getValidIntRange());
}
else if (mComparisonType == Comparison_Numeric)
{
if (mConstSelect.mValue.getType() == ESM::VT_Float)
return conditionIsAlwaysTrue(getConditionFloatRange(), getValidFloatRange());
else
return conditionIsAlwaysTrue(getConditionIntRange(), getValidFloatRange());
}
return false;
}
bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue() const
{
if (!variantTypeIsValid())
return false;
if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer)
{
if (mConstSelect.mValue.getType() == ESM::VT_Float)
return conditionIsNeverTrue(getConditionFloatRange(), getValidIntRange());
else
return conditionIsNeverTrue(getConditionIntRange(), getValidIntRange());
}
else if (mComparisonType == Comparison_Numeric)
{
if (mConstSelect.mValue.getType() == ESM::VT_Float)
return conditionIsNeverTrue(getConditionFloatRange(), getValidFloatRange());
else
return conditionIsNeverTrue(getConditionIntRange(), getValidFloatRange());
}
return false;
}
bool CSMWorld::ConstInfoSelectWrapper::variantTypeIsValid() const
{
return (mConstSelect.mValue.getType() == ESM::VT_Int || mConstSelect.mValue.getType() == ESM::VT_Float);
}
const ESM::Variant& CSMWorld::ConstInfoSelectWrapper::getVariant() const
{
return mConstSelect.mValue;
}
std::string CSMWorld::ConstInfoSelectWrapper::toString() const
{
std::ostringstream stream;
stream << convertToString(mFunctionName) << " ";
if (mHasVariable)
stream << mVariableName << " ";
stream << convertToString(mRelationType) << " ";
switch (mConstSelect.mValue.getType())
{
case ESM::VT_Int:
stream << mConstSelect.mValue.getInteger();
break;
case ESM::VT_Float:
stream << mConstSelect.mValue.getFloat();
break;
default:
stream << "(Invalid value type)";
break;
}
return stream.str();
}
void CSMWorld::ConstInfoSelectWrapper::readRule()
{
if (mConstSelect.mSelectRule.size() < RuleMinSize)
throw std::runtime_error("InfoSelectWrapper: rule is to small");
readFunctionName();
readRelationType();
readVariableName();
updateHasVariable();
updateComparisonType();
}
void CSMWorld::ConstInfoSelectWrapper::readFunctionName()
{
char functionPrefix = mConstSelect.mSelectRule[FunctionPrefixOffset];
std::string functionIndex = mConstSelect.mSelectRule.substr(FunctionIndexOffset, 2);
int convertedIndex = -1;
// Read in function index, form ## from 00 .. 73, skip leading zero
if (functionIndex[0] == '0')
functionIndex = functionIndex[1];
std::stringstream stream;
stream << functionIndex;
stream >> convertedIndex;
switch (functionPrefix)
{
case '1':
if (convertedIndex >= 0 && convertedIndex <= 73)
mFunctionName = static_cast<FunctionName>(convertedIndex);
else
mFunctionName = Function_None;
break;
case '2': mFunctionName = Function_Global; break;
case '3': mFunctionName = Function_Local; break;
case '4': mFunctionName = Function_Journal; break;
case '5': mFunctionName = Function_Item; break;
case '6': mFunctionName = Function_Dead; break;
case '7': mFunctionName = Function_NotId; break;
case '8': mFunctionName = Function_NotFaction; break;
case '9': mFunctionName = Function_NotClass; break;
case 'A': mFunctionName = Function_NotRace; break;
case 'B': mFunctionName = Function_NotCell; break;
case 'C': mFunctionName = Function_NotLocal; break;
default: mFunctionName = Function_None; break;
}
}
void CSMWorld::ConstInfoSelectWrapper::readRelationType()
{
char relationIndex = mConstSelect.mSelectRule[RelationIndexOffset];
switch (relationIndex)
{
case '0': mRelationType = Relation_Equal; break;
case '1': mRelationType = Relation_NotEqual; break;
case '2': mRelationType = Relation_Greater; break;
case '3': mRelationType = Relation_GreaterOrEqual; break;
case '4': mRelationType = Relation_Less; break;
case '5': mRelationType = Relation_LessOrEqual; break;
default: mRelationType = Relation_None;
}
}
void CSMWorld::ConstInfoSelectWrapper::readVariableName()
{
if (mConstSelect.mSelectRule.size() >= VarNameOffset)
mVariableName = mConstSelect.mSelectRule.substr(VarNameOffset);
else
mVariableName.clear();
}
void CSMWorld::ConstInfoSelectWrapper::updateHasVariable()
{
switch (mFunctionName)
{
case Function_Global:
case Function_Local:
case Function_Journal:
case Function_Item:
case Function_Dead:
case Function_NotId:
case Function_NotFaction:
case Function_NotClass:
case Function_NotRace:
case Function_NotCell:
case Function_NotLocal:
mHasVariable = true;
break;
default:
mHasVariable = false;
break;
}
}
void CSMWorld::ConstInfoSelectWrapper::updateComparisonType()
{
switch (mFunctionName)
{
// Boolean
case Function_NotId:
case Function_NotFaction:
case Function_NotClass:
case Function_NotRace:
case Function_NotCell:
case Function_NotLocal:
case Function_PcExpelled:
case Function_PcCommonDisease:
case Function_PcBlightDisease:
case Function_SameSex:
case Function_SameRace:
case Function_SameFaction:
case Function_Detected:
case Function_Alarmed:
case Function_PcCorpus:
case Function_PcVampire:
case Function_Attacked:
case Function_TalkedToPc:
case Function_ShouldAttack:
case Function_Werewolf:
mComparisonType = Comparison_Boolean;
break;
// Integer
case Function_Journal:
case Function_Item:
case Function_Dead:
case Function_RankLow:
case Function_RankHigh:
case Function_RankRequirement:
case Function_Reputation:
case Function_PcReputation:
case Function_PcLevel:
case Function_PcStrength:
case Function_PcBlock:
case Function_PcArmorer:
case Function_PcMediumArmor:
case Function_PcHeavyArmor:
case Function_PcBluntWeapon:
case Function_PcLongBlade:
case Function_PcAxe:
case Function_PcSpear:
case Function_PcAthletics:
case Function_PcEnchant:
case Function_PcDestruction:
case Function_PcAlteration:
case Function_PcIllusion:
case Function_PcConjuration:
case Function_PcMysticism:
case Function_PcRestoration:
case Function_PcAlchemy:
case Function_PcUnarmored:
case Function_PcSecurity:
case Function_PcSneak:
case Function_PcAcrobatics:
case Function_PcLightArmor:
case Function_PcShortBlade:
case Function_PcMarksman:
case Function_PcMerchantile:
case Function_PcSpeechcraft:
case Function_PcHandToHand:
case Function_PcGender:
case Function_PcClothingModifier:
case Function_PcCrimeLevel:
case Function_FactionRankDifference:
case Function_Choice:
case Function_PcIntelligence:
case Function_PcWillpower:
case Function_PcAgility:
case Function_PcSpeed:
case Function_PcEndurance:
case Function_PcPersonality:
case Function_PcLuck:
case Function_Weather:
case Function_Level:
case Function_CreatureTarget:
case Function_FriendHit:
case Function_Fight:
case Function_Hello:
case Function_Alarm:
case Function_Flee:
case Function_PcWerewolfKills:
mComparisonType = Comparison_Integer;
break;
// Numeric
case Function_Global:
case Function_Local:
case Function_Health_Percent:
case Function_PcHealthPercent:
case Function_PcMagicka:
case Function_PcFatigue:
case Function_PcHealth:
mComparisonType = Comparison_Numeric;
break;
default:
mComparisonType = Comparison_None;
break;
}
}
std::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() const
{
const int IntMax = std::numeric_limits<int>::max();
const int IntMin = std::numeric_limits<int>::min();
const std::pair<int, int> InvalidRange(IntMax, IntMin);
int value = mConstSelect.mValue.getInteger();
switch (mRelationType)
{
case Relation_Equal:
case Relation_NotEqual:
return std::pair<int, int>(value, value);
case Relation_Greater:
if (value == IntMax)
{
return InvalidRange;
}
else
{
return std::pair<int, int>(value + 1, IntMax);
}
break;
case Relation_GreaterOrEqual:
return std::pair<int, int>(value, IntMax);
case Relation_Less:
if (value == IntMin)
{
return InvalidRange;
}
else
{
return std::pair<int, int>(IntMin, value - 1);
}
case Relation_LessOrEqual:
return std::pair<int, int>(IntMin, value);
default:
throw std::logic_error("InfoSelectWrapper: relation does not have a range");
}
}
std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange() const
{
const float FloatMax = std::numeric_limits<float>::infinity();
const float FloatMin = -std::numeric_limits<float>::infinity();
const float Epsilon = std::numeric_limits<float>::epsilon();
const std::pair<float, float> InvalidRange(FloatMax, FloatMin);
float value = mConstSelect.mValue.getFloat();
switch (mRelationType)
{
case Relation_Equal:
case Relation_NotEqual:
return std::pair<float, float>(value, value);
case Relation_Greater:
return std::pair<float, float>(value + Epsilon, FloatMax);
case Relation_GreaterOrEqual:
return std::pair<float, float>(value, FloatMax);
case Relation_Less:
return std::pair<float, float>(FloatMin, value - Epsilon);
case Relation_LessOrEqual:
return std::pair<float, float>(FloatMin, value);
default:
throw std::logic_error("InfoSelectWrapper: given relation does not have a range");
}
}
std::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getValidIntRange() const
{
const int IntMax = std::numeric_limits<int>::max();
const int IntMin = std::numeric_limits<int>::min();
switch (mFunctionName)
{
// Boolean
case Function_NotId:
case Function_NotFaction:
case Function_NotClass:
case Function_NotRace:
case Function_NotCell:
case Function_NotLocal:
case Function_PcExpelled:
case Function_PcCommonDisease:
case Function_PcBlightDisease:
case Function_SameSex:
case Function_SameRace:
case Function_SameFaction:
case Function_Detected:
case Function_Alarmed:
case Function_PcCorpus:
case Function_PcVampire:
case Function_Attacked:
case Function_TalkedToPc:
case Function_ShouldAttack:
case Function_Werewolf:
return std::pair<int, int>(0, 1);
// Integer
case Function_RankLow:
case Function_RankHigh:
case Function_Reputation:
case Function_PcReputation:
case Function_Journal:
return std::pair<int, int>(IntMin, IntMax);
case Function_Item:
case Function_Dead:
case Function_PcLevel:
case Function_PcStrength:
case Function_PcBlock:
case Function_PcArmorer:
case Function_PcMediumArmor:
case Function_PcHeavyArmor:
case Function_PcBluntWeapon:
case Function_PcLongBlade:
case Function_PcAxe:
case Function_PcSpear:
case Function_PcAthletics:
case Function_PcEnchant:
case Function_PcDestruction:
case Function_PcAlteration:
case Function_PcIllusion:
case Function_PcConjuration:
case Function_PcMysticism:
case Function_PcRestoration:
case Function_PcAlchemy:
case Function_PcUnarmored:
case Function_PcSecurity:
case Function_PcSneak:
case Function_PcAcrobatics:
case Function_PcLightArmor:
case Function_PcShortBlade:
case Function_PcMarksman:
case Function_PcMerchantile:
case Function_PcSpeechcraft:
case Function_PcHandToHand:
case Function_PcClothingModifier:
case Function_PcCrimeLevel:
case Function_Choice:
case Function_PcIntelligence:
case Function_PcWillpower:
case Function_PcAgility:
case Function_PcSpeed:
case Function_PcEndurance:
case Function_PcPersonality:
case Function_PcLuck:
case Function_Level:
case Function_PcWerewolfKills:
return std::pair<int, int>(0, IntMax);
case Function_Fight:
case Function_Hello:
case Function_Alarm:
case Function_Flee:
return std::pair<int, int>(0, 100);
case Function_Weather:
return std::pair<int, int>(0, 9);
case Function_FriendHit:
return std::pair<int, int>(0, 4);
case Function_RankRequirement:
return std::pair<int, int>(0, 3);
case Function_CreatureTarget:
return std::pair<int, int>(0, 2);
case Function_PcGender:
return std::pair<int, int>(0, 1);
case Function_FactionRankDifference:
return std::pair<int, int>(-9, 9);
// Numeric
case Function_Global:
case Function_Local:
return std::pair<int, int>(IntMin, IntMax);
case Function_PcMagicka:
case Function_PcFatigue:
case Function_PcHealth:
return std::pair<int, int>(0, IntMax);
case Function_Health_Percent:
case Function_PcHealthPercent:
return std::pair<int, int>(0, 100);
default:
throw std::runtime_error("InfoSelectWrapper: function does not exist");
}
}
std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getValidFloatRange() const
{
const float FloatMax = std::numeric_limits<float>::infinity();
const float FloatMin = -std::numeric_limits<float>::infinity();
switch (mFunctionName)
{
// Numeric
case Function_Global:
case Function_Local:
case Function_NotLocal:
return std::pair<float, float>(FloatMin, FloatMax);
case Function_PcMagicka:
case Function_PcFatigue:
case Function_PcHealth:
return std::pair<float, float>(0, FloatMax);
case Function_Health_Percent:
case Function_PcHealthPercent:
return std::pair<float, float>(0, 100);
default:
throw std::runtime_error("InfoSelectWrapper: function does not exist or is not numeric");
}
}
template <typename T1, typename T2>
bool CSMWorld::ConstInfoSelectWrapper::rangeContains(T1 value, std::pair<T2,T2> range) const
{
return (value >= range.first && value <= range.second);
}
template <typename T1, typename T2>
bool CSMWorld::ConstInfoSelectWrapper::rangeFullyContains(std::pair<T1,T1> containingRange,
std::pair<T2,T2> testRange) const
{
return (containingRange.first <= testRange.first) && (testRange.second <= containingRange.second);
}
template <typename T1, typename T2>
bool CSMWorld::ConstInfoSelectWrapper::rangesOverlap(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const
{
// One of the bounds of either range should fall within the other range
return
(range1.first <= range2.first && range2.first <= range1.second) ||
(range1.first <= range2.second && range2.second <= range1.second) ||
(range2.first <= range1.first && range1.first <= range2.second) ||
(range2.first <= range1.second && range1.second <= range2.second);
}
template <typename T1, typename T2>
bool CSMWorld::ConstInfoSelectWrapper::rangesMatch(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const
{
return (range1.first == range2.first && range1.second == range2.second);
}
template <typename T1, typename T2>
bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue(std::pair<T1,T1> conditionRange,
std::pair<T2,T2> validRange) const
{
switch (mRelationType)
{
case Relation_Equal:
return false;
case Relation_NotEqual:
// If value is not within range, it will always be true
return !rangeContains(conditionRange.first, validRange);
case Relation_Greater:
case Relation_GreaterOrEqual:
case Relation_Less:
case Relation_LessOrEqual:
// If the valid range is completely within the condition range, it will always be true
return rangeFullyContains(conditionRange, validRange);
default:
throw std::logic_error("InfoCondition: operator can not be used to compare");
}
return false;
}
template <typename T1, typename T2>
bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue(std::pair<T1,T1> conditionRange,
std::pair<T2,T2> validRange) const
{
switch (mRelationType)
{
case Relation_Equal:
return !rangeContains(conditionRange.first, validRange);
case Relation_NotEqual:
return false;
case Relation_Greater:
case Relation_GreaterOrEqual:
case Relation_Less:
case Relation_LessOrEqual:
// If ranges do not overlap, it will never be true
return !rangesOverlap(conditionRange, validRange);
default:
throw std::logic_error("InfoCondition: operator can not be used to compare");
}
return false;
}
// InfoSelectWrapper
CSMWorld::InfoSelectWrapper::InfoSelectWrapper(ESM::DialInfo::SelectStruct& select)
: CSMWorld::ConstInfoSelectWrapper(select), mSelect(select)
{
}
void CSMWorld::InfoSelectWrapper::setFunctionName(FunctionName name)
{
mFunctionName = name;
updateHasVariable();
updateComparisonType();
}
void CSMWorld::InfoSelectWrapper::setRelationType(RelationType type)
{
mRelationType = type;
}
void CSMWorld::InfoSelectWrapper::setVariableName(const std::string& name)
{
mVariableName = name;
}
void CSMWorld::InfoSelectWrapper::setDefaults()
{
if (!variantTypeIsValid())
mSelect.mValue.setType(ESM::VT_Int);
switch (mComparisonType)
{
case Comparison_Boolean:
setRelationType(Relation_Equal);
mSelect.mValue.setInteger(1);
break;
case Comparison_Integer:
case Comparison_Numeric:
setRelationType(Relation_Greater);
mSelect.mValue.setInteger(0);
break;
default:
// Do nothing
break;
}
update();
}
void CSMWorld::InfoSelectWrapper::update()
{
std::ostringstream stream;
// Leading 0
stream << '0';
// Write Function
bool writeIndex = false;
size_t functionIndex = static_cast<size_t>(mFunctionName);
switch (mFunctionName)
{
case Function_None: stream << '0'; break;
case Function_Global: stream << '2'; break;
case Function_Local: stream << '3'; break;
case Function_Journal: stream << '4'; break;
case Function_Item: stream << '5'; break;
case Function_Dead: stream << '6'; break;
case Function_NotId: stream << '7'; break;
case Function_NotFaction: stream << '8'; break;
case Function_NotClass: stream << '9'; break;
case Function_NotRace: stream << 'A'; break;
case Function_NotCell: stream << 'B'; break;
case Function_NotLocal: stream << 'C'; break;
default: stream << '1'; writeIndex = true; break;
}
if (writeIndex && functionIndex < 10) // leading 0
stream << '0' << functionIndex;
else if (writeIndex)
stream << functionIndex;
else
stream << "00";
// Write Relation
switch (mRelationType)
{
case Relation_Equal: stream << '0'; break;
case Relation_NotEqual: stream << '1'; break;
case Relation_Greater: stream << '2'; break;
case Relation_GreaterOrEqual: stream << '3'; break;
case Relation_Less: stream << '4'; break;
case Relation_LessOrEqual: stream << '5'; break;
default: stream << '0'; break;
}
if (mHasVariable)
stream << mVariableName;
mSelect.mSelectRule = stream.str();
}
ESM::Variant& CSMWorld::InfoSelectWrapper::getVariant()
{
return mSelect.mValue;
}

@ -0,0 +1,243 @@
#ifndef CSM_WORLD_INFOSELECTWRAPPER_H
#define CSM_WORLD_INFOSELECTWRAPPER_H
#include <components/esm/loadinfo.hpp>
namespace CSMWorld
{
// ESM::DialInfo::SelectStruct.mSelectRule
// 012345...
// ^^^ ^^
// ||| ||
// ||| |+------------- condition variable string
// ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc
// ||+---------------- function index (encoded, where function == '1')
// |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc
// +------------------ unknown
//
// Wrapper for DialInfo::SelectStruct
class ConstInfoSelectWrapper
{
public:
// Order matters
enum FunctionName
{
Function_RankLow=0,
Function_RankHigh,
Function_RankRequirement,
Function_Reputation,
Function_Health_Percent,
Function_PcReputation,
Function_PcLevel,
Function_PcHealthPercent,
Function_PcMagicka,
Function_PcFatigue,
Function_PcStrength,
Function_PcBlock,
Function_PcArmorer,
Function_PcMediumArmor,
Function_PcHeavyArmor,
Function_PcBluntWeapon,
Function_PcLongBlade,
Function_PcAxe,
Function_PcSpear,
Function_PcAthletics,
Function_PcEnchant,
Function_PcDestruction,
Function_PcAlteration,
Function_PcIllusion,
Function_PcConjuration,
Function_PcMysticism,
Function_PcRestoration,
Function_PcAlchemy,
Function_PcUnarmored,
Function_PcSecurity,
Function_PcSneak,
Function_PcAcrobatics,
Function_PcLightArmor,
Function_PcShortBlade,
Function_PcMarksman,
Function_PcMerchantile,
Function_PcSpeechcraft,
Function_PcHandToHand,
Function_PcGender,
Function_PcExpelled,
Function_PcCommonDisease,
Function_PcBlightDisease,
Function_PcClothingModifier,
Function_PcCrimeLevel,
Function_SameSex,
Function_SameRace,
Function_SameFaction,
Function_FactionRankDifference,
Function_Detected,
Function_Alarmed,
Function_Choice,
Function_PcIntelligence,
Function_PcWillpower,
Function_PcAgility,
Function_PcSpeed,
Function_PcEndurance,
Function_PcPersonality,
Function_PcLuck,
Function_PcCorpus,
Function_Weather,
Function_PcVampire,
Function_Level,
Function_Attacked,
Function_TalkedToPc,
Function_PcHealth,
Function_CreatureTarget,
Function_FriendHit,
Function_Fight,
Function_Hello,
Function_Alarm,
Function_Flee,
Function_ShouldAttack,
Function_Werewolf,
Function_PcWerewolfKills=73,
Function_Global,
Function_Local,
Function_Journal,
Function_Item,
Function_Dead,
Function_NotId,
Function_NotFaction,
Function_NotClass,
Function_NotRace,
Function_NotCell,
Function_NotLocal,
Function_None
};
enum RelationType
{
Relation_Equal,
Relation_NotEqual,
Relation_Greater,
Relation_GreaterOrEqual,
Relation_Less,
Relation_LessOrEqual,
Relation_None
};
enum ComparisonType
{
Comparison_Boolean,
Comparison_Integer,
Comparison_Numeric,
Comparison_None
};
static const size_t RuleMinSize;
static const size_t FunctionPrefixOffset;
static const size_t FunctionIndexOffset;
static const size_t RelationIndexOffset;
static const size_t VarNameOffset;
static const char* FunctionEnumStrings[];
static const char* RelationEnumStrings[];
static const char* ComparisonEnumStrings[];
static std::string convertToString(FunctionName name);
static std::string convertToString(RelationType type);
static std::string convertToString(ComparisonType type);
ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select);
FunctionName getFunctionName() const;
RelationType getRelationType() const;
ComparisonType getComparisonType() const;
bool hasVariable() const;
const std::string& getVariableName() const;
bool conditionIsAlwaysTrue() const;
bool conditionIsNeverTrue() const;
bool variantTypeIsValid() const;
const ESM::Variant& getVariant() const;
std::string toString() const;
protected:
void readRule();
void readFunctionName();
void readRelationType();
void readVariableName();
void updateHasVariable();
void updateComparisonType();
std::pair<int, int> getConditionIntRange() const;
std::pair<float, float> getConditionFloatRange() const;
std::pair<int, int> getValidIntRange() const;
std::pair<float, float> getValidFloatRange() const;
template <typename Type1, typename Type2>
bool rangeContains(Type1 value, std::pair<Type2,Type2> range) const;
template <typename Type1, typename Type2>
bool rangesOverlap(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;
template <typename Type1, typename Type2>
bool rangeFullyContains(std::pair<Type1,Type1> containing, std::pair<Type2,Type2> test) const;
template <typename Type1, typename Type2>
bool rangesMatch(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;
template <typename Type1, typename Type2>
bool conditionIsAlwaysTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;
template <typename Type1, typename Type2>
bool conditionIsNeverTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;
FunctionName mFunctionName;
RelationType mRelationType;
ComparisonType mComparisonType;
bool mHasVariable;
std::string mVariableName;
private:
const ESM::DialInfo::SelectStruct& mConstSelect;
};
// Wrapper for DialInfo::SelectStruct that can modify the wrapped select struct
class InfoSelectWrapper : public ConstInfoSelectWrapper
{
public:
InfoSelectWrapper(ESM::DialInfo::SelectStruct& select);
// Wrapped SelectStruct will not be modified until update() is called
void setFunctionName(FunctionName name);
void setRelationType(RelationType type);
void setVariableName(const std::string& name);
// Modified wrapped SelectStruct
void update();
// This sets properties based on the function name to its defaults and updates the wrapped object
void setDefaults();
ESM::Variant& getVariant();
private:
ESM::DialInfo::SelectStruct& mSelect;
void writeRule();
};
}
#endif

@ -6,6 +6,7 @@
#include "idcollection.hpp" #include "idcollection.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "info.hpp" #include "info.hpp"
#include "infoselectwrapper.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -26,20 +27,8 @@ namespace CSMWorld
point.mConnectionNum = 0; point.mConnectionNum = 0;
point.mUnknown = 0; point.mUnknown = 0;
// inserting a point should trigger re-indexing of the edges
//
// FIXME: does not auto refresh edges table view
std::vector<ESM::Pathgrid::Edge>::iterator iter = pathgrid.mEdges.begin();
for (;iter != pathgrid.mEdges.end(); ++iter)
{
if ((*iter).mV0 >= position)
(*iter).mV0++;
if ((*iter).mV1 >= position)
(*iter).mV1++;
}
points.insert(points.begin()+position, point); points.insert(points.begin()+position, point);
pathgrid.mData.mS2 += 1; // increment the number of points pathgrid.mData.mS2 = pathgrid.mPoints.size();
record.setModified (pathgrid); record.setModified (pathgrid);
} }
@ -53,28 +42,10 @@ namespace CSMWorld
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (points.size())) if (rowToRemove < 0 || rowToRemove >= static_cast<int> (points.size()))
throw std::runtime_error ("index out of range"); throw std::runtime_error ("index out of range");
// deleting a point should trigger re-indexing of the edges // Do not remove dangling edges, does not work with current undo mechanism
// dangling edges are not allowed and hence removed // Do not automatically adjust indices, what would be done with dangling edges?
//
// FIXME: does not auto refresh edges table view
std::vector<ESM::Pathgrid::Edge>::iterator iter = pathgrid.mEdges.begin();
for (; iter != pathgrid.mEdges.end();)
{
if (((*iter).mV0 == rowToRemove) || ((*iter).mV1 == rowToRemove))
iter = pathgrid.mEdges.erase(iter);
else
{
if ((*iter).mV0 > rowToRemove)
(*iter).mV0--;
if ((*iter).mV1 > rowToRemove)
(*iter).mV1--;
++iter;
}
}
points.erase(points.begin()+rowToRemove); points.erase(points.begin()+rowToRemove);
pathgrid.mData.mS2 -= 1; // decrement the number of points pathgrid.mData.mS2 = pathgrid.mPoints.size();
record.setModified (pathgrid); record.setModified (pathgrid);
} }
@ -83,14 +54,8 @@ namespace CSMWorld
const NestedTableWrapperBase& nestedTable) const const NestedTableWrapperBase& nestedTable) const
{ {
Pathgrid pathgrid = record.get(); Pathgrid pathgrid = record.get();
pathgrid.mPoints = static_cast<const NestedTableWrapper<ESM::Pathgrid::PointList> &>(nestedTable).mNestedTable;
pathgrid.mPoints = pathgrid.mData.mS2 = pathgrid.mPoints.size();
static_cast<const PathgridPointsWrap &>(nestedTable).mRecord.mPoints;
pathgrid.mData.mS2 =
static_cast<const PathgridPointsWrap &>(nestedTable).mRecord.mData.mS2;
// also update edges in case points were added/removed
pathgrid.mEdges =
static_cast<const PathgridPointsWrap &>(nestedTable).mRecord.mEdges;
record.setModified (pathgrid); record.setModified (pathgrid);
} }
@ -98,7 +63,7 @@ namespace CSMWorld
NestedTableWrapperBase* PathgridPointListAdapter::table(const Record<Pathgrid>& record) const NestedTableWrapperBase* PathgridPointListAdapter::table(const Record<Pathgrid>& record) const
{ {
// deleted by dtor of NestedTableStoring // deleted by dtor of NestedTableStoring
return new PathgridPointsWrap(record.get()); return new NestedTableWrapper<ESM::Pathgrid::PointList>(record.get().mPoints);
} }
QVariant PathgridPointListAdapter::getData(const Record<Pathgrid>& record, QVariant PathgridPointListAdapter::getData(const Record<Pathgrid>& record,
@ -146,7 +111,6 @@ namespace CSMWorld
PathgridEdgeListAdapter::PathgridEdgeListAdapter () {} PathgridEdgeListAdapter::PathgridEdgeListAdapter () {}
// ToDo: seems to be auto-sorted in the dialog table display after insertion
void PathgridEdgeListAdapter::addRow(Record<Pathgrid>& record, int position) const void PathgridEdgeListAdapter::addRow(Record<Pathgrid>& record, int position) const
{ {
Pathgrid pathgrid = record.get(); Pathgrid pathgrid = record.get();
@ -217,7 +181,6 @@ namespace CSMWorld
} }
} }
// ToDo: detect duplicates in mEdges
void PathgridEdgeListAdapter::setData(Record<Pathgrid>& record, void PathgridEdgeListAdapter::setData(Record<Pathgrid>& record,
const QVariant& value, int subRowIndex, int subColIndex) const const QVariant& value, int subRowIndex, int subColIndex) const
{ {
@ -277,7 +240,7 @@ namespace CSMWorld
// WARNING: Assumed that the table view has the same order as std::map // WARNING: Assumed that the table view has the same order as std::map
std::map<std::string, int>::iterator iter = reactions.begin(); std::map<std::string, int>::iterator iter = reactions.begin();
for(int i = 0; i < rowToRemove; ++i) for(int i = 0; i < rowToRemove; ++i)
iter++; ++iter;
reactions.erase(iter); reactions.erase(iter);
record.setModified (faction); record.setModified (faction);
@ -314,7 +277,7 @@ namespace CSMWorld
// WARNING: Assumed that the table view has the same order as std::map // WARNING: Assumed that the table view has the same order as std::map
std::map<std::string, int>::const_iterator iter = reactions.begin(); std::map<std::string, int>::const_iterator iter = reactions.begin();
for(int i = 0; i < subRowIndex; ++i) for(int i = 0; i < subRowIndex; ++i)
iter++; ++iter;
switch (subColIndex) switch (subColIndex)
{ {
case 0: return QString((*iter).first.c_str()); case 0: return QString((*iter).first.c_str());
@ -337,7 +300,7 @@ namespace CSMWorld
// WARNING: Assumed that the table view has the same order as std::map // WARNING: Assumed that the table view has the same order as std::map
std::map<std::string, int>::iterator iter = reactions.begin(); std::map<std::string, int>::iterator iter = reactions.begin();
for(int i = 0; i < subRowIndex; ++i) for(int i = 0; i < subRowIndex; ++i)
iter++; ++iter;
std::string factionId = (*iter).first; std::string factionId = (*iter).first;
int reaction = (*iter).second; int reaction = (*iter).second;
@ -529,16 +492,6 @@ namespace CSMWorld
return 1; // fixed at size 1 return 1; // fixed at size 1
} }
// ESM::DialInfo::SelectStruct.mSelectRule
// 012345...
// ^^^ ^^
// ||| ||
// ||| |+------------- condition variable string
// ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc
// ||+---------------- function index (encoded, where function == '1')
// |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc
// +------------------ unknown
//
InfoConditionAdapter::InfoConditionAdapter () {} InfoConditionAdapter::InfoConditionAdapter () {}
void InfoConditionAdapter::addRow(Record<Info>& record, int position) const void InfoConditionAdapter::addRow(Record<Info>& record, int position) const
@ -547,11 +500,11 @@ namespace CSMWorld
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects; std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
// blank row // default row
ESM::DialInfo::SelectStruct condStruct; ESM::DialInfo::SelectStruct condStruct;
condStruct.mSelectRule = "00000"; condStruct.mSelectRule = "01000";
condStruct.mValue = ESM::Variant(); condStruct.mValue = ESM::Variant();
condStruct.mValue.setType(ESM::VT_Int); // default to ints condStruct.mValue.setType(ESM::VT_Int);
conditions.insert(conditions.begin()+position, condStruct); conditions.insert(conditions.begin()+position, condStruct);
@ -589,89 +542,6 @@ namespace CSMWorld
return new NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct> >(record.get().mSelects); return new NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct> >(record.get().mSelects);
} }
// See the mappings in MWDialogue::SelectWrapper::getArgument
// from ESM::Attribute, ESM::Skill and MWMechanics::CreatureStats (for AI)
static std::map<const std::string, std::string> populateEncToInfoFunc()
{
std::map<const std::string, std::string> funcMap;
funcMap["00"] = "Rank Low";
funcMap["01"] = "Rank High";
funcMap["02"] = "Rank Requirement";
funcMap["03"] = "Reputation";
funcMap["04"] = "Health Percent";
funcMap["05"] = "PC Reputation";
funcMap["06"] = "PC Level";
funcMap["07"] = "PC Health Percent";
funcMap["08"] = "PC Magicka";
funcMap["09"] = "PC Fatigue";
funcMap["10"] = "PC Strength";
funcMap["11"] = "PC Block";
funcMap["12"] = "PC Armorer";
funcMap["13"] = "PC Medium Armor";
funcMap["14"] = "PC Heavy Armor";
funcMap["15"] = "PC Blunt Weapon";
funcMap["16"] = "PC Long Blade";
funcMap["17"] = "PC Axe";
funcMap["18"] = "PC Spear";
funcMap["19"] = "PC Athletics";
funcMap["20"] = "PC Enchant";
funcMap["21"] = "PC Destruction";
funcMap["22"] = "PC Alteration";
funcMap["23"] = "PC Illusion";
funcMap["24"] = "PC Conjuration";
funcMap["25"] = "PC Mysticism";
funcMap["26"] = "PC Restoration";
funcMap["27"] = "PC Alchemy";
funcMap["28"] = "PC Unarmored";
funcMap["29"] = "PC Security";
funcMap["30"] = "PC Sneak";
funcMap["31"] = "PC Acrobatics";
funcMap["32"] = "PC Light Armor";
funcMap["33"] = "PC Short Blade";
funcMap["34"] = "PC Marksman";
funcMap["35"] = "PC Merchantile";
funcMap["36"] = "PC Speechcraft";
funcMap["37"] = "PC Hand To Hand";
funcMap["38"] = "PC Sex";
funcMap["39"] = "PC Expelled";
funcMap["40"] = "PC Common Disease";
funcMap["41"] = "PC Blight Disease";
funcMap["42"] = "PC Clothing Modifier";
funcMap["43"] = "PC Crime Level";
funcMap["44"] = "Same Sex";
funcMap["45"] = "Same Race";
funcMap["46"] = "Same Faction";
funcMap["47"] = "Faction Rank Difference";
funcMap["48"] = "Detected";
funcMap["49"] = "Alarmed";
funcMap["50"] = "Choice";
funcMap["51"] = "PC Intelligence";
funcMap["52"] = "PC Willpower";
funcMap["53"] = "PC Agility";
funcMap["54"] = "PC Speed";
funcMap["55"] = "PC Endurance";
funcMap["56"] = "PC Personality";
funcMap["57"] = "PC Luck";
funcMap["58"] = "PC Corpus";
funcMap["59"] = "Weather";
funcMap["60"] = "PC Vampire";
funcMap["61"] = "Level";
funcMap["62"] = "Attacked";
funcMap["63"] = "Talked To PC";
funcMap["64"] = "PC Health";
funcMap["65"] = "Creature Target";
funcMap["66"] = "Friend Hit";
funcMap["67"] = "Fight";
funcMap["68"] = "Hello";
funcMap["69"] = "Alarm";
funcMap["70"] = "Flee";
funcMap["71"] = "Should Attack";
funcMap["72"] = "Werewolf";
funcMap["73"] = "PC Werewolf Kills";
return funcMap;
}
static const std::map<const std::string, std::string> sEncToInfoFunc = populateEncToInfoFunc();
QVariant InfoConditionAdapter::getData(const Record<Info>& record, QVariant InfoConditionAdapter::getData(const Record<Info>& record,
int subRowIndex, int subColIndex) const int subRowIndex, int subColIndex) const
{ {
@ -682,70 +552,36 @@ namespace CSMWorld
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size())) if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
throw std::runtime_error ("index out of range"); throw std::runtime_error ("index out of range");
ConstInfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);
switch (subColIndex) switch (subColIndex)
{ {
case 0: case 0:
{ {
char condType = conditions[subRowIndex].mSelectRule[1]; return infoSelectWrapper.getFunctionName();
switch (condType)
{
case '0': return 0; // blank space
case '1': return 1; // Function
case '2': return 2; // Global
case '3': return 3; // Local
case '4': return 4; // Journal
case '5': return 5; // Item
case '6': return 6; // Dead
case '7': return 7; // Not ID
case '8': return 8; // Not Factio
case '9': return 9; // Not Class
case 'A': return 10; // Not Race
case 'B': return 11; // Not Cell
case 'C': return 12; // Not Local
default: return QVariant(); // TODO: log an error?
}
} }
case 1: case 1:
{ {
if (conditions[subRowIndex].mSelectRule[1] == '1') if (infoSelectWrapper.hasVariable())
{ return QString(infoSelectWrapper.getVariableName().c_str());
// throws an exception if the encoding is not found
return sEncToInfoFunc.at(conditions[subRowIndex].mSelectRule.substr(2, 2)).c_str();
}
else else
return QString(conditions[subRowIndex].mSelectRule.substr(5).c_str()); return "";
} }
case 2: case 2:
{ {
char compType = conditions[subRowIndex].mSelectRule[4]; return infoSelectWrapper.getRelationType();
switch (compType)
{
case '0': return 3; // =
case '1': return 0; // !=
case '2': return 4; // >
case '3': return 5; // >=
case '4': return 1; // <
case '5': return 2; // <=
default: return QVariant(); // TODO: log an error?
}
} }
case 3: case 3:
{ {
switch (conditions[subRowIndex].mValue.getType()) switch (infoSelectWrapper.getVariant().getType())
{ {
case ESM::VT_String:
{
return QString::fromUtf8 (conditions[subRowIndex].mValue.getString().c_str());
}
case ESM::VT_Int: case ESM::VT_Int:
case ESM::VT_Short:
case ESM::VT_Long:
{ {
return conditions[subRowIndex].mValue.getInteger(); return infoSelectWrapper.getVariant().getInteger();
} }
case ESM::VT_Float: case ESM::VT_Float:
{ {
return conditions[subRowIndex].mValue.getFloat(); return infoSelectWrapper.getVariant().getFloat();
} }
default: return QVariant(); default: return QVariant();
} }
@ -764,101 +600,63 @@ namespace CSMWorld
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size())) if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
throw std::runtime_error ("index out of range"); throw std::runtime_error ("index out of range");
InfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);
bool conversionResult = false;
switch (subColIndex) switch (subColIndex)
{ {
case 0: case 0: // Function
{ {
// See sInfoCondFunc in columns.cpp for the enum values infoSelectWrapper.setFunctionName(static_cast<ConstInfoSelectWrapper::FunctionName>(value.toInt()));
switch (value.toInt())
if (infoSelectWrapper.getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric &&
infoSelectWrapper.getVariant().getType() != ESM::VT_Int)
{ {
// FIXME: when these change the values of the other columns need to change infoSelectWrapper.getVariant().setType(ESM::VT_Int);
// correspondingly (and automatically)
case 1:
{
conditions[subRowIndex].mSelectRule[1] = '1'; // Function
// default to "Rank Low"
conditions[subRowIndex].mSelectRule[2] = '0';
conditions[subRowIndex].mSelectRule[3] = '0';
break;
}
case 2: conditions[subRowIndex].mSelectRule[1] = '2'; break; // Global
case 3: conditions[subRowIndex].mSelectRule[1] = '3'; break; // Local
case 4: conditions[subRowIndex].mSelectRule[1] = '4'; break; // Journal
case 5: conditions[subRowIndex].mSelectRule[1] = '5'; break; // Item
case 6: conditions[subRowIndex].mSelectRule[1] = '6'; break; // Dead
case 7: conditions[subRowIndex].mSelectRule[1] = '7'; break; // Not ID
case 8: conditions[subRowIndex].mSelectRule[1] = '8'; break; // Not Faction
case 9: conditions[subRowIndex].mSelectRule[1] = '9'; break; // Not Class
case 10: conditions[subRowIndex].mSelectRule[1] = 'A'; break; // Not Race
case 11: conditions[subRowIndex].mSelectRule[1] = 'B'; break; // Not Cell
case 12: conditions[subRowIndex].mSelectRule[1] = 'C'; break; // Not Local
default: return; // return without saving
} }
infoSelectWrapper.update();
break; break;
} }
case 1: case 1: // Variable
{ {
if (conditions[subRowIndex].mSelectRule[1] == '1') infoSelectWrapper.setVariableName(value.toString().toUtf8().constData());
{ infoSelectWrapper.update();
std::map<const std::string, std::string>::const_iterator it = sEncToInfoFunc.begin();
for (;it != sEncToInfoFunc.end(); ++it)
{
if (it->second == value.toString().toUtf8().constData())
{
std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 2);
rule.append(it->first);
// leave old values for undo (NOTE: may not be vanilla's behaviour)
rule.append(conditions[subRowIndex].mSelectRule.substr(4));
conditions[subRowIndex].mSelectRule = rule;
break;
}
}
if (it == sEncToInfoFunc.end())
return; // return without saving; TODO: maybe log an error here
}
else
{
// FIXME: validate the string values before saving, based on the current function
std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 5);
conditions[subRowIndex].mSelectRule = rule.append(value.toString().toUtf8().constData());
}
break; break;
} }
case 2: case 2: // Relation
{ {
// See sInfoCondComp in columns.cpp for the enum values infoSelectWrapper.setRelationType(static_cast<ConstInfoSelectWrapper::RelationType>(value.toInt()));
switch (value.toInt()) infoSelectWrapper.update();
{
case 0: conditions[subRowIndex].mSelectRule[4] = '1'; break; // !=
case 1: conditions[subRowIndex].mSelectRule[4] = '4'; break; // <
case 2: conditions[subRowIndex].mSelectRule[4] = '5'; break; // <=
case 3: conditions[subRowIndex].mSelectRule[4] = '0'; break; // =
case 4: conditions[subRowIndex].mSelectRule[4] = '2'; break; // >
case 5: conditions[subRowIndex].mSelectRule[4] = '3'; break; // >=
default: return; // return without saving
}
break; break;
} }
case 3: case 3: // Value
{ {
switch (conditions[subRowIndex].mValue.getType()) switch (infoSelectWrapper.getComparisonType())
{ {
case ESM::VT_String: case ConstInfoSelectWrapper::Comparison_Numeric:
{
conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData());
break;
}
case ESM::VT_Int:
case ESM::VT_Short:
case ESM::VT_Long:
{ {
conditions[subRowIndex].mValue.setInteger (value.toInt()); // QVariant seems to have issues converting 0
if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0)
{
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
infoSelectWrapper.getVariant().setInteger(value.toInt());
}
else if (value.toFloat(&conversionResult) && conversionResult)
{
infoSelectWrapper.getVariant().setType(ESM::VT_Float);
infoSelectWrapper.getVariant().setFloat(value.toFloat());
}
break; break;
} }
case ESM::VT_Float: case ConstInfoSelectWrapper::Comparison_Boolean:
case ConstInfoSelectWrapper::Comparison_Integer:
{ {
conditions[subRowIndex].mValue.setFloat (value.toFloat()); if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0)
{
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
infoSelectWrapper.getVariant().setInteger(value.toInt());
}
break; break;
} }
default: break; default: break;
@ -1079,7 +877,7 @@ namespace CSMWorld
cell.mAmbi.mFogDensity : QVariant(QVariant::UserType); cell.mAmbi.mFogDensity : QVariant(QVariant::UserType);
case 5: case 5:
{ {
if (isInterior && !behaveLikeExterior && interiorWater) if (isInterior && interiorWater)
return cell.mWater; return cell.mWater;
else else
return QVariant(QVariant::UserType); return QVariant(QVariant::UserType);
@ -1145,7 +943,7 @@ namespace CSMWorld
} }
case 5: case 5:
{ {
if (isInterior && !behaveLikeExterior && interiorWater) if (isInterior && interiorWater)
cell.mWater = value.toFloat(); cell.mWater = value.toFloat();
else else
return; // return without saving return; // return without saving
@ -1191,4 +989,105 @@ namespace CSMWorld
{ {
return 1; // fixed at size 1 return 1; // fixed at size 1
} }
RegionWeatherAdapter::RegionWeatherAdapter () {}
void RegionWeatherAdapter::addRow(Record<ESM::Region>& record, int position) const
{
throw std::logic_error ("cannot add a row to a fixed table");
}
void RegionWeatherAdapter::removeRow(Record<ESM::Region>& record, int rowToRemove) const
{
throw std::logic_error ("cannot remove a row from a fixed table");
}
void RegionWeatherAdapter::setTable(Record<ESM::Region>& record, const NestedTableWrapperBase& nestedTable) const
{
throw std::logic_error ("table operation not supported");
}
NestedTableWrapperBase* RegionWeatherAdapter::table(const Record<ESM::Region>& record) const
{
throw std::logic_error ("table operation not supported");
}
QVariant RegionWeatherAdapter::getData(const Record<ESM::Region>& record, int subRowIndex, int subColIndex) const
{
const char* WeatherNames[] = {
"Clear",
"Cloudy",
"Fog",
"Overcast",
"Rain",
"Thunder",
"Ash",
"Blight",
"Snow",
"Blizzard"
};
const ESM::Region& region = record.get();
if (subColIndex == 0 && subRowIndex >= 0 && subRowIndex < 10)
{
return WeatherNames[subRowIndex];
}
else if (subColIndex == 1)
{
switch (subRowIndex)
{
case 0: return region.mData.mClear;
case 1: return region.mData.mCloudy;
case 2: return region.mData.mFoggy;
case 3: return region.mData.mOvercast;
case 4: return region.mData.mRain;
case 5: return region.mData.mThunder;
case 6: return region.mData.mAsh;
case 7: return region.mData.mBlight;
case 8: return region.mData.mA; // Snow
case 9: return region.mData.mB; // Blizzard
default: break;
}
}
throw std::runtime_error("index out of range");
}
void RegionWeatherAdapter::setData(Record<ESM::Region>& record, const QVariant& value, int subRowIndex,
int subColIndex) const
{
ESM::Region region = record.get();
unsigned char chance = static_cast<unsigned char>(value.toInt());
if (subColIndex == 1)
{
switch (subRowIndex)
{
case 0: region.mData.mClear = chance; break;
case 1: region.mData.mCloudy = chance; break;
case 2: region.mData.mFoggy = chance; break;
case 3: region.mData.mOvercast = chance; break;
case 4: region.mData.mRain = chance; break;
case 5: region.mData.mThunder = chance; break;
case 6: region.mData.mAsh = chance; break;
case 7: region.mData.mBlight = chance; break;
case 8: region.mData.mA = chance; break;
case 9: region.mData.mB = chance; break;
default: throw std::runtime_error("index out of range");
}
record.setModified (region);
}
}
int RegionWeatherAdapter::getColumnsCount(const Record<ESM::Region>& record) const
{
return 2;
}
int RegionWeatherAdapter::getRowsCount(const Record<ESM::Region>& record) const
{
return 10;
}
} }

@ -25,21 +25,6 @@ namespace CSMWorld
struct Pathgrid; struct Pathgrid;
struct Info; struct Info;
struct PathgridPointsWrap : public NestedTableWrapperBase
{
ESM::Pathgrid mRecord;
PathgridPointsWrap(ESM::Pathgrid pathgrid)
: mRecord(pathgrid) {}
virtual ~PathgridPointsWrap() {}
virtual int size() const
{
return mRecord.mPoints.size(); // used in IdTree::setNestedTable()
}
};
class PathgridPointListAdapter : public NestedColumnAdapter<Pathgrid> class PathgridPointListAdapter : public NestedColumnAdapter<Pathgrid>
{ {
public: public:
@ -540,6 +525,31 @@ namespace CSMWorld
virtual int getRowsCount(const Record<CSMWorld::Cell>& record) const; virtual int getRowsCount(const Record<CSMWorld::Cell>& record) const;
}; };
class RegionWeatherAdapter : public NestedColumnAdapter<ESM::Region>
{
public:
RegionWeatherAdapter ();
virtual void addRow(Record<ESM::Region>& record, int position) const;
virtual void removeRow(Record<ESM::Region>& record, int rowToRemove) const;
virtual void setTable(Record<ESM::Region>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<ESM::Region>& record) const;
virtual QVariant getData(const Record<ESM::Region>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<ESM::Region>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<ESM::Region>& record) const;
virtual int getRowsCount(const Record<ESM::Region>& record) const;
};
} }
#endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H

@ -2,7 +2,11 @@
#include <cmath> #include <cmath>
CSMWorld::CellRef::CellRef() #include <sstream>
#include "cellcoordinates.hpp"
CSMWorld::CellRef::CellRef() : mNew (true)
{ {
mRefNum.mIndex = 0; mRefNum.mIndex = 0;
mRefNum.mContentFile = 0; mRefNum.mContentFile = 0;
@ -10,8 +14,5 @@ CSMWorld::CellRef::CellRef()
std::pair<int, int> CSMWorld::CellRef::getCellIndex() const std::pair<int, int> CSMWorld::CellRef::getCellIndex() const
{ {
const int cellSize = 8192; return CellCoordinates::coordinatesToCellIndex (mPos.pos[0], mPos.pos[1]);
return std::make_pair (
std::floor (mPos.pos[0]/cellSize), std::floor (mPos.pos[1]/cellSize));
} }

@ -13,6 +13,7 @@ namespace CSMWorld
std::string mId; std::string mId;
std::string mCell; std::string mCell;
std::string mOriginalCell; std::string mOriginalCell;
bool mNew; // new reference, not counted yet, ref num not assigned yet
CellRef(); CellRef();

@ -19,6 +19,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
Cell& cell2 = base ? cell.mBase : cell.mModified; Cell& cell2 = base ? cell.mBase : cell.mModified;
CellRef ref; CellRef ref;
ref.mNew = false;
ESM::MovedCellRef mref; ESM::MovedCellRef mref;
bool isDeleted = false; bool isDeleted = false;

@ -108,7 +108,7 @@ void CSMWorld::IngredEffectRefIdAdapter::setNestedTable (const RefIdColumn* colu
ESM::Ingredient ingredient = record.get(); ESM::Ingredient ingredient = record.get();
ingredient.mData = ingredient.mData =
static_cast<const NestedTableWrapper<std::vector<typename ESM::Ingredient::IRDTstruct> >&>(nestedTable).mNestedTable.at(0); static_cast<const NestedTableWrapper<std::vector<ESM::Ingredient::IRDTstruct> >&>(nestedTable).mNestedTable.at(0);
record.setModified (ingredient); record.setModified (ingredient);
} }
@ -120,11 +120,11 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::IngredEffectRefIdAdapter::nestedTabl
static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType))); static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
// return the whole struct // return the whole struct
std::vector<typename ESM::Ingredient::IRDTstruct> wrap; std::vector<ESM::Ingredient::IRDTstruct> wrap;
wrap.push_back(record.get().mData); wrap.push_back(record.get().mData);
// deleted by dtor of NestedTableStoring // deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<typename ESM::Ingredient::IRDTstruct> >(wrap); return new NestedTableWrapper<std::vector<ESM::Ingredient::IRDTstruct> >(wrap);
} }
QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData (const RefIdColumn *column, QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData (const RefIdColumn *column,
@ -1129,7 +1129,7 @@ void CSMWorld::CreatureAttributesRefIdAdapter::setNestedTable (const RefIdColumn
// store the whole struct // store the whole struct
creature.mData = creature.mData =
static_cast<const NestedTableWrapper<std::vector<typename ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0); static_cast<const NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0);
record.setModified (creature); record.setModified (creature);
} }
@ -1141,10 +1141,10 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttributesRefIdAdapter::nest
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
// return the whole struct // return the whole struct
std::vector<typename ESM::Creature::NPDTstruct> wrap; std::vector<ESM::Creature::NPDTstruct> wrap;
wrap.push_back(record.get().mData); wrap.push_back(record.get().mData);
// deleted by dtor of NestedTableStoring // deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<typename ESM::Creature::NPDTstruct> >(wrap); return new NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> >(wrap);
} }
QVariant CSMWorld::CreatureAttributesRefIdAdapter::getNestedData (const RefIdColumn *column, QVariant CSMWorld::CreatureAttributesRefIdAdapter::getNestedData (const RefIdColumn *column,
@ -1153,7 +1153,7 @@ QVariant CSMWorld::CreatureAttributesRefIdAdapter::getNestedData (const RefIdCol
const Record<ESM::Creature>& record = const Record<ESM::Creature>& record =
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
const ESM::Creature creature = record.get(); const ESM::Creature& creature = record.get();
if (subColIndex == 0) if (subColIndex == 0)
return subRowIndex; return subRowIndex;
@ -1235,7 +1235,7 @@ void CSMWorld::CreatureAttackRefIdAdapter::setNestedTable (const RefIdColumn* co
// store the whole struct // store the whole struct
creature.mData = creature.mData =
static_cast<const NestedTableWrapper<std::vector<typename ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0); static_cast<const NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0);
record.setModified (creature); record.setModified (creature);
} }
@ -1247,10 +1247,10 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttackRefIdAdapter::nestedTa
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
// return the whole struct // return the whole struct
std::vector<typename ESM::Creature::NPDTstruct> wrap; std::vector<ESM::Creature::NPDTstruct> wrap;
wrap.push_back(record.get().mData); wrap.push_back(record.get().mData);
// deleted by dtor of NestedTableStoring // deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<typename ESM::Creature::NPDTstruct> >(wrap); return new NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> >(wrap);
} }
QVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn *column, QVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn *column,
@ -1259,7 +1259,7 @@ QVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn
const Record<ESM::Creature>& record = const Record<ESM::Creature>& record =
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
const ESM::Creature creature = record.get(); const ESM::Creature& creature = record.get();
if (subRowIndex < 0 || subRowIndex > 2 || subColIndex < 0 || subColIndex > 2) if (subRowIndex < 0 || subRowIndex > 2 || subColIndex < 0 || subColIndex > 2)
throw std::runtime_error ("index out of range"); throw std::runtime_error ("index out of range");
@ -1337,7 +1337,7 @@ QVariant CSMWorld::CreatureMiscRefIdAdapter::getNestedData (const RefIdColumn *c
const Record<ESM::Creature>& record = const Record<ESM::Creature>& record =
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
const ESM::Creature creature = record.get(); const ESM::Creature& creature = record.get();
switch (subColIndex) switch (subColIndex)
{ {
@ -1440,26 +1440,28 @@ void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData
Record<ESM::Weapon>& record = static_cast<Record<ESM::Weapon>&> ( Record<ESM::Weapon>& record = static_cast<Record<ESM::Weapon>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon)));
ESM::Weapon weapon = record.get();
if (column==mColumns.mType) if (column==mColumns.mType)
record.get().mData.mType = value.toInt(); weapon.mData.mType = value.toInt();
else if (column==mColumns.mHealth) else if (column==mColumns.mHealth)
record.get().mData.mHealth = value.toInt(); weapon.mData.mHealth = value.toInt();
else if (column==mColumns.mSpeed) else if (column==mColumns.mSpeed)
record.get().mData.mSpeed = value.toFloat(); weapon.mData.mSpeed = value.toFloat();
else if (column==mColumns.mReach) else if (column==mColumns.mReach)
record.get().mData.mReach = value.toFloat(); weapon.mData.mReach = value.toFloat();
else if (column==mColumns.mChop[0]) else if (column==mColumns.mChop[0])
record.get().mData.mChop[0] = value.toInt(); weapon.mData.mChop[0] = value.toInt();
else if (column==mColumns.mChop[1]) else if (column==mColumns.mChop[1])
record.get().mData.mChop[1] = value.toInt(); weapon.mData.mChop[1] = value.toInt();
else if (column==mColumns.mSlash[0]) else if (column==mColumns.mSlash[0])
record.get().mData.mSlash[0] = value.toInt(); weapon.mData.mSlash[0] = value.toInt();
else if (column==mColumns.mSlash[1]) else if (column==mColumns.mSlash[1])
record.get().mData.mSlash[1] = value.toInt(); weapon.mData.mSlash[1] = value.toInt();
else if (column==mColumns.mThrust[0]) else if (column==mColumns.mThrust[0])
record.get().mData.mThrust[0] = value.toInt(); weapon.mData.mThrust[0] = value.toInt();
else if (column==mColumns.mThrust[1]) else if (column==mColumns.mThrust[1])
record.get().mData.mThrust[1] = value.toInt(); weapon.mData.mThrust[1] = value.toInt();
else else
{ {
std::map<const RefIdColumn *, unsigned int>::const_iterator iter = std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
@ -1468,11 +1470,16 @@ void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData
if (iter!=mColumns.mFlags.end()) if (iter!=mColumns.mFlags.end())
{ {
if (value.toInt()!=0) if (value.toInt()!=0)
record.get().mData.mFlags |= iter->second; weapon.mData.mFlags |= iter->second;
else else
record.get().mData.mFlags &= ~iter->second; weapon.mData.mFlags &= ~iter->second;
} }
else else
{
EnchantableRefIdAdapter<ESM::Weapon>::setData (column, data, index, value); EnchantableRefIdAdapter<ESM::Weapon>::setData (column, data, index, value);
return; // Don't overwrite changes made by base class
}
} }
record.setModified(weapon);
} }

@ -1187,7 +1187,7 @@ namespace CSMWorld
std::vector<ESM::ContItem>& list = container.mInventory.mList; std::vector<ESM::ContItem>& list = container.mInventory.mList;
ESM::ContItem newRow = {0, {""}}; ESM::ContItem newRow = ESM::ContItem();
if (position >= (int)list.size()) if (position >= (int)list.size())
list.push_back(newRow); list.push_back(newRow);

@ -21,7 +21,7 @@ namespace CSMWorld
/// ///
/// This class provides way to construct mimedata object holding the universalid copy /// This class provides way to construct mimedata object holding the universalid copy
/// Universalid is used in the majority of the tables to store type, id, argument types. /// Universalid is used in the majority of the tables to store type, id, argument types.
/// This way universalid grants a way to retrive record from the concrete table. /// This way universalid grants a way to retrieve record from the concrete table.
/// Please note, that tablemimedata object can hold multiple universalIds in the vector. /// Please note, that tablemimedata object can hold multiple universalIds in the vector.
class TableMimeData : public QMimeData class TableMimeData : public QMimeData

@ -14,9 +14,6 @@ Q_DECLARE_METATYPE (boost::filesystem::path)
#include "ui_filedialog.h" #include "ui_filedialog.h"
class DataFilesModel;
class PluginsProxyModel;
namespace ContentSelectorView namespace ContentSelectorView
{ {
class ContentSelector; class ContentSelector;

@ -47,7 +47,7 @@ void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id)
void CSVDoc::SubView::closeEvent (QCloseEvent *event) void CSVDoc::SubView::closeEvent (QCloseEvent *event)
{ {
emit updateSubViewIndicies (this); emit updateSubViewIndices (this);
} }
std::string CSVDoc::SubView::getTitle() const std::string CSVDoc::SubView::getTitle() const

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

@ -343,7 +343,7 @@ void CSVDoc::View::updateTitle()
setWindowTitle (QString::fromUtf8(stream.str().c_str())); setWindowTitle (QString::fromUtf8(stream.str().c_str()));
} }
void CSVDoc::View::updateSubViewIndicies(SubView *view) void CSVDoc::View::updateSubViewIndices(SubView *view)
{ {
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
@ -373,7 +373,7 @@ void CSVDoc::View::updateSubViewIndicies(SubView *view)
else else
{ {
delete subView->titleBarWidget(); delete subView->titleBarWidget();
subView->setTitleBarWidget (0); subView->setTitleBarWidget (NULL);
} }
} }
} }
@ -402,7 +402,7 @@ void CSVDoc::View::updateActions()
CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
mViewTotal (totalViews), mScroll(0), mScrollbarOnly(false) mViewTotal (totalViews), mScroll(NULL), mScrollbarOnly(false)
{ {
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
@ -419,10 +419,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
} }
else else
{ {
mScroll = new QScrollArea(this); createScrollArea();
mScroll->setWidgetResizable(true);
mScroll->setWidget(&mSubViewWindow);
setCentralWidget(mScroll);
} }
mOperations = new Operations; mOperations = new Operations;
@ -570,36 +567,11 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
// //
mScrollbarOnly = windows["mainwindow-scrollbar"].toString() == "Scrollbar Only"; mScrollbarOnly = windows["mainwindow-scrollbar"].toString() == "Scrollbar Only";
QDesktopWidget *dw = QApplication::desktop(); updateWidth(windows["grow-limit"].isTrue(), minWidth);
QRect rect;
if (windows["grow-limit"].isTrue())
rect = dw->screenGeometry(this);
else
rect = dw->screenGeometry(dw->screen(dw->screenNumber(this)));
if (!mScrollbarOnly && mScroll && mSubViews.size() > 1)
{
int newWidth = width()+minWidth;
int frameWidth = frameGeometry().width() - width();
if (newWidth+frameWidth <= rect.width())
{
resize(newWidth, height());
// WARNING: below code assumes that new subviews are added to the right
if (x() > rect.width()-(newWidth+frameWidth))
move(rect.width()-(newWidth+frameWidth), y()); // shift left to stay within the screen
}
else
{
// full width
resize(rect.width()-frameWidth, height());
mSubViewWindow.setMinimumWidth(mSubViewWindow.width()+minWidth);
move(0, y());
}
}
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
updateSubViewIndicies(); updateSubViewIndices();
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this, connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this,
SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&))); SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&)));
@ -608,8 +580,8 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
connect (view, SIGNAL (updateTitle()), this, SLOT (updateTitle())); connect (view, SIGNAL (updateTitle()), this, SLOT (updateTitle()));
connect (view, SIGNAL (updateSubViewIndicies (SubView *)), connect (view, SIGNAL (updateSubViewIndices (SubView *)),
this, SLOT (updateSubViewIndicies (SubView *))); this, SLOT (updateSubViewIndices (SubView *)));
view->show(); view->show();
@ -631,7 +603,7 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max)
void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
{ {
if (*setting=="Windows/hide-subview") if (*setting=="Windows/hide-subview")
updateSubViewIndicies (0); updateSubViewIndices (NULL);
else if (*setting=="Windows/mainwindow-scrollbar") else if (*setting=="Windows/mainwindow-scrollbar")
{ {
if (setting->toString()!="Grow Only") if (setting->toString()!="Grow Only")
@ -651,10 +623,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
} }
else else
{ {
mScroll = new QScrollArea(this); createScrollArea();
mScroll->setWidgetResizable(true);
mScroll->setWidget(&mSubViewWindow);
setCentralWidget(mScroll);
} }
} }
else if (mScroll) else if (mScroll)
@ -662,7 +631,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
mScroll->takeWidget(); mScroll->takeWidget();
setCentralWidget (&mSubViewWindow); setCentralWidget (&mSubViewWindow);
mScroll->deleteLater(); mScroll->deleteLater();
mScroll = 0; mScroll = NULL;
} }
} }
} }
@ -959,3 +928,41 @@ void CSVDoc::View::merge()
{ {
emit mergeDocument (mDocument); emit mergeDocument (mDocument);
} }
void CSVDoc::View::updateWidth(bool isGrowLimit, int minSubViewWidth)
{
QDesktopWidget *dw = QApplication::desktop();
QRect rect;
if (isGrowLimit)
rect = dw->screenGeometry(this);
else
rect = dw->screenGeometry(dw->screen(dw->screenNumber(this)));
if (!mScrollbarOnly && mScroll && mSubViews.size() > 1)
{
int newWidth = width()+minSubViewWidth;
int frameWidth = frameGeometry().width() - width();
if (newWidth+frameWidth <= rect.width())
{
resize(newWidth, height());
// WARNING: below code assumes that new subviews are added to the right
if (x() > rect.width()-(newWidth+frameWidth))
move(rect.width()-(newWidth+frameWidth), y()); // shift left to stay within the screen
}
else
{
// full width
resize(rect.width()-frameWidth, height());
mSubViewWindow.setMinimumWidth(mSubViewWindow.width()+minSubViewWidth);
move(0, y());
}
}
}
void CSVDoc::View::createScrollArea()
{
mScroll = new QScrollArea(this);
mScroll->setWidgetResizable(true);
mScroll->setWidget(&mSubViewWindow);
setCentralWidget(mScroll);
}

@ -95,7 +95,8 @@ namespace CSVDoc
void resizeViewHeight (int height); void resizeViewHeight (int height);
void updateScrollbar(); void updateScrollbar();
void updateWidth(bool isGrowLimit, int minSubViewWidth);
void createScrollArea();
public: public:
View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews);
@ -143,7 +144,7 @@ namespace CSVDoc
void updateTitle(); void updateTitle();
// called when subviews are added or removed // called when subviews are added or removed
void updateSubViewIndicies (SubView *view = 0); void updateSubViewIndices (SubView *view = NULL);
private slots: private slots:

@ -0,0 +1,650 @@
#include "cameracontroller.hpp"
#include <cmath>
#include <QKeyEvent>
#include <osg/BoundingBox>
#include <osg/Camera>
#include <osg/ComputeBoundsVisitor>
#include <osg/Drawable>
#include <osg/Group>
#include <osg/Matrixd>
#include <osg/Quat>
#include <osgUtil/LineSegmentIntersector>
namespace CSVRender
{
/*
Camera Controller
*/
const osg::Vec3d CameraController::WorldUp = osg::Vec3d(0, 0, 1);
const osg::Vec3d CameraController::LocalUp = osg::Vec3d(0, 1, 0);
const osg::Vec3d CameraController::LocalLeft = osg::Vec3d(1, 0, 0);
const osg::Vec3d CameraController::LocalForward = osg::Vec3d(0, 0, 1);
CameraController::CameraController()
: mActive(false)
, mInverted(false)
, mCameraSensitivity(1/650.f)
, mSecondaryMoveMult(50)
, mWheelMoveMult(8)
, mCamera(NULL)
{
}
CameraController::~CameraController()
{
}
bool CameraController::isActive() const
{
return mActive;
}
osg::Camera* CameraController::getCamera() const
{
return mCamera;
}
double CameraController::getCameraSensitivity() const
{
return mCameraSensitivity;
}
bool CameraController::getInverted() const
{
return mInverted;
}
double CameraController::getSecondaryMovementMultiplier() const
{
return mSecondaryMoveMult;
}
double CameraController::getWheelMovementMultiplier() const
{
return mWheelMoveMult;
}
void CameraController::setCamera(osg::Camera* camera)
{
mCamera = camera;
mActive = (mCamera != NULL);
if (mActive)
onActivate();
}
void CameraController::setCameraSensitivity(double value)
{
mCameraSensitivity = value;
}
void CameraController::setInverted(bool value)
{
mInverted = value;
}
void CameraController::setSecondaryMovementMultiplier(double value)
{
mSecondaryMoveMult = value;
}
void CameraController::setWheelMovementMultiplier(double value)
{
mWheelMoveMult = value;
}
void CameraController::setup(osg::Group* root, unsigned int mask, const osg::Vec3d& up)
{
// Find World bounds
osg::ComputeBoundsVisitor boundsVisitor;
osg::BoundingBox& boundingBox = boundsVisitor.getBoundingBox();
boundsVisitor.setTraversalMask(mask);
root->accept(boundsVisitor);
if (!boundingBox.valid())
{
// Try again without any mask
boundsVisitor.reset();
boundsVisitor.setTraversalMask(~0);
root->accept(boundsVisitor);
// Last resort, set a default
if (!boundingBox.valid())
{
boundingBox.set(-1, -1, -1, 1, 1, 1);
}
}
// Calculate a good starting position
osg::Vec3d minBounds = boundingBox.corner(0) - boundingBox.center();
osg::Vec3d maxBounds = boundingBox.corner(7) - boundingBox.center();
osg::Vec3d camOffset = up * maxBounds > 0 ? maxBounds : minBounds;
camOffset *= 2;
osg::Vec3d eye = camOffset + boundingBox.center();
osg::Vec3d center = boundingBox.center();
getCamera()->setViewMatrixAsLookAt(eye, center, up);
}
/*
Free Camera Controller
*/
FreeCameraController::FreeCameraController()
: mLockUpright(false)
, mModified(false)
, mFast(false)
, mLeft(false)
, mRight(false)
, mForward(false)
, mBackward(false)
, mRollLeft(false)
, mRollRight(false)
, mUp(LocalUp)
, mLinSpeed(1000)
, mRotSpeed(osg::PI / 2)
, mSpeedMult(8)
{
}
double FreeCameraController::getLinearSpeed() const
{
return mLinSpeed;
}
double FreeCameraController::getRotationalSpeed() const
{
return mRotSpeed;
}
double FreeCameraController::getSpeedMultiplier() const
{
return mSpeedMult;
}
void FreeCameraController::setLinearSpeed(double value)
{
mLinSpeed = value;
}
void FreeCameraController::setRotationalSpeed(double value)
{
mRotSpeed = value;
}
void FreeCameraController::setSpeedMultiplier(double value)
{
mSpeedMult = value;
}
void FreeCameraController::fixUpAxis(const osg::Vec3d& up)
{
mLockUpright = true;
mUp = up;
mModified = true;
}
void FreeCameraController::unfixUpAxis()
{
mLockUpright = false;
}
bool FreeCameraController::handleKeyEvent(QKeyEvent* event, bool pressed)
{
if (!isActive())
return false;
if (event->key() == Qt::Key_Q)
{
mRollLeft = pressed;
}
else if (event->key() == Qt::Key_E)
{
mRollRight = pressed;
}
else if (event->key() == Qt::Key_A)
{
mLeft = pressed;
}
else if (event->key() == Qt::Key_D)
{
mRight = pressed;
}
else if (event->key() == Qt::Key_W)
{
mForward = pressed;
}
else if (event->key() == Qt::Key_S)
{
mBackward = pressed;
}
else if (event->key() == Qt::Key_Shift)
{
mFast = pressed;
}
else
{
return false;
}
return true;
}
bool FreeCameraController::handleMouseMoveEvent(std::string mode, int x, int y)
{
if (!isActive())
return false;
if (mode == "p-navi")
{
double scalar = getCameraSensitivity() * (getInverted() ? -1.0 : 1.0);
yaw(x * scalar);
pitch(y * scalar);
}
else if (mode == "s-navi")
{
osg::Vec3d movement;
movement += LocalLeft * -x * getSecondaryMovementMultiplier();
movement += LocalUp * y * getSecondaryMovementMultiplier();
translate(movement);
}
else if (mode == "t-navi")
{
translate(LocalForward * x * (mFast ? getWheelMovementMultiplier() : 1));
}
else
{
return false;
}
return true;
}
void FreeCameraController::update(double dt)
{
if (!isActive())
return;
double linDist = mLinSpeed * dt;
double rotDist = mRotSpeed * dt;
if (mFast)
linDist *= mSpeedMult;
if (mLeft)
translate(LocalLeft * linDist);
if (mRight)
translate(LocalLeft * -linDist);
if (mForward)
translate(LocalForward * linDist);
if (mBackward)
translate(LocalForward * -linDist);
if (!mLockUpright)
{
if (mRollLeft)
roll(-rotDist);
if (mRollRight)
roll(rotDist);
}
else if(mModified)
{
stabilize();
mModified = false;
}
// Normalize the matrix to counter drift
getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix());
}
void FreeCameraController::resetInput()
{
mFast = false;
mLeft = false;
mRight = false;
mForward = false;
mBackward = false;
mRollLeft = false;
mRollRight = false;
}
void FreeCameraController::yaw(double value)
{
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalUp);
mModified = true;
}
void FreeCameraController::pitch(double value)
{
const double Constraint = osg::PI / 2 - 0.1;
if (mLockUpright)
{
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d forward = center - eye;
osg::Vec3d left = up ^ forward;
double pitchAngle = std::acos(up * mUp);
if ((mUp ^ up) * left < 0)
pitchAngle *= -1;
if (std::abs(pitchAngle + value) > Constraint)
value = (pitchAngle > 0 ? 1 : -1) * Constraint - pitchAngle;
}
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalLeft);
mModified = true;
}
void FreeCameraController::roll(double value)
{
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward);
mModified = true;
}
void FreeCameraController::translate(const osg::Vec3d& offset)
{
getCamera()->getViewMatrix() *= osg::Matrixd::translate(offset);
mModified = true;
}
void FreeCameraController::stabilize()
{
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up);
getCamera()->setViewMatrixAsLookAt(eye, center, mUp);
}
/*
Orbit Camera Controller
*/
OrbitCameraController::OrbitCameraController()
: mInitialized(false)
, mFast(false)
, mLeft(false)
, mRight(false)
, mUp(false)
, mDown(false)
, mRollLeft(false)
, mRollRight(false)
, mPickingMask(~0)
, mCenter(0,0,0)
, mDistance(0)
, mOrbitSpeed(osg::PI / 4)
, mOrbitSpeedMult(4)
{
}
osg::Vec3d OrbitCameraController::getCenter() const
{
return mCenter;
}
double OrbitCameraController::getOrbitSpeed() const
{
return mOrbitSpeed;
}
double OrbitCameraController::getOrbitSpeedMultiplier() const
{
return mOrbitSpeedMult;
}
unsigned int OrbitCameraController::getPickingMask() const
{
return mPickingMask;
}
void OrbitCameraController::setCenter(const osg::Vec3d& value)
{
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up);
mCenter = value;
mDistance = (eye - mCenter).length();
getCamera()->setViewMatrixAsLookAt(eye, mCenter, up);
mInitialized = true;
}
void OrbitCameraController::setOrbitSpeed(double value)
{
mOrbitSpeed = value;
}
void OrbitCameraController::setOrbitSpeedMultiplier(double value)
{
mOrbitSpeedMult = value;
}
void OrbitCameraController::setPickingMask(unsigned int value)
{
mPickingMask = value;
}
bool OrbitCameraController::handleKeyEvent(QKeyEvent* event, bool pressed)
{
if (!isActive())
return false;
if (!mInitialized)
initialize();
if (event->key() == Qt::Key_Q)
{
mRollLeft = pressed;
}
else if (event->key() == Qt::Key_E)
{
mRollRight = pressed;
}
else if (event->key() == Qt::Key_A)
{
mLeft = pressed;
}
else if (event->key() == Qt::Key_D)
{
mRight = pressed;
}
else if (event->key() == Qt::Key_W)
{
mUp = pressed;
}
else if (event->key() == Qt::Key_S)
{
mDown = pressed;
}
else if (event->key() == Qt::Key_Shift)
{
mFast = pressed;
}
else
{
return false;
}
return true;
}
bool OrbitCameraController::handleMouseMoveEvent(std::string mode, int x, int y)
{
if (!isActive())
return false;
if (!mInitialized)
initialize();
if (mode == "p-navi")
{
double scalar = getCameraSensitivity() * (getInverted() ? -1.0 : 1.0);
rotateHorizontal(x * scalar);
rotateVertical(-y * scalar);
}
else if (mode == "s-navi")
{
osg::Vec3d movement;
movement += LocalLeft * x * getSecondaryMovementMultiplier();
movement += LocalUp * -y * getSecondaryMovementMultiplier();
translate(movement);
}
else if (mode == "t-navi")
{
zoom(-x * (mFast ? getWheelMovementMultiplier() : 1));
}
else
{
return false;
}
return true;
}
void OrbitCameraController::update(double dt)
{
if (!isActive())
return;
if (!mInitialized)
initialize();
double rotDist = mOrbitSpeed * dt;
if (mFast)
rotDist *= mOrbitSpeedMult;
if (mLeft)
rotateHorizontal(-rotDist);
if (mRight)
rotateHorizontal(rotDist);
if (mUp)
rotateVertical(rotDist);
if (mDown)
rotateVertical(-rotDist);
if (mRollLeft)
roll(-rotDist);
if (mRollRight)
roll(rotDist);
// Normalize the matrix to counter drift
getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix());
}
void OrbitCameraController::resetInput()
{
mFast = false;
mLeft = false;
mRight =false;
mUp = false;
mDown = false;
mRollLeft = false;
mRollRight = false;
}
void OrbitCameraController::onActivate()
{
mInitialized = false;
}
void OrbitCameraController::initialize()
{
static const int DefaultStartDistance = 10000.f;
// Try to intelligently pick focus object
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(
osgUtil::Intersector::PROJECTION, osg::Vec3d(0, 0, 0), LocalForward));
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);
osgUtil::IntersectionVisitor visitor(intersector);
visitor.setTraversalMask(mPickingMask);
getCamera()->accept(visitor);
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up, DefaultStartDistance);
if (intersector->getIntersections().begin() != intersector->getIntersections().end())
{
mCenter = intersector->getIntersections().begin()->getWorldIntersectPoint();
mDistance = (eye - mCenter).length();
}
else
{
mCenter = center;
mDistance = DefaultStartDistance;
}
mInitialized = true;
}
void OrbitCameraController::rotateHorizontal(double value)
{
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Quat rotation = osg::Quat(value, up);
osg::Vec3d oldOffset = eye - mCenter;
osg::Vec3d newOffset = rotation * oldOffset;
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);
}
void OrbitCameraController::rotateVertical(double value)
{
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d forward = center - eye;
osg::Quat rotation = osg::Quat(value, up ^ forward);
osg::Vec3d oldOffset = eye - mCenter;
osg::Vec3d newOffset = rotation * oldOffset;
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);
}
void OrbitCameraController::roll(double value)
{
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward);
}
void OrbitCameraController::translate(const osg::Vec3d& offset)
{
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d newOffset = getCamera()->getViewMatrix().getRotate().inverse() * offset;
mCenter += newOffset;
eye += newOffset;
getCamera()->setViewMatrixAsLookAt(eye, mCenter, up);
}
void OrbitCameraController::zoom(double value)
{
mDistance = std::max(10., mDistance + value);
osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up, 1.f);
osg::Vec3d offset = (eye - center) * mDistance;
getCamera()->setViewMatrixAsLookAt(mCenter + offset, mCenter, up);
}
}

@ -0,0 +1,158 @@
#ifndef OPENCS_VIEW_CAMERACONTROLLER_H
#define OPENCS_VIEW_CAMERACONTROLLER_H
#include <string>
#include <osg/ref_ptr>
#include <osg/Vec3d>
class QKeyEvent;
namespace osg
{
class Camera;
class Group;
}
namespace CSVRender
{
class CameraController
{
public:
static const osg::Vec3d WorldUp;
static const osg::Vec3d LocalUp;
static const osg::Vec3d LocalLeft;
static const osg::Vec3d LocalForward;
CameraController();
virtual ~CameraController();
bool isActive() const;
osg::Camera* getCamera() const;
double getCameraSensitivity() const;
bool getInverted() const;
double getSecondaryMovementMultiplier() const;
double getWheelMovementMultiplier() const;
void setCamera(osg::Camera*);
void setCameraSensitivity(double value);
void setInverted(bool value);
void setSecondaryMovementMultiplier(double value);
void setWheelMovementMultiplier(double value);
// moves the camera to an intelligent position
void setup(osg::Group* root, unsigned int mask, const osg::Vec3d& up);
virtual bool handleKeyEvent(QKeyEvent* event, bool pressed) = 0;
virtual bool handleMouseMoveEvent(std::string mode, int x, int y) = 0;
virtual void update(double dt) = 0;
virtual void resetInput() = 0;
protected:
virtual void onActivate(){}
private:
bool mActive, mInverted;
double mCameraSensitivity;
double mSecondaryMoveMult;
double mWheelMoveMult;
osg::Camera* mCamera;
};
class FreeCameraController : public CameraController
{
public:
FreeCameraController();
double getLinearSpeed() const;
double getRotationalSpeed() const;
double getSpeedMultiplier() const;
void setLinearSpeed(double value);
void setRotationalSpeed(double value);
void setSpeedMultiplier(double value);
void fixUpAxis(const osg::Vec3d& up);
void unfixUpAxis();
bool handleKeyEvent(QKeyEvent* event, bool pressed);
bool handleMouseMoveEvent(std::string mode, int x, int y);
void update(double dt);
void resetInput();
private:
void yaw(double value);
void pitch(double value);
void roll(double value);
void translate(const osg::Vec3d& offset);
void stabilize();
bool mLockUpright, mModified;
bool mFast, mLeft, mRight, mForward, mBackward, mRollLeft, mRollRight;
osg::Vec3d mUp;
double mLinSpeed;
double mRotSpeed;
double mSpeedMult;
};
class OrbitCameraController : public CameraController
{
public:
OrbitCameraController();
osg::Vec3d getCenter() const;
double getOrbitSpeed() const;
double getOrbitSpeedMultiplier() const;
unsigned int getPickingMask() const;
void setCenter(const osg::Vec3d& center);
void setOrbitSpeed(double value);
void setOrbitSpeedMultiplier(double value);
void setPickingMask(unsigned int value);
bool handleKeyEvent(QKeyEvent* event, bool pressed);
bool handleMouseMoveEvent(std::string mode, int x, int y);
void update(double dt);
void resetInput();
private:
void onActivate();
void initialize();
void rotateHorizontal(double value);
void rotateVertical(double value);
void roll(double value);
void translate(const osg::Vec3d& offset);
void zoom(double value);
bool mInitialized;
bool mFast, mLeft, mRight, mUp, mDown, mRollLeft, mRollRight;
unsigned int mPickingMask;
osg::Vec3d mCenter;
double mDistance;
double mOrbitSpeed;
double mOrbitSpeedMult;
};
}
#endif

@ -1,9 +1,14 @@
#include "cell.hpp" #include "cell.hpp"
#include <osg/PositionAttitudeTransform>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group> #include <osg/Group>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadcell.hpp>
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include <components/sceneutil/pathgridutil.hpp>
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
@ -11,7 +16,8 @@
#include "../../model/world/refcollection.hpp" #include "../../model/world/refcollection.hpp"
#include "../../model/world/cellcoordinates.hpp" #include "../../model/world/cellcoordinates.hpp"
#include "elements.hpp" #include "mask.hpp"
#include "pathgrid.hpp"
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
bool CSVRender::Cell::removeObject (const std::string& id) bool CSVRender::Cell::removeObject (const std::string& id)
@ -22,11 +28,18 @@ bool CSVRender::Cell::removeObject (const std::string& id)
if (iter==mObjects.end()) if (iter==mObjects.end())
return false; return false;
delete iter->second; removeObject (iter);
mObjects.erase (iter);
return true; return true;
} }
std::map<std::string, CSVRender::Object *>::iterator CSVRender::Cell::removeObject (
std::map<std::string, Object *>::iterator iter)
{
delete iter->second;
mObjects.erase (iter++);
return iter;
}
bool CSVRender::Cell::addObjects (int start, int end) bool CSVRender::Cell::addObjects (int start, int end)
{ {
bool modified = false; bool modified = false;
@ -43,7 +56,12 @@ bool CSVRender::Cell::addObjects (int start, int end)
{ {
std::string id = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mId); std::string id = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mId);
mObjects.insert (std::make_pair (id, new Object (mData, mCellNode, id, false))); std::auto_ptr<Object> object (new Object (mData, mCellNode, id, false));
if (mSubModeElementMask & Mask_Reference)
object->setSubMode (mSubMode);
mObjects.insert (std::make_pair (id, object.release()));
modified = true; modified = true;
} }
} }
@ -53,7 +71,8 @@ bool CSVRender::Cell::addObjects (int start, int end)
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) : mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0),
mSubModeElementMask (0)
{ {
std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id); std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id);
@ -63,6 +82,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
mCellNode = new osg::Group; mCellNode = new osg::Group;
rootNode->addChild(mCellNode); rootNode->addChild(mCellNode);
setCellMarker();
if (!mDeleted) if (!mDeleted)
{ {
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
@ -80,11 +101,16 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
if (esmLand.getLandData (ESM::Land::DATA_VHGT)) if (esmLand.getLandData (ESM::Land::DATA_VHGT))
{ {
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Element_Terrain<<1)); mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Mask_Terrain));
mTerrain->loadCell(esmLand.mX, mTerrain->loadCell(esmLand.mX,
esmLand.mY); esmLand.mY);
mCellBorder.reset(new CellBorder(mCellNode, mCoordinates));
mCellBorder->buildShape(esmLand);
} }
} }
mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates));
} }
} }
@ -97,6 +123,11 @@ CSVRender::Cell::~Cell()
mCellNode->getParent(0)->removeChild(mCellNode); mCellNode->getParent(0)->removeChild(mCellNode);
} }
CSVRender::Pathgrid* CSVRender::Cell::getPathgrid() const
{
return mPathgrid.get();
}
bool CSVRender::Cell::referenceableDataChanged (const QModelIndex& topLeft, bool CSVRender::Cell::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight) const QModelIndex& bottomRight)
{ {
@ -161,8 +192,8 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
// perform update and remove where needed // perform update and remove where needed
bool modified = false; bool modified = false;
for (std::map<std::string, Object *>::iterator iter (mObjects.begin()); std::map<std::string, Object *>::iterator iter = mObjects.begin();
iter!=mObjects.end(); ++iter) while (iter!=mObjects.end())
{ {
if (iter->second->referenceDataChanged (topLeft, bottomRight)) if (iter->second->referenceDataChanged (topLeft, bottomRight))
modified = true; modified = true;
@ -171,23 +202,30 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
if (iter2!=ids.end()) if (iter2!=ids.end())
{ {
if (iter2->second) bool deleted = iter2->second;
ids.erase (iter2);
if (deleted)
{ {
removeObject (iter->first); iter = removeObject (iter);
modified = true; modified = true;
continue;
} }
ids.erase (iter2);
} }
++iter;
} }
// add new objects // add new objects
for (std::map<std::string, bool>::iterator iter (ids.begin()); iter!=ids.end(); ++iter) for (std::map<std::string, bool>::iterator iter (ids.begin()); iter!=ids.end(); ++iter)
{ {
mObjects.insert (std::make_pair ( if (!iter->second)
iter->first, new Object (mData, mCellNode, iter->first, false))); {
mObjects.insert (std::make_pair (
iter->first, new Object (mData, mCellNode, iter->first, false)));
modified = true; modified = true;
}
} }
return modified; return modified;
@ -228,9 +266,19 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
return addObjects (start, end); return addObjects (start, end);
} }
void CSVRender::Cell::pathgridModified()
{
mPathgrid->recreateGeometry();
}
void CSVRender::Cell::pathgridRemoved()
{
mPathgrid->removeGeometry();
}
void CSVRender::Cell::setSelection (int elementMask, Selection mode) void CSVRender::Cell::setSelection (int elementMask, Selection mode)
{ {
if (elementMask & Element_Reference) if (elementMask & Mask_Reference)
{ {
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)
@ -247,6 +295,49 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode)
iter->second->setSelected (selected); iter->second->setSelected (selected);
} }
} }
if (elementMask & Mask_Pathgrid)
{
// Only one pathgrid may be selected, so some operations will only have an effect
// if the pathgrid is already focused
switch (mode)
{
case Selection_Clear:
mPathgrid->clearSelected();
break;
case Selection_All:
if (mPathgrid->isSelected())
mPathgrid->selectAll();
break;
case Selection_Invert:
if (mPathgrid->isSelected())
mPathgrid->invertSelected();
break;
}
}
}
void CSVRender::Cell::selectAllWithSameParentId (int elementMask)
{
std::set<std::string> ids;
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
{
if (iter->second->getSelected())
ids.insert (iter->second->getReferenceableId());
}
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
{
if (!iter->second->getSelected() &&
ids.find (iter->second->getReferenceableId())!=ids.end())
{
iter->second->setSelected (true);
}
}
} }
void CSVRender::Cell::setCellArrows (int mask) void CSVRender::Cell::setCellArrows (int mask)
@ -267,6 +358,24 @@ void CSVRender::Cell::setCellArrows (int mask)
} }
} }
void CSVRender::Cell::setCellMarker()
{
bool cellExists = false;
bool isInteriorCell = false;
int cellIndex = mData.getCells().searchId(mId);
if (cellIndex > -1)
{
const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mData.getCells().getRecord(cellIndex);
cellExists = !cellRecord.isDeleted();
isInteriorCell = cellRecord.get().mData.mFlags & ESM::Cell::Interior;
}
if (!isInteriorCell) {
mCellMarker.reset(new CellMarker(mCellNode, mCoordinates, cellExists));
}
}
CSMWorld::CellCoordinates CSVRender::Cell::getCoordinates() const CSMWorld::CellCoordinates CSVRender::Cell::getCoordinates() const
{ {
return mCoordinates; return mCoordinates;
@ -276,3 +385,53 @@ bool CSVRender::Cell::isDeleted() const
{ {
return mDeleted; return mDeleted;
} }
std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getSelection (unsigned int elementMask) const
{
std::vector<osg::ref_ptr<TagBase> > result;
if (elementMask & Mask_Reference)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
if (iter->second->getSelected())
result.push_back (iter->second->getTag());
if (elementMask & Mask_Pathgrid)
if (mPathgrid->isSelected())
result.push_back(mPathgrid->getTag());
return result;
}
std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getEdited (unsigned int elementMask) const
{
std::vector<osg::ref_ptr<TagBase> > result;
if (elementMask & Mask_Reference)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
if (iter->second->isEdited())
result.push_back (iter->second->getTag());
return result;
}
void CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask)
{
mSubMode = subMode;
mSubModeElementMask = elementMask;
if (elementMask & Mask_Reference)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
iter->second->setSubMode (subMode);
}
void CSVRender::Cell::reset (unsigned int elementMask)
{
if (elementMask & Mask_Reference)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
iter->second->reset();
if (elementMask & Mask_Pathgrid)
mPathgrid->resetIndicators();
}

@ -15,12 +15,16 @@
#include "object.hpp" #include "object.hpp"
#include "cellarrow.hpp" #include "cellarrow.hpp"
#include "cellmarker.hpp"
#include "cellborder.hpp"
class QModelIndex; class QModelIndex;
namespace osg namespace osg
{ {
class Group; class Group;
class Geometry;
class Geode;
} }
namespace CSMWorld namespace CSMWorld
@ -31,6 +35,9 @@ namespace CSMWorld
namespace CSVRender namespace CSVRender
{ {
class Pathgrid;
class TagBase;
class Cell class Cell
{ {
CSMWorld::Data& mData; CSMWorld::Data& mData;
@ -40,13 +47,22 @@ namespace CSVRender
std::auto_ptr<Terrain::TerrainGrid> mTerrain; std::auto_ptr<Terrain::TerrainGrid> mTerrain;
CSMWorld::CellCoordinates mCoordinates; CSMWorld::CellCoordinates mCoordinates;
std::auto_ptr<CellArrow> mCellArrows[4]; std::auto_ptr<CellArrow> mCellArrows[4];
std::auto_ptr<CellMarker> mCellMarker;
std::auto_ptr<CellBorder> mCellBorder;
std::auto_ptr<Pathgrid> mPathgrid;
bool mDeleted; bool mDeleted;
int mSubMode;
unsigned int mSubModeElementMask;
/// Ignored if cell does not have an object with the given ID. /// Ignored if cell does not have an object with the given ID.
/// ///
/// \return Was the object deleted? /// \return Was the object deleted?
bool removeObject (const std::string& id); bool removeObject (const std::string& id);
// Remove object and return iterator to next object.
std::map<std::string, Object *>::iterator removeObject (
std::map<std::string, Object *>::iterator iter);
/// Add objects from reference table that are within this cell. /// Add objects from reference table that are within this cell.
/// ///
/// \return Have any objects been added? /// \return Have any objects been added?
@ -70,6 +86,9 @@ namespace CSVRender
~Cell(); ~Cell();
/// \note Returns the pathgrid representation which will exist as long as the cell exists
Pathgrid* getPathgrid() const;
/// \return Did this call result in a modification of the visual representation of /// \return Did this call result in a modification of the visual representation of
/// this cell? /// this cell?
bool referenceableDataChanged (const QModelIndex& topLeft, bool referenceableDataChanged (const QModelIndex& topLeft,
@ -91,14 +110,35 @@ namespace CSVRender
/// this cell? /// this cell?
bool referenceAdded (const QModelIndex& parent, int start, int end); bool referenceAdded (const QModelIndex& parent, int start, int end);
void pathgridModified();
void pathgridRemoved();
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
// already selected
void selectAllWithSameParentId (int elementMask);
void setCellArrows (int mask); void setCellArrows (int mask);
/// \brief Set marker for this cell.
void setCellMarker();
/// Returns 0, 0 in case of an unpaged cell. /// Returns 0, 0 in case of an unpaged cell.
CSMWorld::CellCoordinates getCoordinates() const; CSMWorld::CellCoordinates getCoordinates() const;
bool isDeleted() const; bool isDeleted() const;
std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) const;
std::vector<osg::ref_ptr<TagBase> > getEdited (unsigned int elementMask) const;
void setSubMode (int subMode, unsigned int elementMask);
/// Erase all overrides and restore the visual representation of the cell to its
/// true state.
void reset (unsigned int elementMask);
}; };
} }

@ -7,10 +7,10 @@
#include <osg/Geometry> #include <osg/Geometry>
#include <osg/PrimitiveSet> #include <osg/PrimitiveSet>
#include "elements.hpp" #include "mask.hpp"
CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow) CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow)
: TagBase (Element_CellArrow), mArrow (arrow) : TagBase (Mask_CellArrow), mArrow (arrow)
{} {}
CSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const CSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const
@ -165,8 +165,7 @@ CSVRender::CellArrow::CellArrow (osg::Group *cellNode, Direction direction,
mParentNode->addChild (mBaseNode); mParentNode->addChild (mBaseNode);
// 0x1 reserved for separating cull and update visitors mBaseNode->setNodeMask (Mask_CellArrow);
mBaseNode->setNodeMask (Element_CellArrow<<1);
adjustTransform(); adjustTransform();
buildShape(); buildShape();

@ -0,0 +1,96 @@
#include "cellborder.hpp"
#include <osg/Group>
#include <osg/PositionAttitudeTransform>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <components/esm/loadland.hpp>
#include "mask.hpp"
#include "../../model/world/cellcoordinates.hpp"
const int CSVRender::CellBorder::CellSize = ESM::Land::REAL_SIZE;
const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 3;
CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords)
: mParentNode(cellNode)
{
mBaseNode = new osg::PositionAttitudeTransform();
mBaseNode->setNodeMask(Mask_CellBorder);
mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10));
mParentNode->addChild(mBaseNode);
}
CSVRender::CellBorder::~CellBorder()
{
mParentNode->removeChild(mBaseNode);
}
void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand)
{
const ESM::Land::LandData* landData = esmLand.getLandData(ESM::Land::DATA_VHGT);
if (!landData)
return;
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
// Vertices
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array();
int x = 0, y = 0;
for (; x < ESM::Land::LAND_SIZE; ++x)
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
x = ESM::Land::LAND_SIZE - 1;
for (; y < ESM::Land::LAND_SIZE; ++y)
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
y = ESM::Land::LAND_SIZE - 1;
for (; x >= 0; --x)
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
x = 0;
for (; y >= 0; --y)
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
geometry->setVertexArray(vertices);
// Color
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array();
colors->push_back(osg::Vec4f(0.f, 0.5f, 0.f, 1.f));
geometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET);
// Primitive
osg::ref_ptr<osg::DrawElementsUShort> primitives =
new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount+1);
for (size_t i = 0; i < VertexCount; ++i)
primitives->setElement(i, i);
primitives->setElement(VertexCount, 0);
geometry->addPrimitiveSet(primitives);
geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
osg::ref_ptr<osg::Geode> geode = new osg::Geode();
geode->addDrawable(geometry);
mBaseNode->addChild(geode);
}
size_t CSVRender::CellBorder::landIndex(int x, int y)
{
return y * ESM::Land::LAND_SIZE + x;
}
float CSVRender::CellBorder::scaleToWorld(int value)
{
return (CellSize + 128) * (float)value / ESM::Land::LAND_SIZE;
}

@ -0,0 +1,54 @@
#ifndef OPENCS_VIEW_CELLBORDER_H
#define OPENCS_VIEW_CELLBORDER_H
#include <cstddef>
#include <osg/ref_ptr>
namespace osg
{
class Group;
class PositionAttitudeTransform;
}
namespace ESM
{
struct Land;
}
namespace CSMWorld
{
class CellCoordinates;
}
namespace CSVRender
{
class CellBorder
{
public:
CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords);
~CellBorder();
void buildShape(const ESM::Land& esmLand);
private:
static const int CellSize;
static const int VertexCount;
size_t landIndex(int x, int y);
float scaleToWorld(int val);
// unimplemented
CellBorder(const CellBorder&);
CellBorder& operator=(const CellBorder&);
osg::Group* mParentNode;
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
};
}
#endif

@ -0,0 +1,91 @@
#include "cellmarker.hpp"
#include <boost/lexical_cast.hpp>
#include <osg/AutoTransform>
#include <osg/Geode>
#include <osg/Group>
#include <osgText/Text>
#include "mask.hpp"
CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker)
: TagBase(Mask_CellMarker), mMarker(marker)
{}
CSVRender::CellMarker *CSVRender::CellMarkerTag::getCellMarker() const
{
return mMarker;
}
void CSVRender::CellMarker::buildMarker()
{
const int characterSize = 20;
// Set up attributes of marker text.
osg::ref_ptr<osgText::Text> markerText (new osgText::Text);
markerText->setLayout(osgText::Text::LEFT_TO_RIGHT);
markerText->setCharacterSize(characterSize);
markerText->setAlignment(osgText::Text::CENTER_CENTER);
markerText->setDrawMode(osgText::Text::TEXT | osgText::Text::FILLEDBOUNDINGBOX);
// If cell exists then show black bounding box otherwise show red.
if (mExists)
{
markerText->setBoundingBoxColor(osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f));
}
else
{
markerText->setBoundingBoxColor(osg::Vec4f(1.0f, 0.0f, 0.0f, 1.0f));
}
// Add text containing cell's coordinates.
std::string coordinatesText =
boost::lexical_cast<std::string>(mCoordinates.getX()) + "," +
boost::lexical_cast<std::string>(mCoordinates.getY());
markerText->setText(coordinatesText);
// Add text to marker node.
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(markerText);
mMarkerNode->addChild(geode);
}
void CSVRender::CellMarker::positionMarker()
{
const int cellSize = 8192;
const int markerHeight = 0;
// Move marker to center of cell.
int x = (mCoordinates.getX() * cellSize) + (cellSize / 2);
int y = (mCoordinates.getY() * cellSize) + (cellSize / 2);
mMarkerNode->setPosition(osg::Vec3f(x, y, markerHeight));
}
CSVRender::CellMarker::CellMarker(
osg::Group *cellNode,
const CSMWorld::CellCoordinates& coordinates,
const bool cellExists
) : mCellNode(cellNode),
mCoordinates(coordinates),
mExists(cellExists)
{
// Set up node for cell marker.
mMarkerNode = new osg::AutoTransform();
mMarkerNode->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);
mMarkerNode->setAutoScaleToScreen(true);
mMarkerNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mMarkerNode->setUserData(new CellMarkerTag(this));
mMarkerNode->setNodeMask(Mask_CellMarker);
mCellNode->addChild(mMarkerNode);
buildMarker();
positionMarker();
}
CSVRender::CellMarker::~CellMarker()
{
mCellNode->removeChild(mMarkerNode);
}

@ -0,0 +1,68 @@
#ifndef OPENCS_VIEW_CELLMARKER_H
#define OPENCS_VIEW_CELLMARKER_H
#include "tagbase.hpp"
#include <osg/ref_ptr>
#include "../../model/world/cellcoordinates.hpp"
namespace osg
{
class AutoTransform;
class Group;
}
namespace CSVRender
{
class CellMarker;
class CellMarkerTag : public TagBase
{
private:
CellMarker *mMarker;
public:
CellMarkerTag(CellMarker *marker);
CellMarker *getCellMarker() const;
};
/// \brief Marker to display cell coordinates.
class CellMarker
{
private:
osg::Group* mCellNode;
osg::ref_ptr<osg::AutoTransform> mMarkerNode;
CSMWorld::CellCoordinates mCoordinates;
bool mExists;
// Not implemented.
CellMarker(const CellMarker&);
CellMarker& operator=(const CellMarker&);
/// \brief Build marker containing cell's coordinates.
void buildMarker();
/// \brief Position marker at center of cell.
void positionMarker();
public:
/// \brief Constructor.
/// \param cellNode Cell to create marker for.
/// \param coordinates Coordinates of cell.
/// \param cellExists Whether or not cell exists.
CellMarker(
osg::Group *cellNode,
const CSMWorld::CellCoordinates& coordinates,
const bool cellExists);
~CellMarker();
};
}
#endif

@ -29,37 +29,37 @@ void CSVRender::EditMode::setEditLock (bool locked)
} }
void CSVRender::EditMode::primaryEditPressed (osg::ref_ptr<TagBase> tag) {} void CSVRender::EditMode::primaryEditPressed (const WorldspaceHitResult& hit) {}
void CSVRender::EditMode::secondaryEditPressed (osg::ref_ptr<TagBase> tag) {} void CSVRender::EditMode::secondaryEditPressed (const WorldspaceHitResult& hit) {}
void CSVRender::EditMode::primarySelectPressed (osg::ref_ptr<TagBase> tag) {} void CSVRender::EditMode::primarySelectPressed (const WorldspaceHitResult& hit) {}
void CSVRender::EditMode::secondarySelectPressed (osg::ref_ptr<TagBase> tag) {} void CSVRender::EditMode::secondarySelectPressed (const WorldspaceHitResult& hit) {}
bool CSVRender::EditMode::primaryEditStartDrag (osg::ref_ptr<TagBase> tag) bool CSVRender::EditMode::primaryEditStartDrag (const QPoint& pos)
{ {
return false; return false;
} }
bool CSVRender::EditMode::secondaryEditStartDrag (osg::ref_ptr<TagBase> tag) bool CSVRender::EditMode::secondaryEditStartDrag (const QPoint& pos)
{ {
return false; return false;
} }
bool CSVRender::EditMode::primarySelectStartDrag (osg::ref_ptr<TagBase> tag) bool CSVRender::EditMode::primarySelectStartDrag (const QPoint& pos)
{ {
return false; return false;
} }
bool CSVRender::EditMode::secondarySelectStartDrag (osg::ref_ptr<TagBase> tag) bool CSVRender::EditMode::secondarySelectStartDrag (const QPoint& pos)
{ {
return false; return false;
} }
void CSVRender::EditMode::drag (int diffX, int diffY, double speedFactor) {} void CSVRender::EditMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) {}
void CSVRender::EditMode::dragCompleted() {} void CSVRender::EditMode::dragCompleted(const QPoint& pos) {}
void CSVRender::EditMode::dragAborted() {} void CSVRender::EditMode::dragAborted() {}
@ -67,6 +67,11 @@ void CSVRender::EditMode::dragWheel (int diff, double speedFactor) {}
void CSVRender::EditMode::dragEnterEvent (QDragEnterEvent *event) {} void CSVRender::EditMode::dragEnterEvent (QDragEnterEvent *event) {}
void CSVRender::EditMode::dropEvent (QDropEvent* event) {} void CSVRender::EditMode::dropEvent (QDropEvent *event) {}
void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {} void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {}
int CSVRender::EditMode::getSubMode() const
{
return -1;
}

@ -8,10 +8,12 @@
class QDragEnterEvent; class QDragEnterEvent;
class QDropEvent; class QDropEvent;
class QDragMoveEvent; class QDragMoveEvent;
class QPoint;
namespace CSVRender namespace CSVRender
{ {
class WorldspaceWidget; class WorldspaceWidget;
struct WorldspaceHitResult;
class TagBase; class TagBase;
class EditMode : public CSVWidget::ModeButton class EditMode : public CSVWidget::ModeButton
@ -38,42 +40,42 @@ namespace CSVRender
virtual void setEditLock (bool locked); virtual void setEditLock (bool locked);
/// Default-implementation: Ignored. /// Default-implementation: Ignored.
virtual void primaryEditPressed (osg::ref_ptr<TagBase> tag); virtual void primaryEditPressed (const WorldspaceHitResult& hit);
/// Default-implementation: Ignored. /// Default-implementation: Ignored.
virtual void secondaryEditPressed (osg::ref_ptr<TagBase> tag); virtual void secondaryEditPressed (const WorldspaceHitResult& hit);
/// Default-implementation: Ignored. /// Default-implementation: Ignored.
virtual void primarySelectPressed (osg::ref_ptr<TagBase> tag); virtual void primarySelectPressed (const WorldspaceHitResult& hit);
/// Default-implementation: Ignored. /// Default-implementation: Ignored.
virtual void secondarySelectPressed (osg::ref_ptr<TagBase> tag); virtual void secondarySelectPressed (const WorldspaceHitResult& hit);
/// Default-implementation: ignore and return false /// Default-implementation: ignore and return false
/// ///
/// \return Drag accepted? /// \return Drag accepted?
virtual bool primaryEditStartDrag (osg::ref_ptr<TagBase> tag); virtual bool primaryEditStartDrag (const QPoint& pos);
/// Default-implementation: ignore and return false /// Default-implementation: ignore and return false
/// ///
/// \return Drag accepted? /// \return Drag accepted?
virtual bool secondaryEditStartDrag (osg::ref_ptr<TagBase> tag); virtual bool secondaryEditStartDrag (const QPoint& pos);
/// Default-implementation: ignore and return false /// Default-implementation: ignore and return false
/// ///
/// \return Drag accepted? /// \return Drag accepted?
virtual bool primarySelectStartDrag (osg::ref_ptr<TagBase> tag); virtual bool primarySelectStartDrag (const QPoint& pos);
/// Default-implementation: ignore and return false /// Default-implementation: ignore and return false
/// ///
/// \return Drag accepted? /// \return Drag accepted?
virtual bool secondarySelectStartDrag (osg::ref_ptr<TagBase> tag); virtual bool secondarySelectStartDrag (const QPoint& pos);
/// Default-implementation: ignored /// Default-implementation: ignored
virtual void drag (int diffX, int diffY, double speedFactor); virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor);
/// Default-implementation: ignored /// Default-implementation: ignored
virtual void dragCompleted(); virtual void dragCompleted(const QPoint& pos);
/// Default-implementation: ignored /// Default-implementation: ignored
/// ///
@ -88,10 +90,13 @@ namespace CSVRender
virtual void dragEnterEvent (QDragEnterEvent *event); virtual void dragEnterEvent (QDragEnterEvent *event);
/// Default-implementation: ignored /// Default-implementation: ignored
virtual void dropEvent (QDropEvent* event); virtual void dropEvent (QDropEvent *event);
/// Default-implementation: ignored /// Default-implementation: ignored
virtual void dragMoveEvent (QDragMoveEvent *event); virtual void dragMoveEvent (QDragMoveEvent *event);
/// Default: return -1
virtual int getSubMode() const;
}; };
} }

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

Loading…
Cancel
Save