mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-26 00:35:23 +00:00
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
This commit is contained in:
commit
d2fe6fe857
28
.gitignore
vendored
28
.gitignore
vendored
@ -10,6 +10,7 @@ prebuilt
|
||||
|
||||
## doxygen
|
||||
Doxygen
|
||||
!docs/cs-manual/Makefile
|
||||
|
||||
## ides/editors
|
||||
*~
|
||||
@ -21,6 +22,7 @@ Doxygen
|
||||
.project
|
||||
.settings
|
||||
.directory
|
||||
.idea
|
||||
## qt-creator
|
||||
CMakeLists.txt.user*
|
||||
|
||||
@ -38,10 +40,33 @@ resources
|
||||
/openmw
|
||||
/opencs
|
||||
/niftest
|
||||
bsatool
|
||||
openmw-cs
|
||||
openmw-essimporter
|
||||
openmw-iniimporter
|
||||
openmw-launcher
|
||||
openmw-wizard
|
||||
|
||||
## generated objects
|
||||
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/Doxyfile
|
||||
docs/DoxyfilePages
|
||||
moc_*.cxx
|
||||
*.cxx_parameters
|
||||
*qrc_launcher.cxx
|
||||
@ -53,3 +78,6 @@ moc_*.cxx
|
||||
*ui_playpage.h
|
||||
*.[ao]
|
||||
*.so
|
||||
gamecontrollerdb.txt
|
||||
openmw.appdata.xml
|
||||
|
||||
|
11
.travis.yml
11
.travis.yml
@ -1,7 +1,10 @@
|
||||
os:
|
||||
- linux
|
||||
# - osx
|
||||
- osx
|
||||
osx_image: xcode7.2
|
||||
language: cpp
|
||||
sudo: required
|
||||
dist: trusty
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
@ -18,8 +21,8 @@ addons:
|
||||
name: "OpenMW/openmw"
|
||||
description: "<Your project description here>"
|
||||
notification_email: scrawl@baseoftrash.de
|
||||
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE"
|
||||
build_command: "make"
|
||||
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE"
|
||||
build_command: "make -j2"
|
||||
branch_pattern: coverity_scan
|
||||
matrix:
|
||||
include:
|
||||
@ -38,7 +41,7 @@ before_script:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
|
||||
script:
|
||||
- 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}" = "linux" ]; then ./openmw_test_suite; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||
|
15
AUTHORS.md
15
AUTHORS.md
@ -14,15 +14,20 @@ Programmers
|
||||
|
||||
Adam Hogan (aurix)
|
||||
Aesylwinn
|
||||
aegis
|
||||
Aleksandar Jovanov
|
||||
Alex Haddad (rainChu)
|
||||
Alex McKibben (WeirdSexy)
|
||||
alexanderkjall
|
||||
Alexander Nadeau (wareya)
|
||||
Alexander Olofsson (Ace)
|
||||
Allofich
|
||||
Austin Salgat (Salgat)
|
||||
Artem Kotsynyak (greye)
|
||||
artemutin
|
||||
Arthur Moore (EmperorArthur)
|
||||
athile
|
||||
Ben Shealy (bentsherman)
|
||||
Bret Curtis (psi29a)
|
||||
Britt Mathis (galdor557)
|
||||
cc9cii
|
||||
@ -48,6 +53,7 @@ Programmers
|
||||
Gašper Sedej
|
||||
gugus/gus
|
||||
Hallfaer Tuilinn
|
||||
hristoast
|
||||
Internecine
|
||||
Jacob Essex (Yacoby)
|
||||
Jannik Heller (scrawl)
|
||||
@ -56,6 +62,7 @@ Programmers
|
||||
Jeffrey Haines (Jyby)
|
||||
Jengerer
|
||||
Jiří Kuneš (kunesj)
|
||||
Joe Wilkerson (neuralroberts)
|
||||
Joel Graff (graffy)
|
||||
John Blomberg (fstp)
|
||||
Jordan Ayers
|
||||
@ -81,6 +88,7 @@ Programmers
|
||||
Michael Mc Donnell
|
||||
Michael Papageorgiou (werdanith)
|
||||
Michał Bień (Glorf)
|
||||
Michał Moroz (dragonee)
|
||||
Miroslav Puda (pakanek)
|
||||
MiroslavR
|
||||
naclander
|
||||
@ -92,15 +100,17 @@ Programmers
|
||||
Nolan Poe (nopoe)
|
||||
Paul Cercueil (pcercuei)
|
||||
Paul McElroy (Greendogo)
|
||||
Pi03k
|
||||
Pieter van der Kloet (pvdk)
|
||||
pkubik
|
||||
Radu-Marius Popovici (rpopovici)
|
||||
rdimesio
|
||||
riothamus
|
||||
Rob Cutmore (rcutmore)
|
||||
Robert MacGregor (Ragora)
|
||||
Rohit Nirmal
|
||||
Roman Melnik (Kromgart)
|
||||
Roman Proskuryakov (humbug)
|
||||
Roman Proskuryakov (kpp)
|
||||
Sandy Carter (bwrsandman)
|
||||
Scott Howard
|
||||
Sebastian Wick (swick)
|
||||
@ -109,7 +119,9 @@ Programmers
|
||||
smbas
|
||||
Stefan Galowicz (bogglez)
|
||||
Stanislav Bobrov (Jiub)
|
||||
svaante
|
||||
Sylvain Thesnieres (Garvek)
|
||||
t6
|
||||
terrorfisch
|
||||
Thomas Luppi (Digmaster)
|
||||
Tom Mason (wheybags)
|
||||
@ -124,6 +136,7 @@ Manual
|
||||
|
||||
Bodillium
|
||||
Cramal
|
||||
Alejandro Sanchez (HiPhish)
|
||||
sir_herrbatka
|
||||
|
||||
Packagers
|
||||
|
159
CHANGELOG.md
159
CHANGELOG.md
@ -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
|
||||
------
|
||||
|
||||
|
@ -1,22 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
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"
|
||||
wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
fi
|
||||
|
||||
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:boost-latest/ppa
|
||||
sudo apt-get update -qq
|
||||
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 ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-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 cmake-data #workaround for broken osgqt cmake script in ubuntu 12.04
|
||||
if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi
|
||||
sudo mkdir /usr/src/gtest/build
|
||||
cd /usr/src/gtest/build
|
||||
|
@ -1,9 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
brew tap openmw/openmw
|
||||
brew update
|
||||
brew unlink boost
|
||||
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg openmw/openmw/qt unshield
|
||||
brew rm cmake || true
|
||||
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
|
||||
|
||||
free -m
|
||||
mkdir build
|
||||
cd build
|
||||
export CODE_COVERAGE=1
|
||||
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
|
||||
|
671
CI/before_script.msvc.sh
Normal file
671
CI/before_script.msvc.sh
Normal file
@ -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
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||
QT_PATH="/usr/local/opt/qt5"
|
||||
|
||||
mkdir 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" \
|
||||
..
|
||||
|
59
CI/build.msvc.sh
Normal file
59
CI/build.msvc.sh
Normal file
@ -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
|
160
CMakeLists.txt
160
CMakeLists.txt
@ -16,11 +16,16 @@ endif (APPLE)
|
||||
|
||||
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
|
||||
message(STATUS "Configuring OpenMW...")
|
||||
|
||||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 37)
|
||||
set(OPENMW_VERSION_MINOR 39)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
@ -111,68 +116,12 @@ 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)
|
||||
endif()
|
||||
|
||||
# We probably support older versions than this.
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
# Sound setup
|
||||
unset(FFMPEG_LIBRARIES CACHE)
|
||||
|
||||
find_package(FFmpeg REQUIRED)
|
||||
|
||||
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARIES})
|
||||
|
||||
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND OR NOT SWRESAMPLE_FOUND)
|
||||
message(FATAL_ERROR "FFmpeg component required, but not found!")
|
||||
endif()
|
||||
# Required for building the FFmpeg headers
|
||||
add_definitions(-D__STDC_CONSTANT_MACROS)
|
||||
|
||||
# TinyXML
|
||||
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
|
||||
if(USE_SYSTEM_TINYXML)
|
||||
find_library(TINYXML_LIBRARIES tinyxml)
|
||||
find_path(TINYXML_INCLUDE_DIR tinyxml.h)
|
||||
message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}")
|
||||
add_definitions (-DTIXML_USE_STL)
|
||||
if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR)
|
||||
include_directories(${TINYXML_INCLUDE_DIR})
|
||||
message(STATUS "Using system TinyXML library.")
|
||||
else()
|
||||
message(FATAL_ERROR "Detection of system TinyXML incomplete.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Platform specific
|
||||
if (WIN32)
|
||||
if(NOT MINGW)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||
endif(NOT MINGW)
|
||||
|
||||
# Suppress WinMain(), provided by SDL
|
||||
add_definitions(-DSDL_MAIN_HANDLED)
|
||||
|
||||
# Get rid of useless crud from windows.h
|
||||
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
|
||||
set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE)
|
||||
endif (ANDROID)
|
||||
|
||||
option(OPENGL_ES "enable opengl es support" FALSE )
|
||||
|
||||
if (OPENGL_ES)
|
||||
add_definitions(-DOPENGL_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)")
|
||||
@ -191,6 +140,52 @@ if (USE_QT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
|
||||
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows.
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
else()
|
||||
# We probably support older versions than this.
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
endif()
|
||||
|
||||
# Sound setup
|
||||
find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE)
|
||||
# Required for building the FFmpeg headers
|
||||
add_definitions(-D__STDC_CONSTANT_MACROS)
|
||||
|
||||
# TinyXML
|
||||
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
|
||||
if (USE_SYSTEM_TINYXML)
|
||||
find_package(TinyXML REQUIRED)
|
||||
add_definitions (-DTIXML_USE_STL)
|
||||
include_directories(SYSTEM ${TinyXML_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
# Platform specific
|
||||
if (WIN32)
|
||||
if(NOT MINGW)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||
endif(NOT MINGW)
|
||||
|
||||
# Suppress WinMain(), provided by SDL
|
||||
add_definitions(-DSDL_MAIN_HANDLED)
|
||||
|
||||
# Get rid of useless crud from windows.h
|
||||
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
|
||||
endif()
|
||||
|
||||
if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer
|
||||
find_package(LIBUNSHIELD REQUIRED) # required only for non win32 when building openmw-wizard
|
||||
set(OPENMW_USE_UNSHIELD TRUE)
|
||||
endif()
|
||||
|
||||
option(OPENGL_ES "enable opengl es support" FALSE )
|
||||
|
||||
if (OPENGL_ES)
|
||||
add_definitions(-DOPENGL_ES)
|
||||
endif(OPENGL_ES)
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package (Threads)
|
||||
@ -204,12 +199,6 @@ if(NOT HAVE_STDINT_H)
|
||||
message(FATAL_ERROR "stdint.h was not found" )
|
||||
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)
|
||||
if(WIN32)
|
||||
@ -220,11 +209,7 @@ IF(BOOST_STATIC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
endif()
|
||||
|
||||
if (USE_QT)
|
||||
set (OSG_QT osgQt)
|
||||
endif()
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle ${OSG_QT} osgUtil osgFX)
|
||||
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgUtil osgFX)
|
||||
|
||||
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
|
||||
# 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_DBG NAMES ${PLUGIN_NAME_DBG} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH})
|
||||
make_library_set(${PLUGIN_NAME}_LIBRARY)
|
||||
@ -245,12 +231,6 @@ if(OSG_STATIC)
|
||||
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${PLUGIN_NAME}_LIBRARY})
|
||||
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)
|
||||
|
||||
set(PLUGIN_LIST
|
||||
@ -271,6 +251,11 @@ if(OSG_STATIC)
|
||||
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})
|
||||
use_static_osg_plugin_dep(${DEPENDENCY})
|
||||
endforeach()
|
||||
@ -294,7 +279,7 @@ endif()
|
||||
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(Bullet REQUIRED)
|
||||
find_package(Bullet 283 REQUIRED COMPONENTS BulletCollision LinearMath)
|
||||
|
||||
include_directories("."
|
||||
SYSTEM
|
||||
@ -302,7 +287,7 @@ include_directories("."
|
||||
${Boost_INCLUDE_DIR}
|
||||
${MYGUI_INCLUDE_DIRS}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${BULLET_INCLUDE_DIRS}
|
||||
${Bullet_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${MYGUI_LIB_DIR})
|
||||
@ -369,21 +354,17 @@ endif()
|
||||
|
||||
# CXX Compiler settings
|
||||
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)
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)
|
||||
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)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
|
||||
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(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
# Enable link-time code generation globally for all linking
|
||||
if (OPENMW_LTO_BUILD)
|
||||
@ -558,6 +539,9 @@ endif(WIN32)
|
||||
# Extern
|
||||
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
||||
add_subdirectory (extern/oics)
|
||||
if (USE_QT)
|
||||
add_subdirectory (extern/osgQt)
|
||||
endif()
|
||||
|
||||
# Components
|
||||
add_subdirectory (components)
|
||||
@ -685,8 +669,6 @@ if (WIN32)
|
||||
endforeach(d)
|
||||
|
||||
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}")
|
||||
|
||||
if (BUILD_BSATOOL)
|
||||
|
@ -1,13 +1,13 @@
|
||||
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 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)
|
||||
* Website: http://www.openmw.org
|
||||
* IRC: #openmw on irc.freenode.net
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
@ -354,12 +355,12 @@ int load(Arguments& info)
|
||||
esm.getRecHeader(flags);
|
||||
|
||||
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;
|
||||
skipped.push_back(n.val);
|
||||
skipped.push_back(n.intval);
|
||||
}
|
||||
|
||||
esm.skipRecord();
|
||||
@ -391,20 +392,20 @@ int load(Arguments& info)
|
||||
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);
|
||||
}
|
||||
|
||||
if (save)
|
||||
if (save)
|
||||
{
|
||||
info.data.mRecords.push_back(record);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
delete record;
|
||||
}
|
||||
++info.data.mRecordStats[n.val];
|
||||
++info.data.mRecordStats[n.intval];
|
||||
}
|
||||
|
||||
} catch(std::exception &e) {
|
||||
@ -442,23 +443,18 @@ int clone(Arguments& info)
|
||||
size_t recordCount = info.data.mRecords.size();
|
||||
|
||||
int digitCount = 1; // For a nicer output
|
||||
if (recordCount > 9) ++digitCount;
|
||||
if (recordCount > 99) ++digitCount;
|
||||
if (recordCount > 999) ++digitCount;
|
||||
if (recordCount > 9999) ++digitCount;
|
||||
if (recordCount > 99999) ++digitCount;
|
||||
if (recordCount > 999999) ++digitCount;
|
||||
if (recordCount > 0)
|
||||
digitCount = (int)std::log10(recordCount) + 1;
|
||||
|
||||
std::cout << "Loaded " << recordCount << " records:" << std::endl << std::endl;
|
||||
|
||||
ESM::NAME name;
|
||||
|
||||
int i = 0;
|
||||
typedef std::map<int, int> Stats;
|
||||
Stats &stats = info.data.mRecordStats;
|
||||
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;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (name.val == ESM::REC_CELL) {
|
||||
if (typeName.intval == ESM::REC_CELL) {
|
||||
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
|
||||
if (!info.data.mCellRefs[ptr].empty()) {
|
||||
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++;
|
||||
int perc = (int)((saved / (float)recordCount)*100);
|
||||
|
@ -179,7 +179,7 @@ RecordBase::create(ESM::NAME type)
|
||||
{
|
||||
RecordBase *record = 0;
|
||||
|
||||
switch (type.val) {
|
||||
switch (type.intval) {
|
||||
case ESM::REC_ACTI:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Activator>;
|
||||
|
@ -121,7 +121,7 @@ public:
|
||||
{
|
||||
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
|
||||
mContext->mPlayerBase = npc;
|
||||
std::map<const int, float> empty;
|
||||
ESM::SpellState::SpellParams empty;
|
||||
// FIXME: player start spells and birthsign spells aren't listed here,
|
||||
// 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)
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "convertinventory.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
@ -17,6 +17,8 @@ namespace ESSImport
|
||||
faction.mReputation = it->mReputation;
|
||||
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)
|
||||
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
|
||||
for (int i=0; i<27; ++i)
|
||||
@ -28,7 +30,7 @@ namespace ESSImport
|
||||
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell)
|
||||
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();
|
||||
it != pcdt.mKnownDialogueTopics.end(); ++it)
|
||||
|
@ -75,7 +75,7 @@ namespace ESSImport
|
||||
// unsure at which point between TGTN and CRED
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ namespace
|
||||
*(image->data(x,y)+2) = *it++;
|
||||
*(image->data(x,y)+1) = *it++;
|
||||
*image->data(x,y) = *it++;
|
||||
it++; // skip alpha
|
||||
++it; // skip alpha
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,14 +322,14 @@ namespace ESSImport
|
||||
ESM::NAME n = esm.getRecName();
|
||||
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())
|
||||
{
|
||||
it->second->read(esm);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unknownRecords.insert(n.val).second)
|
||||
if (unknownRecords.insert(n.intval).second)
|
||||
{
|
||||
std::ios::fmtflags f(std::cerr.flags());
|
||||
std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
|
||||
|
@ -21,13 +21,18 @@ namespace ESSImport
|
||||
item.mCount = contItem.mCount;
|
||||
item.mRelativeEquipmentSlot = -1;
|
||||
|
||||
// seems that a stack of items can have a set of subrecords for each item? rings0000.ess
|
||||
// doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place?
|
||||
// I guess we should double check the stacking logic in OpenMW
|
||||
for (int i=0;i<std::abs(item.mCount);++i)
|
||||
unsigned int itemCount = std::abs(item.mCount);
|
||||
bool separateStacks = false;
|
||||
for (unsigned int i=0;i<itemCount;++i)
|
||||
{
|
||||
if (esm.isNextSub("XIDX")) // index in the stack?
|
||||
esm.skipHSub();
|
||||
bool newStack = esm.isNextSub("XIDX");
|
||||
if (newStack)
|
||||
{
|
||||
unsigned int idx;
|
||||
esm.getHT(idx);
|
||||
separateStacks = true;
|
||||
item.mCount = 1;
|
||||
}
|
||||
|
||||
item.mSCRI.load(esm);
|
||||
|
||||
@ -38,9 +43,13 @@ namespace ESSImport
|
||||
int charge=-1;
|
||||
esm.getHNOT(charge, "XHLT");
|
||||
item.mChargeInt = charge;
|
||||
|
||||
if (newStack)
|
||||
mItems.push_back(item);
|
||||
}
|
||||
|
||||
mItems.push_back(item);
|
||||
if (!separateStacks)
|
||||
mItems.push_back(item);
|
||||
}
|
||||
|
||||
// equipped items
|
||||
|
@ -43,10 +43,9 @@ struct PCDT
|
||||
DrawState_Weapon = 0x80,
|
||||
DrawState_Spell = 0x100
|
||||
};
|
||||
enum CameraState
|
||||
enum CameraFlags
|
||||
{
|
||||
CameraState_FirstPerson = 0x8,
|
||||
CameraState_ThirdPerson = 0xa
|
||||
CameraFlag_ThirdPerson = 0x2
|
||||
};
|
||||
|
||||
#pragma pack(push)
|
||||
@ -64,11 +63,13 @@ struct PCDT
|
||||
struct PNAM
|
||||
{
|
||||
short mDrawState; // DrawState
|
||||
short mCameraState; // CameraState
|
||||
short mCameraFlags; // CameraFlags
|
||||
unsigned int mLevelProgress;
|
||||
float mSkillProgress[27]; // skill progress, non-uniform scaled
|
||||
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)
|
||||
|
||||
|
@ -99,9 +99,6 @@ if (DESIRED_QT_VERSION MATCHES 4)
|
||||
endif(WIN32)
|
||||
else()
|
||||
qt5_use_modules(openmw-launcher Widgets Core)
|
||||
if (WIN32)
|
||||
target_link_libraries(Qt5::WinMain)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
|
@ -51,10 +51,6 @@ int main(int argc, char *argv[])
|
||||
if (result == Launcher::FirstRunDialogResultFailure)
|
||||
return 0;
|
||||
|
||||
// if (!mainWin.setup()) {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
if (result == Launcher::FirstRunDialogResultContinue)
|
||||
mainWin.show();
|
||||
|
||||
|
@ -50,7 +50,6 @@ namespace Launcher
|
||||
explicit MainDialog(QWidget *parent = 0);
|
||||
~MainDialog();
|
||||
|
||||
bool setup();
|
||||
FirstRunDialogResult showFirstRunDialog();
|
||||
|
||||
bool reloadSettings();
|
||||
@ -65,6 +64,8 @@ namespace Launcher
|
||||
void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
private:
|
||||
bool setup();
|
||||
|
||||
void createIcons();
|
||||
void createPages();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "importer.hpp"
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
@ -897,8 +898,13 @@ std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename
|
||||
boost::filesystem::path resolved = filename;
|
||||
#endif
|
||||
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
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ opencs_units_noqt (model/world
|
||||
universalid record commands columnbase columnimp scriptcontext cell refidcollection
|
||||
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
|
||||
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
|
||||
idcompletionmanager metadata
|
||||
idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/world
|
||||
@ -35,14 +35,14 @@ opencs_hdrs_noqt (model/world
|
||||
|
||||
|
||||
opencs_units (model/tools
|
||||
tools reportmodel mergeoperation
|
||||
tools reportmodel mergeoperation
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/tools
|
||||
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
|
||||
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
|
||||
mergestages
|
||||
mergestages gmstcheck topicinfocheck journalcheck
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/tools
|
||||
@ -67,7 +67,7 @@ opencs_hdrs_noqt (view/doc
|
||||
|
||||
opencs_units (view/world
|
||||
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
|
||||
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
|
||||
)
|
||||
@ -85,16 +85,17 @@ opencs_units (view/widget
|
||||
|
||||
opencs_units (view/render
|
||||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||
previewwidget editmode instancemode
|
||||
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
||||
orbitcameramode pathgridmode selectionmode pathgridselectionmode
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/render
|
||||
lighting lightingday lightingnight
|
||||
lightingbright object cell terrainstorage tagbase cellarrow
|
||||
lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase
|
||||
cellarrow cellmarker cellborder cameracontroller pathgrid
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (view/render
|
||||
elements
|
||||
mask
|
||||
)
|
||||
|
||||
|
||||
@ -192,11 +193,12 @@ endif(APPLE)
|
||||
target_link_libraries(openmw-cs
|
||||
${OSG_LIBRARIES}
|
||||
${OPENTHREADS_LIBRARIES}
|
||||
${OSGTEXT_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
${OSGVIEWER_LIBRARIES}
|
||||
${OSGGA_LIBRARIES}
|
||||
${OSGFX_LIBRARIES}
|
||||
${OSGQT_LIBRARIES}
|
||||
${EXTERN_OSGQT_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
@ -215,9 +217,6 @@ if (DESIRED_QT_VERSION MATCHES 4)
|
||||
endif()
|
||||
else()
|
||||
qt5_use_modules(openmw-cs Widgets Core Network OpenGL)
|
||||
if (WIN32)
|
||||
target_link_libraries(Qt5::WinMain)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
||||
#include <components/fallback/validate.hpp>
|
||||
|
||||
#include <components/nifosg/nifloader.hpp>
|
||||
|
||||
#include "model/doc/document.hpp"
|
||||
@ -17,6 +19,8 @@
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
using namespace Fallback;
|
||||
|
||||
CS::Editor::Editor ()
|
||||
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
|
||||
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"))
|
||||
("fallback-archive", boost::program_options::value<std::vector<std::string> >()->
|
||||
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>(), "")
|
||||
->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)
|
||||
@ -114,6 +120,8 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
||||
|
||||
mDocumentManager.setResourceDir (mResources = variables["resources"].as<std::string>());
|
||||
|
||||
mDocumentManager.setFallbackMap (variables["fallback"].as<FallbackMap>().mMap);
|
||||
|
||||
if (variables["script-blacklist-use"].as<bool>())
|
||||
mDocumentManager.setBlacklistedScripts (
|
||||
variables["script-blacklist"].as<std::vector<std::string> >());
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,9 +25,13 @@
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
namespace Fallback
|
||||
{
|
||||
class Map;
|
||||
}
|
||||
|
||||
namespace VFS
|
||||
{
|
||||
|
||||
class Manager;
|
||||
}
|
||||
|
||||
@ -66,6 +70,7 @@ namespace CSMDoc
|
||||
Saving mSavingOperation;
|
||||
OperationHolder mSaving;
|
||||
boost::filesystem::path mResDir;
|
||||
const Fallback::Map* mFallbackMap;
|
||||
Blacklist mBlacklist;
|
||||
Runner mRunner;
|
||||
bool mDirty;
|
||||
@ -101,6 +106,7 @@ namespace CSMDoc
|
||||
Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration,
|
||||
const std::vector< boost::filesystem::path >& files, bool new_,
|
||||
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
|
||||
const Fallback::Map* fallback,
|
||||
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
|
||||
const std::vector<std::string>& blacklistedScripts);
|
||||
|
||||
|
@ -41,6 +41,7 @@ CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& con
|
||||
CSMDoc::DocumentManager::~DocumentManager()
|
||||
{
|
||||
mLoaderThread.quit();
|
||||
mLoader.stop();
|
||||
mLoader.hasThingsToDo().wakeAll();
|
||||
mLoaderThread.wait();
|
||||
|
||||
@ -64,7 +65,7 @@ CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
|
||||
const std::vector< boost::filesystem::path >& files,
|
||||
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)
|
||||
@ -100,6 +101,11 @@ void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& par
|
||||
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)
|
||||
{
|
||||
mEncoding = encoding;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <QThread>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/fallback/fallback.hpp>
|
||||
|
||||
#include "../world/resourcesmanager.hpp"
|
||||
|
||||
@ -67,6 +68,8 @@ namespace CSMDoc
|
||||
|
||||
void setResourceDir (const boost::filesystem::path& parResDir);
|
||||
|
||||
void setFallbackMap (const std::map<std::string, std::string>& fallbackMap);
|
||||
|
||||
void setEncoding (ToUTF8::FromType encoding);
|
||||
|
||||
void setBlacklistedScripts (const std::vector<std::string>& scriptIds);
|
||||
@ -78,6 +81,7 @@ namespace CSMDoc
|
||||
private:
|
||||
|
||||
boost::filesystem::path mResDir;
|
||||
Fallback::Map mFallbackMap;
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "loader.hpp"
|
||||
|
||||
#include <QTimer>
|
||||
#include <iostream>
|
||||
|
||||
#include "../tools/reportmodel.hpp"
|
||||
|
||||
@ -11,11 +11,12 @@ CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLoaded (0), mRecordsLeft (fa
|
||||
|
||||
|
||||
CSMDoc::Loader::Loader()
|
||||
: mShouldStop(false)
|
||||
{
|
||||
QTimer *timer = new QTimer (this);
|
||||
mTimer = new QTimer (this);
|
||||
|
||||
connect (timer, SIGNAL (timeout()), this, SLOT (load()));
|
||||
timer->start();
|
||||
connect (mTimer, SIGNAL (timeout()), this, SLOT (load()));
|
||||
mTimer->start();
|
||||
}
|
||||
|
||||
QWaitCondition& CSMDoc::Loader::hasThingsToDo()
|
||||
@ -23,6 +24,11 @@ QWaitCondition& CSMDoc::Loader::hasThingsToDo()
|
||||
return mThingsToDo;
|
||||
}
|
||||
|
||||
void CSMDoc::Loader::stop()
|
||||
{
|
||||
mShouldStop = true;
|
||||
}
|
||||
|
||||
void CSMDoc::Loader::load()
|
||||
{
|
||||
if (mDocuments.empty())
|
||||
@ -30,6 +36,10 @@ void CSMDoc::Loader::load()
|
||||
mMutex.lock();
|
||||
mThingsToDo.wait (&mMutex);
|
||||
mMutex.unlock();
|
||||
|
||||
if (mShouldStop)
|
||||
mTimer->stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -45,13 +55,12 @@ void CSMDoc::Loader::load()
|
||||
|
||||
bool done = false;
|
||||
|
||||
const int batchingSize = 50;
|
||||
|
||||
try
|
||||
{
|
||||
if (iter->second.mRecordsLeft)
|
||||
{
|
||||
Messages messages (Message::Severity_Error);
|
||||
const int batchingSize = 50;
|
||||
for (int i=0; i<batchingSize; ++i) // do not flood the system with update signals
|
||||
if (document->getData().continueLoading (messages))
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
#include <QWaitCondition>
|
||||
|
||||
namespace CSMDoc
|
||||
@ -28,12 +29,17 @@ namespace CSMDoc
|
||||
QWaitCondition mThingsToDo;
|
||||
std::vector<std::pair<Document *, Stage> > mDocuments;
|
||||
|
||||
QTimer* mTimer;
|
||||
bool mShouldStop;
|
||||
|
||||
public:
|
||||
|
||||
Loader();
|
||||
|
||||
QWaitCondition& hasThingsToDo();
|
||||
|
||||
void stop();
|
||||
|
||||
private slots:
|
||||
|
||||
void load();
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "messages.hpp"
|
||||
|
||||
CSMDoc::Message::Message() {}
|
||||
CSMDoc::Message::Message() : mSeverity(Severity_Default){}
|
||||
|
||||
CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& message,
|
||||
const std::string& hint, Severity severity)
|
||||
|
@ -39,9 +39,6 @@ namespace CSMDoc
|
||||
{
|
||||
public:
|
||||
|
||||
// \deprecated Use CSMDoc::Message directly instead.
|
||||
typedef CSMDoc::Message Message;
|
||||
|
||||
typedef std::vector<Message> Collection;
|
||||
|
||||
typedef Collection::const_iterator Iterator;
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
#include "operation.hpp"
|
||||
|
||||
CSMDoc::OperationHolder::OperationHolder (Operation *operation) : mRunning (false)
|
||||
CSMDoc::OperationHolder::OperationHolder (Operation *operation)
|
||||
: mOperation(NULL)
|
||||
, mRunning (false)
|
||||
{
|
||||
if (operation)
|
||||
setOperation (operation);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "../world/infocollection.hpp"
|
||||
#include "../world/cellcoordinates.hpp"
|
||||
|
||||
#include "document.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
|
||||
// the current cell. It is possible that a moved ref is moved again.
|
||||
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);
|
||||
else
|
||||
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 =
|
||||
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
|
||||
|
||||
if (cell.isModified() ||
|
||||
if (cell.isModified() ||
|
||||
cell.mState == CSMWorld::RecordBase::State_Deleted ||
|
||||
references!=mState.getSubRecords().end())
|
||||
{
|
||||
CSMWorld::Cell cellRecord = cell.get();
|
||||
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
|
||||
writer.startRecord (cellRecord.sRecordId);
|
||||
|
||||
@ -309,11 +329,18 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
|
||||
stream << "#" << index.first << " " << index.second;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell)
|
||||
if (refRecord.mNew ||
|
||||
(!interior && ref.mState==CSMWorld::RecordBase::State_ModifiedOnly &&
|
||||
refRecord.mCell!=stream.str()))
|
||||
{
|
||||
refRecord.mRefNum.mIndex = newRefNum++;
|
||||
}
|
||||
else if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell)
|
||||
!= 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;
|
||||
moved.mRefNum = refRecord.mRefNum;
|
||||
|
||||
@ -350,7 +377,7 @@ int CSMDoc::WritePathgridCollectionStage::setup()
|
||||
void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages)
|
||||
{
|
||||
ESM::ESMWriter& writer = mState.getWriter();
|
||||
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
|
||||
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
|
||||
mDocument.getData().getPathgrids().getRecord (stage);
|
||||
|
||||
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)
|
||||
{
|
||||
ESM::ESMWriter& writer = mState.getWriter();
|
||||
const CSMWorld::Record<CSMWorld::Land>& land =
|
||||
const CSMWorld::Record<CSMWorld::Land>& land =
|
||||
mDocument.getData().getLand().getRecord (stage);
|
||||
|
||||
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();
|
||||
writer.startRecord (record.sRecordId);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -416,7 +439,7 @@ int CSMDoc::WriteLandTextureCollectionStage::setup()
|
||||
void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages)
|
||||
{
|
||||
ESM::ESMWriter& writer = mState.getWriter();
|
||||
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
|
||||
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
|
||||
mDocument.getData().getLandTextures().getRecord (stage);
|
||||
|
||||
if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted)
|
||||
|
@ -15,10 +15,16 @@
|
||||
CSMPrefs::DoubleSetting::DoubleSetting (Category *parent, Settings::Manager *values,
|
||||
QMutex *mutex, const std::string& key, const std::string& label, double default_)
|
||||
: 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_)
|
||||
{}
|
||||
|
||||
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setPrecision(int precision)
|
||||
{
|
||||
mPrecision = precision;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setRange (double min, double max)
|
||||
{
|
||||
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);
|
||||
|
||||
QDoubleSpinBox *widget = new QDoubleSpinBox (parent);
|
||||
widget->setDecimals(mPrecision);
|
||||
widget->setRange (mMin, mMax);
|
||||
widget->setValue (mDefault);
|
||||
|
||||
|
@ -9,6 +9,7 @@ namespace CSMPrefs
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
int mPrecision;
|
||||
double mMin;
|
||||
double mMax;
|
||||
std::string mTooltip;
|
||||
@ -20,6 +21,8 @@ namespace CSMPrefs
|
||||
QMutex *mutex, const std::string& key, const std::string& label,
|
||||
double default_);
|
||||
|
||||
DoubleSetting& setPrecision (int precision);
|
||||
|
||||
// defaults to [0, std::numeric_limits<double>::max()]
|
||||
DoubleSetting& setRange (double min, double max);
|
||||
|
||||
|
@ -133,12 +133,17 @@ void CSMPrefs::State::declare()
|
||||
declareBool ("show-linenum", "Show Line Numbers", true).
|
||||
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.");
|
||||
declareBool ("wrap-lines", "Wrap Lines", false).
|
||||
setTooltip ("Wrap lines longer than width of script editor.");
|
||||
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");
|
||||
declareEnum ("warnings", "Warning Mode", warningsNormal).
|
||||
addValue ("Ignore", "Do not report warning").
|
||||
addValue (warningsNormal).
|
||||
addValue ("Strcit", "Promote warning to an error");
|
||||
addValue ("Strict", "Promote warning to an error");
|
||||
declareBool ("toolbar", "Show toolbar", true);
|
||||
declareInt ("compile-delay", "Delay between updating of source errors", 100).
|
||||
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);
|
||||
declareEnum ("p-navi", "Primary Camera Navigation Button", left).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 ("s-edit", "Secondary Editing Button", cRight).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);
|
||||
declareInt ("scene-delay", "Tooltip delay in milliseconds", 500).
|
||||
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)
|
||||
|
@ -1,4 +1,4 @@
|
||||
#ifndef CSV_PREFS_STATE_H
|
||||
#ifndef CSM_PREFS_STATE_H
|
||||
#define CSM_PREFS_STATE_H
|
||||
|
||||
#include <map>
|
||||
|
123
apps/opencs/model/tools/gmstcheck.cpp
Normal file
123
apps/opencs/model/tools/gmstcheck.cpp
Normal file
@ -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";
|
||||
}
|
||||
}
|
34
apps/opencs/model/tools/gmstcheck.hpp
Normal file
34
apps/opencs/model/tools/gmstcheck.hpp
Normal file
@ -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
|
79
apps/opencs/model/tools/journalcheck.cpp
Normal file
79
apps/opencs/model/tools/journalcheck.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
35
apps/opencs/model/tools/journalcheck.hpp
Normal file
35
apps/opencs/model/tools/journalcheck.hpp
Normal file
@ -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
|
||||
{
|
||||
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())
|
||||
{
|
||||
|
@ -99,6 +99,7 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa
|
||||
|
||||
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
|
||||
ref.mRefNum.mContentFile = 0;
|
||||
ref.mNew = false;
|
||||
|
||||
CSMWorld::Record<CSMWorld::CellRef> newRecord (
|
||||
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref);
|
||||
@ -224,8 +225,6 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
bool foundReverse = false;
|
||||
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
|
||||
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
|
||||
|
||||
// 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
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ int CSMTools::ReportModel::countErrors() const
|
||||
{
|
||||
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)
|
||||
if (iter->mSeverity==CSMDoc::Message::Severity_Error ||
|
||||
iter->mSeverity==CSMDoc::Message::Severity_SeriousError)
|
||||
|
@ -16,7 +16,7 @@ namespace CSMTools
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
std::vector<CSMDoc::Messages::Message> mRows;
|
||||
std::vector<CSMDoc::Message> mRows;
|
||||
|
||||
// Fixed columns
|
||||
enum Columns
|
||||
|
@ -120,24 +120,25 @@ QString CSMTools::Search::flatten (const QString& text) const
|
||||
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)
|
||||
: 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)
|
||||
throw std::logic_error ("Invalid search parameter (string)");
|
||||
}
|
||||
|
||||
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)
|
||||
throw std::logic_error ("Invalid search parameter (RegExp)");
|
||||
}
|
||||
|
||||
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)
|
||||
throw std::logic_error ("invalid search parameter (int)");
|
||||
|
@ -26,7 +26,7 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages
|
||||
return;
|
||||
}
|
||||
|
||||
const ESM::SoundGenerator soundGen = record.get();
|
||||
const ESM::SoundGenerator& soundGen = record.get();
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId);
|
||||
|
||||
if (!soundGen.mCreature.empty())
|
||||
|
@ -29,6 +29,9 @@
|
||||
#include "soundgencheck.hpp"
|
||||
#include "magiceffectcheck.hpp"
|
||||
#include "mergeoperation.hpp"
|
||||
#include "gmstcheck.hpp"
|
||||
#include "topicinfocheck.hpp"
|
||||
#include "journalcheck.hpp"
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
||||
|
441
apps/opencs/model/tools/topicinfocheck.cpp
Normal file
441
apps/opencs/model/tools/topicinfocheck.cpp
Normal file
@ -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);
|
||||
}
|
95
apps/opencs/model/tools/topicinfocheck.hpp
Normal file
95
apps/opencs/model/tools/topicinfocheck.hpp
Normal file
@ -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 <cmath>
|
||||
|
||||
#include <ostream>
|
||||
#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 (const std::pair<int, int>& coordinates)
|
||||
: mX (coordinates.first), mY (coordinates.second) {}
|
||||
|
||||
int CSMWorld::CellCoordinates::getX() const
|
||||
{
|
||||
return mX;
|
||||
@ -49,6 +54,13 @@ std::pair<CSMWorld::CellCoordinates, bool> CSMWorld::CellCoordinates::fromId (
|
||||
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)
|
||||
{
|
||||
return left.getX()==right.getX() && left.getY()==right.getY();
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
@ -19,6 +20,8 @@ namespace CSMWorld
|
||||
|
||||
CellCoordinates (int x, int y);
|
||||
|
||||
CellCoordinates (const std::pair<int, int>& coordinates);
|
||||
|
||||
int getX() const;
|
||||
|
||||
int getY() const;
|
||||
@ -34,6 +37,8 @@ namespace CSMWorld
|
||||
///
|
||||
/// \note The worldspace part of \a id is ignored
|
||||
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);
|
||||
|
@ -85,6 +85,7 @@ namespace CSMWorld
|
||||
Display_Enchantment,
|
||||
//CONCRETE TYPES ENDS HERE
|
||||
|
||||
Display_UnsignedInteger8,
|
||||
Display_Integer,
|
||||
Display_Float,
|
||||
Display_Var,
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "universalid.hpp"
|
||||
#include "infoselectwrapper.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
@ -273,8 +274,8 @@ namespace CSMWorld
|
||||
{ ColumnId_InfoList, "Info List" },
|
||||
{ ColumnId_InfoCondition, "Info Conditions" },
|
||||
{ ColumnId_InfoCondFunc, "Function" },
|
||||
{ ColumnId_InfoCondVar, "Func/Variable" },
|
||||
{ ColumnId_InfoCondComp, "Comp" },
|
||||
{ ColumnId_InfoCondVar, "Variable/Object" },
|
||||
{ ColumnId_InfoCondComp, "Relation" },
|
||||
{ ColumnId_InfoCondValue, "Values" },
|
||||
{ ColumnId_OriginalCell, "Original Cell" },
|
||||
|
||||
@ -325,6 +326,10 @@ namespace CSMWorld
|
||||
{ ColumnId_Idle7, "Idle 7" },
|
||||
{ ColumnId_Idle8, "Idle 8" },
|
||||
|
||||
{ ColumnId_RegionWeather, "Weather" },
|
||||
{ ColumnId_WeatherName, "Type" },
|
||||
{ ColumnId_WeatherChance, "Percent Chance" },
|
||||
|
||||
{ ColumnId_UseValue1, "Use value 1" },
|
||||
{ ColumnId_UseValue2, "Use value 2" },
|
||||
{ ColumnId_UseValue3, "Use value 3" },
|
||||
@ -546,18 +551,6 @@ namespace
|
||||
"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)
|
||||
{
|
||||
switch (column)
|
||||
@ -585,10 +578,8 @@ namespace
|
||||
case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;
|
||||
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
|
||||
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
|
||||
case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc;
|
||||
// FIXME: don't have dynamic value enum delegate, use Display_String for now
|
||||
//case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond;
|
||||
case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp;
|
||||
case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;
|
||||
case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
|
@ -325,6 +325,10 @@ namespace CSMWorld
|
||||
ColumnId_Idle7 = 292,
|
||||
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
|
||||
// to extend the number of use values.
|
||||
ColumnId_UseValue1 = 0x10000,
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "record.hpp"
|
||||
#include "commands.hpp"
|
||||
#include "idtableproxymodel.hpp"
|
||||
#include "commandmacro.hpp"
|
||||
|
||||
std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const
|
||||
{
|
||||
@ -171,10 +172,9 @@ void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, cons
|
||||
|
||||
if (modifyCell.get())
|
||||
{
|
||||
mDocument.getUndoStack().beginMacro (modifyData->text());
|
||||
mDocument.getUndoStack().push (modifyData.release());
|
||||
mDocument.getUndoStack().push (modifyCell.release());
|
||||
mDocument.getUndoStack().endMacro();
|
||||
CommandMacro macro (mDocument.getUndoStack());
|
||||
macro.push (modifyData.release());
|
||||
macro.push (modifyCell.release());
|
||||
}
|
||||
else
|
||||
mDocument.getUndoStack().push (modifyData.release());
|
||||
@ -194,9 +194,7 @@ void CSMWorld::CommandDispatcher::executeDelete()
|
||||
|
||||
int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);
|
||||
|
||||
if (rows.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Delete multiple records"));
|
||||
|
||||
CommandMacro macro (mDocument.getUndoStack(), rows.size()>1 ? "Delete multiple records" : "");
|
||||
for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
|
||||
{
|
||||
std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
|
||||
@ -204,7 +202,7 @@ void CSMWorld::CommandDispatcher::executeDelete()
|
||||
|
||||
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 (
|
||||
model.getModelIndex (id, columnIndex).row(),
|
||||
model.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType))).toInt())));
|
||||
@ -212,9 +210,6 @@ void CSMWorld::CommandDispatcher::executeDelete()
|
||||
else
|
||||
mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id));
|
||||
}
|
||||
|
||||
if (rows.size()>1)
|
||||
mDocument.getUndoStack().endMacro();
|
||||
}
|
||||
|
||||
void CSMWorld::CommandDispatcher::executeRevert()
|
||||
@ -231,25 +226,19 @@ void CSMWorld::CommandDispatcher::executeRevert()
|
||||
|
||||
int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);
|
||||
|
||||
if (rows.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Revert multiple records"));
|
||||
|
||||
CommandMacro macro (mDocument.getUndoStack(), rows.size()>1 ? "Revert multiple records" : "");
|
||||
for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
|
||||
{
|
||||
std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
|
||||
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()
|
||||
{
|
||||
if (mExtendedTypes.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records"));
|
||||
CommandMacro macro (mDocument.getUndoStack(), mExtendedTypes.size()>1 ? tr ("Extended delete of multiple records") : "");
|
||||
|
||||
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
|
||||
iter!=mExtendedTypes.end(); ++iter)
|
||||
@ -276,20 +265,15 @@ void CSMWorld::CommandDispatcher::executeExtendedDelete()
|
||||
Misc::StringUtils::lowerCase (record.get().mCell)))
|
||||
continue;
|
||||
|
||||
mDocument.getUndoStack().push (
|
||||
new CSMWorld::DeleteCommand (model, record.get().mId));
|
||||
macro.push (new CSMWorld::DeleteCommand (model, record.get().mId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mExtendedTypes.size()>1)
|
||||
mDocument.getUndoStack().endMacro();
|
||||
}
|
||||
|
||||
void CSMWorld::CommandDispatcher::executeExtendedRevert()
|
||||
{
|
||||
if (mExtendedTypes.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records"));
|
||||
CommandMacro macro (mDocument.getUndoStack(), mExtendedTypes.size()>1 ? tr ("Extended revert of multiple records") : "");
|
||||
|
||||
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
|
||||
iter!=mExtendedTypes.end(); ++iter)
|
||||
@ -313,12 +297,8 @@ void CSMWorld::CommandDispatcher::executeExtendedRevert()
|
||||
Misc::StringUtils::lowerCase (record.get().mCell)))
|
||||
continue;
|
||||
|
||||
mDocument.getUndoStack().push (
|
||||
new CSMWorld::RevertCommand (model, record.get().mId));
|
||||
macro.push (new CSMWorld::RevertCommand (model, record.get().mId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mExtendedTypes.size()>1)
|
||||
mDocument.getUndoStack().endMacro();
|
||||
}
|
||||
|
26
apps/opencs/model/world/commandmacro.cpp
Normal file
26
apps/opencs/model/world/commandmacro.cpp
Normal file
@ -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);
|
||||
}
|
34
apps/opencs/model/world/commandmacro.hpp
Normal file
34
apps/opencs/model/world/commandmacro.hpp
Normal file
@ -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 <QAbstractProxyModel>
|
||||
|
||||
#include "cellcoordinates.hpp"
|
||||
#include "idcollection.hpp"
|
||||
#include "idtable.hpp"
|
||||
#include "idtree.hpp"
|
||||
#include "nestedtablewrapper.hpp"
|
||||
#include "pathgrid.hpp"
|
||||
|
||||
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
|
||||
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))
|
||||
{
|
||||
@ -68,9 +71,6 @@ void CSMWorld::ModifyCommand::undo()
|
||||
|
||||
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())
|
||||
{
|
||||
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
|
||||
@ -114,7 +114,7 @@ void CSMWorld::CreateCommand::setType (UniversalId::Type type)
|
||||
|
||||
void CSMWorld::CreateCommand::redo()
|
||||
{
|
||||
mModel.addRecord (mId, mType);
|
||||
mModel.addRecordWithData (mId, mValues, mType);
|
||||
applyModifications();
|
||||
}
|
||||
|
||||
@ -238,6 +238,29 @@ void CSMWorld::CloneCommand::undo()
|
||||
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)
|
||||
: QUndoCommand (parent), mModel (model), mRow (row)
|
||||
|
@ -153,6 +153,15 @@ namespace CSMWorld
|
||||
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
|
||||
///
|
||||
/// \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/cellref.hpp>
|
||||
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
|
||||
#include "idtable.hpp"
|
||||
#include "idtree.hpp"
|
||||
#include "columnimp.hpp"
|
||||
@ -59,10 +61,13 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
|
||||
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),
|
||||
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;
|
||||
|
||||
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 MapColourColumn<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
|
||||
mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionSounds));
|
||||
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));
|
||||
// FIXME: don't have dynamic value enum delegate, use Display_String for now
|
||||
mTopicInfos.getNestableColumn(index)->addColumn(
|
||||
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String));
|
||||
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_InfoCondVar));
|
||||
mTopicInfos.getNestableColumn(index)->addColumn(
|
||||
new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));
|
||||
mTopicInfos.getNestableColumn(index)->addColumn(
|
||||
@ -933,7 +946,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
|
||||
|
||||
bool unhandledRecord = false;
|
||||
|
||||
switch (n.val)
|
||||
switch (n.intval)
|
||||
{
|
||||
case ESM::REC_GLOB: mGlobals.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
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
const Fallback::Map* CSMWorld::Data::getFallbackMap() const
|
||||
{
|
||||
return mFallbackMap;
|
||||
}
|
||||
|
@ -58,6 +58,11 @@ namespace VFS
|
||||
class Manager;
|
||||
}
|
||||
|
||||
namespace Fallback
|
||||
{
|
||||
class Map;
|
||||
}
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
@ -104,6 +109,7 @@ namespace CSMWorld
|
||||
IdCollection<ESM::Filter> mFilters;
|
||||
Collection<MetaData> mMetaData;
|
||||
const ResourcesManager& mResourcesManager;
|
||||
const Fallback::Map* mFallbackMap;
|
||||
std::vector<QAbstractItemModel *> mModels;
|
||||
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
|
||||
ESM::ESMReader *mReader;
|
||||
@ -132,12 +138,14 @@ namespace CSMWorld
|
||||
|
||||
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();
|
||||
|
||||
const VFS::Manager* getVFS() const;
|
||||
|
||||
const Fallback::Map* getFallbackMap() const;
|
||||
|
||||
boost::shared_ptr<Resource::ResourceSystem> getResourceSystem();
|
||||
|
||||
boost::shared_ptr<const Resource::ResourceSystem> getResourceSystem() const;
|
||||
|
2336
apps/opencs/model/world/defaultgmsts.cpp
Normal file
2336
apps/opencs/model/world/defaultgmsts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
34
apps/opencs/model/world/defaultgmsts.hpp
Normal file
34
apps/opencs/model/world/defaultgmsts.hpp
Normal file
@ -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);
|
||||
}
|
||||
|
||||
// Hack for Display_InfoCondVar
|
||||
types.push_back(CSMWorld::ColumnBase::Display_InfoCondVar);
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
@ -104,7 +108,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data)
|
||||
QAbstractItemView *popup = new CSVWidget::CompleterPopup();
|
||||
completer->setPopup(popup); // The completer takes ownership of the popup
|
||||
completer->setMaxVisibleItems(10);
|
||||
|
||||
|
||||
mCompleters[current->first] = completer;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm/cellid.hpp>
|
||||
|
||||
#include "collectionbase.hpp"
|
||||
#include "columnbase.hpp"
|
||||
|
||||
@ -149,6 +151,23 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type
|
||||
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,
|
||||
const std::string& destination,
|
||||
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, "");
|
||||
|
||||
if (id[0]=='#')
|
||||
id = "sys::default";
|
||||
id = ESM::CellId::sDefaultWorldspace;
|
||||
|
||||
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);
|
||||
///< \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,
|
||||
const std::string& destination,
|
||||
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
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
893
apps/opencs/model/world/infoselectwrapper.cpp
Normal file
893
apps/opencs/model/world/infoselectwrapper.cpp
Normal file
@ -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;
|
||||
}
|
243
apps/opencs/model/world/infoselectwrapper.hpp
Normal file
243
apps/opencs/model/world/infoselectwrapper.hpp
Normal file
@ -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 "pathgrid.hpp"
|
||||
#include "info.hpp"
|
||||
#include "infoselectwrapper.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
@ -26,20 +27,8 @@ namespace CSMWorld
|
||||
point.mConnectionNum = 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);
|
||||
pathgrid.mData.mS2 += 1; // increment the number of points
|
||||
pathgrid.mData.mS2 = pathgrid.mPoints.size();
|
||||
|
||||
record.setModified (pathgrid);
|
||||
}
|
||||
@ -53,28 +42,10 @@ namespace CSMWorld
|
||||
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (points.size()))
|
||||
throw std::runtime_error ("index out of range");
|
||||
|
||||
// deleting a point should trigger re-indexing of the edges
|
||||
// dangling edges are not allowed and hence removed
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// Do not remove dangling edges, does not work with current undo mechanism
|
||||
// Do not automatically adjust indices, what would be done with dangling edges?
|
||||
points.erase(points.begin()+rowToRemove);
|
||||
pathgrid.mData.mS2 -= 1; // decrement the number of points
|
||||
pathgrid.mData.mS2 = pathgrid.mPoints.size();
|
||||
|
||||
record.setModified (pathgrid);
|
||||
}
|
||||
@ -83,14 +54,8 @@ namespace CSMWorld
|
||||
const NestedTableWrapperBase& nestedTable) const
|
||||
{
|
||||
Pathgrid pathgrid = record.get();
|
||||
|
||||
pathgrid.mPoints =
|
||||
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;
|
||||
pathgrid.mPoints = static_cast<const NestedTableWrapper<ESM::Pathgrid::PointList> &>(nestedTable).mNestedTable;
|
||||
pathgrid.mData.mS2 = pathgrid.mPoints.size();
|
||||
|
||||
record.setModified (pathgrid);
|
||||
}
|
||||
@ -98,7 +63,7 @@ namespace CSMWorld
|
||||
NestedTableWrapperBase* PathgridPointListAdapter::table(const Record<Pathgrid>& record) const
|
||||
{
|
||||
// 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,
|
||||
@ -146,7 +111,6 @@ namespace CSMWorld
|
||||
|
||||
PathgridEdgeListAdapter::PathgridEdgeListAdapter () {}
|
||||
|
||||
// ToDo: seems to be auto-sorted in the dialog table display after insertion
|
||||
void PathgridEdgeListAdapter::addRow(Record<Pathgrid>& record, int position) const
|
||||
{
|
||||
Pathgrid pathgrid = record.get();
|
||||
@ -217,7 +181,6 @@ namespace CSMWorld
|
||||
}
|
||||
}
|
||||
|
||||
// ToDo: detect duplicates in mEdges
|
||||
void PathgridEdgeListAdapter::setData(Record<Pathgrid>& record,
|
||||
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
|
||||
std::map<std::string, int>::iterator iter = reactions.begin();
|
||||
for(int i = 0; i < rowToRemove; ++i)
|
||||
iter++;
|
||||
++iter;
|
||||
reactions.erase(iter);
|
||||
|
||||
record.setModified (faction);
|
||||
@ -314,7 +277,7 @@ namespace CSMWorld
|
||||
// WARNING: Assumed that the table view has the same order as std::map
|
||||
std::map<std::string, int>::const_iterator iter = reactions.begin();
|
||||
for(int i = 0; i < subRowIndex; ++i)
|
||||
iter++;
|
||||
++iter;
|
||||
switch (subColIndex)
|
||||
{
|
||||
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
|
||||
std::map<std::string, int>::iterator iter = reactions.begin();
|
||||
for(int i = 0; i < subRowIndex; ++i)
|
||||
iter++;
|
||||
++iter;
|
||||
|
||||
std::string factionId = (*iter).first;
|
||||
int reaction = (*iter).second;
|
||||
@ -529,16 +492,6 @@ namespace CSMWorld
|
||||
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 () {}
|
||||
|
||||
void InfoConditionAdapter::addRow(Record<Info>& record, int position) const
|
||||
@ -547,11 +500,11 @@ namespace CSMWorld
|
||||
|
||||
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
|
||||
|
||||
// blank row
|
||||
// default row
|
||||
ESM::DialInfo::SelectStruct condStruct;
|
||||
condStruct.mSelectRule = "00000";
|
||||
condStruct.mSelectRule = "01000";
|
||||
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);
|
||||
|
||||
@ -589,89 +542,6 @@ namespace CSMWorld
|
||||
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,
|
||||
int subRowIndex, int subColIndex) const
|
||||
{
|
||||
@ -682,70 +552,36 @@ namespace CSMWorld
|
||||
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
|
||||
throw std::runtime_error ("index out of range");
|
||||
|
||||
ConstInfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);
|
||||
|
||||
switch (subColIndex)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
char condType = conditions[subRowIndex].mSelectRule[1];
|
||||
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?
|
||||
}
|
||||
return infoSelectWrapper.getFunctionName();
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (conditions[subRowIndex].mSelectRule[1] == '1')
|
||||
{
|
||||
// throws an exception if the encoding is not found
|
||||
return sEncToInfoFunc.at(conditions[subRowIndex].mSelectRule.substr(2, 2)).c_str();
|
||||
}
|
||||
if (infoSelectWrapper.hasVariable())
|
||||
return QString(infoSelectWrapper.getVariableName().c_str());
|
||||
else
|
||||
return QString(conditions[subRowIndex].mSelectRule.substr(5).c_str());
|
||||
return "";
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
char compType = conditions[subRowIndex].mSelectRule[4];
|
||||
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?
|
||||
}
|
||||
return infoSelectWrapper.getRelationType();
|
||||
}
|
||||
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_Short:
|
||||
case ESM::VT_Long:
|
||||
{
|
||||
return conditions[subRowIndex].mValue.getInteger();
|
||||
return infoSelectWrapper.getVariant().getInteger();
|
||||
}
|
||||
case ESM::VT_Float:
|
||||
{
|
||||
return conditions[subRowIndex].mValue.getFloat();
|
||||
return infoSelectWrapper.getVariant().getFloat();
|
||||
}
|
||||
default: return QVariant();
|
||||
}
|
||||
@ -764,101 +600,63 @@ namespace CSMWorld
|
||||
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
|
||||
throw std::runtime_error ("index out of range");
|
||||
|
||||
InfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);
|
||||
bool conversionResult = false;
|
||||
|
||||
switch (subColIndex)
|
||||
{
|
||||
case 0:
|
||||
case 0: // Function
|
||||
{
|
||||
// See sInfoCondFunc in columns.cpp for the enum values
|
||||
switch (value.toInt())
|
||||
{
|
||||
// FIXME: when these change the values of the other columns need to change
|
||||
// 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
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (conditions[subRowIndex].mSelectRule[1] == '1')
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
infoSelectWrapper.setFunctionName(static_cast<ConstInfoSelectWrapper::FunctionName>(value.toInt()));
|
||||
|
||||
if (it == sEncToInfoFunc.end())
|
||||
return; // return without saving; TODO: maybe log an error here
|
||||
}
|
||||
else
|
||||
if (infoSelectWrapper.getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric &&
|
||||
infoSelectWrapper.getVariant().getType() != ESM::VT_Int)
|
||||
{
|
||||
// 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());
|
||||
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
|
||||
}
|
||||
|
||||
infoSelectWrapper.update();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 1: // Variable
|
||||
{
|
||||
// See sInfoCondComp in columns.cpp for the enum values
|
||||
switch (value.toInt())
|
||||
{
|
||||
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
|
||||
}
|
||||
infoSelectWrapper.setVariableName(value.toString().toUtf8().constData());
|
||||
infoSelectWrapper.update();
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
case 2: // Relation
|
||||
{
|
||||
switch (conditions[subRowIndex].mValue.getType())
|
||||
infoSelectWrapper.setRelationType(static_cast<ConstInfoSelectWrapper::RelationType>(value.toInt()));
|
||||
infoSelectWrapper.update();
|
||||
break;
|
||||
}
|
||||
case 3: // Value
|
||||
{
|
||||
switch (infoSelectWrapper.getComparisonType())
|
||||
{
|
||||
case ESM::VT_String:
|
||||
case ConstInfoSelectWrapper::Comparison_Numeric:
|
||||
{
|
||||
conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData());
|
||||
// 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;
|
||||
}
|
||||
case ESM::VT_Int:
|
||||
case ESM::VT_Short:
|
||||
case ESM::VT_Long:
|
||||
case ConstInfoSelectWrapper::Comparison_Boolean:
|
||||
case ConstInfoSelectWrapper::Comparison_Integer:
|
||||
{
|
||||
conditions[subRowIndex].mValue.setInteger (value.toInt());
|
||||
break;
|
||||
}
|
||||
case ESM::VT_Float:
|
||||
{
|
||||
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;
|
||||
}
|
||||
default: break;
|
||||
@ -1079,7 +877,7 @@ namespace CSMWorld
|
||||
cell.mAmbi.mFogDensity : QVariant(QVariant::UserType);
|
||||
case 5:
|
||||
{
|
||||
if (isInterior && !behaveLikeExterior && interiorWater)
|
||||
if (isInterior && interiorWater)
|
||||
return cell.mWater;
|
||||
else
|
||||
return QVariant(QVariant::UserType);
|
||||
@ -1145,7 +943,7 @@ namespace CSMWorld
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
if (isInterior && !behaveLikeExterior && interiorWater)
|
||||
if (isInterior && interiorWater)
|
||||
cell.mWater = value.toFloat();
|
||||
else
|
||||
return; // return without saving
|
||||
@ -1191,4 +989,105 @@ namespace CSMWorld
|
||||
{
|
||||
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 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>
|
||||
{
|
||||
public:
|
||||
@ -540,6 +525,31 @@ namespace CSMWorld
|
||||
|
||||
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
|
||||
|
@ -2,7 +2,11 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
CSMWorld::CellRef::CellRef()
|
||||
#include <sstream>
|
||||
|
||||
#include "cellcoordinates.hpp"
|
||||
|
||||
CSMWorld::CellRef::CellRef() : mNew (true)
|
||||
{
|
||||
mRefNum.mIndex = 0;
|
||||
mRefNum.mContentFile = 0;
|
||||
@ -10,8 +14,5 @@ CSMWorld::CellRef::CellRef()
|
||||
|
||||
std::pair<int, int> CSMWorld::CellRef::getCellIndex() const
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
|
||||
return std::make_pair (
|
||||
std::floor (mPos.pos[0]/cellSize), std::floor (mPos.pos[1]/cellSize));
|
||||
return CellCoordinates::coordinatesToCellIndex (mPos.pos[0], mPos.pos[1]);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace CSMWorld
|
||||
std::string mId;
|
||||
std::string mCell;
|
||||
std::string mOriginalCell;
|
||||
bool mNew; // new reference, not counted yet, ref num not assigned yet
|
||||
|
||||
CellRef();
|
||||
|
||||
|
@ -19,6 +19,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||
Cell& cell2 = base ? cell.mBase : cell.mModified;
|
||||
|
||||
CellRef ref;
|
||||
ref.mNew = false;
|
||||
ESM::MovedCellRef mref;
|
||||
bool isDeleted = false;
|
||||
|
||||
|
@ -108,7 +108,7 @@ void CSMWorld::IngredEffectRefIdAdapter::setNestedTable (const RefIdColumn* colu
|
||||
ESM::Ingredient ingredient = record.get();
|
||||
|
||||
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);
|
||||
}
|
||||
@ -120,11 +120,11 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::IngredEffectRefIdAdapter::nestedTabl
|
||||
static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
||||
|
||||
// return the whole struct
|
||||
std::vector<typename ESM::Ingredient::IRDTstruct> wrap;
|
||||
std::vector<ESM::Ingredient::IRDTstruct> wrap;
|
||||
wrap.push_back(record.get().mData);
|
||||
|
||||
// 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,
|
||||
@ -1129,7 +1129,7 @@ void CSMWorld::CreatureAttributesRefIdAdapter::setNestedTable (const RefIdColumn
|
||||
|
||||
// store the whole struct
|
||||
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);
|
||||
}
|
||||
@ -1141,10 +1141,10 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttributesRefIdAdapter::nest
|
||||
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
|
||||
|
||||
// return the whole struct
|
||||
std::vector<typename ESM::Creature::NPDTstruct> wrap;
|
||||
std::vector<ESM::Creature::NPDTstruct> wrap;
|
||||
wrap.push_back(record.get().mData);
|
||||
// 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,
|
||||
@ -1153,7 +1153,7 @@ QVariant CSMWorld::CreatureAttributesRefIdAdapter::getNestedData (const RefIdCol
|
||||
const Record<ESM::Creature>& record =
|
||||
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)
|
||||
return subRowIndex;
|
||||
@ -1235,7 +1235,7 @@ void CSMWorld::CreatureAttackRefIdAdapter::setNestedTable (const RefIdColumn* co
|
||||
|
||||
// store the whole struct
|
||||
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);
|
||||
}
|
||||
@ -1247,10 +1247,10 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttackRefIdAdapter::nestedTa
|
||||
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
|
||||
|
||||
// return the whole struct
|
||||
std::vector<typename ESM::Creature::NPDTstruct> wrap;
|
||||
std::vector<ESM::Creature::NPDTstruct> wrap;
|
||||
wrap.push_back(record.get().mData);
|
||||
// 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,
|
||||
@ -1259,7 +1259,7 @@ QVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn
|
||||
const Record<ESM::Creature>& record =
|
||||
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)
|
||||
throw std::runtime_error ("index out of range");
|
||||
@ -1337,7 +1337,7 @@ QVariant CSMWorld::CreatureMiscRefIdAdapter::getNestedData (const RefIdColumn *c
|
||||
const Record<ESM::Creature>& record =
|
||||
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)
|
||||
{
|
||||
@ -1440,26 +1440,28 @@ void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData
|
||||
Record<ESM::Weapon>& record = static_cast<Record<ESM::Weapon>&> (
|
||||
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon)));
|
||||
|
||||
ESM::Weapon weapon = record.get();
|
||||
|
||||
if (column==mColumns.mType)
|
||||
record.get().mData.mType = value.toInt();
|
||||
weapon.mData.mType = value.toInt();
|
||||
else if (column==mColumns.mHealth)
|
||||
record.get().mData.mHealth = value.toInt();
|
||||
weapon.mData.mHealth = value.toInt();
|
||||
else if (column==mColumns.mSpeed)
|
||||
record.get().mData.mSpeed = value.toFloat();
|
||||
weapon.mData.mSpeed = value.toFloat();
|
||||
else if (column==mColumns.mReach)
|
||||
record.get().mData.mReach = value.toFloat();
|
||||
weapon.mData.mReach = value.toFloat();
|
||||
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])
|
||||
record.get().mData.mChop[1] = value.toInt();
|
||||
weapon.mData.mChop[1] = value.toInt();
|
||||
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])
|
||||
record.get().mData.mSlash[1] = value.toInt();
|
||||
weapon.mData.mSlash[1] = value.toInt();
|
||||
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])
|
||||
record.get().mData.mThrust[1] = value.toInt();
|
||||
weapon.mData.mThrust[1] = value.toInt();
|
||||
else
|
||||
{
|
||||
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 (value.toInt()!=0)
|
||||
record.get().mData.mFlags |= iter->second;
|
||||
weapon.mData.mFlags |= iter->second;
|
||||
else
|
||||
record.get().mData.mFlags &= ~iter->second;
|
||||
weapon.mData.mFlags &= ~iter->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
|
||||
ESM::ContItem newRow = {0, {""}};
|
||||
ESM::ContItem newRow = ESM::ContItem();
|
||||
|
||||
if (position >= (int)list.size())
|
||||
list.push_back(newRow);
|
||||
|
@ -21,7 +21,7 @@ namespace CSMWorld
|
||||
///
|
||||
/// 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.
|
||||
/// 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.
|
||||
|
||||
class TableMimeData : public QMimeData
|
||||
|
@ -14,9 +14,6 @@ Q_DECLARE_METATYPE (boost::filesystem::path)
|
||||
|
||||
#include "ui_filedialog.h"
|
||||
|
||||
class DataFilesModel;
|
||||
class PluginsProxyModel;
|
||||
|
||||
namespace ContentSelectorView
|
||||
{
|
||||
class ContentSelector;
|
||||
|
@ -47,7 +47,7 @@ void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id)
|
||||
|
||||
void CSVDoc::SubView::closeEvent (QCloseEvent *event)
|
||||
{
|
||||
emit updateSubViewIndicies (this);
|
||||
emit updateSubViewIndices (this);
|
||||
}
|
||||
|
||||
std::string CSVDoc::SubView::getTitle() const
|
||||
|
@ -64,7 +64,7 @@ namespace CSVDoc
|
||||
|
||||
void updateTitle();
|
||||
|
||||
void updateSubViewIndicies (SubView *view = 0);
|
||||
void updateSubViewIndices (SubView *view = NULL);
|
||||
|
||||
void universalIdChanged (const CSMWorld::UniversalId& universalId);
|
||||
|
||||
|
@ -343,7 +343,7 @@ void CSVDoc::View::updateTitle()
|
||||
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"];
|
||||
|
||||
@ -373,7 +373,7 @@ void CSVDoc::View::updateSubViewIndicies(SubView *view)
|
||||
else
|
||||
{
|
||||
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)
|
||||
: 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"];
|
||||
|
||||
@ -419,10 +419,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
|
||||
}
|
||||
else
|
||||
{
|
||||
mScroll = new QScrollArea(this);
|
||||
mScroll->setWidgetResizable(true);
|
||||
mScroll->setWidget(&mSubViewWindow);
|
||||
setCentralWidget(mScroll);
|
||||
createScrollArea();
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
QDesktopWidget *dw = QApplication::desktop();
|
||||
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());
|
||||
}
|
||||
}
|
||||
updateWidth(windows["grow-limit"].isTrue(), minWidth);
|
||||
|
||||
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
|
||||
|
||||
updateSubViewIndicies();
|
||||
updateSubViewIndices();
|
||||
|
||||
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this,
|
||||
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 (updateSubViewIndicies (SubView *)),
|
||||
this, SLOT (updateSubViewIndicies (SubView *)));
|
||||
connect (view, SIGNAL (updateSubViewIndices (SubView *)),
|
||||
this, SLOT (updateSubViewIndices (SubView *)));
|
||||
|
||||
view->show();
|
||||
|
||||
@ -631,7 +603,7 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max)
|
||||
void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
|
||||
{
|
||||
if (*setting=="Windows/hide-subview")
|
||||
updateSubViewIndicies (0);
|
||||
updateSubViewIndices (NULL);
|
||||
else if (*setting=="Windows/mainwindow-scrollbar")
|
||||
{
|
||||
if (setting->toString()!="Grow Only")
|
||||
@ -651,10 +623,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
|
||||
}
|
||||
else
|
||||
{
|
||||
mScroll = new QScrollArea(this);
|
||||
mScroll->setWidgetResizable(true);
|
||||
mScroll->setWidget(&mSubViewWindow);
|
||||
setCentralWidget(mScroll);
|
||||
createScrollArea();
|
||||
}
|
||||
}
|
||||
else if (mScroll)
|
||||
@ -662,7 +631,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
|
||||
mScroll->takeWidget();
|
||||
setCentralWidget (&mSubViewWindow);
|
||||
mScroll->deleteLater();
|
||||
mScroll = 0;
|
||||
mScroll = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -959,3 +928,41 @@ void CSVDoc::View::merge()
|
||||
{
|
||||
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 updateScrollbar();
|
||||
|
||||
void updateWidth(bool isGrowLimit, int minSubViewWidth);
|
||||
void createScrollArea();
|
||||
public:
|
||||
|
||||
View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews);
|
||||
@ -143,7 +144,7 @@ namespace CSVDoc
|
||||
void updateTitle();
|
||||
|
||||
// called when subviews are added or removed
|
||||
void updateSubViewIndicies (SubView *view = 0);
|
||||
void updateSubViewIndices (SubView *view = NULL);
|
||||
|
||||
private slots:
|
||||
|
||||
|
650
apps/opencs/view/render/cameracontroller.cpp
Normal file
650
apps/opencs/view/render/cameracontroller.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
158
apps/opencs/view/render/cameracontroller.hpp
Normal file
158
apps/opencs/view/render/cameracontroller.hpp
Normal file
@ -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 <osg/PositionAttitudeTransform>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Group>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/esm/loadland.hpp>
|
||||
#include <components/sceneutil/pathgridutil.hpp>
|
||||
|
||||
#include "../../model/world/idtable.hpp"
|
||||
#include "../../model/world/columns.hpp"
|
||||
@ -11,7 +16,8 @@
|
||||
#include "../../model/world/refcollection.hpp"
|
||||
#include "../../model/world/cellcoordinates.hpp"
|
||||
|
||||
#include "elements.hpp"
|
||||
#include "mask.hpp"
|
||||
#include "pathgrid.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
|
||||
bool CSVRender::Cell::removeObject (const std::string& id)
|
||||
@ -22,11 +28,18 @@ bool CSVRender::Cell::removeObject (const std::string& id)
|
||||
if (iter==mObjects.end())
|
||||
return false;
|
||||
|
||||
delete iter->second;
|
||||
mObjects.erase (iter);
|
||||
removeObject (iter);
|
||||
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 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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
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);
|
||||
|
||||
@ -63,6 +82,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
|
||||
mCellNode = new osg::Group;
|
||||
rootNode->addChild(mCellNode);
|
||||
|
||||
setCellMarker();
|
||||
|
||||
if (!mDeleted)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
CSVRender::Pathgrid* CSVRender::Cell::getPathgrid() const
|
||||
{
|
||||
return mPathgrid.get();
|
||||
}
|
||||
|
||||
bool CSVRender::Cell::referenceableDataChanged (const QModelIndex& topLeft,
|
||||
const QModelIndex& bottomRight)
|
||||
{
|
||||
@ -161,8 +192,8 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
|
||||
// perform update and remove where needed
|
||||
bool modified = false;
|
||||
|
||||
for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
|
||||
iter!=mObjects.end(); ++iter)
|
||||
std::map<std::string, Object *>::iterator iter = mObjects.begin();
|
||||
while (iter!=mObjects.end())
|
||||
{
|
||||
if (iter->second->referenceDataChanged (topLeft, bottomRight))
|
||||
modified = true;
|
||||
@ -171,23 +202,30 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
|
||||
|
||||
if (iter2!=ids.end())
|
||||
{
|
||||
if (iter2->second)
|
||||
{
|
||||
removeObject (iter->first);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
bool deleted = iter2->second;
|
||||
ids.erase (iter2);
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
iter = removeObject (iter);
|
||||
modified = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
// add new objects
|
||||
for (std::map<std::string, bool>::iterator iter (ids.begin()); iter!=ids.end(); ++iter)
|
||||
{
|
||||
mObjects.insert (std::make_pair (
|
||||
iter->first, new Object (mData, mCellNode, iter->first, false)));
|
||||
if (!iter->second)
|
||||
{
|
||||
mObjects.insert (std::make_pair (
|
||||
iter->first, new Object (mData, mCellNode, iter->first, false)));
|
||||
|
||||
modified = true;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
@ -228,9 +266,19 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
|
||||
return addObjects (start, end);
|
||||
}
|
||||
|
||||
void CSVRender::Cell::pathgridModified()
|
||||
{
|
||||
mPathgrid->recreateGeometry();
|
||||
}
|
||||
|
||||
void CSVRender::Cell::pathgridRemoved()
|
||||
{
|
||||
mPathgrid->removeGeometry();
|
||||
}
|
||||
|
||||
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());
|
||||
iter!=mObjects.end(); ++iter)
|
||||
@ -247,6 +295,49 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode)
|
||||
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)
|
||||
@ -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
|
||||
{
|
||||
return mCoordinates;
|
||||
@ -276,3 +385,53 @@ bool CSVRender::Cell::isDeleted() const
|
||||
{
|
||||
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 "cellarrow.hpp"
|
||||
#include "cellmarker.hpp"
|
||||
#include "cellborder.hpp"
|
||||
|
||||
class QModelIndex;
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Group;
|
||||
class Geometry;
|
||||
class Geode;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
@ -31,6 +35,9 @@ namespace CSMWorld
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
class Pathgrid;
|
||||
class TagBase;
|
||||
|
||||
class Cell
|
||||
{
|
||||
CSMWorld::Data& mData;
|
||||
@ -40,13 +47,22 @@ namespace CSVRender
|
||||
std::auto_ptr<Terrain::TerrainGrid> mTerrain;
|
||||
CSMWorld::CellCoordinates mCoordinates;
|
||||
std::auto_ptr<CellArrow> mCellArrows[4];
|
||||
std::auto_ptr<CellMarker> mCellMarker;
|
||||
std::auto_ptr<CellBorder> mCellBorder;
|
||||
std::auto_ptr<Pathgrid> mPathgrid;
|
||||
bool mDeleted;
|
||||
int mSubMode;
|
||||
unsigned int mSubModeElementMask;
|
||||
|
||||
/// Ignored if cell does not have an object with the given ID.
|
||||
///
|
||||
/// \return Was the object deleted?
|
||||
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.
|
||||
///
|
||||
/// \return Have any objects been added?
|
||||
@ -70,6 +86,9 @@ namespace CSVRender
|
||||
|
||||
~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
|
||||
/// this cell?
|
||||
bool referenceableDataChanged (const QModelIndex& topLeft,
|
||||
@ -91,14 +110,35 @@ namespace CSVRender
|
||||
/// this cell?
|
||||
bool referenceAdded (const QModelIndex& parent, int start, int end);
|
||||
|
||||
void pathgridModified();
|
||||
|
||||
void pathgridRemoved();
|
||||
|
||||
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);
|
||||
|
||||
/// \brief Set marker for this cell.
|
||||
void setCellMarker();
|
||||
|
||||
/// Returns 0, 0 in case of an unpaged cell.
|
||||
CSMWorld::CellCoordinates getCoordinates() 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/PrimitiveSet>
|
||||
|
||||
#include "elements.hpp"
|
||||
#include "mask.hpp"
|
||||
|
||||
CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow)
|
||||
: TagBase (Element_CellArrow), mArrow (arrow)
|
||||
: TagBase (Mask_CellArrow), mArrow (arrow)
|
||||
{}
|
||||
|
||||
CSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const
|
||||
@ -165,8 +165,7 @@ CSVRender::CellArrow::CellArrow (osg::Group *cellNode, Direction direction,
|
||||
|
||||
mParentNode->addChild (mBaseNode);
|
||||
|
||||
// 0x1 reserved for separating cull and update visitors
|
||||
mBaseNode->setNodeMask (Element_CellArrow<<1);
|
||||
mBaseNode->setNodeMask (Mask_CellArrow);
|
||||
|
||||
adjustTransform();
|
||||
buildShape();
|
||||
|
96
apps/opencs/view/render/cellborder.cpp
Normal file
96
apps/opencs/view/render/cellborder.cpp
Normal file
@ -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;
|
||||
}
|
54
apps/opencs/view/render/cellborder.hpp
Normal file
54
apps/opencs/view/render/cellborder.hpp
Normal file
@ -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
|
91
apps/opencs/view/render/cellmarker.cpp
Normal file
91
apps/opencs/view/render/cellmarker.cpp
Normal file
@ -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);
|
||||
}
|
68
apps/opencs/view/render/cellmarker.hpp
Normal file
68
apps/opencs/view/render/cellmarker.hpp
Normal file
@ -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;
|
||||
}
|
||||
|
||||
bool CSVRender::EditMode::secondaryEditStartDrag (osg::ref_ptr<TagBase> tag)
|
||||
bool CSVRender::EditMode::secondaryEditStartDrag (const QPoint& pos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CSVRender::EditMode::primarySelectStartDrag (osg::ref_ptr<TagBase> tag)
|
||||
bool CSVRender::EditMode::primarySelectStartDrag (const QPoint& pos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CSVRender::EditMode::secondarySelectStartDrag (osg::ref_ptr<TagBase> tag)
|
||||
bool CSVRender::EditMode::secondarySelectStartDrag (const QPoint& pos)
|
||||
{
|
||||
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() {}
|
||||
|
||||
@ -67,6 +67,11 @@ void CSVRender::EditMode::dragWheel (int diff, double speedFactor) {}
|
||||
|
||||
void CSVRender::EditMode::dragEnterEvent (QDragEnterEvent *event) {}
|
||||
|
||||
void CSVRender::EditMode::dropEvent (QDropEvent* event) {}
|
||||
void CSVRender::EditMode::dropEvent (QDropEvent *event) {}
|
||||
|
||||
void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {}
|
||||
|
||||
int CSVRender::EditMode::getSubMode() const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -8,10 +8,12 @@
|
||||
class QDragEnterEvent;
|
||||
class QDropEvent;
|
||||
class QDragMoveEvent;
|
||||
class QPoint;
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
class WorldspaceWidget;
|
||||
struct WorldspaceHitResult;
|
||||
class TagBase;
|
||||
|
||||
class EditMode : public CSVWidget::ModeButton
|
||||
@ -38,42 +40,42 @@ namespace CSVRender
|
||||
virtual void setEditLock (bool locked);
|
||||
|
||||
/// Default-implementation: Ignored.
|
||||
virtual void primaryEditPressed (osg::ref_ptr<TagBase> tag);
|
||||
virtual void primaryEditPressed (const WorldspaceHitResult& hit);
|
||||
|
||||
/// Default-implementation: Ignored.
|
||||
virtual void secondaryEditPressed (osg::ref_ptr<TagBase> tag);
|
||||
virtual void secondaryEditPressed (const WorldspaceHitResult& hit);
|
||||
|
||||
/// Default-implementation: Ignored.
|
||||
virtual void primarySelectPressed (osg::ref_ptr<TagBase> tag);
|
||||
virtual void primarySelectPressed (const WorldspaceHitResult& hit);
|
||||
|
||||
/// Default-implementation: Ignored.
|
||||
virtual void secondarySelectPressed (osg::ref_ptr<TagBase> tag);
|
||||
virtual void secondarySelectPressed (const WorldspaceHitResult& hit);
|
||||
|
||||
/// Default-implementation: ignore and return false
|
||||
///
|
||||
/// \return Drag accepted?
|
||||
virtual bool primaryEditStartDrag (osg::ref_ptr<TagBase> tag);
|
||||
virtual bool primaryEditStartDrag (const QPoint& pos);
|
||||
|
||||
/// Default-implementation: ignore and return false
|
||||
///
|
||||
/// \return Drag accepted?
|
||||
virtual bool secondaryEditStartDrag (osg::ref_ptr<TagBase> tag);
|
||||
virtual bool secondaryEditStartDrag (const QPoint& pos);
|
||||
|
||||
/// Default-implementation: ignore and return false
|
||||
///
|
||||
/// \return Drag accepted?
|
||||
virtual bool primarySelectStartDrag (osg::ref_ptr<TagBase> tag);
|
||||
virtual bool primarySelectStartDrag (const QPoint& pos);
|
||||
|
||||
/// Default-implementation: ignore and return false
|
||||
///
|
||||
/// \return Drag accepted?
|
||||
virtual bool secondarySelectStartDrag (osg::ref_ptr<TagBase> tag);
|
||||
virtual bool secondarySelectStartDrag (const QPoint& pos);
|
||||
|
||||
/// 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
|
||||
virtual void dragCompleted();
|
||||
virtual void dragCompleted(const QPoint& pos);
|
||||
|
||||
/// Default-implementation: ignored
|
||||
///
|
||||
@ -88,10 +90,13 @@ namespace CSVRender
|
||||
virtual void dragEnterEvent (QDragEnterEvent *event);
|
||||
|
||||
/// Default-implementation: ignored
|
||||
virtual void dropEvent (QDropEvent* event);
|
||||
virtual void dropEvent (QDropEvent *event);
|
||||
|
||||
/// Default-implementation: ignored
|
||||
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…
x
Reference in New Issue
Block a user