Merge pull request #172 from TES3MP/master

Test coverity_scan
coverity_scan
Stanislav Zhukov 7 years ago committed by GitHub
commit 1d84cbe963

31
.gitignore vendored

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

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "extern/breakpad"]
path = extern/breakpad
url = https://chromium.googlesource.com/breakpad/breakpad

@ -1,6 +1,7 @@
os: os:
- linux - linux
# - osx # - osx
osx_image: xcode8.2
language: cpp language: cpp
sudo: required sudo: required
dist: trusty dist: trusty
@ -11,17 +12,37 @@ branches:
- /openmw-.*$/ - /openmw-.*$/
env: env:
global: global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - macos_qt_formula=qt@5.5
# via the "travis encrypt" command using the project repo's public key - secure: NZmvVuA0O9NJXVQ12tXQZHDJC2mbFgYNFcsicw0DgW1It2Nk5hxIkF0pfu4/Z59mhQuOPgRVjl5b0FKy2Axh0gkWc1DJEXGwNaiW5lpTMNWR1LJG5rxa8LrDUpFkycpbzfAFuTUZu5z3iYVv64XzELvBuqNGhPMu1LeBnrlech0jFNjkR9p5qtJGWb8zYcPMCC57rig8a9g1ABoVYS6UXjrKpx0946ZLRsE5ukc9pXsypGwPmOMyfzZkxxzIqFaxoE5JIEdaJTWba/6Za315ozYYIi/N35ROI1YAv5GHRe/Iw9XAa4vQpbDzjM7ZSsZdTvvQsSU598gD2xC6jFUKSrpW6GZKwM2x236fZLGnOk5Uw7DUbG+AwpcEmxBwoy9PjBl9ZF3tJykI0gROewCy8MODhdsVMKr1HGIMVBIJySm/RnNqtoDbYV8mYnSl5b8rwJiCajoiR8Zuv4CIfGneeH1a3DOQDPH/qkDsU6ilzF4ANsBlMUUpgY653KBMBmTlNuVZSH527tnD7Fg6JgHVuSQkTbRa1vSkR7Zcre604RZcAoaEdbX3bhVDasPPghU/I742L0RH3oQNlR09pPBDZ8kG7ydl4aPHwpCWnvXNM1vgxtGvnYLztwrse7IoaRXRYiMFmrso78WhMWUDKgvY4wV9aeUu0DtnMezZVIQwCKg=
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
addons: addons:
apt:
sources:
- sourceline: 'ppa:openmw/openmw'
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.6
packages: [
# Dev
clang-3.6, libunshield-dev, libtinyxml-dev,
g++-6,
# Tests
libgtest-dev, google-mock,
# Boost
libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, libboost-thread-dev,
# FFmpeg
libavcodec-dev, libavformat-dev, libavutil-dev, libswscale-dev,
# Audio & Video
libsdl2-dev, qtbase5-dev, libopenal-dev,
# The other ones from OpenMW ppa
libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev
]
coverity_scan: coverity_scan:
project: project:
name: "OpenMW/openmw" name: "TES3MP/openmw-tes3mp"
description: "<Your project description here>" description: "<Your project description here>"
notification_email: scrawl@baseoftrash.de notification_email: stas5978@gmail.com
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE" build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE"
build_command: "make -j2" build_command: "make -j3"
branch_pattern: coverity_scan branch_pattern: coverity_scan
matrix: matrix:
include: include:
@ -32,27 +53,24 @@ matrix:
allow_failures: allow_failures:
- env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 " - env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 "
before_install: before_install:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi - ./CI/before_install.${TRAVIS_OS_NAME}.sh
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi before_script: ./CI/before_script.${TRAVIS_OS_NAME}.sh
before_script:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_script.linux.sh; fi
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
script: script:
- cd ./build - cd ./build
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
notifications: #notifications:
recipients: # email:
- corrmage+travis-ci@gmail.com # recipients:
email: # - corrmage+travis-ci@gmail.com
on_success: change # on_success: change
on_failure: always # on_failure: always
irc: # irc:
channels: # channels:
- "chat.freenode.net#openmw" # - "chat.freenode.net#openmw"
on_success: change # on_success: change
on_failure: always # on_failure: always
use_notice: true # use_notice: true

@ -14,15 +14,23 @@ Programmers
Adam Hogan (aurix) Adam Hogan (aurix)
Aesylwinn Aesylwinn
aegis
Aleksandar Jovanov Aleksandar Jovanov
Alex Haddad (rainChu) Alex Haddad (rainChu)
Alex McKibben (WeirdSexy) Alex McKibben
alexanderkjall
Alexander Nadeau (wareya) Alexander Nadeau (wareya)
Alexander Olofsson (Ace) Alexander Olofsson (Ace)
Allofich
AnyOldName3
Aussiemon
Austin Salgat (Salgat)
Artem Kotsynyak (greye) Artem Kotsynyak (greye)
artemutin artemutin
Arthur Moore (EmperorArthur) Arthur Moore (EmperorArthur)
Assumeru
athile athile
Ben Shealy (bentsherman)
Bret Curtis (psi29a) Bret Curtis (psi29a)
Britt Mathis (galdor557) Britt Mathis (galdor557)
cc9cii cc9cii
@ -31,6 +39,7 @@ Programmers
Cory F. Cohen (cfcohen) Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam) Cris Mihalache (Mirceam)
darkf darkf
devnexen
Dieho Dieho
Dmitry Shkurskiy (endorph) Dmitry Shkurskiy (endorph)
Douglas Diniz (Dgdiniz) Douglas Diniz (Dgdiniz)
@ -44,6 +53,7 @@ Programmers
eroen eroen
escondida escondida
Evgeniy Mineev (sandstranger) Evgeniy Mineev (sandstranger)
Federico Guerra (FedeWar)
Fil Krynicki (filkry) Fil Krynicki (filkry)
Gašper Sedej Gašper Sedej
gugus/gus gugus/gus
@ -62,13 +72,18 @@ Programmers
John Blomberg (fstp) John Blomberg (fstp)
Jordan Ayers Jordan Ayers
Jordan Milne Jordan Milne
Jules Blok (Armada651)
Julien Voisin (jvoisin/ap0) Julien Voisin (jvoisin/ap0)
Karl-Felix Glatzer (k1ll) Karl-Felix Glatzer (k1ll)
Kevin Poitra (PuppyKevin) Kevin Poitra (PuppyKevin)
Koncord Koncord
Kurnevsky Evgeny (kurnevsky)
Lars Söderberg (Lazaroth) Lars Söderberg (Lazaroth)
lazydev lazydev
Leon Krieg (lkrieg)
Leon Saunders (emoose) Leon Saunders (emoose)
logzero
lohikaarme
Lukasz Gromanowski (lgro) Lukasz Gromanowski (lgro)
Manuel Edelmann (vorenon) Manuel Edelmann (vorenon)
Marc Bouvier (CramitDeFrog) Marc Bouvier (CramitDeFrog)
@ -76,6 +91,7 @@ Programmers
Mark Siewert (mark76) Mark Siewert (mark76)
Marco Melletti (mellotanica) Marco Melletti (mellotanica)
Marco Schulze Marco Schulze
Martin Otto (MAtahualpa)
Mateusz Kołaczek (PL_kolek) Mateusz Kołaczek (PL_kolek)
Mateusz Malisz (malice) Mateusz Malisz (malice)
megaton megaton
@ -83,6 +99,7 @@ Programmers
Michael Mc Donnell Michael Mc Donnell
Michael Papageorgiou (werdanith) Michael Papageorgiou (werdanith)
Michał Bień (Glorf) Michał Bień (Glorf)
Michał Moroz (dragonee)
Miroslav Puda (pakanek) Miroslav Puda (pakanek)
MiroslavR MiroslavR
naclander naclander
@ -92,26 +109,31 @@ Programmers
Nikolay Kasyanov (corristo) Nikolay Kasyanov (corristo)
nobrakal nobrakal
Nolan Poe (nopoe) Nolan Poe (nopoe)
Oleg Chkan (mrcheko)
Paul Cercueil (pcercuei) Paul Cercueil (pcercuei)
Paul McElroy (Greendogo) Paul McElroy (Greendogo)
Pi03k
Pieter van der Kloet (pvdk) Pieter van der Kloet (pvdk)
pkubik pkubik
Radu-Marius Popovici (rpopovici) Radu-Marius Popovici (rpopovici)
rcutmore
rdimesio rdimesio
riothamus riothamus
Rob Cutmore (rcutmore)
Robert MacGregor (Ragora) Robert MacGregor (Ragora)
Rohit Nirmal Rohit Nirmal
Roman Melnik (Kromgart) Roman Melnik (Kromgart)
Roman Proskuryakov (humbug) Roman Proskuryakov (kpp)
Sandy Carter (bwrsandman) Sandy Carter (bwrsandman)
Scott Howard Scott Howard
Sebastian Wick (swick) Sebastian Wick (swick)
Sergey Shambir Sergey Shambir
ShadowRadiance
sir_herrbatka sir_herrbatka
smbas smbas
Stefan Galowicz (bogglez) Stefan Galowicz (bogglez)
Stanislav Bobrov (Jiub) Stanislav Bobrov (Jiub)
stil-t
svaante
Sylvain Thesnieres (Garvek) Sylvain Thesnieres (Garvek)
t6 t6
terrorfisch terrorfisch
@ -123,19 +145,21 @@ Programmers
vocollapse vocollapse
zelurker zelurker
Manual Documentation
------ -------------
Alejandro Sanchez (HiPhish)
Bodillium Bodillium
Bret Curtis (psi29a)
Cramal Cramal
Alejandro Sanchez (HiPhish) Ryan Tucker (Ravenwing)
sir_herrbatka sir_herrbatka
Packagers Packagers
--------- ---------
Alexander Olofsson (Ace) - Windows Alexander Olofsson (Ace) - Windows
Bret Curtis (psi29a) - Ubuntu Linux Bret Curtis (psi29a) - Debian and Ubuntu Linux
Edmondo Tommasina (edmondo) - Gentoo Linux Edmondo Tommasina (edmondo) - Gentoo Linux
Julian Ospald (hasufell) - Gentoo Linux Julian Ospald (hasufell) - Gentoo Linux
Karl-Felix Glatzer (k1ll) - Linux Binaries Karl-Felix Glatzer (k1ll) - Linux Binaries
@ -146,16 +170,16 @@ Packagers
Public Relations and Translations Public Relations and Translations
--------------------------------- ---------------------------------
Alex McKibben (WeirdSexy) - Podcaster
Artem Kotsynyak (greye) - Russian News Writer Artem Kotsynyak (greye) - Russian News Writer
Dawid Lakomy (Vedyimyn) - Polish News Writer
Jim Clauwaert (Zedd) - Public Outreach Jim Clauwaert (Zedd) - Public Outreach
Julien Voisin (jvoisin/ap0) - French News Writer Julien Voisin (jvoisin/ap0) - French News Writer
Tom Koenderink (Okulo) - English News Writer
Lukasz Gromanowski (lgro) - English News Writer Lukasz Gromanowski (lgro) - English News Writer
Martin Otto (Atahualpa) - Podcaster, Public Outreach, German Translator
Mickey Lyle (raevol) - Release Manager Mickey Lyle (raevol) - Release Manager
Pithorn - Chinese News Writer Pithorn - Chinese News Writer
sir_herrbatka - Polish News Writer sir_herrbatka - Polish News Writer
Dawid Lakomy (Vedyimyn) - Polish News Writer Tom Koenderink (Okulo) - English News Writer
Website Website
------- -------
@ -206,7 +230,6 @@ Inactive Contributors
Nekochan Nekochan
pchan3 pchan3
penguinroad penguinroad
psi29a
sergoz sergoz
spyboot spyboot
Star-Demon Star-Demon

@ -1,3 +1,228 @@
0.41.0
------
Bug #1138: Casting water walking doesn't move the player out of the water
Bug #1931: Rocks from blocked passage in Bamz-Amschend, Radacs Forge can reset and cant be removed again.
Bug #2048: Almvisi and Divine Intervention display wrong spell effect
Bug #2054: Show effect-indicator for "instant effect" spells and potions
Bug #2150: Clockwork City door animation problem
Bug #2288: Playback of weapon idle animation not correct
Bug #2410: Stat-review window doesn't display starting spells, powers, or abilities
Bug #2493: Repairing occasionally very slow
Bug #2716: [OSG] Water surface is too transparent from some angles
Bug #2859: [MAC OS X] Cannot exit fullscreen once enabled
Bug #3091: Editor: will not save addon if global variable value type is null
Bug #3277: Editor: Non-functional nested tables in subviews need to be hidden instead of being disabled
Bug #3348: Disabled map markers show on minimap
Bug #3350: Extending selection to instances with same object results in duplicates.
Bug #3353: [Mod] Romance version 3.7 script failed
Bug #3376: [Mod] Vampire Embrace script fails to execute
Bug #3385: Banners don't animate in stormy weather as they do in the original game
Bug #3393: Akulakhan re-enabled after main quest
Bug #3427: Editor: OpenMW-CS instances won´t get deleted
Bug #3451: Feril Salmyn corpse isn't where it is supposed to be
Bug #3497: Zero-weight armor is displayed as "heavy" in inventory tooltip
Bug #3499: Idle animations don't always loop
Bug #3500: Spark showers at Sotha Sil do not appear until you look at the ceiling
Bug #3515: Editor: Moved objects in interior cells are teleported to exterior cells.
Bug #3520: Editor: OpenMW-CS cannot find project file when launching the game
Bug #3521: Armed NPCs don't use correct melee attacks
Bug #3535: Changing cell immediately after dying causes character to freeze.
Bug #3542: Unable to rest if unalerted slaughterfish are in the cell with you
Bug #3549: Blood effects occur even when a hit is resisted
Bug #3551: NPC Todwendy in german version can't interact
Bug #3552: Opening the journal when fonts are missing results in a crash
Bug #3555: SetInvisible command should not apply graphic effect
Bug #3561: Editor: changes from omwaddon are not loaded in [New Addon] mode
Bug #3562: Non-hostile NPCs can be disarmed by stealing their weapons via sneaking
Bug #3564: Editor: openmw-cs verification results
Bug #3568: Items that should be invisible are shown in the inventory
Bug #3574: Alchemy: Alembics and retorts are used in reverse
Bug #3575: Diaglog choices don't work in mw 0.40
Bug #3576: Minor differences in AI reaction to hostile spell effects
Bug #3577: not local nolore dialog test
Bug #3578: Animation Replacer hangs after one cicle/step
Bug #3579: Bound Armor skillups and sounds
Bug #3583: Targetted GetCurrentAiPackage returns 0
Bug #3584: Persuasion bug
Bug #3590: Vendor, Ilen Faveran, auto equips items from stock
Bug #3594: Weather doesn't seem to update correctly in Mournhold
Bug #3598: Saving doesn't save status of objects
Bug #3600: Screen goes black when trying to travel to Sadrith Mora
Bug #3608: Water ripples aren't created when walking on water
Bug #3626: Argonian NPCs swim like khajiits
Bug #3627: Cannot delete "Blessed touch" spell from spellbook
Bug #3634: An enchanted throwing weapon consumes charges from the stack in your inventory. (0.40.0)
Bug #3635: Levelled items in merchants are "re-rolled" (not bug 2952, see inside)
Feature #1118: AI combat: flee
Feature #1596: Editor: Render water
Feature #2042: Adding a non-portable Light to the inventory should cause the player to glow
Feature #3166: Editor: Instance editing mode - rotate sub mode
Feature #3167: Editor: Instance editing mode - scale sub mode
Feature #3420: ess-Importer: player control flags
Feature #3489: You shouldn't be be able to re-cast a bound equipment spell
Feature #3496: Zero-weight boots should play light boot footsteps
Feature #3516: Water Walking should give a "can't cast" message and fail when you are too deep
Feature #3519: Play audio and visual effects for all effects in a spell
Feature #3527: Double spell explosion scaling
Feature #3534: Play particle textures for spell effects
Feature #3539: Make NPCs use opponent's weapon range to decide whether to dodge
Feature #3540: Allow dodging for creatures with "biped" flag
Feature #3545: Drop shadow for items in menu
Feature #3558: Implement same spell range for "on touch" spells as original engine
Feature #3560: Allow using telekinesis with touch spells on objects
Task #3585: Some objects added by Morrowind Rebirth do not display properly their texture
0.40.0
------
Bug #1320: AiWander - Creatures in cells without pathgrids do not wander
Bug #1873: Death events are triggered at the beginning of the death animation
Bug #1996: Resting interrupts magic effects
Bug #2399: Vampires can rest in broad daylight and survive the experience
Bug #2604: Incorrect magicka recalculation
Bug #2721: Telekinesis extends interaction range where it shouldn't
Bug #2981: When waiting, NPCs can go where they wouldn't go normally.
Bug #3045: Esp files containing the letter '#' in the file name cannot be loaded on startup
Bug #3071: Slowfall does not stop momentum when jumping
Bug #3085: Plugins can not replace parent cell references with a cell reference of different type
Bug #3145: Bug with AI Cliff Racer. He will not attack you, unless you put in front of him.
Bug #3149: Editor: Weather tables were missing from regions
Bug #3201: Netch shoots over your head
Bug #3269: If you deselect a mod and try to load a save made inside a cell added by it, you end bellow the terrain in the grid 0/0
Bug #3286: Editor: Script editor tab width
Bug #3329: Teleportation spells cause crash to desktop after build update from 0.37 to 0.38.0
Bug #3331: Editor: Start Scripts table: Adding a script doesn't refresh the list of Start Scripts and allows to add a single script multiple times
Bug #3332: Editor: Scene view: Tool tips only occur when holding the left mouse button
Bug #3340: ESS-Importer does not separate item stacks
Bug #3342: Editor: Creation of pathgrids did not check if the pathgrid already existed
Bug #3346: "Talked to PC" is always 0 for "Hello" dialogue
Bug #3349: AITravel doesn't repeat
Bug #3370: NPCs wandering to invalid locations after training
Bug #3378: "StopCombat" command does not function in vanilla quest
Bug #3384: Battle at Nchurdamz - Larienna Macrina does not stop combat after killing Hrelvesuu
Bug #3388: Monster Respawn tied to Quicksave
Bug #3390: Strange visual effect in Dagoth Ur's chamber
Bug #3391: Inappropriate Blight weather behavior at end of main quest
Bug #3394: Replaced dialogue inherits some of its old data
Bug #3397: Actors that start the game dead always have the same death pose
Bug #3401: Sirollus Saccus sells not glass arrows
Bug #3402: Editor: Weapon data not being properly set
Bug #3405: Mulvisic Othril will not use her chitin throwing stars
Bug #3407: Tanisie Verethi will immediately detect the player
Bug #3408: Improper behavior of ashmire particles
Bug #3412: Ai Wander start time resets when saving/loading the game
Bug #3416: 1st person and 3rd person camera isn't converted from .ess correctly
Bug #3421: Idling long enough while paralyzed sometimes causes character to get stuck
Bug #3423: Sleep interruption inside dungeons too agressive
Bug #3424: Pickpocketing sometimes won't work
Bug #3432: AiFollow / AiEscort durations handled incorrectly
Bug #3434: Dead NPC's and Creatures still contribute to sneak skill increases
Bug #3437: Weather-conditioned dialogue should not play in interiors
Bug #3439: Effects cast by summon stick around after their death
Bug #3440: Parallax maps looks weird
Bug #3443: Class graphic for custom class should be Acrobat
Bug #3446: OpenMW segfaults when using Atrayonis's "Anthology Solstheim: Tomb of the Snow Prince" mod
Bug #3448: After dispelled, invisibility icon is still displayed
Bug #3453: First couple of seconds of NPC speech is muted
Bug #3455: Portable house mods lock player and npc movement up exiting house.
Bug #3456: Equipping an item will undo dispel of constant effect invisibility
Bug #3458: Constant effect restore health doesn't work during Wait
Bug #3466: It is possible to stack multiple scroll effects of the same type
Bug #3471: When two mods delete the same references, many references are not disabled by the engine.
Bug #3473: 3rd person camera can be glitched
Feature #1424: NPC "Face" function
Feature #2974: Editor: Multiple Deletion of Subrecords
Feature #3044: Editor: Render path grid v2
Feature #3362: Editor: Configurable key bindings
Feature #3375: Make sun / moon reflections weather dependent
Feature #3386: Editor: Edit pathgrid
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 0.38.0
------ ------

@ -1,21 +1,24 @@
#!/bin/sh #!/bin/sh
echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang
sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++
if [ "${ANALYZE}" ]; then # build libgtest & libgtest_main
echo "yes" | sudo add-apt-repository "deb http://llvm.org/apt/`lsb_release -sc`/ llvm-toolchain-`lsb_release -sc`-3.6 main"
wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
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
sudo apt-get update -qq
sudo apt-get install -qq libgtest-dev google-mock
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
if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi
sudo mkdir /usr/src/gtest/build sudo mkdir /usr/src/gtest/build
cd /usr/src/gtest/build cd /usr/src/gtest/build
sudo cmake .. -DBUILD_SHARED_LIBS=1 sudo cmake .. -DBUILD_SHARED_LIBS=1
sudo make -j4 sudo make -j4
sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
cd ~/
git clone https://github.com/TES3MP/RakNet
cd RakNet
cmake . -DRAKNET_ENABLE_DLL=OFF -DRAKNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
mkdir ./lib
make -j3 install
cp ./Lib/RakNetLibStatic/libRakNetLibStatic.a ./lib
cd ..
wget https://github.com/zdevito/terra/releases/download/release-2016-03-25/terra-Linux-x86_64-332a506.zip
unzip terra-Linux-x86_64-332a506.zip

@ -1,9 +1,11 @@
#!/bin/sh #!/bin/sh
export CXX=clang++
export CC=clang
brew tap openmw/openmw
brew update 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 $macos_qt_formula
curl https://downloads.openmw.org/osx/dependencies/openmw-deps-0ecece4.zip -o ~/openmw-deps.zip
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

@ -4,5 +4,14 @@ free -m
mkdir build mkdir build
cd build cd build
export CODE_COVERAGE=1 export CODE_COVERAGE=1
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi export RAKNET_ROOT=~/RakNet
${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 export Terra_ROOT=~/terra-Linux-x86_64-332a506
export BUILD_SERVER=OFF
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0;
else
export COMPILER_NAME=gcc
export CXX=g++-6
export CC=gcc-6
export BUILD_SERVER=ON
fi
${ANALYZE}cmake .. -DBUILD_OPENMW_MP=${BUILD_SERVER} -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_BSATOOL=OFF -DBUILD_ESMTOOL=OFF -DBUILD_ESSIMPORTER=OFF -DBUILD_LAUNCHER=OFF -DBUILD_MWINIIMPORTER=OFF -DBUILD_MYGUI_PLUGIN=OFF -DBUILD_OPENCS=OFF -DBUILD_WIZARD=OFF -DBUILD_BROWSER=OFF -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE -DRakNet_LIBRARY_RELEASE=~/RakNet/lib/libRakNetLibStatic.a -DRakNet_LIBRARY_DEBUG=~/RakNet/lib/libRakNetLibStatic.a

@ -1,5 +1,21 @@
#!/bin/bash #!/bin/bash
set -euo pipefail
APPVEYOR=${APPVEYOR:-}
CI=${CI:-}
STEP=${STEP:-}
VERBOSE=""
STRIP=""
SKIP_DOWNLOAD=""
SKIP_EXTRACT=""
KEEP=""
UNITY_BUILD=""
VS_VERSION=""
PLATFORM=""
CONFIGURATION=""
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
ARGSTR=$1 ARGSTR=$1
shift shift
@ -16,10 +32,6 @@ while [ $# -gt 0 ]; do
V ) V )
VERBOSE=true ;; VERBOSE=true ;;
v )
VS_VERSION=$1
shift ;;
d ) d )
SKIP_DOWNLOAD=true ;; SKIP_DOWNLOAD=true ;;
@ -32,6 +44,10 @@ while [ $# -gt 0 ]; do
u ) u )
UNITY_BUILD=true ;; UNITY_BUILD=true ;;
v )
VS_VERSION=$1
shift ;;
p ) p )
PLATFORM=$1 PLATFORM=$1
shift ;; shift ;;
@ -78,9 +94,6 @@ done
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
STRIP="> /dev/null 2>&1" STRIP="> /dev/null 2>&1"
fi fi
if [ -z $VS_VERSION ]; then
VS_VERSION="2013"
fi
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
echo "Running prebuild outside of Appveyor." echo "Running prebuild outside of Appveyor."
@ -90,9 +103,7 @@ if [ -z $APPVEYOR ]; then
else else
echo "Running prebuild in Appveyor." echo "Running prebuild in Appveyor."
cd $APPVEYOR_BUILD_FOLDER 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 fi
run_cmd() { run_cmd() {
@ -105,7 +116,7 @@ run_cmd() {
if [ $RET -ne 0 ]; then if [ $RET -ne 0 ]; then
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
echo "Command $CMD failed, output can be found in `real_pwd`/output.log" echo "Command $CMD failed, output can be found in $(real_pwd)/output.log"
else else
echo echo
echo "Command $CMD failed;" echo "Command $CMD failed;"
@ -184,44 +195,58 @@ add_osg_dlls() {
OSG_PLUGINS="$OSG_PLUGINS $@" OSG_PLUGINS="$OSG_PLUGINS $@"
} }
QT_PLATFORMS=""
add_qt_platform_dlls() {
QT_PLATFORMS="$QT_PLATFORMS $@"
}
if [ -z $PLATFORM ]; then if [ -z $PLATFORM ]; then
PLATFORM=`uname -m` PLATFORM="$(uname -m)"
fi fi
if [ -z $CONFIGURATION ]; then if [ -z $CONFIGURATION ]; then
CONFIGURATION="Debug" CONFIGURATION="Debug"
fi fi
if [ -z $VS_VERSION ]; then
VS_VERSION="2013"
fi
case $VS_VERSION in case $VS_VERSION in
14|2015 ) 14|14.0|2015 )
GENERATOR="Visual Studio 14 2015" GENERATOR="Visual Studio 14 2015"
XP_TOOLSET="v140_xp" XP_TOOLSET="v140_xp"
TOOLSET="v140"
MSVC_VER="14"
MSVC_YEAR="2015"
;; ;;
# 12|2013| 12|12.0|2013 )
* )
GENERATOR="Visual Studio 12 2013" GENERATOR="Visual Studio 12 2013"
XP_TOOLSET="v120_xp" XP_TOOLSET="v120_xp"
TOOLSET="v120"
MSVC_VER="12"
MSVC_YEAR="2013"
;; ;;
esac esac
case $PLATFORM in case $PLATFORM in
x64|x86_64|x86-64|win64|Win64 ) x64|x86_64|x86-64|win64|Win64 )
ARCHNAME=x86-64 ARCHNAME="x86-64"
ARCHSUFFIX=64 ARCHSUFFIX="64"
BITS=64 BITS="64"
BASE_OPTS="-G\"$GENERATOR Win64\"" BASE_OPTS="-G\"$GENERATOR Win64\""
add_cmake_opts "-G\"$GENERATOR Win64\"" add_cmake_opts "-G\"$GENERATOR Win64\""
;; ;;
x32|x86|i686|i386|win32|Win32 ) x32|x86|i686|i386|win32|Win32 )
ARCHNAME=x86 ARCHNAME="x86"
ARCHSUFFIX=86 ARCHSUFFIX="86"
BITS=32 BITS="32"
BASE_OPTS="-G\"$GENERATOR\" -T$XP_TOOLSET" BASE_OPTS="-G\"$GENERATOR\""
add_cmake_opts "-G\"$GENERATOR\"" -T$XP_TOOLSET add_cmake_opts "-G\"$GENERATOR\""
;; ;;
* ) * )
@ -230,35 +255,38 @@ case $PLATFORM in
;; ;;
esac esac
if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi
case $CONFIGURATION in case $CONFIGURATION in
debug|Debug|DEBUG ) debug|Debug|DEBUG )
CONFIGURATION=Debug CONFIGURATION=Debug
BUILD_CONFIG=Debug
;; ;;
release|Release|RELEASE ) release|Release|RELEASE )
CONFIGURATION=Release CONFIGURATION=Release
BUILD_CONFIG=Release
;; ;;
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO ) relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
CONFIGURATION=RelWithDebInfo CONFIGURATION=Release
BUILD_CONFIG=RelWithDebInfo
;; ;;
esac esac
if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi
echo echo
echo "==========================" echo "==================================="
echo "Starting prebuild on win$BITS" echo "Starting prebuild on MSVC${MSVC_YEAR} WIN${BITS}"
echo "==========================" echo "==================================="
echo echo
# cd OpenMW/AppVeyor-test # cd OpenMW/AppVeyor-test
mkdir -p deps mkdir -p deps
cd deps cd deps
DEPS="`pwd`" DEPS="$(pwd)"
if [ -z $SKIP_DOWNLOAD ]; then if [ -z $SKIP_DOWNLOAD ]; then
echo "Downloading dependency packages." echo "Downloading dependency packages."
@ -266,162 +294,165 @@ if [ -z $SKIP_DOWNLOAD ]; then
# Boost # Boost
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
download "Boost 1.58.0" \ download "Boost 1.61.0" \
http://sourceforge.net/projects/boost/files/boost-binaries/1.58.0/boost_1_58_0-msvc-12.0-$BITS.exe \ "http://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \
boost-1.58.0-win$BITS.exe "boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe"
fi fi
# Bullet # Bullet
download "Bullet 2.83.5" \ download "Bullet 2.86" \
http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.83.5-win$BITS.7z \ "http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \
Bullet-2.83.5-win$BITS.7z "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z"
# FFmpeg # FFmpeg
download "FFmpeg 2.5.2" \ download "FFmpeg 3.0.1" \
http://ffmpeg.zeranoe.com/builds/win$BITS/shared/ffmpeg-2.5.2-win$BITS-shared.7z \ "http://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.0.1-win${BITS}-shared.7z" \
ffmpeg$BITS-2.5.2.7z \ "ffmpeg-3.0.1-win${BITS}.7z" \
http://ffmpeg.zeranoe.com/builds/win$BITS/dev/ffmpeg-2.5.2-win$BITS-dev.7z \ "http://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.0.1-win${BITS}-dev.7z" \
ffmpeg$BITS-2.5.2-dev.7z "ffmpeg-3.0.1-dev-win${BITS}.7z"
# MyGUI # MyGUI
download "MyGUI 3.2.2" \ download "MyGUI 3.2.3-git" \
http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-win$BITS.7z \ "http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.3-git-msvc${MSVC_YEAR}-win${BITS}.7z" \
MyGUI-3.2.2-win$BITS.7z "MyGUI-3.2.3-git-msvc${MSVC_YEAR}-win${BITS}.7z"
# OpenAL # OpenAL
download "OpenAL-Soft 1.16.0" \ download "OpenAL-Soft 1.17.2" \
http://kcat.strangesoft.net/openal-binaries/openal-soft-1.16.0-bin.zip \ "http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \
OpenAL-Soft-1.16.0.zip "OpenAL-Soft-1.17.2.zip"
# OSG # OSG
download "OpenSceneGraph 3.3.8" \ download "OpenSceneGraph 3.4.0-scrawl" \
http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.3.8-win$BITS.7z \ "http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \
OSG-3.3.8-win$BITS.7z "OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z"
# Qt # Qt
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
download "Qt 4.8.6" \ if [ $BITS == "64" ]; then
http://sourceforge.net/projects/qt64ng/files/qt/$ARCHNAME/4.8.6/msvc2013/qt-4.8.6-x$ARCHSUFFIX-msvc2013.7z \ QT_SUFFIX="_64"
qt$BITS-4.8.6.7z else
QT_SUFFIX=""
fi
download "Qt 5.7.2" \
"http://download.qt.io/official_releases/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
"qt-5-install.qs"
fi fi
# SDL2 # SDL2
download "SDL 2.0.3" \ download "SDL 2.0.4" \
https://www.libsdl.org/release/SDL2-devel-2.0.3-VC.zip \ "https://www.libsdl.org/release/SDL2-devel-2.0.4-VC.zip" \
SDL2-2.0.3.zip "SDL2-2.0.4.zip"
fi fi
cd .. #/.. cd .. #/..
# Set up dependencies # Set up dependencies
BUILD_DIR="MSVC${MSVC_YEAR}_${BITS}"
if [ -z $KEEP ]; then if [ -z $KEEP ]; then
echo echo
printf "Preparing build directory... " echo "(Re)Creating build directory."
rm -rf Build_$BITS
mkdir -p Build_$BITS/deps
echo Done. rm -rf "$BUILD_DIR"
fi fi
mkdir -p Build_$BITS/deps
cd Build_$BITS/deps
DEPS_INSTALL=`pwd` mkdir -p "${BUILD_DIR}/deps"
cd "${BUILD_DIR}/deps"
DEPS_INSTALL="$(pwd)"
cd $DEPS cd $DEPS
echo echo
echo "Extracting dependencies..." echo "Extracting dependencies, this might take a while..."
echo "---------------------------------------------------"
echo
# Boost # Boost
printf "Boost 1.58.0... " if [ -z $APPVEYOR ]; then
printf "Boost 1.61.0... "
else
if [ $MSVC_VER -eq 12 ]; then
printf "Boost 1.58.0 AppVeyor... "
else
printf "Boost 1.60.0 AppVeyor... "
fi
fi
{ {
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
cd $DEPS_INSTALL cd $DEPS_INSTALL
BOOST_SDK="`real_pwd`/Boost" BOOST_SDK="$(real_pwd)/Boost"
if [ -d Boost ] && grep "BOOST_VERSION 105800" Boost/boost/version.hpp > /dev/null; then if [ -d Boost ] && grep "BOOST_VERSION 106100" Boost/boost/version.hpp > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Boost rm -rf Boost
$DEPS/boost-1.58.0-win$BITS.exe //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent "${DEPS}/boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe" //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent
fi fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0" -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.0"
echo Done. echo Done.
else else
# Appveyor unstable has all the boost we need already # Appveyor unstable has all the boost we need already
BOOST_SDK="c:/Libraries/boost" if [ $MSVC_VER -eq 12 ]; then
BOOST_SDK="c:/Libraries/boost_1_58_0"
else
BOOST_SDK="c:/Libraries/boost_1_60_0"
fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0" -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.0"
echo AppVeyor. echo Done.
fi fi
} }
cd $DEPS cd $DEPS
echo
# Bullet # Bullet
printf "Bullet 2.83.5... " printf "Bullet 2.86... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d Bullet ]; then if [ -d Bullet ]; then
printf "Exists. (No version checking) " printf -- "Exists. (No version checking) "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Bullet rm -rf Bullet
eval 7z x -y $DEPS/Bullet-2.83.5-win$BITS.7z $STRIP eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv Bullet-2.83.5-win$BITS Bullet mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
fi fi
BULLET_SDK="`real_pwd`/Bullet" export BULLET_ROOT="$(real_pwd)/Bullet"
add_cmake_opts -DBULLET_INCLUDE_DIR="$BULLET_SDK/include/bullet" \
-DBULLET_COLLISION_LIBRARY="$BULLET_SDK/lib/BulletCollision.lib" \
-DBULLET_COLLISION_LIBRARY_DEBUG="$BULLET_SDK/lib/BulletCollision_Debug.lib" \
-DBULLET_MATH_LIBRARY="$BULLET_SDK/lib/LinearMath.lib" \
-DBULLET_MATH_LIBRARY_DEBUG="$BULLET_SDK/lib/LinearMath_Debug.lib"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo
# FFmpeg # FFmpeg
printf "FFmpeg 2.5.2... " printf "FFmpeg 3.0.1... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d FFmpeg ] && grep "FFmpeg version: 2.5.2" FFmpeg/README.txt > /dev/null; then if [ -d FFmpeg ] && grep "FFmpeg version: 3.0.1" FFmpeg/README.txt > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf FFmpeg rm -rf FFmpeg
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2.7z $STRIP eval 7z x -y "${DEPS}/ffmpeg-3.0.1-win${BITS}.7z" $STRIP
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2-dev.7z $STRIP eval 7z x -y "${DEPS}/ffmpeg-3.0.1-dev-win${BITS}.7z" $STRIP
mv ffmpeg-2.5.2-win$BITS-shared FFmpeg mv "ffmpeg-3.0.1-win${BITS}-shared" FFmpeg
cp -r ffmpeg-2.5.2-win$BITS-dev/* FFmpeg/ cp -r "ffmpeg-3.0.1-win${BITS}-dev/"* FFmpeg/
rm -rf ffmpeg-2.5.2-win$BITS-dev rm -rf "ffmpeg-3.0.1-win${BITS}-dev"
fi fi
FFMPEG_SDK="`real_pwd`/FFmpeg" export FFMPEG_HOME="$(real_pwd)/FFmpeg"
add_cmake_opts -DAVCODEC_INCLUDE_DIRS="$FFMPEG_SDK/include" \ add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
-DAVCODEC_LIBRARIES="$FFMPEG_SDK/lib/avcodec.lib" \
-DAVDEVICE_INCLUDE_DIRS="$FFMPEG_SDK/include" \
-DAVDEVICE_LIBRARIES="$FFMPEG_SDK/lib/avdevice.lib" \
-DAVFORMAT_INCLUDE_DIRS="$FFMPEG_SDK/include" \
-DAVFORMAT_LIBRARIES="$FFMPEG_SDK/lib/avformat.lib" \
-DAVUTIL_INCLUDE_DIRS="$FFMPEG_SDK/include" \
-DAVUTIL_LIBRARIES="$FFMPEG_SDK/lib/avutil.lib" \
-DPOSTPROC_INCLUDE_DIRS="$FFMPEG_SDK/include" \
-DPOSTPROC_LIBRARIES="$FFMPEG_SDK/lib/postproc.lib" \
-DSWRESAMPLE_INCLUDE_DIRS="$FFMPEG_SDK/include" \
-DSWRESAMPLE_LIBRARIES="$FFMPEG_SDK/lib/swresample.lib" \
-DSWSCALE_INCLUDE_DIRS="$FFMPEG_SDK/include" \
-DSWSCALE_LIBRARIES="$FFMPEG_SDK/lib/swscale.lib"
add_runtime_dlls `pwd`/FFmpeg/bin/{avcodec-56,avformat-56,avutil-54,swresample-1,swscale-3}.dll
if [ $BITS -eq 32 ]; then if [ $BITS -eq 32 ]; then
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\"" add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
@ -430,78 +461,79 @@ printf "FFmpeg 2.5.2... "
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo
# MyGUI # MyGUI
printf "MyGUI 3.2.2... " printf "MyGUI 3.2.3-git... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d MyGUI ] && \ if [ -d MyGUI ] && \
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ 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_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null grep "MYGUI_VERSION_PATCH 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
then then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf MyGUI rm -rf MyGUI
eval 7z x -y $DEPS/MyGUI-3.2.2-win$BITS.7z $STRIP eval 7z x -y "${DEPS}/MyGUI-3.2.3-git-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv MyGUI-3.2.2-win$BITS MyGUI mv "MyGUI-3.2.3-git-msvc${MSVC_YEAR}-win${BITS}" MyGUI
fi fi
MYGUI_SDK="`real_pwd`/MyGUI" export MYGUI_HOME="$(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 if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="_d" SUFFIX="_d"
else else
SUFFIX="" SUFFIX=""
fi fi
add_runtime_dlls `pwd`/MyGUI/bin/$CONFIGURATION/MyGUIEngine$SUFFIX.dll add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo
# OpenAL # OpenAL
printf "OpenAL-Soft 1.16.0... " printf "OpenAL-Soft 1.17.2... "
{ {
if [ -d openal-soft-1.16.0-bin ]; then if [ -d openal-soft-1.17.2-bin ]; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf openal-soft-1.16.0-bin rm -rf openal-soft-1.17.2-bin
eval 7z x -y OpenAL-Soft-1.16.0.zip $STRIP eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
fi fi
OPENAL_SDK="`real_pwd`/openal-soft-1.16.0-bin" OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
add_cmake_opts -DOPENAL_INCLUDE_DIR="$OPENAL_SDK/include/AL" \ add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
-DOPENAL_LIBRARY="$OPENAL_SDK/libs/Win$BITS/OpenAL32.lib"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo
# OSG # OSG
printf "OSG 3.3.8... " printf "OSG 3.4.0-scrawl... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d OSG ] && \ if [ -d OSG ] && \
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_MINOR_VERSION 3" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_PATCH_VERSION 8" OSG/include/osg/Version > /dev/null grep "OPENSCENEGRAPH_PATCH_VERSION 0" OSG/include/osg/Version > /dev/null
then then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf OSG rm -rf OSG
eval 7z x -y $DEPS/OSG-3.3.8-win$BITS.7z $STRIP eval 7z x -y "${DEPS}/OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv OSG-3.3.8-win$BITS OSG mv "OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
fi fi
OSG_SDK="`real_pwd`/OSG" OSG_SDK="$(real_pwd)/OSG"
add_cmake_opts -DOSG_DIR="$OSG_SDK" add_cmake_opts -DOSG_DIR="$OSG_SDK"
@ -511,85 +543,101 @@ printf "OSG 3.3.8... "
SUFFIX="" SUFFIX=""
fi fi
add_runtime_dlls `pwd`/OSG/bin/{OpenThreads,zlib}$SUFFIX.dll \ add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
`pwd`/OSG/bin/osg{,Animation,DB,FX,GA,Particle,Qt,Text,Util,Viewer}$SUFFIX.dll "$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll
add_osg_dlls `pwd`/OSG/bin/osgPlugins-3.3.8/osgdb_{bmp,dds,gif,jpeg,png,tga}$SUFFIX.dll add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.0/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.0/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo
# Qt # Qt
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
printf "Qt 4.8.6... " printf "Qt 5.7.0... "
else else
printf "Qt 5.4... " printf "Qt 5.7 AppVeyor... "
fi fi
{ {
if [ $BITS -eq 64 ]; then
SUFFIX="_64"
else
SUFFIX=""
fi
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
cd $DEPS_INSTALL cd $DEPS_INSTALL
QT_SDK="`real_pwd`/Qt" QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
if [ -d Qt ] && head -n2 Qt/BUILDINFO.txt | grep "4.8.6" > /dev/null; then if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Qt rm -rf Qt
eval 7z x -y $DEPS/qt$BITS-4.8.6.7z $STRIP cp "${DEPS}/qt-5-install.qs" qt-install.qs
mv qt-4.8.6-* Qt
cd Qt
eval ./qtbinpatcher.exe $STRIP sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
printf -- "(Installation might take a while) "
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
mv qt-install.qs Qt/
echo Done.
printf " Cleaning up extraneous data... "
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
fi fi
cd $QT_SDK cd $QT_SDK
add_cmake_opts -DDESIRED_QT_VERSION=4 \ add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe" -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d4" SUFFIX="d"
else else
SUFFIX="4" SUFFIX=""
fi fi
add_runtime_dlls `pwd`/bin/Qt{Core,Gui,Network,OpenGL}$SUFFIX.dll add_runtime_dlls "$(pwd)/bin/lib"{EGL,GLESv2}${SUFFIX}.dll \
"$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done. echo Done.
else else
if [ $BITS -eq 32 ]; then QT_SDK="C:/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
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 \ add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe" \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK" -DCMAKE_PREFIX_PATH="$QT_SDK"
echo AppVeyor. echo Done.
fi fi
} }
cd $DEPS cd $DEPS
echo
# SDL2 # SDL2
printf "SDL 2.0.3... " printf "SDL 2.0.4... "
{ {
if [ -d SDL2-2.0.3 ]; then if [ -d SDL2-2.0.4 ]; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf SDL2-2.0.3 rm -rf SDL2-2.0.4
eval 7z x -y SDL2-2.0.3.zip $STRIP eval 7z x -y SDL2-2.0.4.zip $STRIP
fi fi
SDL_SDK="`real_pwd`/SDL2-2.0.3" export SDL2DIR="$(real_pwd)/SDL2-2.0.4"
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 add_runtime_dlls "$(pwd)/SDL2-2.0.4/lib/x${ARCHSUFFIX}/SDL2.dll"
echo Done. echo Done.
} }
echo
cd $DEPS_INSTALL/.. cd $DEPS_INSTALL/..
@ -602,12 +650,10 @@ add_cmake_opts -DBUILD_BSATOOL=no \
-DBUILD_MYGUI_PLUGIN=no \ -DBUILD_MYGUI_PLUGIN=no \
-DOPENMW_MP_BUILD=on -DOPENMW_MP_BUILD=on
if [ -z $CI ]; then if [ ! -z $CI ]; then
echo " (Outside of CI, doing full build.)"
else
case $STEP in case $STEP in
components ) components )
echo " Subproject: Components." echo " Building subproject: Components."
add_cmake_opts -DBUILD_ESSIMPORTER=no \ add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \ -DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \ -DBUILD_MWINIIMPORTER=no \
@ -615,64 +661,98 @@ else
-DBUILD_OPENMW=no \ -DBUILD_OPENMW=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
openmw ) openmw )
echo " Subproject: OpenMW." echo " Building subproject: OpenMW."
add_cmake_opts -DBUILD_ESSIMPORTER=no \ add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \ -DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \ -DBUILD_MWINIIMPORTER=no \
-DBUILD_OPENCS=no \ -DBUILD_OPENCS=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
opencs ) opencs )
echo " Subproject: OpenCS." echo " Building subproject: OpenCS."
add_cmake_opts -DBUILD_ESSIMPORTER=no \ add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \ -DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \ -DBUILD_MWINIIMPORTER=no \
-DBUILD_OPENMW=no \ -DBUILD_OPENMW=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
misc ) misc )
echo " Subproject: Misc." echo " Building subprojects: Misc."
add_cmake_opts -DBUILD_OPENCS=no \ add_cmake_opts -DBUILD_OPENCS=no \
-DBUILD_OPENMW=no -DBUILD_OPENMW=no
;; ;;
* )
echo " Building everything."
;;
esac esac
fi fi
# NOTE: Disable this when/if we want to run test cases
if [ -z $CI ]; then
echo "- Copying Runtime DLLs..."
mkdir -p $BUILD_CONFIG
for DLL in $RUNTIME_DLLS; do
TARGET="$(basename "$DLL")"
if [[ "$DLL" == *":"* ]]; then
IFS=':'; SPLIT=( ${DLL} ); unset IFS
DLL=${SPLIT[0]}
TARGET=${SPLIT[1]}
fi
echo " ${TARGET}."
cp "$DLL" "$BUILD_CONFIG/$TARGET"
done
echo
echo "- OSG Plugin DLLs..."
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.0
for DLL in $OSG_PLUGINS; do
echo " $(basename $DLL)."
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.0
done
echo
echo "- Qt Platform DLLs..."
mkdir -p ${BUILD_CONFIG}/platforms
for DLL in $QT_PLATFORMS; do
echo " $(basename $DLL)"
cp "$DLL" "${BUILD_CONFIG}/platforms"
done
echo
fi
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
printf " Configuring... " printf -- "- Configuring... "
else else
echo " cmake .. $CMAKE_OPTS" echo "- cmake .. $CMAKE_OPTS"
fi fi
run_cmd cmake .. $CMAKE_OPTS run_cmd cmake .. $CMAKE_OPTS
RET=$? RET=$?
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then echo Done. if [ $RET -eq 0 ]; then
else echo Failed.; fi echo Done.
else
echo Failed.
fi
fi fi
echo
# NOTE: Disable this when/if we want to run test cases
if [ -z $CI ]; then if [ -z $CI ]; then
echo "Copying Runtime DLLs..." echo "- Copying Runtime Resources/Config Files"
mkdir -p $CONFIGURATION echo " gamecontrollerdb.txt"
for DLL in $RUNTIME_DLLS; do cp gamecontrollerdb.txt $BUILD_CONFIG/gamecontrollerdb.txt
echo " `basename $DLL`." echo " openmw.cfg"
cp "$DLL" $CONFIGURATION/ cp openmw.cfg.install $BUILD_CONFIG/openmw.cfg
done echo " openmw-cs.cfg"
echo "OSG Plugin DLLs..." cp openmw-cs.cfg $BUILD_CONFIG/openmw-cs.cfg
mkdir -p $CONFIGURATION/osgPlugins-3.3.8 echo " settings-default.cfg"
for DLL in $OSG_PLUGINS; do cp settings-default.cfg $BUILD_CONFIG/settings-default.cfg
echo " `basename $DLL`." echo " resources/"
cp "$DLL" $CONFIGURATION/osgPlugins-3.3.8 cp -r resources $BUILD_CONFIG/resources
done
echo echo
fi fi
exit $RET exit $RET

@ -1,5 +1,22 @@
#!/bin/sh #!/bin/sh
export CXX=clang++
export CC=clang
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
QT_PATH=`brew --prefix $macos_qt_formula`
mkdir build mkdir build
cd build cd build
cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" ..
cmake \
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
-D CMAKE_OSX_SYSROOT="macosx10.12" \
-D CMAKE_BUILD_TYPE=Debug \
-D OPENMW_OSX_DEPLOYMENT=TRUE \
-D DESIRED_QT_VERSION=5 \
-D BUILD_ESMTOOL=FALSE \
-D BUILD_MYGUI_PLUGIN=FALSE \
-G"Unix Makefiles" \
..

@ -1,5 +1,13 @@
#!/bin/bash #!/bin/bash
APPVEYOR=""
CI=""
PACKAGE=""
PLATFORM=""
CONFIGURATION=""
VS_VERSION=""
if [ -z $PLATFORM ]; then if [ -z $PLATFORM ]; then
PLATFORM=`uname -m` PLATFORM=`uname -m`
fi fi
@ -8,39 +16,63 @@ if [ -z $CONFIGURATION ]; then
CONFIGURATION="Debug" CONFIGURATION="Debug"
fi fi
case $VS_VERSION in
14|14.0|2015 )
GENERATOR="Visual Studio 14 2015"
MSVC_YEAR="2015"
MSVC_VER="14.0"
;;
# 12|2013|
* )
GENERATOR="Visual Studio 12 2013"
MSVC_YEAR="2013"
MVSC_VER="12.0"
;;
esac
case $PLATFORM in case $PLATFORM in
x64|x86_64|x86-64|win64|Win64 )
BITS=64
;;
x32|x86|i686|i386|win32|Win32 ) x32|x86|i686|i386|win32|Win32 )
BITS=32 BITS=32
PLATFORM=Win32
;; ;;
esac
x64|x86_64|x86-64|win64|Win64 ) case $CONFIGURATION in
BITS=64 debug|Debug|DEBUG )
PLATFORM=x64 CONFIGURATION=Debug
;; ;;
* ) release|Release|RELEASE )
echo "Unknown platform $PLATFORM." CONFIGURATION=Release
exit 1 ;; ;;
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
CONFIGURATION=RelWithDebInfo
;;
esac esac
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
echo "Running $BITS-bit $CONFIGURATION build outside of Appveyor." echo "Running ${BITS}-bit MSVC${MSVC_YEAR} ${CONFIGURATION} build outside of Appveyor."
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
cd $(dirname "$DIR")/.. cd $(dirname "$DIR")/..
else else
echo "Running $BITS-bit $CONFIGURATION build in Appveyor." echo "Running ${BITS}-bit MSVC${MSVC_YEAR} ${CONFIGURATION} build in Appveyor."
cd $APPVEYOR_BUILD_FOLDER cd $APPVEYOR_BUILD_FOLDER
fi fi
cd build_$BITS BUILD_DIR="MSVC${MSVC_YEAR}_${BITS}"
cd ${BUILD_DIR}
which msbuild > /dev/null which msbuild > /dev/null
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
msbuild() { msbuild() {
/c/Program\ Files\ \(x86\)/MSBuild/12.0/Bin/MSBuild.exe "$@" /c/Program\ Files\ \(x86\)/MSBuild/${MSVC_VER}/Bin/MSBuild.exe "$@"
} }
fi fi

@ -25,7 +25,7 @@ endif()
message(STATUS "Configuring OpenMW...") message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 38) set(OPENMW_VERSION_MINOR 41)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
@ -46,7 +46,7 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
message(STATUS "Shallow Git clone detected, not attempting to retrieve version info") message(STATUS "Shallow Git clone detected, not attempting to retrieve version info")
endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
# Macros # Macros
include(OpenMWMacros) include(OpenMWMacros)
@ -65,9 +65,11 @@ option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time"
# Apps and tools # Apps and tools
option(BUILD_OPENMW "build OpenMW" ON) option(BUILD_OPENMW "build OpenMW" ON)
option(BUILD_OPENMW_MP "build OpenMW-MP" ON)
option(BUILD_BSATOOL "build BSA extractor" ON) option(BUILD_BSATOOL "build BSA extractor" ON)
option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON) option(BUILD_LAUNCHER "build Launcher" ON)
option(BUILD_BROWSER "build tes3mp Server Browser" ON)
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON) option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_OPENCS "build OpenMW Construction Set" ON)
@ -76,6 +78,14 @@ option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
option(BUILD_NIFTEST "build nif file tester" OFF) option(BUILD_NIFTEST "build nif file tester" OFF)
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON) option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
option(BUILD_DOCS "build documentation." OFF )
# what is necessary to build documentation
IF( BUILD_DOCS )
# Builds the documentation.
FIND_PACKAGE( Sphinx REQUIRED )
FIND_PACKAGE( Doxygen REQUIRED )
ENDIF()
# OS X deployment # OS X deployment
option(OPENMW_OSX_DEPLOYMENT OFF) option(OPENMW_OSX_DEPLOYMENT OFF)
@ -88,7 +98,7 @@ endif()
# Set up common paths # Set up common paths
if (APPLE) if (APPLE)
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") set(OPENMW_RESOURCE_FILES "../Resources/resources" CACHE PATH "location of OpenMW resources files")
elseif(UNIX) elseif(UNIX)
# Paths # Paths
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
@ -116,7 +126,10 @@ if (WIN32)
option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON) option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON)
endif() endif()
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD) find_package(RakNet REQUIRED)
include_directories(${RakNet_INCLUDES})
if (NOT BUILD_LAUNCHER AND NOT BUILD_BROWSER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE) set(USE_QT FALSE)
else() else()
set(USE_QT TRUE) set(USE_QT TRUE)
@ -124,7 +137,7 @@ endif()
# Dependencies # Dependencies
if (USE_QT) if (USE_QT)
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)") set(DESIRED_QT_VERSION 5 CACHE STRING "The QT version OpenMW should use (4 or 5)")
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5) set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
message(STATUS "Using Qt${DESIRED_QT_VERSION}") message(STATUS "Using Qt${DESIRED_QT_VERSION}")
@ -140,41 +153,31 @@ if (USE_QT)
endif() endif()
endif() endif()
if (USE_QT AND DESIRED_QT_VERSION MATCHES 5) if (APPLE)
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows. # OS X build process relies on this fix: https://github.com/Kitware/CMake/commit/3df5147043d83aa09acd5c9ce31d5c602efb99db
cmake_minimum_required(VERSION 2.8.11) cmake_minimum_required(VERSION 3.1.0)
elseif (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows.
cmake_minimum_required(VERSION 2.8.11)
else() else()
# We probably support older versions than this. # We probably support older versions than this.
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 2.6)
endif() endif()
# Sound setup IF(BUILD_OPENMW OR BUILD_OPENCS)
unset(FFMPEG_LIBRARIES CACHE) # Sound setup
find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE)
find_package(FFmpeg REQUIRED) # Required for building the FFmpeg headers
add_definitions(-D__STDC_CONSTANT_MACROS)
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 # TinyXML
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
if(USE_SYSTEM_TINYXML) if (USE_SYSTEM_TINYXML)
find_library(TINYXML_LIBRARIES tinyxml) find_package(TinyXML REQUIRED)
find_path(TINYXML_INCLUDE_DIR tinyxml.h)
message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}")
add_definitions (-DTIXML_USE_STL) add_definitions (-DTIXML_USE_STL)
if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR) include_directories(SYSTEM ${TinyXML_INCLUDE_DIRS})
include_directories(${TINYXML_INCLUDE_DIR})
message(STATUS "Using system TinyXML library.")
else()
message(FATAL_ERROR "Detection of system TinyXML incomplete.")
endif()
endif() endif()
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
# Platform specific # Platform specific
if (WIN32) if (WIN32)
@ -190,9 +193,10 @@ if (WIN32)
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
endif() endif()
if (ANDROID) if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer
set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE) find_package(LIBUNSHIELD REQUIRED) # required only for non win32 when building openmw-wizard
endif (ANDROID) set(OPENMW_USE_UNSHIELD TRUE)
endif()
option(OPENGL_ES "enable opengl es support" FALSE ) option(OPENGL_ES "enable opengl es support" FALSE )
@ -213,108 +217,77 @@ if(NOT HAVE_STDINT_H)
message(FATAL_ERROR "stdint.h was not found" ) message(FATAL_ERROR "stdint.h was not found" )
endif() endif()
include (CheckIncludeFileCXX)
check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP)
if (HAVE_UNORDERED_MAP)
add_definitions(-DHAVE_UNORDERED_MAP)
endif ()
set(BOOST_COMPONENTS system filesystem program_options thread)
if(WIN32)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
endif(WIN32)
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)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
if(OSG_STATIC)
macro(use_static_osg_plugin_library PLUGIN_NAME)
set(PLUGIN_NAME_DBG ${PLUGIN_NAME}d ${PLUGIN_NAME}D ${PLUGIN_NAME}_d ${PLUGIN_NAME}_D ${PLUGIN_NAME}_debug ${PLUGIN_NAME})
# 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>
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)
if("${${PLUGIN_NAME}_LIBRARY}" STREQUAL "") IF(BUILD_OPENMW OR BUILD_OPENCS)
message(FATAL_ERROR "Unable to find static OpenSceneGraph plugin: ${PLUGIN_NAME}")
endif()
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${PLUGIN_NAME}_LIBRARY}) find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgUtil osgFX)
endmacro()
macro(use_static_osg_plugin_dep DEPENDENCY) set(USED_OSG_PLUGINS
find_package(${DEPENDENCY} REQUIRED) osgdb_bmp
osgdb_dds
osgdb_jpeg
osgdb_osg
osgdb_png
osgdb_serializers_osg
osgdb_tga
)
get_filename_component(OSG_LIB_DIR ${OSGDB_LIBRARY} DIRECTORY)
set(OSGPlugins_LIB_DIR "${OSG_LIB_DIR}/osgPlugins-${OPENSCENEGRAPH_VERSION}")
if(OSG_STATIC)
add_definitions(-DOSG_LIBRARY_STATIC)
find_package(OSGPlugins REQUIRED COMPONENTS ${USED_OSG_PLUGINS})
list(APPEND OPENSCENEGRAPH_LIBRARIES ${OSGPlugins_LIBRARIES})
endif()
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${DEPENDENCY}_LIBRARIES}) if(QT_STATIC)
endmacro() if(WIN32)
if(DESIRED_QT_VERSION MATCHES 4)
# QtCore needs WSAAsyncSelect from Ws2_32.lib
set(QT_QTCORE_LIBRARY ${QT_QTCORE_LIBRARY} Ws2_32.lib)
message("QT_QTCORE_LIBRARY: ${QT_QTCORE_LIBRARY}")
endif()
endif()
endif()
add_definitions(-DOSG_LIBRARY_STATIC) set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape
if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR})
set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine
endif()
set(PLUGIN_LIST find_package(MyGUI 3.2.1 REQUIRED)
osgdb_png # depends on libpng, zlib find_package(SDL2 REQUIRED)
osgdb_tga find_package(OpenAL REQUIRED)
osgdb_dds find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)
osgdb_jpeg # depends on libjpeg
)
foreach(PLUGIN ${PLUGIN_LIST}) ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
use_static_osg_plugin_library(${PLUGIN})
endforeach()
# OSG static plugins need to linked against their respective dependencies include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
set(PLUGIN_DEPS_LIST
PNG # needed by osgdb_png
ZLIB # needed by osgdb_png
JPEG # needed by osgdb_jpeg
)
foreach(DEPENDENCY ${PLUGIN_DEPS_LIST})
use_static_osg_plugin_dep(${DEPENDENCY})
endforeach()
endif()
if(QT_STATIC) set(BOOST_COMPONENTS system filesystem program_options)
if(WIN32) if(WIN32)
if(DESIRED_QT_VERSION MATCHES 4) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
# QtCore needs WSAAsyncSelect from Ws2_32.lib endif(WIN32)
set(QT_QTCORE_LIBRARY ${QT_QTCORE_LIBRARY} Ws2_32.lib)
message("QT_QTCORE_LIBRARY: ${QT_QTCORE_LIBRARY}")
endif()
endif()
endif()
find_package(MyGUI REQUIRED) IF(BOOST_STATIC)
if (${MYGUI_VERSION} VERSION_LESS "3.2.1") set(Boost_USE_STATIC_LIBS ON)
message(FATAL_ERROR "OpenMW requires MyGUI 3.2.1 or later, please install the latest version from http://mygui.info")
endif() endif()
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED)
find_package(Bullet REQUIRED)
include_directories("." include_directories("."
SYSTEM SYSTEM
${SDL2_INCLUDE_DIR} ${SDL2_INCLUDE_DIR}
${Boost_INCLUDE_DIR} ${Boost_INCLUDE_DIR}
${MYGUI_INCLUDE_DIRS} ${MyGUI_INCLUDE_DIRS}
${OPENAL_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR}
${BULLET_INCLUDE_DIRS} ${Bullet_INCLUDE_DIRS}
) )
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${MYGUI_LIB_DIR}) link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})
if(MYGUI_STATIC) if(MYGUI_STATIC)
add_definitions(-DMYGUI_STATIC) add_definitions(-DMYGUI_STATIC)
@ -331,6 +304,11 @@ endif (APPLE)
# Set up DEBUG define # Set up DEBUG define
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1) set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1)
if (NOT APPLE)
set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR})
set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR})
endif ()
add_subdirectory(files/) add_subdirectory(files/)
# Specify build paths # Specify build paths
@ -349,14 +327,24 @@ endif (APPLE)
# Other files # Other files
configure_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-client-default.cfg
"${OpenMW_BINARY_DIR}/tes3mp-client-default.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-server-default.cfg
"${OpenMW_BINARY_DIR}/tes3mp-server-default.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
"${OpenMW_BINARY_DIR}/settings-default.cfg") "${OpenMW_BINARY_DIR}/settings-default.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local if (NOT APPLE)
"${OpenMW_BINARY_DIR}/openmw.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${OpenMW_BINARY_DIR}/openmw.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install") "${OpenMW_BINARY_DIR}/openmw.cfg.install")
else ()
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg")
endif ()
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
"${OpenMW_BINARY_DIR}/openmw-cs.cfg") "${OpenMW_BINARY_DIR}/openmw-cs.cfg")
@ -378,21 +366,17 @@ endif()
# CXX Compiler settings # CXX Compiler settings
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long -Wno-variadic-macros")
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION) if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
string(REGEX REPLACE ".*version ([0-9\\.]*).*" "\\1" CLANG_VERSION ${CLANG_VERSION})
if ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression")
endif ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6) endif ()
endif(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) endif()
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.6)
OUTPUT_VARIABLE GCC_VERSION)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
endif(CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) endif()
elseif (MSVC) elseif (MSVC)
# Enable link-time code generation globally for all linking # Enable link-time code generation globally for all linking
if (OPENMW_LTO_BUILD) if (OPENMW_LTO_BUILD)
@ -415,6 +399,9 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_LAUNCHER) IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER) ENDIF(BUILD_LAUNCHER)
IF(BUILD_BROWSER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-browser" DESTINATION "${BINDIR}" )
ENDIF(BUILD_BROWSER)
IF(BUILD_BSATOOL) IF(BUILD_BSATOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_BSATOOL) ENDIF(BUILD_BSATOOL)
@ -458,6 +445,11 @@ IF(NOT WIN32 AND NOT APPLE)
INSTALL(FILES "${OpenMW_BINARY_DIR}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-client-default" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-client.install" DESTINATION "${SYSCONFDIR}" RENAME "tes3mp-client.cfg" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-server-default" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw-mp")
INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-server.install" DESTINATION "${SYSCONFDIR}" RENAME "tes3mp-server.cfg" COMPONENT "openmw-mp")
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
@ -468,8 +460,10 @@ IF(NOT WIN32 AND NOT APPLE)
ENDIF(NOT WIN32 AND NOT APPLE) ENDIF(NOT WIN32 AND NOT APPLE)
if(WIN32) if(WIN32)
FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll") FILE(GLOB dll_files_debug "${OpenMW_BINARY_DIR}/Debug/*.dll")
INSTALL(FILES ${dll_files} DESTINATION ".") FILE(GLOB dll_files_release "${OpenMW_BINARY_DIR}/Release/*.dll")
INSTALL(FILES ${dll_files_debug} DESTINATION "." CONFIGURATIONS Debug)
INSTALL(FILES ${dll_files_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
@ -477,33 +471,25 @@ if(WIN32)
"${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt" "${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt"
"${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt" "${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/tes3mp-client-default.cfg"
"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt"
"${OpenMW_BINARY_DIR}/Release/openmw.exe"
DESTINATION ".") DESTINATION ".")
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-launcher.exe" DESTINATION ".")
ENDIF(BUILD_LAUNCHER)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-iniimporter.exe" DESTINATION ".")
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_ESSIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".")
ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".")
ENDIF(BUILD_WIZARD)
if(BUILD_MYGUI_PLUGIN) if(BUILD_MYGUI_PLUGIN)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION ".") INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Debug/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
ENDIF(BUILD_MYGUI_PLUGIN) ENDIF(BUILD_MYGUI_PLUGIN)
IF(DESIRED_QT_VERSION MATCHES 5)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
ENDIF()
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
FILE(GLOB plugin_dir "${OpenMW_BINARY_DIR}/Release/osgPlugins-*") FILE(GLOB plugin_dir_debug "${OpenMW_BINARY_DIR}/Debug/osgPlugins-*")
INSTALL(DIRECTORY ${plugin_dir} DESTINATION ".") FILE(GLOB plugin_dir_release "${OpenMW_BINARY_DIR}/Release/osgPlugins-*")
INSTALL(DIRECTORY ${plugin_dir_debug} DESTINATION "." CONFIGURATIONS Debug)
INSTALL(DIRECTORY ${plugin_dir_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
SET(CPACK_GENERATOR "NSIS") SET(CPACK_GENERATOR "NSIS")
SET(CPACK_PACKAGE_NAME "OpenMW") SET(CPACK_PACKAGE_NAME "OpenMW")
@ -516,6 +502,9 @@ if(WIN32)
IF(BUILD_LAUNCHER) IF(BUILD_LAUNCHER)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher") SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher")
ENDIF(BUILD_LAUNCHER) ENDIF(BUILD_LAUNCHER)
IF(BUILD_BROWSER)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};tes3mp-browser;tes3mp Launcher")
ENDIF(BUILD_BROWSER)
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set") SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set")
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
@ -534,8 +523,8 @@ if(WIN32)
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe") SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico") SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico") SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp.ico")
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp") SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe") SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
@ -565,8 +554,13 @@ if(WIN32)
endif(WIN32) endif(WIN32)
# Extern # Extern
IF(BUILD_OPENMW OR BUILD_OPENCS)
add_subdirectory (extern/osg-ffmpeg-videoplayer) add_subdirectory (extern/osg-ffmpeg-videoplayer)
add_subdirectory (extern/oics) add_subdirectory (extern/oics)
if (BUILD_OPENCS)
add_subdirectory (extern/osgQt)
endif()
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
# Components # Components
add_subdirectory (components) add_subdirectory (components)
@ -577,6 +571,10 @@ add_subdirectory (components)
#endif() #endif()
# Apps and tools # Apps and tools
if (BUILD_OPENMW_MP)
add_subdirectory( apps/openmw-mp )
endif()
if (BUILD_OPENMW) if (BUILD_OPENMW)
add_subdirectory( apps/openmw ) add_subdirectory( apps/openmw )
endif() endif()
@ -593,6 +591,10 @@ if (BUILD_LAUNCHER)
add_subdirectory( apps/launcher ) add_subdirectory( apps/launcher )
endif() endif()
if (BUILD_BROWSER)
add_subdirectory( apps/browser )
endif()
if (BUILD_MWINIIMPORTER) if (BUILD_MWINIIMPORTER)
add_subdirectory( apps/mwiniimporter ) add_subdirectory( apps/mwiniimporter )
endif() endif()
@ -631,20 +633,20 @@ if (WIN32)
endforeach( OUTPUTCONFIG ) endforeach( OUTPUTCONFIG )
if (USE_DEBUG_CONSOLE AND BUILD_OPENMW) if (USE_DEBUG_CONSOLE AND BUILD_OPENMW)
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE") set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
elseif (BUILD_OPENMW) elseif (BUILD_OPENMW)
# Turn off debug console, debug output will be written to visual studio output instead # Turn off debug console, debug output will be written to visual studio output instead
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
endif() endif()
if (BUILD_OPENMW) if (BUILD_OPENMW)
# Release builds use the debug console # Release builds use the debug console
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE") set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
endif() endif()
# Play a bit with the warning levels # Play a bit with the warning levels
@ -667,9 +669,15 @@ if (WIN32)
4987 # nonstandard extension used (triggered by setjmp.h) 4987 # nonstandard extension used (triggered by setjmp.h)
4996 # Function was declared deprecated 4996 # Function was declared deprecated
# caused by OSG
4589 # Constructor of abstract class 'osg::Operation' ignores initializer for virtual base class 'osg::Referenced' (False warning)
# caused by boost # caused by boost
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
# caused by MyGUI
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
# OpenMW specific warnings # OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type 4099 # Type mismatch, declared class or struct is defined with other type
4100 # Unreferenced formal parameter (-Wunused-parameter) 4100 # Unreferenced formal parameter (-Wunused-parameter)
@ -683,19 +691,25 @@ if (WIN32)
4309 # Variable overflow, trying to store 128 in a signed char for example 4309 # Variable overflow, trying to store 128 in a signed char for example
4351 # New behavior: elements of array 'array' will be default initialized (desired behavior) 4351 # New behavior: elements of array 'array' will be default initialized (desired behavior)
4355 # Using 'this' in member initialization list 4355 # Using 'this' in member initialization list
4464 # relative include path contains '..'
4505 # Unreferenced local function has been removed 4505 # Unreferenced local function has been removed
4701 # Potentially uninitialized local variable used 4701 # Potentially uninitialized local variable used
4702 # Unreachable code 4702 # Unreachable code
4714 # function 'QString QString::trimmed(void) &&' marked as __forceinline not inlined
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt 4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
) )
if (MSVC_VERSION GREATER 1800)
set(WARNINGS_DISABLE ${WARNINGS_DISABLE} 5026 5027
5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file (config_begin.hpp, config_end.hpp)
)
endif()
foreach(d ${WARNINGS_DISABLE}) foreach(d ${WARNINGS_DISABLE})
set(WARNINGS "${WARNINGS} /wd${d}") set(WARNINGS "${WARNINGS} /wd${d}")
endforeach(d) endforeach(d)
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
# oics uses tinyxml, which has an initialized but unused variable
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${WARNINGS} /wd4189 ${MT_BUILD}")
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_BSATOOL) if (BUILD_BSATOOL)
@ -714,6 +728,10 @@ if (WIN32)
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif() endif()
if (BUILD_BROWSER)
set_target_properties(tes3mp-browser PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
if (BUILD_MWINIIMPORTER) if (BUILD_MWINIIMPORTER)
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif() endif()
@ -723,7 +741,12 @@ if (WIN32)
endif() endif()
if (BUILD_OPENMW) if (BUILD_OPENMW)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") # Very specific issue this, only needed on 32-bit VS2015 during unity builds.
if (MSVC_VERSION GREATER 1800 AND CMAKE_SIZEOF_VOID_P EQUAL 4 AND OPENMW_UNITY_BUILD)
set_target_properties(tes3mp PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD} /bigobj")
else()
set_target_properties(tes3mp PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
endif() endif()
if (BUILD_WIZARD) if (BUILD_WIZARD)
@ -732,8 +755,8 @@ if (WIN32)
endif(MSVC) endif(MSVC)
# TODO: At some point release builds should not use the console but rather write to a log file # TODO: At some point release builds should not use the console but rather write to a log file
#set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") #set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
#set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") #set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif() endif()
# Apple bundling # Apple bundling
@ -750,14 +773,7 @@ if (APPLE)
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY) configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
endif () endif ()
set(INSTALL_SUBDIR OpenMW) install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime)
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
@ -765,8 +781,8 @@ if (APPLE)
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_NAME}")
set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}") set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${OPENCS_BUNDLE_NAME}")
install(CODE " install(CODE "
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)
@ -776,19 +792,16 @@ if (APPLE)
" COMPONENT Runtime) " COMPONENT Runtime)
set(ABSOLUTE_PLUGINS "") set(ABSOLUTE_PLUGINS "")
set(USED_OSG_PLUGINS
osgdb_dds
osgdb_jpeg
osgdb_png
osgdb_tga
)
foreach (PLUGIN_NAME ${USED_OSG_PLUGINS}) foreach (PLUGIN_NAME ${USED_OSG_PLUGINS})
set(PLUGIN_ABS "${OSG_PLUGIN_LIB_SEARCH_PATH}/${PLUGIN_NAME}.so") set(PLUGIN_ABS "${OSGPlugins_LIB_DIR}/${PLUGIN_NAME}.so")
set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS}) set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS})
endforeach () endforeach ()
get_filename_component(OSG_PLUGIN_PREFIX_DIR "${OSG_PLUGIN_LIB_SEARCH_PATH}" NAME) get_filename_component(OSG_PLUGIN_PREFIX_DIR "${OSGPlugins_LIB_DIR}" NAME)
if (NOT OSG_PLUGIN_PREFIX_DIR)
message(FATAL_ERROR "Can't get directory name for OSG plugins from '${OSGPlugins_LIB_DIR}'")
endif()
# installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX}) # installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX})
# and returns list of install paths for all installed plugins # and returns list of install paths for all installed plugins
@ -813,8 +826,8 @@ if (APPLE)
set(${plugins_var} ${PLUGINS} PARENT_SCOPE) set(${plugins_var} ${PLUGINS} PARENT_SCOPE)
endfunction (install_plugins_for_bundle) endfunction (install_plugins_for_bundle)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS) install_plugins_for_bundle("${APP_BUNDLE_NAME}" PLUGINS)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) install_plugins_for_bundle("${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
@ -856,3 +869,4 @@ if (DOXYGEN_FOUND)
WORKING_DIRECTORY ${OpenMW_BINARY_DIR} WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM) COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
endif () endif ()

@ -1,107 +1,41 @@
OpenMW TES3MP
====== ======
[![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) [![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=master)](https://travis-ci.org/TES3MP/openmw-tes3mp)
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. TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind".
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set. * Version: 0.5.1
* License: GPLv3 (see docs/license/GPL3.txt for more information)
* Version: 0.38.0 * Website: https://steamcommunity.com/groups/mwmulti
* License: GPL (see docs/license/GPL3.txt for more information)
* Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net
Font Licenses: Font Licenses:
* DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information) * DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information)
Current Status Project Status
-------------- --------------
The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://bugs.openmw.org/versions/21) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces. [Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
Pre-existing modifications created for the original Morrowind engine can be hit-and-miss. The OpenMW script compiler performs more thorough error-checking than Morrowind does, meaning that a mod created for Morrowind may not necessarily run in OpenMW. Some mods also rely on quirky behaviour or engine bugs in order to work. We are considering such compatibility issues on a case-by-case basis - in some cases adding a workaround to OpenMW may be feasible, in other cases fixing the mod will be the only option. If you know of any mods that work or don't work, feel free to add them to the [Mod status](https://wiki.openmw.org/index.php?title=Mod_status) wiki page.
Getting Started Our project is not yet in a playable state, though we are getting close. At the moment we have synchronization of character appearance, character skills, stats, attributes and death, movement in interiors and exteriors, melee and ranged combat, spell casting, picking up and dropping items in the world, using doors and levers, and adding and removing items from containers, as well as [serverside Lua scripts](https://github.com/TES3MP/PluginExamples) used to save and load the state of most of the aforementioned.
---------------
* [Official forums](https://forum.openmw.org/) Contributing
* [Installation instructions](https://wiki.openmw.org/index.php?title=Installation_Instructions) --------------
* [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup)
* [Testing the game](https://wiki.openmw.org/index.php?title=Testing)
* [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted)
* [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) before submitting your first bug!
* [Known issues](http://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker)
The data path Development has been relatively fast, but any contribution regarding [code](https://github.com/TES3MP/openmw-tes3mp/blob/master/CONTRIBUTING.md), documentation, bug hunting or video showcases is greatly appreciated.
-------------
The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install). Test sessions are often advertised in [our Steam group](https://steamcommunity.com/groups/mwmulti) or [our Discord server](https://discord.gg/H8zhhuk).
Command line options Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have.
--------------------
Syntax: openmw <options> Getting Started
Allowed options: ---------------
--help print help message
--version print version information and quit
--data arg (=data) set data directories (later directories
have higher priority)
--data-local arg set local data directory (highest
priority)
--fallback-archive arg (=fallback-archive)
set fallback BSA archives (later
archives have higher priority)
--resources arg (=resources) set resources directory
--start arg set initial cell
--content arg content file(s): esm/esp, or
omwgame/omwaddon
--no-sound [=arg(=1)] (=0) disable all sounds
--script-verbose [=arg(=1)] (=0) verbose script output
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
scripts) at startup
--script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup
--script-console [=arg(=1)] (=0) enable console-only script
functionality
--script-run arg select a file containing a list of
console commands that is executed on
startup
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
scripts
0 - ignore warning
1 - show warning but consider script as
correctly compiled anyway
2 - treat warnings as errors
--script-blacklist arg ignore the specified script (if the use
of the blacklist is enabled)
--script-blacklist-use [=arg(=1)] (=1)
enable script blacklisting
--load-savegame arg load a save game file on game startup
(specify an absolute filename or a
filename relative to the current
working directory)
--skip-menu [=arg(=1)] (=0) skip main menu on game startup
--new-game [=arg(=1)] (=0) run new game sequence (ignored if
skip-menu=0)
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
folding)
--encoding arg (=win1252) Character encoding used in OpenMW game
messages:
win1250 - Central and Eastern European * [Community forums](https://steamcommunity.com/groups/mwmulti)
such as Polish, Czech, Slovak, * [Installation and build instructions](https://github.com/TES3MP/openmw-tes3mp/wiki/Installation-and-build-instructions)
Hungarian, Slovene, Bosnian, Croatian, * [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
Serbian (Latin script), Romanian and
Albanian languages
win1251 - Cyrillic alphabet such as Donations
Russian, Bulgarian, Serbian Cyrillic ---------------
and other languages
win1252 - Western European (Latin) You can benefit the project by supporting OpenMW and/or by [becoming Koncord's patron](https://www.patreon.com/Koncord).
alphabet, used by default
--fallback arg fallback values
--no-grab Don't grab mouse cursor
--export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG
image and XML file in current directory
--activate-dist arg (=-1) activation distance override

@ -0,0 +1,93 @@
set (CMAKE_CXX_STANDARD 11)
set(BROWSER_UI
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/Main.ui
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/ServerInfo.ui
)
set(BROWSER
main.cpp
MainWindow.cpp
ServerModel.cpp
NetController.cpp
ServerInfoDialog.cpp
MySortFilterProxyModel.cpp
netutils/HTTPNetwork.cpp
netutils/Utils.cpp
${CMAKE_SOURCE_DIR}/files/tes3mp/browser.rc
)
set(BROWSER_HEADER_MOC
MainWindow.hpp
ServerModel.hpp
ServerInfoDialog.hpp
MySortFilterProxyModel.hpp
)
set(BROWSER_HEADER
${BROWSER_HEADER_MOC}
NetController.hpp
netutils/HTTPNetwork.hpp
netutils/Utils.hpp
)
source_group(browser FILES ${BROWSER} ${BROWSER_HEADER})
set(QT_USE_QTGUI 1)
# Set some platform specific settings
if(WIN32)
set(GUI_TYPE WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
if (DESIRED_QT_VERSION MATCHES 4)
message(SEND_ERROR "QT4 is not supported.")
#include(${QT_USE_FILE})
#QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
#QT4_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})
#QT4_WRAP_UI(UI_HDRS ${BROWSER_UI})
else()
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT5_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})
QT5_WRAP_UI(UI_HDRS ${BROWSER_UI})
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(NOT WIN32)
include_directories(${LIBUNSHIELD_INCLUDE_DIR})
endif(NOT WIN32)
# Main executable
add_executable(tes3mp-browser
${GUI_TYPE}
${BROWSER}
${BROWSER_HEADER}
${RCC_SRCS}
${MOC_SRCS}
${UI_HDRS}
)
if (WIN32)
INSTALL(TARGETS tes3mp-browser RUNTIME DESTINATION ".")
endif (WIN32)
target_link_libraries(tes3mp-browser
${SDL2_LIBRARY_ONLY}
${RakNet_LIBRARY}
components
)
if (DESIRED_QT_VERSION MATCHES 4)
# target_link_libraries(tes3mp-browser ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY})
# if(WIN32)
# target_link_libraries(tes3mp-browser ${QT_QTMAIN_LIBRARY})
# endif(WIN32)
else()
qt5_use_modules(tes3mp-browser Widgets Core)
endif()
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(tes3mp-browser gcov)
endif()

@ -0,0 +1,228 @@
//
// Created by koncord on 06.01.17.
//
#include "MainWindow.hpp"
#include "NetController.hpp"
#include "ServerInfoDialog.hpp"
#include "components/files/configurationmanager.hpp"
#include <qdebug.h>
#include <QInputDialog>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QJsonDocument>
using namespace Process;
MainWindow::MainWindow(QWidget *parent)
{
setupUi(this);
mGameInvoker = new ProcessInvoker();
browser = new ServerModel;
favorites = new ServerModel;
proxyModel = new MySortFilterProxyModel(this);
proxyModel->setSourceModel(browser);
tblServerBrowser->setModel(proxyModel);
tblFavorites->setModel(proxyModel);
tblServerBrowser->hideColumn(ServerData::ADDR);
tblFavorites->hideColumn(ServerData::ADDR);
connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSwitched(int)));
connect(actionAdd, SIGNAL(triggered(bool)), this, SLOT(addServer()));
connect(actionAdd_by_IP, SIGNAL(triggered(bool)), this, SLOT(addServerByIP()));
connect(actionDelete, SIGNAL(triggered(bool)), this, SLOT(deleteServer()));
connect(actionRefresh, SIGNAL(triggered(bool)), this, SLOT(refresh()));
connect(actionPlay, SIGNAL(triggered(bool)), this, SLOT(play()));
connect(tblServerBrowser, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));
connect(tblFavorites, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));
connect(tblFavorites, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(bool)));
connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
loadFavorites();
}
MainWindow::~MainWindow()
{
delete mGameInvoker;
}
void MainWindow::addServerAndUpdate(QString addr)
{
favorites->insertRow(0);
QModelIndex mi = favorites->index(0, ServerData::ADDR);
favorites->setData(mi, addr, Qt::EditRole);
NetController::get()->updateInfo(favorites, mi);
}
void MainWindow::addServer()
{
int id = tblServerBrowser->selectionModel()->currentIndex().row();
if(id >= 0)
{
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
favorites->myData.push_back(browser->myData[sourceId]);
}
}
void MainWindow::addServerByIP()
{
bool ok;
QString text = QInputDialog::getText(this, tr("Add Server by address"), tr("Address:"), QLineEdit::Normal, "", &ok);
if(ok && !text.isEmpty())
addServerAndUpdate(text);
}
void MainWindow::deleteServer()
{
if(tabWidget->currentIndex() != 1)
return;
int id = tblFavorites->selectionModel()->currentIndex().row();
if(id >= 0)
{
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
favorites->removeRow(sourceId);
if(favorites->myData.isEmpty())
{
actionPlay->setEnabled(false);
actionDelete->setEnabled(false);
}
}
}
bool MainWindow::refresh()
{
return NetController::get()->updateInfo(proxyModel->sourceModel());
/*tblServerBrowser->resizeColumnToContents(ServerData::HOSTNAME);
tblServerBrowser->resizeColumnToContents(ServerData::MODNAME);
tblFavorites->resizeColumnToContents(ServerData::HOSTNAME);
tblFavorites->resizeColumnToContents(ServerData::MODNAME);*/
}
void MainWindow::play()
{
QTableView *curTable = tabWidget->currentIndex() ? tblFavorites : tblServerBrowser;
int id = curTable->selectionModel()->currentIndex().row();
if(id < 0)
return;
ServerInfoDialog infoDialog(this);
ServerModel *sm = ((ServerModel*)proxyModel->sourceModel());
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
NetController::get()->selectServer(&sm->myData[sourceId]);
infoDialog.refresh();
if(!infoDialog.exec())
return;
QStringList arguments;
arguments.append(QLatin1String("--connect=") + sm->myData[sourceId].addr.toLatin1());
if(sm->myData[sourceId].needPassw)
{
bool ok;
QString passw = QInputDialog::getText(this, "Connecting to: " + sm->myData[sourceId].addr, "Password: ", QLineEdit::Password, "", &ok);
if(!ok)
return;
arguments.append(QLatin1String("--password=") + passw.toLatin1());
}
if (mGameInvoker->startProcess(QLatin1String("tes3mp"), arguments, true))
return qApp->quit();
}
void MainWindow::tabSwitched(int index)
{
if(index == 0)
{
proxyModel->setSourceModel(browser);
actionDelete->setEnabled(false);
}
else
{
proxyModel->setSourceModel(favorites);
}
actionPlay->setEnabled(false);
actionAdd->setEnabled(false);
}
void MainWindow::serverSelected()
{
actionPlay->setEnabled(true);
if(tabWidget->currentIndex() == 0)
actionAdd->setEnabled(true);
if(tabWidget->currentIndex() == 1)
actionDelete->setEnabled(true);
}
void MainWindow::closeEvent(QCloseEvent *event)
{
Files::ConfigurationManager cfgMgr;
QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / "favorites.dat").string());
QJsonArray saveData;
for(auto server : favorites->myData)
saveData.push_back(server.addr);
QFile file(cfgPath);
if(!file.open(QIODevice::WriteOnly))
{
qDebug() << "Cannot save " << cfgPath;
return;
}
file.write(QJsonDocument(saveData).toJson());
file.close();
}
void MainWindow::loadFavorites()
{
Files::ConfigurationManager cfgMgr;
QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / "favorites.dat").string());
QFile file(cfgPath);
if(!file.open(QIODevice::ReadOnly))
{
qDebug() << "Cannot open " << cfgPath;
return;
}
QJsonDocument jsonDoc(QJsonDocument::fromJson(file.readAll()));
for(auto server : jsonDoc.array())
addServerAndUpdate(server.toString());
file.close();
}
void MainWindow::notFullSwitch(bool state)
{
proxyModel->filterFullServer(state);
}
void MainWindow::havePlayersSwitch(bool state)
{
proxyModel->filterEmptyServers(state);
}
void MainWindow::maxLatencyChanged(int index)
{
int maxLatency = index * 50;
proxyModel->pingLessThan(maxLatency);
}
void MainWindow::gamemodeChanged(const QString &text)
{
proxyModel->setFilterFixedString(text);
proxyModel->setFilterKeyColumn(ServerData::MODNAME);
}

@ -0,0 +1,44 @@
//
// Created by koncord on 06.01.17.
//
#ifndef NEWLAUNCHER_MAIN_HPP
#define NEWLAUNCHER_MAIN_HPP
#include "ui_Main.h"
#include "ServerModel.hpp"
#include "MySortFilterProxyModel.hpp"
#include <components/process/processinvoker.hpp>
class MainWindow : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
protected:
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
void addServerAndUpdate(QString addr);
public slots:
bool refresh();
protected slots:
void tabSwitched(int index);
void addServer();
void addServerByIP();
void deleteServer();
void play();
void serverSelected();
void notFullSwitch(bool state);
void havePlayersSwitch(bool state);
void maxLatencyChanged(int index);
void gamemodeChanged(const QString &text);
private:
Process::ProcessInvoker *mGameInvoker;
ServerModel *browser, *favorites;
MySortFilterProxyModel *proxyModel;
void loadFavorites();
};
#endif //NEWLAUNCHER_MAIN_HPP

@ -0,0 +1,54 @@
//
// Created by koncord on 30.01.17.
//
#include "MySortFilterProxyModel.hpp"
#include "ServerModel.hpp"
#include <qdebug.h>
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QModelIndex pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_PLAYERS, sourceParent);
int ping = sourceModel()->data(pingIndex).toInt();
int players = sourceModel()->data(plIndex).toInt();
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
if(maxPing > 0 && (ping == -1 || ping > maxPing))
return false;
if(filterEmpty && players == 0)
return false;
if(filterFull && players >= maxPlayers)
return false;
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{
filterEmpty = false;
filterFull = false;
maxPing = 0;
}
void MySortFilterProxyModel::filterEmptyServers(bool state)
{
filterEmpty = state;
invalidateFilter();
}
void MySortFilterProxyModel::filterFullServer(bool state)
{
filterFull = state;
invalidateFilter();
}
void MySortFilterProxyModel::pingLessThan(int maxPing)
{
this->maxPing = maxPing;
invalidateFilter();
}

@ -0,0 +1,27 @@
//
// Created by koncord on 30.01.17.
//
#ifndef OPENMW_MYSORTFILTERPROXYMODEL_HPP
#define OPENMW_MYSORTFILTERPROXYMODEL_HPP
#include <QSortFilterProxyModel>
class MySortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;
public:
MySortFilterProxyModel(QObject *parent);
void filterFullServer(bool state);
void filterEmptyServers(bool state);
void pingLessThan(int maxPing);
private:
bool filterEmpty, filterFull;
int maxPing;
};
#endif //OPENMW_MYSORTFILTERPROXYMODEL_HPP

@ -0,0 +1,266 @@
//
// Created by koncord on 07.01.17.
//
#include <cassert>
#include <QtCore/QTime>
#include "NetController.hpp"
#include "qdebug.h"
#include <RakPeer.h>
#include <RakSleep.h>
#include <sstream>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <memory>
#include <QtWidgets/QMessageBox>
using namespace std;
NetController *NetController::mThis = nullptr;
NetController *NetController::get()
{
assert(mThis);
return mThis;
}
void NetController::Create(std::string addr, unsigned short port)
{
assert(!mThis);
mThis = new NetController(addr, port);
}
void NetController::Destroy()
{
assert(mThis);
delete mThis;
mThis = nullptr;
}
NetController::NetController(std::string addr, unsigned short port) : httpNetwork(addr, port)
{
}
NetController::~NetController()
{
}
struct pattern
{
pattern(QString value): value(value) {}
bool operator()(const ServerData &data)
{
return value == data.addr;
}
QString value;
};
void NetController::setData(QString address, QJsonObject server, ServerModel *model)
{
QModelIndex mi = model->index(0, ServerData::ADDR);
model->setData(mi, address);
mi = model->index(0, ServerData::PLAYERS);
model->setData(mi, server["players"].toInt());
mi = model->index(0, ServerData::MAX_PLAYERS);
model->setData(mi, server["max_players"].toInt());
mi = model->index(0, ServerData::HOSTNAME);
model->setData(mi, server["hostname"].toString());
mi = model->index(0, ServerData::MODNAME);
model->setData(mi, server["modname"].toString());
mi = model->index(0, ServerData::VERSION);
model->setData(mi, server["version"].toString());
mi = model->index(0, ServerData::PASSW);
model->setData(mi, server["passw"].toBool());
mi = model->index(0, ServerData::PING);
// This *should* fix a crash when a port isn't returned by data.
if(!address.contains(":"))
address.append(":25565");
QStringList addr = address.split(":");
model->setData(mi, PingRakNetServer(addr[0].toLatin1().data(), addr[1].toUShort()));
}
bool NetController::downloadInfo(QAbstractItemModel *pModel, QModelIndex index)
{
ServerModel *model = ((ServerModel *) pModel);
/*
* download stuff
*/
QString data;
QJsonParseError err;
if(index.isValid() && index.row() >= 0)
{
const ServerData &sd = model->myData[index.row()];
while(true)
{
data = QString::fromStdString(httpNetwork.getData((QString("/api/servers/") + sd.addr).toLatin1()));
if (!data.isEmpty() && data != "NO_CONTENT" && data != "LOST_CONNECTION")
break;
RakSleep(30);
}
qDebug() << "Content for \"" << sd.addr << "\": " << data;
if(data == "bad request" || data == "not found") // TODO: if server is not registered we should download info directly from the server
{
qDebug() << "Server is not registered";
return false;
}
QJsonDocument jsonDocument = QJsonDocument::fromJson(data.toLatin1(), &err);
QJsonObject server = jsonDocument.object()["server"].toObject();
setData(sd.addr, server, model);
return true;
}
while (true)
{
data = QString::fromStdString(httpNetwork.getData("/api/servers"));
if (!data.isEmpty() && data != "NO_CONTENT" && data != "LOST_CONNECTION")
break;
RakSleep(30);
}
if(data == "UNKNOWN_ADDRESS")
{
QMessageBox::critical(0, "Error", "Cannot connect to the master server!");
return false;
}
qDebug() << "Content: " << data;
QJsonDocument jsonDocument = QJsonDocument::fromJson(data.toLatin1(), &err);
QJsonObject listServers = jsonDocument.object()["list servers"].toObject();
for(auto iter = listServers.begin(); iter != listServers.end(); iter++)
{
QJsonObject server = iter->toObject();
qDebug() << iter.key();
qDebug() << server["hostname"].toString();
qDebug() << server["modname"].toString();
qDebug() << server["players"].toInt();
qDebug() << server["max_players"].toInt();
qDebug() << server["version"].toString();
qDebug() << server["passw"].toBool();
QVector<ServerData>::Iterator value = std::find_if(model->myData.begin(), model->myData.end(), pattern(iter.key()));
if(value == model->myData.end())
model->insertRow(0);
setData(iter.key(), server, model);
}
return true;
}
bool NetController::updateInfo(QAbstractItemModel *pModel, QModelIndex index)
{
ServerModel *model = ((ServerModel*)pModel);
bool result;
if (index.isValid() && index.row() >= 0)
result = downloadInfo(pModel, index);
else
{
for (auto iter = model->myData.begin(); iter != model->myData.end(); iter++)
{
qDebug() << iter->addr;
}
model->removeRows(0, model->rowCount(index));
result = downloadInfo(pModel, index);
}
return result;
}
void NetController::updateInfo()
{
QString data;
QString uri = "/api/servers/" + sd->addr;
while (true)
{
data = QString::fromStdString(httpNetwork.getData(uri.toLatin1()));
if (!data.isEmpty() && data != "NO_CONTENT" && data != "LOST_CONNECTION")
break;
RakSleep(30);
}
if(data == "UNKNOWN_ADDRESS")
{
QMessageBox::critical(0, "Error", "Cannot connect to the master server!");
return;
}
qDebug() << "Content: " << data;
QJsonParseError err;
QJsonDocument jsonDocument = QJsonDocument::fromJson(data.toLatin1(), &err);
QMap<QString, QVariant> map = jsonDocument.toVariant().toMap()["server"].toMap();
qDebug() << sd->addr;
qDebug() << map["hostname"].toString();
qDebug() << map["modname"].toString();
qDebug() << map["players"].toInt();
qDebug() << map["max_players"].toInt();
qDebug() << map["version"].toString();
qDebug() << map["passw"].toBool();
sd->hostName = map["hostname"].toString();
sd->modName = map["modname"].toString();
sd->players = map["players"].toInt();
sd->maxPlayers = map["max_players"].toInt();
if(!sd->addr.contains(":"))
sd->addr.append(":25565");
QStringList addr = sd->addr.split(":");
sd->ping = PingRakNetServer(addr[0].toLatin1(), addr[1].toUShort());
if(sd->ping != PING_UNREACHABLE)
sed = getExtendedData(addr[0].toLatin1(), addr[1].toUShort());
else
qDebug() << "Server is unreachable";
}
QStringList NetController::players()
{
QStringList listPlayers;
for(auto player = sed.players.begin(); player != sed.players.end(); player++)
listPlayers.push_back(player->c_str());
return listPlayers;
}
QStringList NetController::plugins()
{
QStringList listPlugins;
for(auto plugin = sed.plugins.begin(); plugin != sed.plugins.end(); plugin++)
listPlugins.push_back(plugin->c_str());
return listPlugins;
}
void NetController::selectServer(ServerData *pServerData)
{
sd = pServerData;
}
ServerData *NetController::selectedServer()
{
return sd;
}

@ -0,0 +1,42 @@
//
// Created by koncord on 07.01.17.
//
#ifndef NEWLAUNCHER_NETCONTROLLER_HPP
#define NEWLAUNCHER_NETCONTROLLER_HPP
#include "ServerModel.hpp"
#include "netutils/HTTPNetwork.hpp"
#include "netutils/Utils.hpp"
struct ServerModel;
class NetController
{
public:
static NetController *get();
static void Create(std::string addr, unsigned short port);
static void Destroy();
bool updateInfo(QAbstractItemModel *pModel, QModelIndex index= QModelIndex());
void updateInfo();
QStringList players();
QStringList plugins();
void selectServer(ServerData *pServerData);
ServerData *selectedServer();
protected:
NetController(std::string addr, unsigned short port);
~NetController();
private:
NetController(const NetController &controller);
bool downloadInfo(QAbstractItemModel *pModel, QModelIndex index);
void setData(QString addr, QJsonObject server, ServerModel *model);
static NetController *mThis;
ServerData *sd;
HTTPNetwork httpNetwork;
ServerExtendedData sed;
};
#endif //NEWLAUNCHER_NETCONTROLLER_HPP

@ -0,0 +1,39 @@
//
// Created by koncord on 07.01.17.
//
#include "qdebug.h"
#include "NetController.hpp"
#include "ServerInfoDialog.hpp"
ServerInfoDialog::ServerInfoDialog(QWidget *parent): QDialog(parent)
{
setupUi(this);
connect(btnRefresh, SIGNAL(clicked()), this, SLOT(refresh()));
}
ServerInfoDialog::~ServerInfoDialog()
{
}
void ServerInfoDialog::refresh()
{
NetController::get()->updateInfo();
ServerData *sd = NetController::get()->selectedServer();
if (sd)
{
leAddr->setText(sd->addr);
lblName->setText(sd->hostName);
lblPing->setNum(sd->ping);
listPlayers->clear();
QStringList players = NetController::get()->players();
listPlayers->addItems(players);
listPlugins->clear();
listPlugins->addItems(NetController::get()->plugins());
lblPlayers->setText(QString::number(players.size()) + " / " + QString::number(sd->maxPlayers));
}
}

@ -0,0 +1,21 @@
//
// Created by koncord on 07.01.17.
//
#ifndef NEWLAUNCHER_SERVERINFODIALOG_HPP
#define NEWLAUNCHER_SERVERINFODIALOG_HPP
#include "ui_ServerInfo.h"
class ServerInfoDialog : public QDialog, public Ui::Dialog
{
Q_OBJECT
public:
explicit ServerInfoDialog(QWidget *parent = 0);
virtual ~ServerInfoDialog();
public slots:
void refresh();
};
#endif //NEWLAUNCHER_SERVERINFODIALOG_HPP

@ -0,0 +1,200 @@
#include <qmessagebox.h>
#include "ServerModel.hpp"
#include <qdebug.h>
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
{
}
ServerModel::~ServerModel()
{
}
/*QHash<int, QByteArray> ServerModel::roleNames() const
{
return roles;
}*/
QVariant ServerModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() < 0 || index.row() > myData.size())
return QVariant();
const ServerData &sd = myData.at(index.row());
if(role == Qt::DisplayRole)
{
QVariant var;
switch (index.column())
{
case ServerData::ADDR:
var = sd.addr;
break;
case ServerData::PASSW:
var = sd.needPassw ? "Yes" : "No";
break;
case ServerData::VERSION:
var = sd.version;
break;
case ServerData::PLAYERS:
var = sd.players;
break;
case ServerData::MAX_PLAYERS:
var = sd.maxPlayers;
break;
case ServerData::HOSTNAME:
var = sd.hostName;
break;
case ServerData::PING:
var = sd.ping;
break;
case ServerData::MODNAME:
if(sd.modName.isEmpty())
var = "default";
else
var = sd.modName;
break;
}
return var;
}
return QVariant();
}
QVariant ServerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant var;
if (orientation == Qt::Horizontal)
{
if (role == Qt::SizeHintRole)
{
/*if(section == ServerData::HOSTNAME)
var = QSize(200, 25);*/
}
else if (role == Qt::DisplayRole)
{
switch (section)
{
case ServerData::ADDR:
var = "Address";
break;
case ServerData::PASSW:
var = "Password";
break;
case ServerData::VERSION:
var = "Version";
break;
case ServerData::HOSTNAME:
var = "Host name";
break;
case ServerData::PLAYERS:
var = "Players";
break;
case ServerData::MAX_PLAYERS:
var = "Max players";
break;
case ServerData::PING:
var = "Ping";
break;
case ServerData::MODNAME:
var = "Game mode";
}
}
}
return var;
}
int ServerModel::rowCount(const QModelIndex &parent) const
{
return myData.size();
}
int ServerModel::columnCount(const QModelIndex &parent) const
{
return ServerData::LAST;
}
bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole)
{
int row = index.row();
int col = index.column();
ServerData &sd = myData[row];
bool ok = true;
switch(col)
{
case ServerData::ADDR:
sd.addr = value.toString();
ok = !sd.addr.isEmpty();
break;
case ServerData::PASSW:
sd.needPassw = value.toBool();
break;
case ServerData::VERSION:
sd.version = value.toString();
ok = !sd.addr.isEmpty();
break;
case ServerData::PLAYERS:
sd.players = value.toInt(&ok);
break;
case ServerData::MAX_PLAYERS:
sd.maxPlayers = value.toInt(&ok);
break;
case ServerData::HOSTNAME:
sd.hostName = value.toString();
ok = !sd.addr.isEmpty();
break;
case ServerData::PING:
sd.ping = value.toInt(&ok);
break;
case ServerData::MODNAME:
sd.modName = value.toString();
break;
default:
return false;
}
if(ok)
emit(dataChanged(index, index));
return true;
}
return false;
}
bool ServerModel::insertRows(int position, int count, const QModelIndex &index)
{
Q_UNUSED(index);
beginInsertRows(QModelIndex(), position, position + count - 1);
for (int row = 0; row < count; ++row) {
ServerData sd {"", -1, -1, -1, "", "", false, 0};
myData.insert(position, sd);
}
endInsertRows();
return true;
}
bool ServerModel::removeRows(int position, int count, const QModelIndex &parent)
{
if (count == 0)
return false;
beginRemoveRows(parent, position, position + count - 1);
myData.erase(myData.begin()+position, myData.begin() + position + count);
endRemoveRows();
return true;
}
QModelIndex ServerModel::index(int row, int column, const QModelIndex &parent) const
{
QModelIndex index = QAbstractTableModel::index(row, column, parent);
//qDebug() << "Valid index? " << index.isValid() << " " << row << " " << column;
return index;
}

@ -0,0 +1,56 @@
#ifndef SERVERMODEL_FONTMODEL_HPP
#define SERVERMODEL_FONTMODEL_HPP
#include <QObject>
#include <vector>
#include <QString>
#include <QAbstractTableModel>
struct ServerData
{
QString addr;
int players, maxPlayers;
int ping;
QString hostName;
QString modName;
bool needPassw;
QString version;
enum IDS
{
ADDR,
HOSTNAME,
PLAYERS,
MAX_PLAYERS,
PASSW,
MODNAME,
PING,
VERSION,
LAST
};
};
class ServerModel: public QAbstractTableModel
{
Q_OBJECT
public:
explicit ServerModel(QObject *parent = 0);
~ServerModel();
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_FINAL;
bool insertRows(int row, int count, const QModelIndex &index = QModelIndex()) Q_DECL_FINAL;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_FINAL;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_FINAL;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
public:
//QHash<int, QByteArray> roles;
QVector<ServerData> myData;
};
#endif //SERVERMODEL_FONTMODEL_HPP

@ -0,0 +1,57 @@
#include <QApplication>
#include <components/settings/settings.hpp>
#include <components/files/configurationmanager.hpp>
#include "MainWindow.hpp"
#include "NetController.hpp"
std::string loadSettings (Settings::Manager & settings)
{
Files::ConfigurationManager mCfgMgr;
// Create the settings manager and load default settings file
const std::string localdefault = (mCfgMgr.getLocalPath() / "tes3mp-client-default.cfg").string();
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "tes3mp-client-default.cfg").string();
// prefer local
if (boost::filesystem::exists(localdefault))
settings.loadDefault(localdefault);
else if (boost::filesystem::exists(globaldefault))
settings.loadDefault(globaldefault);
else
throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-client-default.cfg\" was properly installed.");
// load user settings if they exist
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "tes3mp-client.cfg").string();
if (boost::filesystem::exists(settingspath))
settings.loadUser(settingspath);
return settingspath;
}
int main(int argc, char *argv[])
{
Settings::Manager mgr;
loadSettings(mgr);
std::string addr = mgr.getString("address", "Master");
int port = mgr.getInt("port", "Master");
// initialize resources, if needed
// Q_INIT_RESOURCE(resfile);
NetController::Create(addr, port);
atexit(NetController::Destroy);
QApplication app(argc, argv);
MainWindow d;
if (d.refresh())
{
d.show();
return app.exec();
}
else
{
app.exit();
return 0;
}
}

@ -0,0 +1,99 @@
//
// Created by koncord on 07.01.17.
//
#include <RakPeer.h>
#include <HTTPConnection2.h>
#include <TCPInterface.h>
#include <RakSleep.h>
#include <sstream>
#include "HTTPNetwork.hpp"
using namespace RakNet;
HTTPNetwork::HTTPNetwork(std::string addr, unsigned short port) : address(addr), port(port)
{
httpConnection = HTTPConnection2::GetInstance();
tcpInterface = new TCPInterface;
tcpInterface->Start(0, 64);
tcpInterface->AttachPlugin(httpConnection);
}
HTTPNetwork::~HTTPNetwork()
{
delete tcpInterface;
}
std::string HTTPNetwork::answer()
{
RakNet::SystemAddress sa;
RakNet::Packet *packet;
RakNet::SystemAddress hostReceived;
RakNet::RakString response;
RakNet::RakString transmitted, hostTransmitted;
int contentOffset = 0;
while (true)
{
// This is kind of crappy, but for TCP plugins, always do HasCompletedConnectionAttempt,
// then Receive(), then HasFailedConnectionAttempt(),HasLostConnection()
sa = tcpInterface->HasCompletedConnectionAttempt();
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
printf("Connected to master server: %s\n", sa.ToString());
sa = tcpInterface->HasFailedConnectionAttempt();
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
{
printf("Failed to connect to master server: %s\n", sa.ToString());
return "FAIL_CONNECT";
}
sa = tcpInterface->HasLostConnection();
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
{
printf("Lost connection to master server: %s\n", sa.ToString());
return "LOST_CONNECTION";
}
for (packet = tcpInterface->Receive(); packet; tcpInterface->DeallocatePacket(
packet), packet = tcpInterface->Receive());
if (httpConnection->GetResponse(transmitted, hostTransmitted, response, hostReceived, contentOffset))
{
if (contentOffset < 0)
return "NO_CONTENT"; // no content
tcpInterface->CloseConnection(sa);
return (response.C_String() + contentOffset);
}
RakSleep(30);
}
}
std::string HTTPNetwork::getData(const char *uri)
{
RakNet::RakString createRequest = RakNet::RakString::FormatForGET(uri);
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
return "UNKNOWN_ADDRESS";
return answer();
}
std::string HTTPNetwork::getDataPOST(const char *uri, const char* body, const char* contentType)
{
RakNet::RakString createRequest = RakNet::RakString::FormatForPOST(uri, contentType, body);
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
return "UNKNOWN_ADDRESS";
return answer();
}
std::string HTTPNetwork::getDataPUT(const char *uri, const char* body, const char* contentType)
{
RakNet::RakString createRequest = RakNet::RakString::FormatForPUT(uri, contentType, body);
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
return "UNKNOWN_ADDRESS";
return answer();
}

@ -0,0 +1,35 @@
//
// Created by koncord on 07.01.17.
//
#ifndef NEWLAUNCHER_HTTPNETWORK_HPP
#define NEWLAUNCHER_HTTPNETWORK_HPP
#include <string>
namespace RakNet
{
class TCPInterface;
class HTTPConnection2;
}
class HTTPNetwork
{
public:
HTTPNetwork(std::string addr, unsigned short port);
~HTTPNetwork();
std::string getData(const char *uri);
std::string getDataPOST(const char *uri, const char* body, const char* contentType = "application/json");
std::string getDataPUT(const char *uri, const char* body, const char* contentType = "application/json");
protected:
RakNet::TCPInterface *tcpInterface;
RakNet::HTTPConnection2 *httpConnection;
std::string address;
unsigned short port;
std::string answer();
};
#endif //NEWLAUNCHER_HTTPNETWORK_HPP

@ -0,0 +1,156 @@
//
// Created by koncord on 07.01.17.
//
#include <RakPeer.h>
#include <MessageIdentifiers.h>
#include <RakSleep.h>
#include <GetTime.h>
#include <iostream>
#include <sstream>
#include <components/openmw-mp/Version.hpp>
#include "Utils.hpp"
using namespace std;
unsigned int PingRakNetServer(const char *addr, unsigned short port)
{
RakNet::Packet *packet;
bool done = false;
int attempts = 0;
RakNet::TimeMS time = PING_UNREACHABLE;
RakNet::SocketDescriptor socketDescriptor {0, ""};
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
peer->Startup(1,&socketDescriptor, 1);
peer->Ping(addr, port, false);
while (!done)
{
for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
{
if(packet->data[0] == ID_UNCONNECTED_PONG)
{
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
bsIn.Read(time);
time = RakNet::GetTimeMS() - time - 5;
done = true;
break;
}
}
if (attempts >= 60) // wait 300 msec
done = true;
attempts++;
RakSleep(5);
}
peer->Shutdown(0);
RakNet::RakPeerInterface::DestroyInstance(peer);
return time;
}
ServerExtendedData getExtendedData(const char *addr, unsigned short port)
{
ServerExtendedData data;
RakNet::SocketDescriptor socketDescriptor = {0, ""};
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
peer->Startup(1, &socketDescriptor, 1);
stringstream sstr(TES3MP_VERSION);
sstr << TES3MP_PROTO_VERSION;
std::string msg = "";
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), 0, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
msg = "Connection attempt failed.\n";
int queue = 0;
while (queue == 0)
{
for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(
packet), packet = peer->Receive())
{
switch (packet->data[0])
{
case ID_CONNECTION_ATTEMPT_FAILED:
{
msg = "Connection failed.\n"
"Either the IP address is wrong or a firewall on either system is blocking\n"
"UDP packets on the port you have chosen.";
queue = -1;
break;
}
case ID_INVALID_PASSWORD:
{
msg = "Connection failed.\n"
"The client or server is outdated.\n";
queue = -1;
break;
}
case ID_CONNECTION_REQUEST_ACCEPTED:
{
msg = "Connection accepted.\n";
queue = 1;
break;
}
case ID_DISCONNECTION_NOTIFICATION:
throw runtime_error("ID_DISCONNECTION_NOTIFICATION.\n");
case ID_CONNECTION_BANNED:
throw runtime_error("ID_CONNECTION_BANNED.\n");
case ID_CONNECTION_LOST:
throw runtime_error("ID_CONNECTION_LOST.\n");
default:
printf("Connection message with identifier %i has arrived in initialization.\n", packet->data[0]);
}
}
}
puts(msg.c_str());
if(queue == -1) // connection is failed
return data;
{
RakNet::BitStream bs;
bs.Write((unsigned char) (ID_USER_PACKET_ENUM + 1));
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
}
RakNet::Packet *packet;
bool done = false;
while (!done)
{
for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
{
if(packet->data[0] == (ID_USER_PACKET_ENUM+1))
{
RakNet::BitStream bs(packet->data, packet->length, false);
bs.IgnoreBytes(1);
size_t length = 0;
bs.Read(length);
for(int i = 0; i < length; i++)
{
RakNet::RakString str;
bs.Read(str);
data.players.push_back(str.C_String());
}
bs.Read(length);
for(int i = 0; i < length; i++)
{
RakNet::RakString str;
bs.Read(str);
data.plugins.push_back(str.C_String());
}
done = true;
}
}
}
peer->Shutdown(0);
RakSleep(10);
RakNet::RakPeerInterface::DestroyInstance(peer);
return data;
}

@ -0,0 +1,24 @@
//
// Created by koncord on 07.01.17.
//
#ifndef NEWLAUNCHER_PING_HPP
#define NEWLAUNCHER_PING_HPP
#include <vector>
#include <string>
#define PING_UNREACHABLE 999
unsigned int PingRakNetServer(const char *addr, unsigned short port);
struct ServerExtendedData
{
std::vector<std::string> players;
std::vector<std::string> plugins;
};
ServerExtendedData getExtendedData(const char *addr, unsigned short port);
#endif //NEWLAUNCHER_PING_HPP

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

@ -179,7 +179,7 @@ RecordBase::create(ESM::NAME type)
{ {
RecordBase *record = 0; RecordBase *record = 0;
switch (type.val) { switch (type.intval) {
case ESM::REC_ACTI: case ESM::REC_ACTI:
{ {
record = new EsmTool::Record<ESM::Activator>; record = new EsmTool::Record<ESM::Activator>;
@ -494,7 +494,7 @@ void Record<ESM::Book>::print()
std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl; std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl;
std::cout << " SkillID: " << mData.mData.mSkillID << std::endl; std::cout << " SkillId: " << mData.mData.mSkillId << std::endl;
std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl;
if (mPrintPlain) if (mPrintPlain)
{ {

@ -42,3 +42,7 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(openmw-essimporter gcov) target_link_libraries(openmw-essimporter gcov)
endif() endif()
if (WIN32)
INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION ".")
endif(WIN32)

@ -1,3 +1,9 @@
#include <string>
#include <iostream>
#include <limits>
#include <components/misc/stringops.hpp>
#include "convertacdt.hpp" #include "convertacdt.hpp"
namespace ESSImport namespace ESSImport
@ -49,4 +55,49 @@ namespace ESSImport
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter; npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;
} }
void convertANIS (const ANIS& anis, ESM::AnimationState& state)
{
static const char* animGroups[] =
{
"Idle", "Idle2", "Idle3", "Idle4", "Idle5", "Idle6", "Idle7", "Idle8", "Idle9", "Idlehh", "Idle1h", "Idle2c",
"Idle2w", "IdleSwim", "IdleSpell", "IdleCrossbow", "IdleSneak", "IdleStorm", "Torch", "Hit1", "Hit2", "Hit3",
"Hit4", "Hit5", "SwimHit1", "SwimHit2", "SwimHit3", "Death1", "Death2", "Death3", "Death4", "Death5",
"DeathKnockDown", "DeathKnockOut", "KnockDown", "KnockOut", "SwimDeath", "SwimDeath2", "SwimDeath3",
"SwimDeathKnockDown", "SwimDeathKnockOut", "SwimKnockOut", "SwimKnockDown", "SwimWalkForward",
"SwimWalkBack", "SwimWalkLeft", "SwimWalkRight", "SwimRunForward", "SwimRunBack", "SwimRunLeft",
"SwimRunRight", "SwimTurnLeft", "SwimTurnRight", "WalkForward", "WalkBack", "WalkLeft", "WalkRight",
"TurnLeft", "TurnRight", "RunForward", "RunBack", "RunLeft", "RunRight", "SneakForward", "SneakBack",
"SneakLeft", "SneakRight", "Jump", "WalkForwardhh", "WalkBackhh", "WalkLefthh", "WalkRighthh",
"TurnLefthh", "TurnRighthh", "RunForwardhh", "RunBackhh", "RunLefthh", "RunRighthh", "SneakForwardhh",
"SneakBackhh", "SneakLefthh", "SneakRighthh", "Jumphh", "WalkForward1h", "WalkBack1h", "WalkLeft1h",
"WalkRight1h", "TurnLeft1h", "TurnRight1h", "RunForward1h", "RunBack1h", "RunLeft1h", "RunRight1h",
"SneakForward1h", "SneakBack1h", "SneakLeft1h", "SneakRight1h", "Jump1h", "WalkForward2c", "WalkBack2c",
"WalkLeft2c", "WalkRight2c", "TurnLeft2c", "TurnRight2c", "RunForward2c", "RunBack2c", "RunLeft2c",
"RunRight2c", "SneakForward2c", "SneakBack2c", "SneakLeft2c", "SneakRight2c", "Jump2c", "WalkForward2w",
"WalkBack2w", "WalkLeft2w", "WalkRight2w", "TurnLeft2w", "TurnRight2w", "RunForward2w", "RunBack2w",
"RunLeft2w", "RunRight2w", "SneakForward2w", "SneakBack2w", "SneakLeft2w", "SneakRight2w", "Jump2w",
"SpellCast", "SpellTurnLeft", "SpellTurnRight", "Attack1", "Attack2", "Attack3", "SwimAttack1",
"SwimAttack2", "SwimAttack3", "HandToHand", "Crossbow", "BowAndArrow", "ThrowWeapon", "WeaponOneHand",
"WeaponTwoHand", "WeaponTwoWide", "Shield", "PickProbe", "InventoryHandToHand", "InventoryWeaponOneHand",
"InventoryWeaponTwoHand", "InventoryWeaponTwoWide"
};
if (anis.mGroupIndex < (sizeof(animGroups) / sizeof(*animGroups)))
{
std::string group(animGroups[anis.mGroupIndex]);
Misc::StringUtils::lowerCaseInPlace(group);
ESM::AnimationState::ScriptedAnimation scriptedAnim;
scriptedAnim.mGroup = group;
scriptedAnim.mTime = anis.mTime;
scriptedAnim.mAbsolute = true;
// Neither loop count nor queueing seems to be supported by the ess format.
scriptedAnim.mLoopCount = std::numeric_limits<size_t>::max();
state.mScriptedAnims.push_back(scriptedAnim);
}
else
// TODO: Handle 0xFF index, which seems to be used for finished animations.
std::cerr << "unknown animation group index: " << static_cast<unsigned int>(anis.mGroupIndex) << std::endl;
}
} }

@ -4,6 +4,7 @@
#include <components/esm/creaturestats.hpp> #include <components/esm/creaturestats.hpp>
#include <components/esm/npcstats.hpp> #include <components/esm/npcstats.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
#include <components/esm/animationstate.hpp>
#include "importacdt.hpp" #include "importacdt.hpp"
@ -18,6 +19,8 @@ namespace ESSImport
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats); void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats); void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
void convertANIS (const ANIS& anis, ESM::AnimationState& state);
} }
#endif #endif

@ -34,6 +34,9 @@ namespace
objstate.mCount = 0; objstate.mCount = 0;
convertSCRI(cellref.mSCRI, objstate.mLocals); convertSCRI(cellref.mSCRI, objstate.mLocals);
objstate.mHasLocals = !objstate.mLocals.mVariables.empty(); objstate.mHasLocals = !objstate.mLocals.mVariables.empty();
if (cellref.mHasANIS)
convertANIS(cellref.mANIS, objstate.mAnimationState);
} }
bool isIndexedRefId(const std::string& indexedRefId) bool isIndexedRefId(const std::string& indexedRefId)

@ -121,7 +121,7 @@ public:
{ {
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
mContext->mPlayerBase = npc; mContext->mPlayerBase = npc;
std::map<int, float> empty; ESM::SpellState::SpellParams empty;
// FIXME: player start spells and birthsign spells aren't listed here, // FIXME: player start spells and birthsign spells aren't listed here,
// need to fix openmw to account for this // need to fix openmw to account for this
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)
@ -202,7 +202,7 @@ public:
bool isDeleted = false; bool isDeleted = false;
book.load(esm, isDeleted); book.load(esm, isDeleted);
if (book.mData.mSkillID == -1) if (book.mData.mSkillId == -1)
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId)); mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));
mRecords[book.mId] = book; mRecords[book.mId] = book;
@ -271,23 +271,34 @@ private:
class ConvertPCDT : public Converter class ConvertPCDT : public Converter
{ {
public: public:
ConvertPCDT() : mFirstPersonCam(true) {} ConvertPCDT()
: mFirstPersonCam(true),
mTeleportingEnabled(true),
mLevitationEnabled(true)
{}
virtual void read(ESM::ESMReader &esm) virtual void read(ESM::ESMReader &esm)
{ {
PCDT pcdt; PCDT pcdt;
pcdt.load(esm); pcdt.load(esm);
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam); convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState);
} }
virtual void write(ESM::ESMWriter &esm) virtual void write(ESM::ESMWriter &esm)
{ {
esm.startRecord(ESM::REC_ENAB);
esm.writeHNT("TELE", mTeleportingEnabled);
esm.writeHNT("LEVT", mLevitationEnabled);
esm.endRecord(ESM::REC_ENAB);
esm.startRecord(ESM::REC_CAM_); esm.startRecord(ESM::REC_CAM_);
esm.writeHNT("FIRS", mFirstPersonCam); esm.writeHNT("FIRS", mFirstPersonCam);
esm.endRecord(ESM::REC_CAM_); esm.endRecord(ESM::REC_CAM_);
} }
private: private:
bool mFirstPersonCam; bool mFirstPersonCam;
bool mTeleportingEnabled;
bool mLevitationEnabled;
}; };
class ConvertCNTC : public Converter class ConvertCNTC : public Converter

@ -5,7 +5,7 @@
namespace ESSImport namespace ESSImport
{ {
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam) void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls)
{ {
out.mBirthsign = pcdt.mBirthsign; out.mBirthsign = pcdt.mBirthsign;
out.mObject.mNpcStats.mBounty = pcdt.mBounty; out.mObject.mNpcStats.mBounty = pcdt.mBounty;
@ -17,24 +17,72 @@ namespace ESSImport
faction.mReputation = it->mReputation; faction.mReputation = it->mReputation;
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction; out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
} }
for (int i=0; i<3; ++i)
out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
for (int i=0; i<27; ++i) for (int i=0; i<27; ++i)
out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i]; out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];
out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress; out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon) if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawn)
out.mObject.mCreatureStats.mDrawState = 1; out.mObject.mCreatureStats.mDrawState = 1;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell) if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawn)
out.mObject.mCreatureStats.mDrawState = 2; out.mObject.mCreatureStats.mDrawState = 2;
firstPersonCam = (pcdt.mPNAM.mCameraState == PCDT::CameraState_FirstPerson); firstPersonCam = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ThirdPerson);
teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled);
levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled);
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin(); for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
it != pcdt.mKnownDialogueTopics.end(); ++it) it != pcdt.mKnownDialogueTopics.end(); ++it)
{ {
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it)); outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
} }
controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled;
controls.mControlsDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ControlsDisabled;
controls.mJumpingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_JumpingDisabled;
controls.mLookingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LookingDisabled;
controls.mVanityModeDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_VanityModeDisabled;
controls.mWeaponDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawingDisabled;
controls.mSpellDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawingDisabled;
if (pcdt.mHasMark)
{
out.mHasMark = 1;
const PCDT::PNAM::MarkLocation& mark = pcdt.mPNAM.mMarkLocation;
ESM::CellId cell;
cell.mWorldspace = ESM::CellId::sDefaultWorldspace;
cell.mPaged = true;
cell.mIndex.mX = mark.mCellX;
cell.mIndex.mY = mark.mCellY;
// TODO: Figure out a better way to detect interiors. (0, 0) is a valid exterior cell.
if (mark.mCellX == 0 && mark.mCellY == 0)
{
cell.mWorldspace = pcdt.mMNAM;
cell.mPaged = false;
}
out.mMarkedCell = cell;
out.mMarkedPosition.pos[0] = mark.mX;
out.mMarkedPosition.pos[1] = mark.mY;
out.mMarkedPosition.pos[2] = mark.mZ;
out.mMarkedPosition.rot[0] = out.mMarkedPosition.rot[1] = 0.0f;
out.mMarkedPosition.rot[2] = mark.mRotZ;
}
if (pcdt.mHasENAM)
{
const int cellSize = 8192;
out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize;
out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize;
out.mLastKnownExteriorPosition[2] = 0.0f;
}
} }
} }

@ -4,11 +4,12 @@
#include "importplayer.hpp" #include "importplayer.hpp"
#include <components/esm/player.hpp> #include <components/esm/player.hpp>
#include <components/esm/controlsstate.hpp>
namespace ESSImport namespace ESSImport
{ {
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam); void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls);
} }

@ -75,7 +75,7 @@ namespace ESSImport
// unsure at which point between TGTN and CRED // unsure at which point between TGTN and CRED
if (esm.isNextSub("AADT")) if (esm.isNextSub("AADT"))
{ {
// occured when a creature was in the middle of its attack, 44 bytes // occurred when a creature was in the middle of its attack, 44 bytes
esm.skipHSub(); esm.skipHSub();
} }
@ -123,8 +123,13 @@ namespace ESSImport
if (esm.isNextSub("ND3D")) if (esm.isNextSub("ND3D"))
esm.skipHSub(); esm.skipHSub();
mHasANIS = false;
if (esm.isNextSub("ANIS")) if (esm.isNextSub("ANIS"))
esm.skipHSub(); {
mHasANIS = true;
esm.getHT(mANIS);
}
} }
} }

@ -55,6 +55,12 @@ namespace ESSImport
unsigned char mCorpseClearCountdown; // hours? unsigned char mCorpseClearCountdown; // hours?
unsigned char mUnknown3[71]; unsigned char mUnknown3[71];
}; };
struct ANIS
{
unsigned char mGroupIndex;
unsigned char mUnknown[3];
float mTime;
};
#pragma pack(pop) #pragma pack(pop)
struct ActorData : public ESM::CellRef struct ActorData : public ESM::CellRef
@ -77,6 +83,9 @@ namespace ESSImport
SCRI mSCRI; SCRI mSCRI;
bool mHasANIS;
ANIS mANIS; // scripted animation state
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
}; };

@ -51,6 +51,7 @@ namespace
{ {
for (int x=0; x<128; ++x) for (int x=0; x<128; ++x)
{ {
assert(image->data(x,y));
*(image->data(x,y)+2) = *it++; *(image->data(x,y)+2) = *it++;
*(image->data(x,y)+1) = *it++; *(image->data(x,y)+1) = *it++;
*image->data(x,y) = *it++; *image->data(x,y) = *it++;
@ -322,14 +323,14 @@ namespace ESSImport
ESM::NAME n = esm.getRecName(); ESM::NAME n = esm.getRecName();
esm.getRecHeader(); esm.getRecHeader();
std::map<unsigned int, boost::shared_ptr<Converter> >::iterator it = converters.find(n.val); std::map<unsigned int, boost::shared_ptr<Converter> >::iterator it = converters.find(n.intval);
if (it != converters.end()) if (it != converters.end())
{ {
it->second->read(esm); it->second->read(esm);
} }
else else
{ {
if (unknownRecords.insert(n.val).second) if (unknownRecords.insert(n.intval).second)
{ {
std::ios::fmtflags f(std::cerr.flags()); std::ios::fmtflags f(std::cerr.flags());
std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl; std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
@ -422,6 +423,10 @@ namespace ESSImport
writer.startRecord (ESM::REC_DIAS); writer.startRecord (ESM::REC_DIAS);
context.mDialogueState.save(writer); context.mDialogueState.save(writer);
writer.endRecord(ESM::REC_DIAS); writer.endRecord(ESM::REC_DIAS);
writer.startRecord(ESM::REC_INPU);
context.mControlsState.save(writer);
writer.endRecord(ESM::REC_INPU);
} }

@ -9,6 +9,7 @@
#include <components/esm/globalmap.hpp> #include <components/esm/globalmap.hpp>
#include <components/esm/loadcrea.hpp> #include <components/esm/loadcrea.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/controlsstate.hpp>
#include "importnpcc.hpp" #include "importnpcc.hpp"
#include "importcrec.hpp" #include "importcrec.hpp"
@ -32,6 +33,8 @@ namespace ESSImport
ESM::DialogueState mDialogueState; ESM::DialogueState mDialogueState;
ESM::ControlsState mControlsState;
// cells which should show an explored overlay on the global map // cells which should show an explored overlay on the global map
std::set<std::pair<int, int> > mExploredCells; std::set<std::pair<int, int> > mExploredCells;
@ -59,10 +62,14 @@ namespace ESSImport
playerCellId.mPaged = true; playerCellId.mPaged = true;
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
mPlayer.mCellId = playerCellId; mPlayer.mCellId = playerCellId;
//mPlayer.mLastKnownExteriorPosition mPlayer.mLastKnownExteriorPosition[0]
mPlayer.mHasMark = 0; // TODO = mPlayer.mLastKnownExteriorPosition[1]
= mPlayer.mLastKnownExteriorPosition[2]
= 0.0f;
mPlayer.mHasMark = 0;
mPlayer.mCurrentCrimeId = 0; // TODO mPlayer.mCurrentCrimeId = 0; // TODO
mPlayer.mObject.blank(); mPlayer.mObject.blank();
mPlayer.mObject.mEnabled = true;
mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame
mGlobalMapState.mBounds.mMinX = 0; mGlobalMapState.mBounds.mMinX = 0;

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

@ -23,9 +23,12 @@ namespace ESSImport
mKnownDialogueTopics.push_back(esm.getHString()); mKnownDialogueTopics.push_back(esm.getHString());
} }
mHasMark = false;
if (esm.isNextSub("MNAM")) if (esm.isNextSub("MNAM"))
esm.skipHSub(); // If this field is here it seems to specify the interior cell the player is in, {
// but it's not always here, so it's kinda useless mHasMark = true;
mMNAM = esm.getHString();
}
esm.getHNT(mPNAM, "PNAM"); esm.getHNT(mPNAM, "PNAM");
@ -34,6 +37,14 @@ namespace ESSImport
if (esm.isNextSub("NAM9")) if (esm.isNextSub("NAM9"))
esm.skipHSub(); esm.skipHSub();
// Rest state. You shouldn't even be able to save during rest, but skip just in case.
if (esm.isNextSub("RNAM"))
/*
int hoursLeft;
float x, y, z; // resting position
*/
esm.skipHSub(); // 16 bytes
mBounty = 0; mBounty = 0;
esm.getHNOT(mBounty, "CNAM"); esm.getHNOT(mBounty, "CNAM");
@ -50,8 +61,12 @@ namespace ESSImport
if (esm.isNextSub("NAM3")) if (esm.isNextSub("NAM3"))
esm.skipHSub(); esm.skipHSub();
mHasENAM = false;
if (esm.isNextSub("ENAM")) if (esm.isNextSub("ENAM"))
esm.skipHSub(); {
mHasENAM = true;
esm.getHT(mENAM);
}
if (esm.isNextSub("LNAM")) if (esm.isNextSub("LNAM"))
esm.skipHSub(); esm.skipHSub();
@ -63,12 +78,19 @@ namespace ESSImport
mFactions.push_back(fnam); mFactions.push_back(fnam);
} }
if (esm.isNextSub("AADT")) mHasAADT = false;
esm.skipHSub(); // 44 bytes, no clue if (esm.isNextSub("AADT")) // Attack animation data?
{
mHasAADT = true;
esm.getHT(mAADT);
}
if (esm.isNextSub("KNAM")) if (esm.isNextSub("KNAM"))
esm.skipHSub(); // assigned Quick Keys, I think esm.skipHSub(); // assigned Quick Keys, I think
if (esm.isNextSub("ANIS"))
esm.skipHSub(); // 16 bytes
if (esm.isNextSub("WERE")) if (esm.isNextSub("WERE"))
{ {
// some werewolf data, 152 bytes // some werewolf data, 152 bytes
@ -76,10 +98,6 @@ namespace ESSImport
esm.getSubHeader(); esm.getSubHeader();
esm.skip(152); esm.skip(152);
} }
// unsure if before or after WERE
if (esm.isNextSub("ANIS"))
esm.skipHSub();
} }
} }

@ -38,15 +38,23 @@ struct PCDT
std::vector<std::string> mKnownDialogueTopics; std::vector<std::string> mKnownDialogueTopics;
enum DrawState_ enum PlayerFlags
{ {
DrawState_Weapon = 0x80, PlayerFlags_ViewSwitchDisabled = 0x1,
DrawState_Spell = 0x100 PlayerFlags_ControlsDisabled = 0x4,
}; PlayerFlags_Sleeping = 0x10,
enum CameraState PlayerFlags_Waiting = 0x40,
{ PlayerFlags_WeaponDrawn = 0x80,
CameraState_FirstPerson = 0x8, PlayerFlags_SpellDrawn = 0x100,
CameraState_ThirdPerson = 0xa PlayerFlags_InJail = 0x200,
PlayerFlags_JumpingDisabled = 0x1000,
PlayerFlags_LookingDisabled = 0x2000,
PlayerFlags_VanityModeDisabled = 0x4000,
PlayerFlags_WeaponDrawingDisabled = 0x8000,
PlayerFlags_SpellDrawingDisabled = 0x10000,
PlayerFlags_ThirdPerson = 0x20000,
PlayerFlags_TeleportingDisabled = 0x40000,
PlayerFlags_LevitationDisabled = 0x80000
}; };
#pragma pack(push) #pragma pack(push)
@ -63,18 +71,53 @@ struct PCDT
struct PNAM struct PNAM
{ {
short mDrawState; // DrawState struct MarkLocation
short mCameraState; // CameraState {
float mX, mY, mZ; // worldspace position
float mRotZ; // Z angle in radians
int mCellX, mCellY; // grid coordinates; for interior cells this is always (0, 0)
};
int mPlayerFlags; // controls, camera and draw state
unsigned int mLevelProgress; unsigned int mLevelProgress;
float mSkillProgress[27]; // skill progress, non-uniform scaled float mSkillProgress[27]; // skill progress, non-uniform scaled
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
unsigned char mUnknown3[88]; int mTelekinesisRangeBonus; // in units; seems redundant
float mVisionBonus; // range: <0.0, 1.0>; affected by light spells and Get/Mod/SetPCVisionBonus
int mDetectKeyMagnitude; // seems redundant
int mDetectEnchantmentMagnitude; // seems redundant
int mDetectAnimalMagnitude; // seems redundant
MarkLocation mMarkLocation;
unsigned char mUnknown3[40];
unsigned char mSpecIncreases[3]; // number of skill increases for each specialization
unsigned char mUnknown4;
};
struct ENAM
{
int mCellX;
int mCellY;
};
struct AADT // 44 bytes
{
int animGroupIndex; // See convertANIS() for the mapping.
unsigned char mUnknown5[40];
}; };
#pragma pack(pop) #pragma pack(pop)
std::vector<FNAM> mFactions; std::vector<FNAM> mFactions;
PNAM mPNAM; PNAM mPNAM;
bool mHasMark;
std::string mMNAM; // mark cell name; can also be sDefaultCellname or region name
bool mHasENAM;
ENAM mENAM; // last exterior cell
bool mHasAADT;
AADT mAADT;
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
}; };

@ -13,7 +13,7 @@ namespace ESM
namespace ESSImport namespace ESSImport
{ {
/// Local variable assigments for a running script /// Local variable assignments for a running script
struct SCRI struct SCRI
{ {
std::string mScript; std::string mScript;

@ -87,6 +87,10 @@ add_executable(openmw-launcher
${UI_HDRS} ${UI_HDRS}
) )
if (WIN32)
INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION ".")
endif (WIN32)
target_link_libraries(openmw-launcher target_link_libraries(openmw-launcher
${SDL2_LIBRARY_ONLY} ${SDL2_LIBRARY_ONLY}
components components

@ -35,14 +35,6 @@ int main(int argc, char *argv[])
// Now we make sure the current dir is set to application path // Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
#ifdef Q_OS_MAC
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
#endif
QDir::setCurrent(dir.absolutePath()); QDir::setCurrent(dir.absolutePath());
Launcher::MainDialog mainWin; Launcher::MainDialog mainWin;

@ -293,6 +293,7 @@ bool Launcher::MainDialog::setupGameSettings()
{ {
mGameSettings.clear(); mGameSettings.clear();
QString localPath = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str());
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
@ -320,13 +321,13 @@ bool Launcher::MainDialog::setupGameSettings()
// Now the rest - priority: user > local > global // Now the rest - priority: user > local > global
QStringList paths; QStringList paths;
paths.append(globalPath + QString("openmw.cfg")); paths.append(globalPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg")); paths.append(localPath + QString("openmw.cfg"));
paths.append(userPath + QString("openmw.cfg")); paths.append(userPath + QString("openmw.cfg"));
foreach (const QString &path, paths) { foreach (const QString &path2, paths) {
qDebug() << "Loading config file:" << path.toUtf8().constData(); qDebug() << "Loading config file:" << path2.toUtf8().constData();
QFile file(path); file.setFileName(path2);
if (file.exists()) { if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
cfgError(tr("Error opening OpenMW configuration file"), cfgError(tr("Error opening OpenMW configuration file"),
@ -346,13 +347,13 @@ bool Launcher::MainDialog::setupGameSettings()
QStringList dataDirs; QStringList dataDirs;
// Check if the paths actually contain data files // Check if the paths actually contain data files
foreach (const QString path, mGameSettings.getDataDirs()) { foreach (const QString path3, mGameSettings.getDataDirs()) {
QDir dir(path); QDir dir(path3);
QStringList filters; QStringList filters;
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
if (!dir.entryList(filters).isEmpty()) if (!dir.entryList(filters).isEmpty())
dataDirs.append(path); dataDirs.append(path3);
} }
if (dataDirs.isEmpty()) if (dataDirs.isEmpty())
@ -579,6 +580,6 @@ void Launcher::MainDialog::play()
// Launch the game detached // Launch the game detached
if (mGameInvoker->startProcess(QLatin1String("openmw"), true)) if (mGameInvoker->startProcess(QLatin1String("tes3mp-browser"), true))
return qApp->quit(); return qApp->quit();
} }

@ -22,7 +22,8 @@ target_link_libraries(openmw-iniimporter
if (WIN32) if (WIN32)
target_link_libraries(openmw-iniimporter target_link_libraries(openmw-iniimporter
${Boost_LOCALE_LIBRARY}) ${Boost_LOCALE_LIBRARY})
endif() INSTALL(TARGETS openmw-iniimporter RUNTIME DESTINATION ".")
endif(WIN32)
if (MINGW) if (MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")

@ -824,8 +824,8 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
// does not appears in the ini file // does not appears in the ini file
cfg["fallback-archive"].push_back("Morrowind.bsa"); cfg["fallback-archive"].push_back("Morrowind.bsa");
for(std::vector<std::string>::const_iterator it=archives.begin(); it!=archives.end(); ++it) { for(std::vector<std::string>::const_iterator iter=archives.begin(); iter!=archives.end(); ++iter) {
cfg["fallback-archive"].push_back(*it); cfg["fallback-archive"].push_back(*iter);
} }
} }
@ -865,8 +865,8 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
// this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed. // this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed.
sort(contentFiles.begin(), contentFiles.end()); sort(contentFiles.begin(), contentFiles.end());
for(std::vector<std::pair<std::time_t, std::string> >::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) { for(std::vector<std::pair<std::time_t, std::string> >::const_iterator iter=contentFiles.begin(); iter!=contentFiles.end(); ++iter) {
cfg["content"].push_back(it->second); cfg["content"].push_back(iter->second);
} }
} }

@ -26,7 +26,7 @@ opencs_units_noqt (model/world
universalid record commands columnbase columnimp scriptcontext cell refidcollection universalid record commands columnbase columnimp scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager metadata defaultgmsts idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro
) )
opencs_hdrs_noqt (model/world opencs_hdrs_noqt (model/world
@ -35,14 +35,14 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools opencs_units (model/tools
tools reportmodel mergeoperation tools reportmodel mergeoperation
) )
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages gmstcheck mergestages gmstcheck topicinfocheck journalcheck
) )
opencs_hdrs_noqt (model/tools opencs_hdrs_noqt (model/tools
@ -66,8 +66,8 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator globalcreator
cellcreator referenceablecreator startscriptcreator referencecreator scenesubview cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
) )
@ -85,12 +85,14 @@ opencs_units (view/widget
opencs_units (view/render opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render
lighting lightingday lightingnight lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase
lightingbright object cell terrainstorage tagbase cellarrow cellarrow cellmarker cellborder pathgrid
) )
opencs_hdrs_noqt (view/render opencs_hdrs_noqt (view/render
@ -107,11 +109,12 @@ opencs_units_noqt (view/tools
) )
opencs_units (view/prefs opencs_units (view/prefs
dialogue pagebase page dialogue pagebase page keybindingpage
) )
opencs_units (model/prefs opencs_units (model/prefs
state setting intsetting doublesetting boolsetting enumsetting coloursetting state setting intsetting doublesetting boolsetting enumsetting coloursetting shortcut
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting
) )
opencs_units_noqt (model/prefs opencs_units_noqt (model/prefs
@ -159,9 +162,15 @@ endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(APPLE) if(APPLE)
set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns) set (OPENCS_MAC_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns")
set (OPENCS_CFG "${OpenMW_BINARY_DIR}/openmw-cs.cfg")
set (OPENCS_DEFAULT_FILTERS_FILE "${OpenMW_BINARY_DIR}/resources/defaultfilters")
set (OPENCS_OPENMW_CFG "${OpenMW_BINARY_DIR}/openmw.cfg")
else() else()
set (OPENCS_MAC_ICON "") set (OPENCS_MAC_ICON "")
set (OPENCS_CFG "")
set (OPENCS_DEFAULT_FILTERS_FILE "")
set (OPENCS_OPENMW_CFG "")
endif(APPLE) endif(APPLE)
add_executable(openmw-cs add_executable(openmw-cs
@ -171,12 +180,23 @@ add_executable(openmw-cs
${OPENCS_MOC_SRC} ${OPENCS_MOC_SRC}
${OPENCS_RES_SRC} ${OPENCS_RES_SRC}
${OPENCS_MAC_ICON} ${OPENCS_MAC_ICON}
${OPENCS_CFG}
${OPENCS_DEFAULT_FILTERS_FILE}
${OPENCS_OPENMW_CFG}
) )
if(APPLE) if(APPLE)
set(OPENCS_BUNDLE_NAME "OpenMW-CS")
set(OPENCS_BUNDLE_RESOURCES_DIR "${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources")
set(OPENMW_MYGUI_FILES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
set(OPENMW_SHADERS_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)
set_target_properties(openmw-cs PROPERTIES set_target_properties(openmw-cs PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
OUTPUT_NAME "OpenMW-CS" OUTPUT_NAME ${OPENCS_BUNDLE_NAME}
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns" MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
@ -187,16 +207,27 @@ if(APPLE)
set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources) MACOSX_PACKAGE_LOCATION Resources)
set_source_files_properties(${OPENCS_CFG} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
set_source_files_properties(${OPENCS_DEFAULT_FILTERS_FILE} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources/resources)
set_source_files_properties(${OPENCS_OPENMW_CFG} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
add_custom_command(TARGET openmw-cs
POST_BUILD
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${OPENCS_BUNDLE_RESOURCES_DIR}/resources")
endif(APPLE) endif(APPLE)
target_link_libraries(openmw-cs target_link_libraries(openmw-cs
${OSG_LIBRARIES} ${OSG_LIBRARIES}
${OPENTHREADS_LIBRARIES} ${OPENTHREADS_LIBRARIES}
${OSGTEXT_LIBRARIES}
${OSGUTIL_LIBRARIES} ${OSGUTIL_LIBRARIES}
${OSGVIEWER_LIBRARIES} ${OSGVIEWER_LIBRARIES}
${OSGGA_LIBRARIES} ${OSGGA_LIBRARIES}
${OSGFX_LIBRARIES} ${OSGFX_LIBRARIES}
${OSGQT_LIBRARIES} ${EXTERN_OSGQT_LIBRARY}
${Boost_SYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}
@ -219,9 +250,18 @@ endif()
if (WIN32) if (WIN32)
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY}) target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
INSTALL(TARGETS openmw-cs RUNTIME DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".")
endif() endif()
if (MSVC)
# Debug version needs increased number of sections beyond 2^16
if (CMAKE_CL_64)
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
endif (CMAKE_CL_64)
endif (MSVC)
if(APPLE) if(APPLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT BUNDLE)
endif() endif()

@ -0,0 +1,90 @@
#include "editor.hpp"
#include <exception>
#include <iostream>
#include <string>
#include <QApplication>
#include <QIcon>
#include <QMetaType>
#include "model/doc/messages.hpp"
#include "model/world/universalid.hpp"
#ifdef Q_OS_MAC
#include <QDir>
#endif
Q_DECLARE_METATYPE (std::string)
class Application : public QApplication
{
private:
bool notify (QObject *receiver, QEvent *event)
{
try
{
return QApplication::notify (receiver, event);
}
catch (const std::exception& exception)
{
std::cerr << "An exception has been caught: " << exception.what() << std::endl;
}
return false;
}
public:
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
};
int main(int argc, char *argv[])
{
#ifdef Q_OS_MAC
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
try
{
// To allow background thread drawing in OSG
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
Application application (argc, argv);
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath());
#endif
application.setWindowIcon (QIcon (":./openmw-cs.png"));
CS::Editor editor;
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
return editor.run();
}
catch (std::exception& e)
{
std::cerr << "ERROR: " << e.what() << std::endl;
return 0;
}
}

@ -62,11 +62,6 @@ int main(int argc, char *argv[])
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath()); QDir::setCurrent(dir.absolutePath());
#endif #endif

@ -274,7 +274,7 @@ CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationM
const Fallback::Map* fallback, const Fallback::Map* fallback,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts) const std::vector<std::string>& blacklistedScripts)
: mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager, fallback), : mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager, fallback, resDir),
mTools (*this, encoding), mTools (*this, encoding),
mProjectPath ((configuration.getUserDataPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().string() + ".project")),

@ -41,6 +41,7 @@ CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& con
CSMDoc::DocumentManager::~DocumentManager() CSMDoc::DocumentManager::~DocumentManager()
{ {
mLoaderThread.quit(); mLoaderThread.quit();
mLoader.stop();
mLoader.hasThingsToDo().wakeAll(); mLoader.hasThingsToDo().wakeAll();
mLoaderThread.wait(); mLoaderThread.wait();

@ -1,6 +1,6 @@
#include "loader.hpp" #include "loader.hpp"
#include <QTimer> #include <iostream>
#include "../tools/reportmodel.hpp" #include "../tools/reportmodel.hpp"
@ -11,11 +11,12 @@ CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLoaded (0), mRecordsLeft (fa
CSMDoc::Loader::Loader() CSMDoc::Loader::Loader()
: mShouldStop(false)
{ {
QTimer *timer = new QTimer (this); mTimer = new QTimer (this);
connect (timer, SIGNAL (timeout()), this, SLOT (load())); connect (mTimer, SIGNAL (timeout()), this, SLOT (load()));
timer->start(); mTimer->start();
} }
QWaitCondition& CSMDoc::Loader::hasThingsToDo() QWaitCondition& CSMDoc::Loader::hasThingsToDo()
@ -23,6 +24,11 @@ QWaitCondition& CSMDoc::Loader::hasThingsToDo()
return mThingsToDo; return mThingsToDo;
} }
void CSMDoc::Loader::stop()
{
mShouldStop = true;
}
void CSMDoc::Loader::load() void CSMDoc::Loader::load()
{ {
if (mDocuments.empty()) if (mDocuments.empty())
@ -30,6 +36,10 @@ void CSMDoc::Loader::load()
mMutex.lock(); mMutex.lock();
mThingsToDo.wait (&mMutex); mThingsToDo.wait (&mMutex);
mMutex.unlock(); mMutex.unlock();
if (mShouldStop)
mTimer->stop();
return; return;
} }
@ -63,11 +73,11 @@ void CSMDoc::Loader::load()
CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0); CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0);
{ // silence a g++ warning { // silence a g++ warning
for (CSMDoc::Messages::Iterator iter (messages.begin()); for (CSMDoc::Messages::Iterator messageIter (messages.begin());
iter!=messages.end(); ++iter) messageIter!=messages.end(); ++messageIter)
{ {
document->getReport (log)->add (*iter); document->getReport (log)->add (*messageIter);
emit loadMessage (document, iter->mMessage); emit loadMessage (document, messageIter->mMessage);
} }
} }

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

@ -81,7 +81,7 @@ void CSMDoc::Runner::start (bool delayed)
arguments << ("--script-run="+mStartup->fileName());; arguments << ("--script-run="+mStartup->fileName());;
arguments << arguments <<
QString::fromUtf8 (("--data="+mProjectPath.parent_path().string()).c_str()); QString::fromUtf8 (("--data=\""+mProjectPath.parent_path().string()+"\"").c_str());
for (std::vector<std::string>::const_iterator iter (mContentFiles.begin()); for (std::vector<std::string>::const_iterator iter (mContentFiles.begin());
iter!=mContentFiles.end(); ++iter) iter!=mContentFiles.end(); ++iter)

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

@ -313,7 +313,7 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseNAry (const Token& ke
nodes.push_back (node); nodes.push_back (node);
Token token = getNextToken(); token = getNextToken();
if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma)) if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma))
{ {

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

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

@ -0,0 +1,147 @@
#include "modifiersetting.hpp"
#include <QEvent>
#include <QKeyEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QPushButton>
#include <QWidget>
#include "state.hpp"
#include "shortcutmanager.hpp"
namespace CSMPrefs
{
ModifierSetting::ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
const std::string& label)
: Setting(parent, values, mutex, key, label)
, mButton(0)
, mEditorActive(false)
{
}
std::pair<QWidget*, QWidget*> ModifierSetting::makeWidgets(QWidget* parent)
{
int modifier = 0;
State::get().getShortcutManager().getModifier(getKey(), modifier);
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(modifier).c_str());
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
QPushButton* widget = new QPushButton(text, parent);
widget->setCheckable(true);
widget->installEventFilter(this);
mButton = widget;
connect(widget, SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool)));
return std::make_pair(label, widget);
}
bool ModifierSetting::eventFilter(QObject* target, QEvent* event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->isAutoRepeat())
return true;
int mod = keyEvent->modifiers();
int key = keyEvent->key();
return handleEvent(target, mod, key);
}
else if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
int mod = mouseEvent->modifiers();
int button = mouseEvent->button();
return handleEvent(target, mod, button);
}
else if (event->type() == QEvent::FocusOut)
{
resetState();
}
return false;
}
bool ModifierSetting::handleEvent(QObject* target, int mod, int value)
{
// For potential future exceptions
const int Blacklist[] =
{
0
};
const size_t BlacklistSize = sizeof(Blacklist) / sizeof(int);
if (!mEditorActive)
{
if (value == Qt::RightButton)
{
// Clear modifier
int modifier = 0;
storeValue(modifier);
resetState();
}
return false;
}
// Handle blacklist
for (size_t i = 0; i < BlacklistSize; ++i)
{
if (value == Blacklist[i])
return true;
}
// Update modifier
int modifier = value;
storeValue(modifier);
resetState();
return true;
}
void ModifierSetting::storeValue(int modifier)
{
State::get().getShortcutManager().setModifier(getKey(), modifier);
// Convert to string and assign
std::string value = State::get().getShortcutManager().convertToString(modifier);
{
QMutexLocker lock(getMutex());
getValues().setString(getKey(), getParent()->getKey(), value);
}
getParent()->getState()->update(*this);
}
void ModifierSetting::resetState()
{
mButton->setChecked(false);
mEditorActive = false;
// Button text
int modifier = 0;
State::get().getShortcutManager().getModifier(getKey(), modifier);
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(modifier).c_str());
mButton->setText(text);
}
void ModifierSetting::buttonToggled(bool checked)
{
if (checked)
mButton->setText("Press keys or click here...");
mEditorActive = checked;
}
}

@ -0,0 +1,44 @@
#ifndef CSM_PREFS_MODIFIERSETTING_H
#define CSM_PREFS_MODIFIERSETTING_H
#include <QKeySequence>
#include "setting.hpp"
class QEvent;
class QPushButton;
namespace CSMPrefs
{
class ModifierSetting : public Setting
{
Q_OBJECT
public:
ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
const std::string& label);
virtual std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent);
protected:
bool eventFilter(QObject* target, QEvent* event);
private:
bool handleEvent(QObject* target, int mod, int value);
void storeValue(int modifier);
void resetState();
QPushButton* mButton;
bool mEditorActive;
private slots:
void buttonToggled(bool checked);
};
}
#endif

@ -0,0 +1,214 @@
#include "shortcut.hpp"
#include <cassert>
#include <QAction>
#include <QWidget>
#include "state.hpp"
#include "shortcutmanager.hpp"
namespace CSMPrefs
{
Shortcut::Shortcut(const std::string& name, QWidget* parent)
: QObject(parent)
, mEnabled(true)
, mName(name)
, mModName("")
, mSecondaryMode(SM_Ignore)
, mModifier(0)
, mCurrentPos(0)
, mLastPos(0)
, mActivationStatus(AS_Inactive)
, mModifierStatus(false)
, mAction(0)
{
assert (parent);
State::get().getShortcutManager().addShortcut(this);
State::get().getShortcutManager().getSequence(name, mSequence);
}
Shortcut::Shortcut(const std::string& name, const std::string& modName, QWidget* parent)
: QObject(parent)
, mEnabled(true)
, mName(name)
, mModName(modName)
, mSecondaryMode(SM_Ignore)
, mModifier(0)
, mCurrentPos(0)
, mLastPos(0)
, mActivationStatus(AS_Inactive)
, mModifierStatus(false)
, mAction(0)
{
assert (parent);
State::get().getShortcutManager().addShortcut(this);
State::get().getShortcutManager().getSequence(name, mSequence);
State::get().getShortcutManager().getModifier(modName, mModifier);
}
Shortcut::Shortcut(const std::string& name, const std::string& modName, SecondaryMode secMode, QWidget* parent)
: QObject(parent)
, mEnabled(true)
, mName(name)
, mModName(modName)
, mSecondaryMode(secMode)
, mModifier(0)
, mCurrentPos(0)
, mLastPos(0)
, mActivationStatus(AS_Inactive)
, mModifierStatus(false)
, mAction(0)
{
assert (parent);
State::get().getShortcutManager().addShortcut(this);
State::get().getShortcutManager().getSequence(name, mSequence);
State::get().getShortcutManager().getModifier(modName, mModifier);
}
Shortcut::~Shortcut()
{
State::get().getShortcutManager().removeShortcut(this);
}
bool Shortcut::isEnabled() const
{
return mEnabled;
}
const std::string& Shortcut::getName() const
{
return mName;
}
const std::string& Shortcut::getModifierName() const
{
return mModName;
}
Shortcut::SecondaryMode Shortcut::getSecondaryMode() const
{
return mSecondaryMode;
}
const QKeySequence& Shortcut::getSequence() const
{
return mSequence;
}
int Shortcut::getModifier() const
{
return mModifier;
}
int Shortcut::getPosition() const
{
return mCurrentPos;
}
int Shortcut::getLastPosition() const
{
return mLastPos;
}
Shortcut::ActivationStatus Shortcut::getActivationStatus() const
{
return mActivationStatus;
}
bool Shortcut::getModifierStatus() const
{
return mModifierStatus;
}
void Shortcut::enable(bool state)
{
mEnabled = state;
}
void Shortcut::setSequence(const QKeySequence& sequence)
{
mSequence = sequence;
mCurrentPos = 0;
mLastPos = sequence.count() - 1;
if (mAction)
{
mAction->setText(mActionText + "\t" + State::get().getShortcutManager().convertToString(mSequence).data());
}
}
void Shortcut::setModifier(int modifier)
{
mModifier = modifier;
}
void Shortcut::setPosition(int pos)
{
mCurrentPos = pos;
}
void Shortcut::setActivationStatus(ActivationStatus status)
{
mActivationStatus = status;
}
void Shortcut::setModifierStatus(bool status)
{
mModifierStatus = status;
}
void Shortcut::associateAction(QAction* action)
{
if (mAction)
{
mAction->setText(mActionText);
disconnect(this, SIGNAL(activated()), mAction, SLOT(trigger()));
disconnect(mAction, SIGNAL(destroyed()), this, SLOT(actionDeleted()));
}
mAction = action;
if (mAction)
{
mActionText = mAction->text();
mAction->setText(mActionText + "\t" + State::get().getShortcutManager().convertToString(mSequence).data());
connect(this, SIGNAL(activated()), mAction, SLOT(trigger()));
connect(mAction, SIGNAL(destroyed()), this, SLOT(actionDeleted()));
}
}
void Shortcut::signalActivated(bool state)
{
emit activated(state);
}
void Shortcut::signalActivated()
{
emit activated();
}
void Shortcut::signalSecondary(bool state)
{
emit secondary(state);
}
void Shortcut::signalSecondary()
{
emit secondary();
}
QString Shortcut::toString() const
{
return QString(State::get().getShortcutManager().convertToString(mSequence, mModifier).data());
}
void Shortcut::actionDeleted()
{
mAction = 0;
}
}

@ -0,0 +1,122 @@
#ifndef CSM_PREFS_SHORTCUT_H
#define CSM_PREFS_SHORTCUT_H
#include <string>
#include <QKeySequence>
#include <QObject>
#include <QString>
class QAction;
class QWidget;
namespace CSMPrefs
{
/// A class similar in purpose to QShortcut, but with the ability to use mouse buttons
class Shortcut : public QObject
{
Q_OBJECT
public:
enum ActivationStatus
{
AS_Regular,
AS_Secondary,
AS_Inactive
};
enum SecondaryMode
{
SM_Replace, ///< The secondary signal replaces the regular signal when the modifier is active
SM_Detach, ///< The secondary signal is emitted independent of the regular signal, even if not active
SM_Ignore ///< The secondary signal will not ever be emitted
};
Shortcut(const std::string& name, QWidget* parent);
Shortcut(const std::string& name, const std::string& modName, QWidget* parent);
Shortcut(const std::string& name, const std::string& modName, SecondaryMode secMode, QWidget* parent);
~Shortcut();
bool isEnabled() const;
const std::string& getName() const;
const std::string& getModifierName() const;
SecondaryMode getSecondaryMode() const;
const QKeySequence& getSequence() const;
int getModifier() const;
/// The position in the sequence
int getPosition() const;
/// The position in the sequence
int getLastPosition() const;
ActivationStatus getActivationStatus() const;
bool getModifierStatus() const;
void enable(bool state);
void setSequence(const QKeySequence& sequence);
void setModifier(int modifier);
/// The position in the sequence
void setPosition(int pos);
void setActivationStatus(ActivationStatus status);
void setModifierStatus(bool status);
/// Appends the sequence to the QAction text, also keeps it up to date
void associateAction(QAction* action);
// Workaround for Qt4 signals being "protected"
void signalActivated(bool state);
void signalActivated();
void signalSecondary(bool state);
void signalSecondary();
QString toString() const;
private:
bool mEnabled;
std::string mName;
std::string mModName;
SecondaryMode mSecondaryMode;
QKeySequence mSequence;
int mModifier;
int mCurrentPos;
int mLastPos;
ActivationStatus mActivationStatus;
bool mModifierStatus;
QAction* mAction;
QString mActionText;
private slots:
void actionDeleted();
signals:
/// Triggered when the shortcut is activated or deactivated; can be determined from \p state
void activated(bool state);
/// Convenience signal.
void activated();
/// Triggered depending on SecondaryMode
void secondary(bool state);
/// Convenience signal.
void secondary();
};
}
#endif

@ -0,0 +1,338 @@
#include "shortcuteventhandler.hpp"
#include <algorithm>
#include <cassert>
#include <QEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QWidget>
#include "shortcut.hpp"
namespace CSMPrefs
{
ShortcutEventHandler::ShortcutEventHandler(QObject* parent)
: QObject(parent)
{
}
void ShortcutEventHandler::addShortcut(Shortcut* shortcut)
{
// Enforced by shortcut class
QWidget* widget = static_cast<QWidget*>(shortcut->parent());
// Check if widget setup is needed
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
if (shortcutListIt == mWidgetShortcuts.end())
{
// Create list
shortcutListIt = mWidgetShortcuts.insert(std::make_pair(widget, ShortcutList())).first;
// Check if widget has a parent with shortcuts, unfortunately it is not typically set yet
updateParent(widget);
// Intercept widget events
widget->installEventFilter(this);
connect(widget, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
}
// Add to list
shortcutListIt->second.push_back(shortcut);
}
void ShortcutEventHandler::removeShortcut(Shortcut* shortcut)
{
// Enforced by shortcut class
QWidget* widget = static_cast<QWidget*>(shortcut->parent());
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
if (shortcutListIt != mWidgetShortcuts.end())
{
std::remove(shortcutListIt->second.begin(), shortcutListIt->second.end(), shortcut);
}
}
bool ShortcutEventHandler::eventFilter(QObject* watched, QEvent* event)
{
// Process event
if (event->type() == QEvent::KeyPress)
{
QWidget* widget = static_cast<QWidget*>(watched);
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
unsigned int mod = (unsigned int) keyEvent->modifiers();
unsigned int key = (unsigned int) keyEvent->key();
if (!keyEvent->isAutoRepeat())
return activate(widget, mod, key);
}
else if (event->type() == QEvent::KeyRelease)
{
QWidget* widget = static_cast<QWidget*>(watched);
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
unsigned int mod = (unsigned int) keyEvent->modifiers();
unsigned int key = (unsigned int) keyEvent->key();
if (!keyEvent->isAutoRepeat())
return deactivate(widget, mod, key);
}
else if (event->type() == QEvent::MouseButtonPress)
{
QWidget* widget = static_cast<QWidget*>(watched);
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
unsigned int mod = (unsigned int) mouseEvent->modifiers();
unsigned int button = (unsigned int) mouseEvent->button();
return activate(widget, mod, button);
}
else if (event->type() == QEvent::MouseButtonRelease)
{
QWidget* widget = static_cast<QWidget*>(watched);
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
unsigned int mod = (unsigned int) mouseEvent->modifiers();
unsigned int button = (unsigned int) mouseEvent->button();
return deactivate(widget, mod, button);
}
else if (event->type() == QEvent::FocusOut)
{
QWidget* widget = static_cast<QWidget*>(watched);
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
// Deactivate in case events are missed
for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
{
Shortcut* shortcut = *it;
shortcut->setPosition(0);
shortcut->setModifierStatus(false);
if (shortcut->getActivationStatus() == Shortcut::AS_Regular)
{
shortcut->setActivationStatus(Shortcut::AS_Inactive);
shortcut->signalActivated(false);
}
else if (shortcut->getActivationStatus() == Shortcut::AS_Secondary)
{
shortcut->setActivationStatus(Shortcut::AS_Inactive);
shortcut->signalSecondary(false);
}
}
}
else if (event->type() == QEvent::FocusIn)
{
QWidget* widget = static_cast<QWidget*>(watched);
updateParent(widget);
}
return false;
}
void ShortcutEventHandler::updateParent(QWidget* widget)
{
QWidget* parent = widget->parentWidget();
while (parent)
{
ShortcutMap::iterator parentIt = mWidgetShortcuts.find(parent);
if (parentIt != mWidgetShortcuts.end())
{
mChildParentRelations.insert(std::make_pair(widget, parent));
updateParent(parent);
break;
}
// Check next
parent = parent->parentWidget();
}
}
bool ShortcutEventHandler::activate(QWidget* widget, unsigned int mod, unsigned int button)
{
std::vector<std::pair<MatchResult, Shortcut*> > potentials;
bool used = false;
while (widget)
{
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
assert(shortcutListIt != mWidgetShortcuts.end());
// Find potential activations
for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
{
Shortcut* shortcut = *it;
if (!shortcut->isEnabled())
continue;
if (checkModifier(mod, button, shortcut, true))
used = true;
if (shortcut->getActivationStatus() != Shortcut::AS_Inactive)
continue;
int pos = shortcut->getPosition();
int lastPos = shortcut->getLastPosition();
MatchResult result = match(mod, button, shortcut->getSequence()[pos]);
if (result == Matches_WithMod || result == Matches_NoMod)
{
if (pos < lastPos && (result == Matches_WithMod || pos > 0))
{
shortcut->setPosition(pos+1);
}
else if (pos == lastPos)
{
potentials.push_back(std::make_pair(result, shortcut));
}
}
}
// Move on to parent
WidgetMap::iterator widgetIt = mChildParentRelations.find(widget);
widget = (widgetIt != mChildParentRelations.end()) ? widgetIt->second : 0;
}
// Only activate the best match; in exact conflicts, this will favor the first shortcut added.
if (!potentials.empty())
{
std::sort(potentials.begin(), potentials.end(), ShortcutEventHandler::sort);
Shortcut* shortcut = potentials.front().second;
if (shortcut->getModifierStatus() && shortcut->getSecondaryMode() == Shortcut::SM_Replace)
{
shortcut->setActivationStatus(Shortcut::AS_Secondary);
shortcut->signalSecondary(true);
shortcut->signalSecondary();
}
else
{
shortcut->setActivationStatus(Shortcut::AS_Regular);
shortcut->signalActivated(true);
shortcut->signalActivated();
}
used = true;
}
return used;
}
bool ShortcutEventHandler::deactivate(QWidget* widget, unsigned int mod, unsigned int button)
{
const int KeyMask = 0x01FFFFFF;
bool used = false;
while (widget)
{
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
assert(shortcutListIt != mWidgetShortcuts.end());
for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
{
Shortcut* shortcut = *it;
if (checkModifier(mod, button, shortcut, false))
used = true;
int pos = shortcut->getPosition();
MatchResult result = match(0, button, shortcut->getSequence()[pos] & KeyMask);
if (result != Matches_Not)
{
shortcut->setPosition(0);
if (shortcut->getActivationStatus() == Shortcut::AS_Regular)
{
shortcut->setActivationStatus(Shortcut::AS_Inactive);
shortcut->signalActivated(false);
used = true;
}
else if (shortcut->getActivationStatus() == Shortcut::AS_Secondary)
{
shortcut->setActivationStatus(Shortcut::AS_Inactive);
shortcut->signalSecondary(false);
used = true;
}
}
}
// Move on to parent
WidgetMap::iterator widgetIt = mChildParentRelations.find(widget);
widget = (widgetIt != mChildParentRelations.end()) ? widgetIt->second : 0;
}
return used;
}
bool ShortcutEventHandler::checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate)
{
if (!shortcut->isEnabled() || !shortcut->getModifier() || shortcut->getSecondaryMode() == Shortcut::SM_Ignore ||
shortcut->getModifierStatus() == activate)
return false;
MatchResult result = match(mod, button, shortcut->getModifier());
bool used = false;
if (result != Matches_Not)
{
shortcut->setModifierStatus(activate);
if (shortcut->getSecondaryMode() == Shortcut::SM_Detach)
{
if (activate)
{
shortcut->signalSecondary(true);
shortcut->signalSecondary();
}
else
{
shortcut->signalSecondary(false);
}
}
else if (!activate && shortcut->getActivationStatus() == Shortcut::AS_Secondary)
{
shortcut->setActivationStatus(Shortcut::AS_Inactive);
shortcut->setPosition(0);
shortcut->signalSecondary(false);
used = true;
}
}
return used;
}
ShortcutEventHandler::MatchResult ShortcutEventHandler::match(unsigned int mod, unsigned int button,
unsigned int value)
{
if ((mod | button) == value)
{
return Matches_WithMod;
}
else if (button == value)
{
return Matches_NoMod;
}
else
{
return Matches_Not;
}
}
bool ShortcutEventHandler::sort(const std::pair<MatchResult, Shortcut*>& left,
const std::pair<MatchResult, Shortcut*>& right)
{
if (left.first == Matches_WithMod && right.first == Matches_NoMod)
return true;
else
return left.second->getPosition() >= right.second->getPosition();
}
void ShortcutEventHandler::widgetDestroyed()
{
QWidget* widget = static_cast<QWidget*>(sender());
mWidgetShortcuts.erase(widget);
mChildParentRelations.erase(widget);
}
}

@ -0,0 +1,69 @@
#ifndef CSM_PREFS_SHORTCUT_EVENT_HANDLER_H
#define CSM_PREFS_SHORTCUT_EVENT_HANDLER_H
#include <map>
#include <vector>
#include <QObject>
class QEvent;
class QWidget;
namespace CSMPrefs
{
class Shortcut;
/// Users of this class should install it as an event handler
class ShortcutEventHandler : public QObject
{
Q_OBJECT
public:
ShortcutEventHandler(QObject* parent);
void addShortcut(Shortcut* shortcut);
void removeShortcut(Shortcut* shortcut);
protected:
bool eventFilter(QObject* watched, QEvent* event);
private:
typedef std::vector<Shortcut*> ShortcutList;
// Child, Parent
typedef std::map<QWidget*, QWidget*> WidgetMap;
typedef std::map<QWidget*, ShortcutList> ShortcutMap;
enum MatchResult
{
Matches_WithMod,
Matches_NoMod,
Matches_Not
};
void updateParent(QWidget* widget);
bool activate(QWidget* widget, unsigned int mod, unsigned int button);
bool deactivate(QWidget* widget, unsigned int mod, unsigned int button);
bool checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate);
MatchResult match(unsigned int mod, unsigned int button, unsigned int value);
// Prefers Matches_WithMod and a larger number of buttons
static bool sort(const std::pair<MatchResult, Shortcut*>& left,
const std::pair<MatchResult, Shortcut*>& right);
WidgetMap mChildParentRelations;
ShortcutMap mWidgetShortcuts;
private slots:
void widgetDestroyed();
};
}
#endif

@ -0,0 +1,791 @@
#include "shortcutmanager.hpp"
#include <algorithm>
#include <QApplication>
#include <QStringList>
#include "shortcut.hpp"
#include "shortcuteventhandler.hpp"
namespace CSMPrefs
{
ShortcutManager::ShortcutManager()
{
createLookupTables();
mEventHandler = new ShortcutEventHandler(this);
}
void ShortcutManager::addShortcut(Shortcut* shortcut)
{
mShortcuts.insert(std::make_pair(shortcut->getName(), shortcut));
mShortcuts.insert(std::make_pair(shortcut->getModifierName(), shortcut));
mEventHandler->addShortcut(shortcut);
}
void ShortcutManager::removeShortcut(Shortcut* shortcut)
{
std::pair<ShortcutMap::iterator, ShortcutMap::iterator> range = mShortcuts.equal_range(shortcut->getName());
for (ShortcutMap::iterator it = range.first; it != range.second;)
{
if (it->second == shortcut)
{
mShortcuts.erase(it++);
}
else
{
++it;
}
}
mEventHandler->removeShortcut(shortcut);
}
bool ShortcutManager::getSequence(const std::string& name, QKeySequence& sequence) const
{
SequenceMap::const_iterator item = mSequences.find(name);
if (item != mSequences.end())
{
sequence = item->second;
return true;
}
else
return false;
}
void ShortcutManager::setSequence(const std::string& name, const QKeySequence& sequence)
{
// Add to map/modify
SequenceMap::iterator item = mSequences.find(name);
if (item != mSequences.end())
{
item->second = sequence;
}
else
{
mSequences.insert(std::make_pair(name, sequence));
}
// Change active shortcuts
std::pair<ShortcutMap::iterator, ShortcutMap::iterator> rangeS = mShortcuts.equal_range(name);
for (ShortcutMap::iterator it = rangeS.first; it != rangeS.second; ++it)
{
it->second->setSequence(sequence);
}
}
bool ShortcutManager::getModifier(const std::string& name, int& modifier) const
{
ModifierMap::const_iterator item = mModifiers.find(name);
if (item != mModifiers.end())
{
modifier = item->second;
return true;
}
else
return false;
}
void ShortcutManager::setModifier(const std::string& name, int modifier)
{
// Add to map/modify
ModifierMap::iterator item = mModifiers.find(name);
if (item != mModifiers.end())
{
item->second = modifier;
}
else
{
mModifiers.insert(std::make_pair(name, modifier));
}
// Change active shortcuts
std::pair<ShortcutMap::iterator, ShortcutMap::iterator> rangeS = mShortcuts.equal_range(name);
for (ShortcutMap::iterator it = rangeS.first; it != rangeS.second; ++it)
{
it->second->setModifier(modifier);
}
}
std::string ShortcutManager::convertToString(const QKeySequence& sequence) const
{
const int MouseKeyMask = 0x01FFFFFF;
const int ModMask = 0x7E000000;
std::string result;
for (int i = 0; i < (int)sequence.count(); ++i)
{
int mods = sequence[i] & ModMask;
int key = sequence[i] & MouseKeyMask;
if (key)
{
NameMap::const_iterator searchResult = mNames.find(key);
if (searchResult != mNames.end())
{
if (mods && i == 0)
{
if (mods & Qt::ControlModifier)
result.append("Ctl+");
if (mods & Qt::ShiftModifier)
result.append("Shift+");
if (mods & Qt::AltModifier)
result.append("Alt+");
if (mods & Qt::MetaModifier)
result.append("Meta+");
if (mods & Qt::KeypadModifier)
result.append("Keypad+");
if (mods & Qt::GroupSwitchModifier)
result.append("GroupSwitch+");
}
else if (i > 0)
{
result.append("+");
}
result.append(searchResult->second);
}
}
}
return result;
}
std::string ShortcutManager::convertToString(int modifier) const
{
NameMap::const_iterator searchResult = mNames.find(modifier);
if (searchResult != mNames.end())
{
return searchResult->second;
}
else
return "";
}
std::string ShortcutManager::convertToString(const QKeySequence& sequence, int modifier) const
{
std::string concat = convertToString(sequence) + ";" + convertToString(modifier);
return concat;
}
void ShortcutManager::convertFromString(const std::string& data, QKeySequence& sequence) const
{
const int MaxKeys = 4; // A limitation of QKeySequence
size_t end = data.find(";");
size_t size = std::min(end, data.size());
std::string value = data.substr(0, size);
size_t start = 0;
int keyPos = 0;
int mods = 0;
int keys[MaxKeys] = {};
while (start < value.size())
{
end = data.find("+", start);
end = std::min(end, value.size());
std::string name = value.substr(start, end - start);
if (name == "Ctl")
{
mods |= Qt::ControlModifier;
}
else if (name == "Shift")
{
mods |= Qt::ShiftModifier;
}
else if (name == "Alt")
{
mods |= Qt::AltModifier;
}
else if (name == "Meta")
{
mods |= Qt::MetaModifier;
}
else if (name == "Keypad")
{
mods |= Qt::KeypadModifier;
}
else if (name == "GroupSwitch")
{
mods |= Qt::GroupSwitchModifier;
}
else
{
KeyMap::const_iterator searchResult = mKeys.find(name);
if (searchResult != mKeys.end())
{
keys[keyPos] = mods | searchResult->second;
mods = 0;
keyPos += 1;
if (keyPos >= MaxKeys)
break;
}
}
start = end + 1;
}
sequence = QKeySequence(keys[0], keys[1], keys[2], keys[3]);
}
void ShortcutManager::convertFromString(const std::string& data, int& modifier) const
{
size_t start = data.find(";") + 1;
start = std::min(start, data.size());
std::string name = data.substr(start);
KeyMap::const_iterator searchResult = mKeys.find(name);
if (searchResult != mKeys.end())
{
modifier = searchResult->second;
}
else
{
modifier = 0;
}
}
void ShortcutManager::convertFromString(const std::string& data, QKeySequence& sequence, int& modifier) const
{
convertFromString(data, sequence);
convertFromString(data, modifier);
}
void ShortcutManager::createLookupTables()
{
// Mouse buttons
mNames.insert(std::make_pair(Qt::LeftButton, "LMB"));
mNames.insert(std::make_pair(Qt::RightButton, "RMB"));
mNames.insert(std::make_pair(Qt::MiddleButton, "MMB"));
mNames.insert(std::make_pair(Qt::XButton1, "Mouse4"));
mNames.insert(std::make_pair(Qt::XButton2, "Mouse5"));
// Keyboard buttons
for (size_t i = 0; QtKeys[i].first != 0; ++i)
{
mNames.insert(QtKeys[i]);
}
// Generate inverse map
for (NameMap::const_iterator it = mNames.begin(); it != mNames.end(); ++it)
{
mKeys.insert(std::make_pair(it->second, it->first));
}
}
QString ShortcutManager::processToolTip(const QString& toolTip) const
{
const QChar SequenceStart = '{';
const QChar SequenceEnd = '}';
QStringList substrings;
int prevIndex = 0;
int startIndex = toolTip.indexOf(SequenceStart);
int endIndex = (startIndex != -1) ? toolTip.indexOf(SequenceEnd, startIndex) : -1;
// Process every valid shortcut escape sequence
while (startIndex != -1 && endIndex != -1)
{
int count = startIndex - prevIndex;
if (count > 0)
{
substrings.push_back(toolTip.mid(prevIndex, count));
}
// Find sequence name
startIndex += 1; // '{' character
count = endIndex - startIndex;
if (count > 0)
{
QString settingName = toolTip.mid(startIndex, count);
QKeySequence sequence;
int modifier;
if (getSequence(settingName.toUtf8().data(), sequence))
{
QString value = QString::fromUtf8(convertToString(sequence).c_str());
substrings.push_back(value);
}
else if (getModifier(settingName.toUtf8().data(), modifier))
{
QString value = QString::fromUtf8(convertToString(modifier).c_str());
substrings.push_back(value);
}
prevIndex = endIndex + 1; // '}' character
}
startIndex = toolTip.indexOf(SequenceStart, endIndex);
endIndex = (startIndex != -1) ? toolTip.indexOf(SequenceEnd, startIndex) : -1;
}
if (prevIndex < toolTip.size())
{
substrings.push_back(toolTip.mid(prevIndex));
}
return substrings.join("");
}
const std::pair<int, const char*> ShortcutManager::QtKeys[] =
{
std::make_pair((int)Qt::Key_Space , "Space"),
std::make_pair((int)Qt::Key_Exclam , "Exclam"),
std::make_pair((int)Qt::Key_QuoteDbl , "QuoteDbl"),
std::make_pair((int)Qt::Key_NumberSign , "NumberSign"),
std::make_pair((int)Qt::Key_Dollar , "Dollar"),
std::make_pair((int)Qt::Key_Percent , "Percent"),
std::make_pair((int)Qt::Key_Ampersand , "Ampersand"),
std::make_pair((int)Qt::Key_Apostrophe , "Apostrophe"),
std::make_pair((int)Qt::Key_ParenLeft , "ParenLeft"),
std::make_pair((int)Qt::Key_ParenRight , "ParenRight"),
std::make_pair((int)Qt::Key_Asterisk , "Asterisk"),
std::make_pair((int)Qt::Key_Plus , "Plus"),
std::make_pair((int)Qt::Key_Comma , "Comma"),
std::make_pair((int)Qt::Key_Minus , "Minus"),
std::make_pair((int)Qt::Key_Period , "Period"),
std::make_pair((int)Qt::Key_Slash , "Slash"),
std::make_pair((int)Qt::Key_0 , "0"),
std::make_pair((int)Qt::Key_1 , "1"),
std::make_pair((int)Qt::Key_2 , "2"),
std::make_pair((int)Qt::Key_3 , "3"),
std::make_pair((int)Qt::Key_4 , "4"),
std::make_pair((int)Qt::Key_5 , "5"),
std::make_pair((int)Qt::Key_6 , "6"),
std::make_pair((int)Qt::Key_7 , "7"),
std::make_pair((int)Qt::Key_8 , "8"),
std::make_pair((int)Qt::Key_9 , "9"),
std::make_pair((int)Qt::Key_Colon , "Colon"),
std::make_pair((int)Qt::Key_Semicolon , "Semicolon"),
std::make_pair((int)Qt::Key_Less , "Less"),
std::make_pair((int)Qt::Key_Equal , "Equal"),
std::make_pair((int)Qt::Key_Greater , "Greater"),
std::make_pair((int)Qt::Key_Question , "Question"),
std::make_pair((int)Qt::Key_At , "At"),
std::make_pair((int)Qt::Key_A , "A"),
std::make_pair((int)Qt::Key_B , "B"),
std::make_pair((int)Qt::Key_C , "C"),
std::make_pair((int)Qt::Key_D , "D"),
std::make_pair((int)Qt::Key_E , "E"),
std::make_pair((int)Qt::Key_F , "F"),
std::make_pair((int)Qt::Key_G , "G"),
std::make_pair((int)Qt::Key_H , "H"),
std::make_pair((int)Qt::Key_I , "I"),
std::make_pair((int)Qt::Key_J , "J"),
std::make_pair((int)Qt::Key_K , "K"),
std::make_pair((int)Qt::Key_L , "L"),
std::make_pair((int)Qt::Key_M , "M"),
std::make_pair((int)Qt::Key_N , "N"),
std::make_pair((int)Qt::Key_O , "O"),
std::make_pair((int)Qt::Key_P , "P"),
std::make_pair((int)Qt::Key_Q , "Q"),
std::make_pair((int)Qt::Key_R , "R"),
std::make_pair((int)Qt::Key_S , "S"),
std::make_pair((int)Qt::Key_T , "T"),
std::make_pair((int)Qt::Key_U , "U"),
std::make_pair((int)Qt::Key_V , "V"),
std::make_pair((int)Qt::Key_W , "W"),
std::make_pair((int)Qt::Key_X , "X"),
std::make_pair((int)Qt::Key_Y , "Y"),
std::make_pair((int)Qt::Key_Z , "Z"),
std::make_pair((int)Qt::Key_BracketLeft , "BracketLeft"),
std::make_pair((int)Qt::Key_Backslash , "Backslash"),
std::make_pair((int)Qt::Key_BracketRight , "BracketRight"),
std::make_pair((int)Qt::Key_AsciiCircum , "AsciiCircum"),
std::make_pair((int)Qt::Key_Underscore , "Underscore"),
std::make_pair((int)Qt::Key_QuoteLeft , "QuoteLeft"),
std::make_pair((int)Qt::Key_BraceLeft , "BraceLeft"),
std::make_pair((int)Qt::Key_Bar , "Bar"),
std::make_pair((int)Qt::Key_BraceRight , "BraceRight"),
std::make_pair((int)Qt::Key_AsciiTilde , "AsciiTilde"),
std::make_pair((int)Qt::Key_nobreakspace , "nobreakspace"),
std::make_pair((int)Qt::Key_exclamdown , "exclamdown"),
std::make_pair((int)Qt::Key_cent , "cent"),
std::make_pair((int)Qt::Key_sterling , "sterling"),
std::make_pair((int)Qt::Key_currency , "currency"),
std::make_pair((int)Qt::Key_yen , "yen"),
std::make_pair((int)Qt::Key_brokenbar , "brokenbar"),
std::make_pair((int)Qt::Key_section , "section"),
std::make_pair((int)Qt::Key_diaeresis , "diaeresis"),
std::make_pair((int)Qt::Key_copyright , "copyright"),
std::make_pair((int)Qt::Key_ordfeminine , "ordfeminine"),
std::make_pair((int)Qt::Key_guillemotleft , "guillemotleft"),
std::make_pair((int)Qt::Key_notsign , "notsign"),
std::make_pair((int)Qt::Key_hyphen , "hyphen"),
std::make_pair((int)Qt::Key_registered , "registered"),
std::make_pair((int)Qt::Key_macron , "macron"),
std::make_pair((int)Qt::Key_degree , "degree"),
std::make_pair((int)Qt::Key_plusminus , "plusminus"),
std::make_pair((int)Qt::Key_twosuperior , "twosuperior"),
std::make_pair((int)Qt::Key_threesuperior , "threesuperior"),
std::make_pair((int)Qt::Key_acute , "acute"),
std::make_pair((int)Qt::Key_mu , "mu"),
std::make_pair((int)Qt::Key_paragraph , "paragraph"),
std::make_pair((int)Qt::Key_periodcentered , "periodcentered"),
std::make_pair((int)Qt::Key_cedilla , "cedilla"),
std::make_pair((int)Qt::Key_onesuperior , "onesuperior"),
std::make_pair((int)Qt::Key_masculine , "masculine"),
std::make_pair((int)Qt::Key_guillemotright , "guillemotright"),
std::make_pair((int)Qt::Key_onequarter , "onequarter"),
std::make_pair((int)Qt::Key_onehalf , "onehalf"),
std::make_pair((int)Qt::Key_threequarters , "threequarters"),
std::make_pair((int)Qt::Key_questiondown , "questiondown"),
std::make_pair((int)Qt::Key_Agrave , "Agrave"),
std::make_pair((int)Qt::Key_Aacute , "Aacute"),
std::make_pair((int)Qt::Key_Acircumflex , "Acircumflex"),
std::make_pair((int)Qt::Key_Atilde , "Atilde"),
std::make_pair((int)Qt::Key_Adiaeresis , "Adiaeresis"),
std::make_pair((int)Qt::Key_Aring , "Aring"),
std::make_pair((int)Qt::Key_AE , "AE"),
std::make_pair((int)Qt::Key_Ccedilla , "Ccedilla"),
std::make_pair((int)Qt::Key_Egrave , "Egrave"),
std::make_pair((int)Qt::Key_Eacute , "Eacute"),
std::make_pair((int)Qt::Key_Ecircumflex , "Ecircumflex"),
std::make_pair((int)Qt::Key_Ediaeresis , "Ediaeresis"),
std::make_pair((int)Qt::Key_Igrave , "Igrave"),
std::make_pair((int)Qt::Key_Iacute , "Iacute"),
std::make_pair((int)Qt::Key_Icircumflex , "Icircumflex"),
std::make_pair((int)Qt::Key_Idiaeresis , "Idiaeresis"),
std::make_pair((int)Qt::Key_ETH , "ETH"),
std::make_pair((int)Qt::Key_Ntilde , "Ntilde"),
std::make_pair((int)Qt::Key_Ograve , "Ograve"),
std::make_pair((int)Qt::Key_Oacute , "Oacute"),
std::make_pair((int)Qt::Key_Ocircumflex , "Ocircumflex"),
std::make_pair((int)Qt::Key_Otilde , "Otilde"),
std::make_pair((int)Qt::Key_Odiaeresis , "Odiaeresis"),
std::make_pair((int)Qt::Key_multiply , "multiply"),
std::make_pair((int)Qt::Key_Ooblique , "Ooblique"),
std::make_pair((int)Qt::Key_Ugrave , "Ugrave"),
std::make_pair((int)Qt::Key_Uacute , "Uacute"),
std::make_pair((int)Qt::Key_Ucircumflex , "Ucircumflex"),
std::make_pair((int)Qt::Key_Udiaeresis , "Udiaeresis"),
std::make_pair((int)Qt::Key_Yacute , "Yacute"),
std::make_pair((int)Qt::Key_THORN , "THORN"),
std::make_pair((int)Qt::Key_ssharp , "ssharp"),
std::make_pair((int)Qt::Key_division , "division"),
std::make_pair((int)Qt::Key_ydiaeresis , "ydiaeresis"),
std::make_pair((int)Qt::Key_Escape , "Escape"),
std::make_pair((int)Qt::Key_Tab , "Tab"),
std::make_pair((int)Qt::Key_Backtab , "Backtab"),
std::make_pair((int)Qt::Key_Backspace , "Backspace"),
std::make_pair((int)Qt::Key_Return , "Return"),
std::make_pair((int)Qt::Key_Enter , "Enter"),
std::make_pair((int)Qt::Key_Insert , "Insert"),
std::make_pair((int)Qt::Key_Delete , "Delete"),
std::make_pair((int)Qt::Key_Pause , "Pause"),
std::make_pair((int)Qt::Key_Print , "Print"),
std::make_pair((int)Qt::Key_SysReq , "SysReq"),
std::make_pair((int)Qt::Key_Clear , "Clear"),
std::make_pair((int)Qt::Key_Home , "Home"),
std::make_pair((int)Qt::Key_End , "End"),
std::make_pair((int)Qt::Key_Left , "Left"),
std::make_pair((int)Qt::Key_Up , "Up"),
std::make_pair((int)Qt::Key_Right , "Right"),
std::make_pair((int)Qt::Key_Down , "Down"),
std::make_pair((int)Qt::Key_PageUp , "PageUp"),
std::make_pair((int)Qt::Key_PageDown , "PageDown"),
std::make_pair((int)Qt::Key_Shift , "Shift"),
std::make_pair((int)Qt::Key_Control , "Control"),
std::make_pair((int)Qt::Key_Meta , "Meta"),
std::make_pair((int)Qt::Key_Alt , "Alt"),
std::make_pair((int)Qt::Key_CapsLock , "CapsLock"),
std::make_pair((int)Qt::Key_NumLock , "NumLock"),
std::make_pair((int)Qt::Key_ScrollLock , "ScrollLock"),
std::make_pair((int)Qt::Key_F1 , "F1"),
std::make_pair((int)Qt::Key_F2 , "F2"),
std::make_pair((int)Qt::Key_F3 , "F3"),
std::make_pair((int)Qt::Key_F4 , "F4"),
std::make_pair((int)Qt::Key_F5 , "F5"),
std::make_pair((int)Qt::Key_F6 , "F6"),
std::make_pair((int)Qt::Key_F7 , "F7"),
std::make_pair((int)Qt::Key_F8 , "F8"),
std::make_pair((int)Qt::Key_F9 , "F9"),
std::make_pair((int)Qt::Key_F10 , "F10"),
std::make_pair((int)Qt::Key_F11 , "F11"),
std::make_pair((int)Qt::Key_F12 , "F12"),
std::make_pair((int)Qt::Key_F13 , "F13"),
std::make_pair((int)Qt::Key_F14 , "F14"),
std::make_pair((int)Qt::Key_F15 , "F15"),
std::make_pair((int)Qt::Key_F16 , "F16"),
std::make_pair((int)Qt::Key_F17 , "F17"),
std::make_pair((int)Qt::Key_F18 , "F18"),
std::make_pair((int)Qt::Key_F19 , "F19"),
std::make_pair((int)Qt::Key_F20 , "F20"),
std::make_pair((int)Qt::Key_F21 , "F21"),
std::make_pair((int)Qt::Key_F22 , "F22"),
std::make_pair((int)Qt::Key_F23 , "F23"),
std::make_pair((int)Qt::Key_F24 , "F24"),
std::make_pair((int)Qt::Key_F25 , "F25"),
std::make_pair((int)Qt::Key_F26 , "F26"),
std::make_pair((int)Qt::Key_F27 , "F27"),
std::make_pair((int)Qt::Key_F28 , "F28"),
std::make_pair((int)Qt::Key_F29 , "F29"),
std::make_pair((int)Qt::Key_F30 , "F30"),
std::make_pair((int)Qt::Key_F31 , "F31"),
std::make_pair((int)Qt::Key_F32 , "F32"),
std::make_pair((int)Qt::Key_F33 , "F33"),
std::make_pair((int)Qt::Key_F34 , "F34"),
std::make_pair((int)Qt::Key_F35 , "F35"),
std::make_pair((int)Qt::Key_Super_L , "Super_L"),
std::make_pair((int)Qt::Key_Super_R , "Super_R"),
std::make_pair((int)Qt::Key_Menu , "Menu"),
std::make_pair((int)Qt::Key_Hyper_L , "Hyper_L"),
std::make_pair((int)Qt::Key_Hyper_R , "Hyper_R"),
std::make_pair((int)Qt::Key_Help , "Help"),
std::make_pair((int)Qt::Key_Direction_L , "Direction_L"),
std::make_pair((int)Qt::Key_Direction_R , "Direction_R"),
std::make_pair((int)Qt::Key_Back , "Back"),
std::make_pair((int)Qt::Key_Forward , "Forward"),
std::make_pair((int)Qt::Key_Stop , "Stop"),
std::make_pair((int)Qt::Key_Refresh , "Refresh"),
std::make_pair((int)Qt::Key_VolumeDown , "VolumeDown"),
std::make_pair((int)Qt::Key_VolumeMute , "VolumeMute"),
std::make_pair((int)Qt::Key_VolumeUp , "VolumeUp"),
std::make_pair((int)Qt::Key_BassBoost , "BassBoost"),
std::make_pair((int)Qt::Key_BassUp , "BassUp"),
std::make_pair((int)Qt::Key_BassDown , "BassDown"),
std::make_pair((int)Qt::Key_TrebleUp , "TrebleUp"),
std::make_pair((int)Qt::Key_TrebleDown , "TrebleDown"),
std::make_pair((int)Qt::Key_MediaPlay , "MediaPlay"),
std::make_pair((int)Qt::Key_MediaStop , "MediaStop"),
std::make_pair((int)Qt::Key_MediaPrevious , "MediaPrevious"),
std::make_pair((int)Qt::Key_MediaNext , "MediaNext"),
std::make_pair((int)Qt::Key_MediaRecord , "MediaRecord"),
std::make_pair((int)Qt::Key_MediaPause , "MediaPause"),
std::make_pair((int)Qt::Key_MediaTogglePlayPause , "MediaTogglePlayPause"),
std::make_pair((int)Qt::Key_HomePage , "HomePage"),
std::make_pair((int)Qt::Key_Favorites , "Favorites"),
std::make_pair((int)Qt::Key_Search , "Search"),
std::make_pair((int)Qt::Key_Standby , "Standby"),
std::make_pair((int)Qt::Key_OpenUrl , "OpenUrl"),
std::make_pair((int)Qt::Key_LaunchMail , "LaunchMail"),
std::make_pair((int)Qt::Key_LaunchMedia , "LaunchMedia"),
std::make_pair((int)Qt::Key_Launch0 , "Launch0"),
std::make_pair((int)Qt::Key_Launch1 , "Launch1"),
std::make_pair((int)Qt::Key_Launch2 , "Launch2"),
std::make_pair((int)Qt::Key_Launch3 , "Launch3"),
std::make_pair((int)Qt::Key_Launch4 , "Launch4"),
std::make_pair((int)Qt::Key_Launch5 , "Launch5"),
std::make_pair((int)Qt::Key_Launch6 , "Launch6"),
std::make_pair((int)Qt::Key_Launch7 , "Launch7"),
std::make_pair((int)Qt::Key_Launch8 , "Launch8"),
std::make_pair((int)Qt::Key_Launch9 , "Launch9"),
std::make_pair((int)Qt::Key_LaunchA , "LaunchA"),
std::make_pair((int)Qt::Key_LaunchB , "LaunchB"),
std::make_pair((int)Qt::Key_LaunchC , "LaunchC"),
std::make_pair((int)Qt::Key_LaunchD , "LaunchD"),
std::make_pair((int)Qt::Key_LaunchE , "LaunchE"),
std::make_pair((int)Qt::Key_LaunchF , "LaunchF"),
std::make_pair((int)Qt::Key_MonBrightnessUp , "MonBrightnessUp"),
std::make_pair((int)Qt::Key_MonBrightnessDown , "MonBrightnessDown"),
std::make_pair((int)Qt::Key_KeyboardLightOnOff , "KeyboardLightOnOff"),
std::make_pair((int)Qt::Key_KeyboardBrightnessUp , "KeyboardBrightnessUp"),
std::make_pair((int)Qt::Key_KeyboardBrightnessDown , "KeyboardBrightnessDown"),
std::make_pair((int)Qt::Key_PowerOff , "PowerOff"),
std::make_pair((int)Qt::Key_WakeUp , "WakeUp"),
std::make_pair((int)Qt::Key_Eject , "Eject"),
std::make_pair((int)Qt::Key_ScreenSaver , "ScreenSaver"),
std::make_pair((int)Qt::Key_WWW , "WWW"),
std::make_pair((int)Qt::Key_Memo , "Memo"),
std::make_pair((int)Qt::Key_LightBulb , "LightBulb"),
std::make_pair((int)Qt::Key_Shop , "Shop"),
std::make_pair((int)Qt::Key_History , "History"),
std::make_pair((int)Qt::Key_AddFavorite , "AddFavorite"),
std::make_pair((int)Qt::Key_HotLinks , "HotLinks"),
std::make_pair((int)Qt::Key_BrightnessAdjust , "BrightnessAdjust"),
std::make_pair((int)Qt::Key_Finance , "Finance"),
std::make_pair((int)Qt::Key_Community , "Community"),
std::make_pair((int)Qt::Key_AudioRewind , "AudioRewind"),
std::make_pair((int)Qt::Key_BackForward , "BackForward"),
std::make_pair((int)Qt::Key_ApplicationLeft , "ApplicationLeft"),
std::make_pair((int)Qt::Key_ApplicationRight , "ApplicationRight"),
std::make_pair((int)Qt::Key_Book , "Book"),
std::make_pair((int)Qt::Key_CD , "CD"),
std::make_pair((int)Qt::Key_Calculator , "Calculator"),
std::make_pair((int)Qt::Key_ToDoList , "ToDoList"),
std::make_pair((int)Qt::Key_ClearGrab , "ClearGrab"),
std::make_pair((int)Qt::Key_Close , "Close"),
std::make_pair((int)Qt::Key_Copy , "Copy"),
std::make_pair((int)Qt::Key_Cut , "Cut"),
std::make_pair((int)Qt::Key_Display , "Display"),
std::make_pair((int)Qt::Key_DOS , "DOS"),
std::make_pair((int)Qt::Key_Documents , "Documents"),
std::make_pair((int)Qt::Key_Excel , "Excel"),
std::make_pair((int)Qt::Key_Explorer , "Explorer"),
std::make_pair((int)Qt::Key_Game , "Game"),
std::make_pair((int)Qt::Key_Go , "Go"),
std::make_pair((int)Qt::Key_iTouch , "iTouch"),
std::make_pair((int)Qt::Key_LogOff , "LogOff"),
std::make_pair((int)Qt::Key_Market , "Market"),
std::make_pair((int)Qt::Key_Meeting , "Meeting"),
std::make_pair((int)Qt::Key_MenuKB , "MenuKB"),
std::make_pair((int)Qt::Key_MenuPB , "MenuPB"),
std::make_pair((int)Qt::Key_MySites , "MySites"),
std::make_pair((int)Qt::Key_News , "News"),
std::make_pair((int)Qt::Key_OfficeHome , "OfficeHome"),
std::make_pair((int)Qt::Key_Option , "Option"),
std::make_pair((int)Qt::Key_Paste , "Paste"),
std::make_pair((int)Qt::Key_Phone , "Phone"),
std::make_pair((int)Qt::Key_Calendar , "Calendar"),
std::make_pair((int)Qt::Key_Reply , "Reply"),
std::make_pair((int)Qt::Key_Reload , "Reload"),
std::make_pair((int)Qt::Key_RotateWindows , "RotateWindows"),
std::make_pair((int)Qt::Key_RotationPB , "RotationPB"),
std::make_pair((int)Qt::Key_RotationKB , "RotationKB"),
std::make_pair((int)Qt::Key_Save , "Save"),
std::make_pair((int)Qt::Key_Send , "Send"),
std::make_pair((int)Qt::Key_Spell , "Spell"),
std::make_pair((int)Qt::Key_SplitScreen , "SplitScreen"),
std::make_pair((int)Qt::Key_Support , "Support"),
std::make_pair((int)Qt::Key_TaskPane , "TaskPane"),
std::make_pair((int)Qt::Key_Terminal , "Terminal"),
std::make_pair((int)Qt::Key_Tools , "Tools"),
std::make_pair((int)Qt::Key_Travel , "Travel"),
std::make_pair((int)Qt::Key_Video , "Video"),
std::make_pair((int)Qt::Key_Word , "Word"),
std::make_pair((int)Qt::Key_Xfer , "Xfer"),
std::make_pair((int)Qt::Key_ZoomIn , "ZoomIn"),
std::make_pair((int)Qt::Key_ZoomOut , "ZoomOut"),
std::make_pair((int)Qt::Key_Away , "Away"),
std::make_pair((int)Qt::Key_Messenger , "Messenger"),
std::make_pair((int)Qt::Key_WebCam , "WebCam"),
std::make_pair((int)Qt::Key_MailForward , "MailForward"),
std::make_pair((int)Qt::Key_Pictures , "Pictures"),
std::make_pair((int)Qt::Key_Music , "Music"),
std::make_pair((int)Qt::Key_Battery , "Battery"),
std::make_pair((int)Qt::Key_Bluetooth , "Bluetooth"),
std::make_pair((int)Qt::Key_WLAN , "WLAN"),
std::make_pair((int)Qt::Key_UWB , "UWB"),
std::make_pair((int)Qt::Key_AudioForward , "AudioForward"),
std::make_pair((int)Qt::Key_AudioRepeat , "AudioRepeat"),
std::make_pair((int)Qt::Key_AudioRandomPlay , "AudioRandomPlay"),
std::make_pair((int)Qt::Key_Subtitle , "Subtitle"),
std::make_pair((int)Qt::Key_AudioCycleTrack , "AudioCycleTrack"),
std::make_pair((int)Qt::Key_Time , "Time"),
std::make_pair((int)Qt::Key_Hibernate , "Hibernate"),
std::make_pair((int)Qt::Key_View , "View"),
std::make_pair((int)Qt::Key_TopMenu , "TopMenu"),
std::make_pair((int)Qt::Key_PowerDown , "PowerDown"),
std::make_pair((int)Qt::Key_Suspend , "Suspend"),
std::make_pair((int)Qt::Key_ContrastAdjust , "ContrastAdjust"),
std::make_pair((int)Qt::Key_LaunchG , "LaunchG"),
std::make_pair((int)Qt::Key_LaunchH , "LaunchH"),
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
std::make_pair((int)Qt::Key_TouchpadToggle , "TouchpadToggle"),
std::make_pair((int)Qt::Key_TouchpadOn , "TouchpadOn"),
std::make_pair((int)Qt::Key_TouchpadOff , "TouchpadOff"),
std::make_pair((int)Qt::Key_MicMute , "MicMute"),
std::make_pair((int)Qt::Key_Red , "Red"),
std::make_pair((int)Qt::Key_Green , "Green"),
std::make_pair((int)Qt::Key_Yellow , "Yellow"),
std::make_pair((int)Qt::Key_Blue , "Blue"),
std::make_pair((int)Qt::Key_ChannelUp , "ChannelUp"),
std::make_pair((int)Qt::Key_ChannelDown , "ChannelDown"),
std::make_pair((int)Qt::Key_Guide , "Guide"),
std::make_pair((int)Qt::Key_Info , "Info"),
std::make_pair((int)Qt::Key_Settings , "Settings"),
std::make_pair((int)Qt::Key_MicVolumeUp , "MicVolumeUp"),
std::make_pair((int)Qt::Key_MicVolumeDown , "MicVolumeDown"),
std::make_pair((int)Qt::Key_New , "New"),
std::make_pair((int)Qt::Key_Open , "Open"),
std::make_pair((int)Qt::Key_Find , "Find"),
std::make_pair((int)Qt::Key_Undo , "Undo"),
std::make_pair((int)Qt::Key_Redo , "Redo"),
#endif
std::make_pair((int)Qt::Key_AltGr , "AltGr"),
std::make_pair((int)Qt::Key_Multi_key , "Multi_key"),
std::make_pair((int)Qt::Key_Kanji , "Kanji"),
std::make_pair((int)Qt::Key_Muhenkan , "Muhenkan"),
std::make_pair((int)Qt::Key_Henkan , "Henkan"),
std::make_pair((int)Qt::Key_Romaji , "Romaji"),
std::make_pair((int)Qt::Key_Hiragana , "Hiragana"),
std::make_pair((int)Qt::Key_Katakana , "Katakana"),
std::make_pair((int)Qt::Key_Hiragana_Katakana , "Hiragana_Katakana"),
std::make_pair((int)Qt::Key_Zenkaku , "Zenkaku"),
std::make_pair((int)Qt::Key_Hankaku , "Hankaku"),
std::make_pair((int)Qt::Key_Zenkaku_Hankaku , "Zenkaku_Hankaku"),
std::make_pair((int)Qt::Key_Touroku , "Touroku"),
std::make_pair((int)Qt::Key_Massyo , "Massyo"),
std::make_pair((int)Qt::Key_Kana_Lock , "Kana_Lock"),
std::make_pair((int)Qt::Key_Kana_Shift , "Kana_Shift"),
std::make_pair((int)Qt::Key_Eisu_Shift , "Eisu_Shift"),
std::make_pair((int)Qt::Key_Eisu_toggle , "Eisu_toggle"),
std::make_pair((int)Qt::Key_Hangul , "Hangul"),
std::make_pair((int)Qt::Key_Hangul_Start , "Hangul_Start"),
std::make_pair((int)Qt::Key_Hangul_End , "Hangul_End"),
std::make_pair((int)Qt::Key_Hangul_Hanja , "Hangul_Hanja"),
std::make_pair((int)Qt::Key_Hangul_Jamo , "Hangul_Jamo"),
std::make_pair((int)Qt::Key_Hangul_Romaja , "Hangul_Romaja"),
std::make_pair((int)Qt::Key_Codeinput , "Codeinput"),
std::make_pair((int)Qt::Key_Hangul_Jeonja , "Hangul_Jeonja"),
std::make_pair((int)Qt::Key_Hangul_Banja , "Hangul_Banja"),
std::make_pair((int)Qt::Key_Hangul_PreHanja , "Hangul_PreHanja"),
std::make_pair((int)Qt::Key_Hangul_PostHanja , "Hangul_PostHanja"),
std::make_pair((int)Qt::Key_SingleCandidate , "SingleCandidate"),
std::make_pair((int)Qt::Key_MultipleCandidate , "MultipleCandidate"),
std::make_pair((int)Qt::Key_PreviousCandidate , "PreviousCandidate"),
std::make_pair((int)Qt::Key_Hangul_Special , "Hangul_Special"),
std::make_pair((int)Qt::Key_Mode_switch , "Mode_switch"),
std::make_pair((int)Qt::Key_Dead_Grave , "Dead_Grave"),
std::make_pair((int)Qt::Key_Dead_Acute , "Dead_Acute"),
std::make_pair((int)Qt::Key_Dead_Circumflex , "Dead_Circumflex"),
std::make_pair((int)Qt::Key_Dead_Tilde , "Dead_Tilde"),
std::make_pair((int)Qt::Key_Dead_Macron , "Dead_Macron"),
std::make_pair((int)Qt::Key_Dead_Breve , "Dead_Breve"),
std::make_pair((int)Qt::Key_Dead_Abovedot , "Dead_Abovedot"),
std::make_pair((int)Qt::Key_Dead_Diaeresis , "Dead_Diaeresis"),
std::make_pair((int)Qt::Key_Dead_Abovering , "Dead_Abovering"),
std::make_pair((int)Qt::Key_Dead_Doubleacute , "Dead_Doubleacute"),
std::make_pair((int)Qt::Key_Dead_Caron , "Dead_Caron"),
std::make_pair((int)Qt::Key_Dead_Cedilla , "Dead_Cedilla"),
std::make_pair((int)Qt::Key_Dead_Ogonek , "Dead_Ogonek"),
std::make_pair((int)Qt::Key_Dead_Iota , "Dead_Iota"),
std::make_pair((int)Qt::Key_Dead_Voiced_Sound , "Dead_Voiced_Sound"),
std::make_pair((int)Qt::Key_Dead_Semivoiced_Sound , "Dead_Semivoiced_Sound"),
std::make_pair((int)Qt::Key_Dead_Belowdot , "Dead_Belowdot"),
std::make_pair((int)Qt::Key_Dead_Hook , "Dead_Hook"),
std::make_pair((int)Qt::Key_Dead_Horn , "Dead_Horn"),
std::make_pair((int)Qt::Key_MediaLast , "MediaLast"),
std::make_pair((int)Qt::Key_Select , "Select"),
std::make_pair((int)Qt::Key_Yes , "Yes"),
std::make_pair((int)Qt::Key_No , "No"),
std::make_pair((int)Qt::Key_Cancel , "Cancel"),
std::make_pair((int)Qt::Key_Printer , "Printer"),
std::make_pair((int)Qt::Key_Execute , "Execute"),
std::make_pair((int)Qt::Key_Sleep , "Sleep"),
std::make_pair((int)Qt::Key_Play , "Play"),
std::make_pair((int)Qt::Key_Zoom , "Zoom"),
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
std::make_pair((int)Qt::Key_Exit , "Exit"),
#endif
std::make_pair((int)Qt::Key_Context1 , "Context1"),
std::make_pair((int)Qt::Key_Context2 , "Context2"),
std::make_pair((int)Qt::Key_Context3 , "Context3"),
std::make_pair((int)Qt::Key_Context4 , "Context4"),
std::make_pair((int)Qt::Key_Call , "Call"),
std::make_pair((int)Qt::Key_Hangup , "Hangup"),
std::make_pair((int)Qt::Key_Flip , "Flip"),
std::make_pair((int)Qt::Key_ToggleCallHangup , "ToggleCallHangup"),
std::make_pair((int)Qt::Key_VoiceDial , "VoiceDial"),
std::make_pair((int)Qt::Key_LastNumberRedial , "LastNumberRedial"),
std::make_pair((int)Qt::Key_Camera , "Camera"),
std::make_pair((int)Qt::Key_CameraFocus , "CameraFocus"),
std::make_pair(0 , (const char*) 0)
};
}

@ -0,0 +1,73 @@
#ifndef CSM_PREFS_SHORTCUTMANAGER_H
#define CSM_PREFS_SHORTCUTMANAGER_H
#include <map>
#include <QKeySequence>
#include <QObject>
#include <QString>
namespace CSMPrefs
{
class Shortcut;
class ShortcutEventHandler;
/// Class used to track and update shortcuts/sequences
class ShortcutManager : public QObject
{
Q_OBJECT
public:
ShortcutManager();
/// The shortcut class will do this automatically
void addShortcut(Shortcut* shortcut);
/// The shortcut class will do this automatically
void removeShortcut(Shortcut* shortcut);
bool getSequence(const std::string& name, QKeySequence& sequence) const;
void setSequence(const std::string& name, const QKeySequence& sequence);
bool getModifier(const std::string& name, int& modifier) const;
void setModifier(const std::string& name, int modifier);
std::string convertToString(const QKeySequence& sequence) const;
std::string convertToString(int modifier) const;
std::string convertToString(const QKeySequence& sequence, int modifier) const;
void convertFromString(const std::string& data, QKeySequence& sequence) const;
void convertFromString(const std::string& data, int& modifier) const;
void convertFromString(const std::string& data, QKeySequence& sequence, int& modifier) const;
/// Replaces "{sequence-name}" or "{modifier-name}" with the appropriate text
QString processToolTip(const QString& toolTip) const;
private:
// Need a multimap in case multiple shortcuts share the same name
typedef std::multimap<std::string, Shortcut*> ShortcutMap;
typedef std::map<std::string, QKeySequence> SequenceMap;
typedef std::map<std::string, int> ModifierMap;
typedef std::map<int, std::string> NameMap;
typedef std::map<std::string, int> KeyMap;
ShortcutMap mShortcuts;
SequenceMap mSequences;
ModifierMap mModifiers;
NameMap mNames;
KeyMap mKeys;
ShortcutEventHandler* mEventHandler;
void createLookupTables();
static const std::pair<int, const char*> QtKeys[];
};
}
#endif

@ -0,0 +1,197 @@
#include "shortcutsetting.hpp"
#include <QEvent>
#include <QKeyEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QPushButton>
#include <QWidget>
#include "state.hpp"
#include "shortcutmanager.hpp"
namespace CSMPrefs
{
const int ShortcutSetting::MaxKeys;
ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
const std::string& label)
: Setting(parent, values, mutex, key, label)
, mButton(0)
, mEditorActive(false)
, mEditorPos(0)
{
for (int i = 0; i < MaxKeys; ++i)
{
mEditorKeys[i] = 0;
}
}
std::pair<QWidget*, QWidget*> ShortcutSetting::makeWidgets(QWidget* parent)
{
QKeySequence sequence;
State::get().getShortcutManager().getSequence(getKey(), sequence);
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(sequence).c_str());
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
QPushButton* widget = new QPushButton(text, parent);
widget->setCheckable(true);
widget->installEventFilter(this);
mButton = widget;
connect(widget, SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool)));
return std::make_pair(label, widget);
}
bool ShortcutSetting::eventFilter(QObject* target, QEvent* event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->isAutoRepeat())
return true;
int mod = keyEvent->modifiers();
int key = keyEvent->key();
return handleEvent(target, mod, key, true);
}
else if (event->type() == QEvent::KeyRelease)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->isAutoRepeat())
return true;
int mod = keyEvent->modifiers();
int key = keyEvent->key();
return handleEvent(target, mod, key, false);
}
else if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
int mod = mouseEvent->modifiers();
int key = mouseEvent->button();
return handleEvent(target, mod, key, true);
}
else if (event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
int mod = mouseEvent->modifiers();
int key = mouseEvent->button();
return handleEvent(target, mod, key, false);
}
else if (event->type() == QEvent::FocusOut)
{
resetState();
}
return false;
}
bool ShortcutSetting::handleEvent(QObject* target, int mod, int value, bool active)
{
// Modifiers are handled differently
const int Blacklist[] =
{
Qt::Key_Shift,
Qt::Key_Control,
Qt::Key_Meta,
Qt::Key_Alt,
Qt::Key_AltGr
};
const size_t BlacklistSize = sizeof(Blacklist) / sizeof(int);
if (!mEditorActive)
{
if (value == Qt::RightButton && !active)
{
// Clear sequence
QKeySequence sequence = QKeySequence(0, 0, 0, 0);
storeValue(sequence);
resetState();
}
return false;
}
// Handle blacklist
for (size_t i = 0; i < BlacklistSize; ++i)
{
if (value == Blacklist[i])
return true;
}
if (!active || mEditorPos >= MaxKeys)
{
// Update key
QKeySequence sequence = QKeySequence(mEditorKeys[0], mEditorKeys[1], mEditorKeys[2], mEditorKeys[3]);
storeValue(sequence);
resetState();
}
else
{
if (mEditorPos == 0)
{
mEditorKeys[0] = mod | value;
}
else
{
mEditorKeys[mEditorPos] = value;
}
mEditorPos += 1;
}
return true;
}
void ShortcutSetting::storeValue(const QKeySequence& sequence)
{
State::get().getShortcutManager().setSequence(getKey(), sequence);
// Convert to string and assign
std::string value = State::get().getShortcutManager().convertToString(sequence);
{
QMutexLocker lock(getMutex());
getValues().setString(getKey(), getParent()->getKey(), value);
}
getParent()->getState()->update(*this);
}
void ShortcutSetting::resetState()
{
mButton->setChecked(false);
mEditorActive = false;
mEditorPos = 0;
for (int i = 0; i < MaxKeys; ++i)
{
mEditorKeys[i] = 0;
}
// Button text
QKeySequence sequence;
State::get().getShortcutManager().getSequence(getKey(), sequence);
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(sequence).c_str());
mButton->setText(text);
}
void ShortcutSetting::buttonToggled(bool checked)
{
if (checked)
mButton->setText("Press keys or click here...");
mEditorActive = checked;
}
}

@ -0,0 +1,49 @@
#ifndef CSM_PREFS_SHORTCUTSETTING_H
#define CSM_PREFS_SHORTCUTSETTING_H
#include <QKeySequence>
#include "setting.hpp"
class QEvent;
class QPushButton;
namespace CSMPrefs
{
class ShortcutSetting : public Setting
{
Q_OBJECT
public:
ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
const std::string& label);
virtual std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent);
protected:
bool eventFilter(QObject* target, QEvent* event);
private:
bool handleEvent(QObject* target, int mod, int value, bool active);
void storeValue(const QKeySequence& sequence);
void resetState();
static const int MaxKeys = 4;
QPushButton* mButton;
bool mEditorActive;
int mEditorPos;
int mEditorKeys[MaxKeys];
private slots:
void buttonToggled(bool checked);
};
}
#endif

@ -9,6 +9,8 @@
#include "doublesetting.hpp" #include "doublesetting.hpp"
#include "boolsetting.hpp" #include "boolsetting.hpp"
#include "coloursetting.hpp" #include "coloursetting.hpp"
#include "shortcutsetting.hpp"
#include "modifiersetting.hpp"
CSMPrefs::State *CSMPrefs::State::sThis = 0; CSMPrefs::State *CSMPrefs::State::sThis = 0;
@ -136,6 +138,9 @@ void CSMPrefs::State::declare()
declareBool ("wrap-lines", "Wrap Lines", false). declareBool ("wrap-lines", "Wrap Lines", false).
setTooltip ("Wrap lines longer than width of script editor."); setTooltip ("Wrap lines longer than width of script editor.");
declareBool ("mono-font", "Use monospace font", true); declareBool ("mono-font", "Use monospace font", true);
declareInt ("tab-width", "Tab Width", 4).
setTooltip ("Number of characters for tab width").
setRange (1, 10);
EnumValue warningsNormal ("Normal", "Report warnings as warning"); EnumValue warningsNormal ("Normal", "Report warnings as warning");
declareEnum ("warnings", "Warning Mode", warningsNormal). declareEnum ("warnings", "Warning Mode", warningsNormal).
addValue ("Ignore", "Do not report warning"). addValue ("Ignore", "Do not report warning").
@ -162,20 +167,17 @@ void CSMPrefs::State::declare()
"list go to the first/last item"); "list go to the first/last item");
declareCategory ("3D Scene Input"); declareCategory ("3D Scene Input");
EnumValue left ("Left Mouse-Button"); declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
EnumValue cLeft ("Ctrl-Left Mouse-Button"); declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false);
EnumValue right ("Right Mouse-Button"); declareDouble ("p-navi-orbit-sensitivity", "Orbit Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
EnumValue cRight ("Ctrl-Right Mouse-Button"); declareBool ("p-navi-orbit-invert", "Invert Orbit Camera Mouse Input", false);
EnumValue middle ("Middle Mouse-Button"); declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
EnumValue cMiddle ("Ctrl-Middle Mouse-Button"); declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
EnumValues inputButtons; declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0);
inputButtons.add (left).add (cLeft).add (right).add (cRight).add (middle).add (cMiddle); declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);
declareEnum ("p-navi", "Primary Camera Navigation Button", left).addValues (inputButtons); declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0);
declareEnum ("s-navi", "Secondary Camera Navigation Button", cLeft).addValues (inputButtons); declareDouble ("navi-orbit-rot-speed", "Orbital Camera Rotational Speed", 3.14 / 4).setRange(0.001, 6.28);
declareEnum ("p-edit", "Primary Editing Button", right).addValues (inputButtons); declareDouble ("navi-orbit-speed-mult", "Orbital Camera Speed Multiplier (from Modifier)", 4).setRange(0.001, 1000.0);
declareEnum ("s-edit", "Secondary Editing Button", cRight).addValues (inputButtons);
declareEnum ("p-select", "Primary Selection Button", middle).addValues (inputButtons);
declareEnum ("s-select", "Secondary Selection Button", cMiddle).addValues (inputButtons);
declareSeparator(); declareSeparator();
declareBool ("context-select", "Context Sensitive Selection", false); declareBool ("context-select", "Context Sensitive Selection", false);
declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0). declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0).
@ -186,6 +188,7 @@ void CSMPrefs::State::declare()
"Shift-acceleration factor during drag operations", 4.0). "Shift-acceleration factor during drag operations", 4.0).
setTooltip ("Acceleration factor during drag operations while holding down shift"). setTooltip ("Acceleration factor during drag operations while holding down shift").
setRange (0.001, 100.0); setRange (0.001, 100.0);
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
declareCategory ("Tooltips"); declareCategory ("Tooltips");
declareBool ("scene", "Show Tooltips in 3D scenes", true); declareBool ("scene", "Show Tooltips in 3D scenes", true);
@ -210,6 +213,119 @@ void CSMPrefs::State::declare()
addValues (insertOutsideCell); addValues (insertOutsideCell);
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert). declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
addValues (insertOutsideVisibleCell); addValues (insertOutsideVisibleCell);
declareCategory ("Key Bindings");
declareSubcategory ("Document");
declareShortcut ("document-file-newgame", "New Game", QKeySequence(Qt::ControlModifier | Qt::Key_N));
declareShortcut ("document-file-newaddon", "New Addon", QKeySequence());
declareShortcut ("document-file-open", "Open", QKeySequence(Qt::ControlModifier | Qt::Key_O));
declareShortcut ("document-file-save", "Save", QKeySequence(Qt::ControlModifier | Qt::Key_S));
declareShortcut ("document-file-verify", "Verify", QKeySequence());
declareShortcut ("document-file-merge", "Merge", QKeySequence());
declareShortcut ("document-file-errorlog", "Open Load Error Log", QKeySequence());
declareShortcut ("document-file-metadata", "Meta Data", QKeySequence());
declareShortcut ("document-file-close", "Close Document", QKeySequence(Qt::ControlModifier | Qt::Key_W));
declareShortcut ("document-file-exit", "Exit Application", QKeySequence(Qt::ControlModifier | Qt::Key_Q));
declareShortcut ("document-edit-undo", "Undo", QKeySequence(Qt::ControlModifier | Qt::Key_Z));
declareShortcut ("document-edit-redo", "Redo", QKeySequence(Qt::ControlModifier | Qt::ShiftModifier | Qt::Key_Z));
declareShortcut ("document-edit-preferences", "Open Preferences", QKeySequence());
declareShortcut ("document-edit-search", "Search", QKeySequence(Qt::ControlModifier | Qt::Key_F));
declareShortcut ("document-view-newview", "New View", QKeySequence());
declareShortcut ("document-view-statusbar", "Toggle Status Bar", QKeySequence());
declareShortcut ("document-view-filters", "Open Filter List", QKeySequence());
declareShortcut ("document-world-regions", "Open Region List", QKeySequence());
declareShortcut ("document-world-cells", "Open Cell List", QKeySequence());
declareShortcut ("document-world-referencables", "Open Object List", QKeySequence());
declareShortcut ("document-world-references", "Open Instance List", QKeySequence());
declareShortcut ("document-world-pathgrid", "Open Pathgrid List", QKeySequence());
declareShortcut ("document-world-regionmap", "Open Region Map", QKeySequence());
declareShortcut ("document-mechanics-globals", "Open Global List", QKeySequence());
declareShortcut ("document-mechanics-gamesettings", "Open Game Settings", QKeySequence());
declareShortcut ("document-mechanics-scripts", "Open Script List", QKeySequence());
declareShortcut ("document-mechanics-spells", "Open Spell List", QKeySequence());
declareShortcut ("document-mechanics-enchantments", "Open Enchantment List", QKeySequence());
declareShortcut ("document-mechanics-magiceffects", "Open Magic Effect List", QKeySequence());
declareShortcut ("document-mechanics-startscripts", "Open Start Script List", QKeySequence());
declareShortcut ("document-character-skills", "Open Skill List", QKeySequence());
declareShortcut ("document-character-classes", "Open Class List", QKeySequence());
declareShortcut ("document-character-factions", "Open Faction List", QKeySequence());
declareShortcut ("document-character-races", "Open Race List", QKeySequence());
declareShortcut ("document-character-birthsigns", "Open Birthsign List", QKeySequence());
declareShortcut ("document-character-topics", "Open Topic List", QKeySequence());
declareShortcut ("document-character-journals", "Open Journal List", QKeySequence());
declareShortcut ("document-character-topicinfos", "Open Topic Info List", QKeySequence());
declareShortcut ("document-character-journalinfos", "Open Journal Info List", QKeySequence());
declareShortcut ("document-character-bodyparts", "Open Body Part List", QKeySequence());
declareShortcut ("document-assets-sounds", "Open Sound Asset List", QKeySequence());
declareShortcut ("document-assets-soundgens", "Open Sound Generator List", QKeySequence());
declareShortcut ("document-assets-meshes", "Open Mesh Asset List", QKeySequence());
declareShortcut ("document-assets-icons", "Open Icon Asset List", QKeySequence());
declareShortcut ("document-assets-music", "Open Music Asset List", QKeySequence());
declareShortcut ("document-assets-soundres", "Open Sound File List", QKeySequence());
declareShortcut ("document-assets-textures", "Open Texture Asset List", QKeySequence());
declareShortcut ("document-assets-videos", "Open Video Asset List", QKeySequence());
declareShortcut ("document-debug-run", "Run Debug", QKeySequence());
declareShortcut ("document-debug-shutdown", "Stop Debug", QKeySequence());
declareShortcut ("document-debug-runlog", "Open Run Log", QKeySequence());
declareSubcategory ("Table");
declareShortcut ("table-edit", "Edit Record", QKeySequence());
declareShortcut ("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A));
declareShortcut ("table-clone", "Clone Record", QKeySequence(Qt::ShiftModifier | Qt::Key_D));
declareShortcut ("table-revert", "Revert Record", QKeySequence());
declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete));
declareShortcut ("table-moveup", "Move Record Up", QKeySequence());
declareShortcut ("table-movedown", "Move Record Down", QKeySequence());
declareShortcut ("table-view", "View Record", QKeySequence());
declareShortcut ("table-preview", "Preview Record", QKeySequence());
declareShortcut ("table-extendeddelete", "Extended Record Deletion", QKeySequence());
declareShortcut ("table-extendedrevert", "Extended Record Revertion", QKeySequence());
declareSubcategory ("Report Table");
declareShortcut ("reporttable-show", "Show Report", QKeySequence());
declareShortcut ("reporttable-remove", "Remove Report", QKeySequence(Qt::Key_Delete));
declareShortcut ("reporttable-replace", "Replace Report", QKeySequence());
declareShortcut ("reporttable-refresh", "Refresh Report", QKeySequence());
declareSubcategory ("Scene");
declareShortcut ("scene-navi-primary", "Camera Rotation From Mouse Movement", QKeySequence(Qt::LeftButton));
declareShortcut ("scene-navi-secondary", "Camera Translation From Mouse Movement",
QKeySequence(Qt::ControlModifier | (int)Qt::LeftButton));
declareShortcut ("scene-edit-primary", "Primary Edit", QKeySequence(Qt::RightButton));
declareShortcut ("scene-edit-secondary", "Secondary Edit",
QKeySequence(Qt::ControlModifier | (int)Qt::RightButton));
declareShortcut ("scene-select-primary", "Primary Select", QKeySequence(Qt::MiddleButton));
declareShortcut ("scene-select-secondary", "Secondary Select",
QKeySequence(Qt::ControlModifier | (int)Qt::MiddleButton));
declareModifier ("scene-speed-modifier", "Speed Modifier", Qt::Key_Shift);
declareShortcut ("scene-load-cam-cell", "Load Camera Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_5));
declareShortcut ("scene-load-cam-eastcell", "Load East Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_6));
declareShortcut ("scene-load-cam-northcell", "Load North Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_8));
declareShortcut ("scene-load-cam-westcell", "Load West Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_4));
declareShortcut ("scene-load-cam-southcell", "Load South Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_2));
declareShortcut ("scene-edit-abort", "Abort", QKeySequence(Qt::Key_Escape));
declareShortcut ("scene-focus-toolbar", "Toggle Toolbar Focus", QKeySequence(Qt::Key_T));
declareShortcut ("scene-render-stats", "Debug Rendering Stats", QKeySequence(Qt::Key_F3));
declareSubcategory ("1st/Free Camera");
declareShortcut ("free-forward", "Forward", QKeySequence(Qt::Key_W));
declareShortcut ("free-backward", "Backward", QKeySequence(Qt::Key_S));
declareShortcut ("free-left", "Left", QKeySequence(Qt::Key_A));
declareShortcut ("free-right", "Right", QKeySequence(Qt::Key_D));
declareShortcut ("free-roll-left", "Roll Left", QKeySequence(Qt::Key_Q));
declareShortcut ("free-roll-right", "Roll Right", QKeySequence(Qt::Key_E));
declareShortcut ("free-speed-mode", "Toggle Speed Mode", QKeySequence(Qt::Key_F));
declareSubcategory ("Orbit Camera");
declareShortcut ("orbit-up", "Up", QKeySequence(Qt::Key_W));
declareShortcut ("orbit-down", "Down", QKeySequence(Qt::Key_S));
declareShortcut ("orbit-left", "Left", QKeySequence(Qt::Key_A));
declareShortcut ("orbit-right", "Right", QKeySequence(Qt::Key_D));
declareShortcut ("orbit-roll-left", "Roll Left", QKeySequence(Qt::Key_Q));
declareShortcut ("orbit-roll-right", "Roll Right", QKeySequence(Qt::Key_E));
declareShortcut ("orbit-speed-mode", "Toggle Speed Mode", QKeySequence(Qt::Key_F));
declareShortcut ("orbit-center-selection", "Center On Selected", QKeySequence(Qt::Key_C));
} }
void CSMPrefs::State::declareCategory (const std::string& key) void CSMPrefs::State::declareCategory (const std::string& key)
@ -326,6 +442,50 @@ CSMPrefs::ColourSetting& CSMPrefs::State::declareColour (const std::string& key,
return *setting; return *setting;
} }
CSMPrefs::ShortcutSetting& CSMPrefs::State::declareShortcut (const std::string& key, const std::string& label,
const QKeySequence& default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
std::string seqStr = getShortcutManager().convertToString(default_);
setDefault (key, seqStr);
// Setup with actual data
QKeySequence sequence;
getShortcutManager().convertFromString(mSettings.getString(key, mCurrentCategory->second.getKey()), sequence);
getShortcutManager().setSequence(key, sequence);
CSMPrefs::ShortcutSetting *setting = new CSMPrefs::ShortcutSetting (&mCurrentCategory->second, &mSettings, &mMutex,
key, label);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::ModifierSetting& CSMPrefs::State::declareModifier(const std::string& key, const std::string& label,
int default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
std::string modStr = getShortcutManager().convertToString(default_);
setDefault (key, modStr);
// Setup with actual data
int modifier;
getShortcutManager().convertFromString(mSettings.getString(key, mCurrentCategory->second.getKey()), modifier);
getShortcutManager().setModifier(key, modifier);
CSMPrefs::ModifierSetting *setting = new CSMPrefs::ModifierSetting (&mCurrentCategory->second, &mSettings, &mMutex,
key, label);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
void CSMPrefs::State::declareSeparator() void CSMPrefs::State::declareSeparator()
{ {
if (mCurrentCategory==mCategories.end()) if (mCurrentCategory==mCategories.end())
@ -337,6 +497,17 @@ void CSMPrefs::State::declareSeparator()
mCurrentCategory->second.addSetting (setting); mCurrentCategory->second.addSetting (setting);
} }
void CSMPrefs::State::declareSubcategory(const std::string& label)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
CSMPrefs::Setting *setting =
new CSMPrefs::Setting (&mCurrentCategory->second, &mSettings, &mMutex, "", label);
mCurrentCategory->second.addSetting (setting);
}
void CSMPrefs::State::setDefault (const std::string& key, const std::string& default_) void CSMPrefs::State::setDefault (const std::string& key, const std::string& default_)
{ {
Settings::CategorySetting fullKey (mCurrentCategory->second.getKey(), key); Settings::CategorySetting fullKey (mCurrentCategory->second.getKey(), key);
@ -355,10 +526,10 @@ CSMPrefs::State::State (const Files::ConfigurationManager& configurationManager)
if (sThis) if (sThis)
throw std::logic_error ("An instance of CSMPRefs::State already exists"); throw std::logic_error ("An instance of CSMPRefs::State already exists");
sThis = this;
load(); load();
declare(); declare();
sThis = this;
} }
CSMPrefs::State::~State() CSMPrefs::State::~State()
@ -382,6 +553,11 @@ CSMPrefs::State::Iterator CSMPrefs::State::end()
return mCategories.end(); return mCategories.end();
} }
CSMPrefs::ShortcutManager& CSMPrefs::State::getShortcutManager()
{
return mShortcutManager;
}
CSMPrefs::Category& CSMPrefs::State::operator[] (const std::string& key) CSMPrefs::Category& CSMPrefs::State::operator[] (const std::string& key)
{ {
Iterator iter = mCategories.find (key); Iterator iter = mCategories.find (key);

@ -1,4 +1,4 @@
#ifndef CSV_PREFS_STATE_H #ifndef CSM_PREFS_STATE_H
#define CSM_PREFS_STATE_H #define CSM_PREFS_STATE_H
#include <map> #include <map>
@ -16,6 +16,7 @@
#include "category.hpp" #include "category.hpp"
#include "setting.hpp" #include "setting.hpp"
#include "enumsetting.hpp" #include "enumsetting.hpp"
#include "shortcutmanager.hpp"
class QColor; class QColor;
@ -25,6 +26,8 @@ namespace CSMPrefs
class DoubleSetting; class DoubleSetting;
class BoolSetting; class BoolSetting;
class ColourSetting; class ColourSetting;
class ShortcutSetting;
class ModifierSetting;
/// \brief User settings state /// \brief User settings state
/// ///
@ -45,6 +48,7 @@ namespace CSMPrefs
const std::string mConfigFile; const std::string mConfigFile;
const Files::ConfigurationManager& mConfigurationManager; const Files::ConfigurationManager& mConfigurationManager;
ShortcutManager mShortcutManager;
Settings::Manager mSettings; Settings::Manager mSettings;
Collection mCategories; Collection mCategories;
Iterator mCurrentCategory; Iterator mCurrentCategory;
@ -71,8 +75,15 @@ namespace CSMPrefs
ColourSetting& declareColour (const std::string& key, const std::string& label, QColor default_); ColourSetting& declareColour (const std::string& key, const std::string& label, QColor default_);
ShortcutSetting& declareShortcut (const std::string& key, const std::string& label,
const QKeySequence& default_);
ModifierSetting& declareModifier(const std::string& key, const std::string& label, int modifier_);
void declareSeparator(); void declareSeparator();
void declareSubcategory(const std::string& label);
void setDefault (const std::string& key, const std::string& default_); void setDefault (const std::string& key, const std::string& default_);
public: public:
@ -87,6 +98,8 @@ namespace CSMPrefs
Iterator end(); Iterator end();
ShortcutManager& getShortcutManager();
Category& operator[](const std::string& key); Category& operator[](const std::string& key);
void update (const Setting& setting); void update (const Setting& setting);

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

@ -0,0 +1,35 @@
#ifndef CSM_TOOLS_JOURNALCHECK_H
#define CSM_TOOLS_JOURNALCHECK_H
#include <components/esm/loaddial.hpp>
#include "../world/idcollection.hpp"
#include "../world/infocollection.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that journal infos are good
class JournalCheckStage : public CSMDoc::Stage
{
public:
JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue>& journals,
const CSMWorld::InfoCollection& journalInfos);
virtual int setup();
///< \return number of steps
virtual void perform(int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages
private:
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
const CSMWorld::InfoCollection& mJournalInfos;
};
}
#endif

@ -99,6 +99,7 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++; ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
ref.mRefNum.mContentFile = 0; ref.mRefNum.mContentFile = 0;
ref.mNew = false;
CSMWorld::Record<CSMWorld::CellRef> newRecord ( CSMWorld::Record<CSMWorld::CellRef> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref); CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref);
@ -128,7 +129,7 @@ void CSMTools::ListLandTexturesMergeStage::perform (int stage, CSMDoc::Messages&
// make sure record is loaded // make sure record is loaded
land.loadData (ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | land.loadData (ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM); ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX);
if (const ESM::Land::LandData *data = land.getLandData (ESM::Land::DATA_VTEX)) if (const ESM::Land::LandData *data = land.getLandData (ESM::Land::DATA_VTEX))
{ {
@ -183,7 +184,7 @@ void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& mes
CSMWorld::LandTexture texture = CSMWorld::LandTexture texture =
mState.mSource.getData().getLandTextures().getRecord (index).get(); mState.mSource.getData().getLandTextures().getRecord (index).get();
std::ostringstream stream; stream.clear();
stream << mNext->second-1 << "_0"; stream << mNext->second-1 << "_0";
texture.mIndex = mNext->second-1; texture.mIndex = mNext->second-1;
@ -220,7 +221,7 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
const CSMWorld::Land& land = record.get(); const CSMWorld::Land& land = record.get();
land.loadData (ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | land.loadData (ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM); ESM::Land::DATA_VTEX);
CSMWorld::Land newLand (land); CSMWorld::Land newLand (land);

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

@ -425,7 +425,7 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
//stats checks //stats checks
if (creature.mData.mLevel < 1) if (creature.mData.mLevel < 1)
messages.push_back (std::make_pair (id, creature.mId + " has non-postive level")); messages.push_back (std::make_pair (id, creature.mId + " has non-positive level"));
if (creature.mData.mStrength < 0) if (creature.mData.mStrength < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative strength")); messages.push_back (std::make_pair (id, creature.mId + " has negative strength"));
@ -659,7 +659,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
{ {
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
{ {
messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happend? messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happen?
return; return;
} }
@ -915,7 +915,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
id + " contains non-existing item (" + itemName + ")")); id + " contains non-existing item (" + itemName + ")"));
else else
{ {
// Needs to accomodate Containers, Creatures, and NPCs // Needs to accommodate containers, creatures, and NPCs
switch (localIndex.second) switch (localIndex.second)
{ {
case CSMWorld::UniversalId::Type_Potion: case CSMWorld::UniversalId::Type_Potion:

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

@ -37,14 +37,18 @@ int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const
QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
{ {
if (role!=Qt::DisplayRole) if (role!=Qt::DisplayRole && role!=Qt::UserRole)
return QVariant(); return QVariant();
switch (index.column()) switch (index.column())
{ {
case Column_Type: case Column_Type:
return static_cast<int> (mRows.at (index.row()).mId.getType()); if(role == Qt::UserRole)
return QString::fromUtf8 (
mRows.at (index.row()).mId.getTypeName().c_str());
else
return static_cast<int> (mRows.at (index.row()).mId.getType());
case Column_Id: case Column_Id:
{ {

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

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

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

@ -1,5 +1,7 @@
#include "cellcoordinates.hpp" #include "cellcoordinates.hpp"
#include <cmath>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
@ -7,6 +9,9 @@ CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {}
CSMWorld::CellCoordinates::CellCoordinates (const std::pair<int, int>& coordinates)
: mX (coordinates.first), mY (coordinates.second) {}
int CSMWorld::CellCoordinates::getX() const int CSMWorld::CellCoordinates::getX() const
{ {
return mX; return mX;
@ -32,11 +37,16 @@ std::string CSMWorld::CellCoordinates::getId (const std::string& worldspace) con
return stream.str(); return stream.str();
} }
bool CSMWorld::CellCoordinates::isExteriorCell (const std::string& id)
{
return (!id.empty() && id[0]=='#');
}
std::pair<CSMWorld::CellCoordinates, bool> CSMWorld::CellCoordinates::fromId ( std::pair<CSMWorld::CellCoordinates, bool> CSMWorld::CellCoordinates::fromId (
const std::string& id) const std::string& id)
{ {
// no worldspace for now, needs to be changed for 1.1 // no worldspace for now, needs to be changed for 1.1
if (!id.empty() && id[0]=='#') if (isExteriorCell(id))
{ {
int x, y; int x, y;
char ignore; char ignore;
@ -49,6 +59,13 @@ std::pair<CSMWorld::CellCoordinates, bool> CSMWorld::CellCoordinates::fromId (
return std::make_pair (CellCoordinates(), false); return std::make_pair (CellCoordinates(), false);
} }
std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y)
{
const int cellSize = 8192;
return std::make_pair (std::floor (x/cellSize), std::floor (y/cellSize));
}
bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)
{ {
return left.getX()==right.getX() && left.getY()==right.getY(); return left.getX()==right.getX() && left.getY()==right.getY();

@ -3,6 +3,7 @@
#include <iosfwd> #include <iosfwd>
#include <string> #include <string>
#include <utility>
#include <QMetaType> #include <QMetaType>
@ -19,6 +20,8 @@ namespace CSMWorld
CellCoordinates (int x, int y); CellCoordinates (int x, int y);
CellCoordinates (const std::pair<int, int>& coordinates);
int getX() const; int getX() const;
int getY() const; int getY() const;
@ -29,11 +32,16 @@ namespace CSMWorld
std::string getId (const std::string& worldspace) const; std::string getId (const std::string& worldspace) const;
///< Return the ID for the cell at these coordinates. ///< Return the ID for the cell at these coordinates.
static bool isExteriorCell (const std::string& id);
/// \return first: CellCoordinates (or 0, 0 if cell does not have coordinates), /// \return first: CellCoordinates (or 0, 0 if cell does not have coordinates),
/// second: is cell paged? /// second: is cell paged?
/// ///
/// \note The worldspace part of \a id is ignored /// \note The worldspace part of \a id is ignored
static std::pair<CellCoordinates, bool> fromId (const std::string& id); static std::pair<CellCoordinates, bool> fromId (const std::string& id);
/// \return cell coordinates such that given world coordinates are in it.
static std::pair<int, int> coordinatesToCellIndex (float x, float y);
}; };
bool operator== (const CellCoordinates& left, const CellCoordinates& right); bool operator== (const CellCoordinates& left, const CellCoordinates& right);

@ -85,6 +85,7 @@ namespace CSMWorld
Display_Enchantment, Display_Enchantment,
//CONCRETE TYPES ENDS HERE //CONCRETE TYPES ENDS HERE
Display_UnsignedInteger8,
Display_Integer, Display_Integer,
Display_Float, Display_Float,
Display_Var, Display_Var,
@ -130,10 +131,14 @@ namespace CSMWorld
Display_InfoCondComp, Display_InfoCondComp,
Display_String32, Display_String32,
Display_LongString256, Display_LongString256,
Display_BookType,
Display_BloodType,
Display_EmitterType,
Display_EffectSkill, // must display at least one, unlike Display_Skill Display_EffectSkill, // must display at least one, unlike Display_Skill
Display_EffectAttribute, // must display at least one, unlike Display_Attribute Display_EffectAttribute, // must display at least one, unlike Display_Attribute
Display_IngredEffectId, // display none allowed, unlike Display_EffectId Display_IngredEffectId, // display none allowed, unlike Display_EffectId
Display_GenderNpc, // must display at least one, unlike Display_Gender
//top level columns that nest other columns //top level columns that nest other columns
Display_NestedHeader Display_NestedHeader

@ -1757,6 +1757,41 @@ namespace CSMWorld
return true; return true;
} }
}; };
template<typename ESXRecordT>
struct GenderNpcColumn : public Column<ESXRecordT>
{
GenderNpcColumn()
: Column<ESXRecordT>(Columns::ColumnId_GenderNpc, ColumnBase::Display_GenderNpc)
{}
virtual QVariant get(const Record<ESXRecordT>& record) const
{
// Implemented this way to allow additional gender types in the future.
if ((record.get().mData.mFlags & ESM::BodyPart::BPF_Female) == ESM::BodyPart::BPF_Female)
return 1;
return 0;
}
virtual void set(Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
// Implemented this way to allow additional gender types in the future.
if (data.toInt() == 1)
record2.mData.mFlags = (record2.mData.mFlags & ~ESM::BodyPart::BPF_Female) | ESM::BodyPart::BPF_Female;
else
record2.mData.mFlags = record2.mData.mFlags & ~ESM::BodyPart::BPF_Female;
record.setModified(record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT> template<typename ESXRecordT>
struct EnchantmentTypeColumn : public Column<ESXRecordT> struct EnchantmentTypeColumn : public Column<ESXRecordT>

@ -3,6 +3,7 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "universalid.hpp" #include "universalid.hpp"
#include "infoselectwrapper.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -97,7 +98,7 @@ namespace CSMWorld
{ ColumnId_ArmorType, "Armor Type" }, { ColumnId_ArmorType, "Armor Type" },
{ ColumnId_Health, "Health" }, { ColumnId_Health, "Health" },
{ ColumnId_ArmorValue, "Armor Value" }, { ColumnId_ArmorValue, "Armor Value" },
{ ColumnId_Scroll, "Scroll" }, { ColumnId_BookType, "Book Type" },
{ ColumnId_ClothingType, "Clothing Type" }, { ColumnId_ClothingType, "Clothing Type" },
{ ColumnId_WeightCapacity, "Weight Capacity" }, { ColumnId_WeightCapacity, "Weight Capacity" },
{ ColumnId_OrganicContainer, "Organic Container" }, { ColumnId_OrganicContainer, "Organic Container" },
@ -111,8 +112,8 @@ namespace CSMWorld
{ ColumnId_Flies, "Flies" }, { ColumnId_Flies, "Flies" },
{ ColumnId_Walks, "Walks" }, { ColumnId_Walks, "Walks" },
{ ColumnId_Essential, "Essential" }, { ColumnId_Essential, "Essential" },
{ ColumnId_SkeletonBlood, "Skeleton Blood" }, { ColumnId_BloodType, "Blood Type" },
{ ColumnId_MetalBlood, "Metal Blood" },
{ ColumnId_OpenSound, "Open Sound" }, { ColumnId_OpenSound, "Open Sound" },
{ ColumnId_CloseSound, "Close Sound" }, { ColumnId_CloseSound, "Close Sound" },
{ ColumnId_Duration, "Duration" }, { ColumnId_Duration, "Duration" },
@ -122,10 +123,8 @@ namespace CSMWorld
{ ColumnId_Dynamic, "Dynamic" }, { ColumnId_Dynamic, "Dynamic" },
{ ColumnId_Portable, "Portable" }, { ColumnId_Portable, "Portable" },
{ ColumnId_NegativeLight, "Negative Light" }, { ColumnId_NegativeLight, "Negative Light" },
{ ColumnId_Flickering, "Flickering" }, { ColumnId_EmitterType, "Emitter Type" },
{ ColumnId_SlowFlickering, "Slow Flickering" },
{ ColumnId_Pulsing, "Pulsing" },
{ ColumnId_SlowPulsing, "Slow Pulsing" },
{ ColumnId_Fire, "Fire" }, { ColumnId_Fire, "Fire" },
{ ColumnId_OffByDefault, "Off by default" }, { ColumnId_OffByDefault, "Off by default" },
{ ColumnId_IsKey, "Is Key" }, { ColumnId_IsKey, "Is Key" },
@ -273,8 +272,8 @@ namespace CSMWorld
{ ColumnId_InfoList, "Info List" }, { ColumnId_InfoList, "Info List" },
{ ColumnId_InfoCondition, "Info Conditions" }, { ColumnId_InfoCondition, "Info Conditions" },
{ ColumnId_InfoCondFunc, "Function" }, { ColumnId_InfoCondFunc, "Function" },
{ ColumnId_InfoCondVar, "Func/Variable" }, { ColumnId_InfoCondVar, "Variable/Object" },
{ ColumnId_InfoCondComp, "Comp" }, { ColumnId_InfoCondComp, "Relation" },
{ ColumnId_InfoCondValue, "Values" }, { ColumnId_InfoCondValue, "Values" },
{ ColumnId_OriginalCell, "Original Cell" }, { ColumnId_OriginalCell, "Original Cell" },
@ -284,6 +283,7 @@ namespace CSMWorld
{ ColumnId_NpcMisc, "NPC Misc" }, { ColumnId_NpcMisc, "NPC Misc" },
{ ColumnId_Level, "Level" }, { ColumnId_Level, "Level" },
{ ColumnId_NpcFactionID, "Faction ID" }, { ColumnId_NpcFactionID, "Faction ID" },
{ ColumnId_GenderNpc, "Gender"},
{ ColumnId_Mana, "Mana" }, { ColumnId_Mana, "Mana" },
{ ColumnId_Fatigue, "Fatigue" }, { ColumnId_Fatigue, "Fatigue" },
{ ColumnId_NpcDisposition, "NPC Disposition" }, { ColumnId_NpcDisposition, "NPC Disposition" },
@ -325,6 +325,12 @@ namespace CSMWorld
{ ColumnId_Idle7, "Idle 7" }, { ColumnId_Idle7, "Idle 7" },
{ ColumnId_Idle8, "Idle 8" }, { ColumnId_Idle8, "Idle 8" },
{ ColumnId_RegionWeather, "Weather" },
{ ColumnId_WeatherName, "Type" },
{ ColumnId_WeatherChance, "Percent Chance" },
{ ColumnId_Text, "Text" },
{ ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },
{ ColumnId_UseValue3, "Use value 3" }, { ColumnId_UseValue3, "Use value 3" },
@ -546,16 +552,19 @@ namespace
"AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0 "AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0
}; };
static const char *sInfoCondFunc[] = static const char *sBookType[] =
{
"Book", "Scroll", 0
};
static const char *sBloodType[] =
{ {
" ", "Function", "Global", "Local", "Journal", "Default (Red)", "Skeleton Blood (White)", "Metal Blood (Golden)", 0
"Item", "Dead", "Not ID", "Not Faction", "Not Class",
"Not Race", "Not Cell", "Not Local", 0
}; };
static const char *sInfoCondComp[] = static const char *sEmitterType[] =
{ {
"!=", "<", "<=", "=", ">", ">=", 0 "<None>", "Flickering", "Flickering (Slow)", "Pulsing", "Pulsing (Slow)", 0
}; };
const char **getEnumNames (CSMWorld::Columns::ColumnId column) const char **getEnumNames (CSMWorld::Columns::ColumnId column)
@ -585,10 +594,11 @@ namespace
case CSMWorld::Columns::ColumnId_EffectId: return sEffectId; case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType; case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType; case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc; case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;
// FIXME: don't have dynamic value enum delegate, use Display_String for now case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
//case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond; case CSMWorld::Columns::ColumnId_BookType: return sBookType;
case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp; case CSMWorld::Columns::ColumnId_BloodType: return sBloodType;
case CSMWorld::Columns::ColumnId_EmitterType: return sEmitterType;
default: return 0; default: return 0;
} }

@ -92,7 +92,7 @@ namespace CSMWorld
ColumnId_ArmorType = 77, ColumnId_ArmorType = 77,
ColumnId_Health = 78, ColumnId_Health = 78,
ColumnId_ArmorValue = 79, ColumnId_ArmorValue = 79,
ColumnId_Scroll = 80, ColumnId_BookType = 80,
ColumnId_ClothingType = 81, ColumnId_ClothingType = 81,
ColumnId_WeightCapacity = 82, ColumnId_WeightCapacity = 82,
ColumnId_OrganicContainer = 83, ColumnId_OrganicContainer = 83,
@ -107,8 +107,8 @@ namespace CSMWorld
ColumnId_Flies = 92, ColumnId_Flies = 92,
ColumnId_Walks = 93, ColumnId_Walks = 93,
ColumnId_Essential = 94, ColumnId_Essential = 94,
ColumnId_SkeletonBlood = 95, ColumnId_BloodType = 95,
ColumnId_MetalBlood = 96, // unused
ColumnId_OpenSound = 97, ColumnId_OpenSound = 97,
ColumnId_CloseSound = 98, ColumnId_CloseSound = 98,
ColumnId_Duration = 99, ColumnId_Duration = 99,
@ -118,10 +118,8 @@ namespace CSMWorld
ColumnId_Dynamic = 103, ColumnId_Dynamic = 103,
ColumnId_Portable = 104, ColumnId_Portable = 104,
ColumnId_NegativeLight = 105, ColumnId_NegativeLight = 105,
ColumnId_Flickering = 106, ColumnId_EmitterType = 106,
ColumnId_SlowFlickering = 107, // unused (3x)
ColumnId_Pulsing = 108,
ColumnId_SlowPulsing = 109,
ColumnId_Fire = 110, ColumnId_Fire = 110,
ColumnId_OffByDefault = 111, ColumnId_OffByDefault = 111,
ColumnId_IsKey = 112, ColumnId_IsKey = 112,
@ -278,7 +276,7 @@ namespace CSMWorld
ColumnId_NpcMisc = 251, ColumnId_NpcMisc = 251,
ColumnId_Level = 252, ColumnId_Level = 252,
ColumnId_NpcFactionID = 253, ColumnId_NpcFactionID = 253,
// unused ColumnId_GenderNpc = 254,
ColumnId_Mana = 255, ColumnId_Mana = 255,
ColumnId_Fatigue = 256, ColumnId_Fatigue = 256,
ColumnId_NpcDisposition = 257, ColumnId_NpcDisposition = 257,
@ -325,6 +323,12 @@ namespace CSMWorld
ColumnId_Idle7 = 292, ColumnId_Idle7 = 292,
ColumnId_Idle8 = 293, ColumnId_Idle8 = 293,
ColumnId_RegionWeather = 294,
ColumnId_WeatherName = 295,
ColumnId_WeatherChance = 296,
ColumnId_Text = 297,
// Allocated to a separate value range, so we don't get a collision should we ever need // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.
ColumnId_UseValue1 = 0x10000, ColumnId_UseValue1 = 0x10000,

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

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

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

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

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

@ -10,6 +10,8 @@
#include <components/esm/loadglob.hpp> #include <components/esm/loadglob.hpp>
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
#include <components/resource/scenemanager.hpp>
#include "idtable.hpp" #include "idtable.hpp"
#include "idtree.hpp" #include "idtree.hpp"
#include "columnimp.hpp" #include "columnimp.hpp"
@ -59,11 +61,13 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
return number; return number;
} }
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback) CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir)
: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells), : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
mResourcesManager (resourcesManager), mFallbackMap(fallback), mResourcesManager (resourcesManager), mFallbackMap(fallback),
mReader (0), mDialogue (0), mReaderIndex(0), mResourceSystem(new Resource::ResourceSystem(resourcesManager.getVFS())) mReader (0), mDialogue (0), mReaderIndex(1), mResourceSystem(new Resource::ResourceSystem(resourcesManager.getVFS()))
{ {
mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string());
int index = 0; int index = 0;
mGlobals.addColumn (new StringIdColumn<ESM::Global>); mGlobals.addColumn (new StringIdColumn<ESM::Global>);
@ -177,6 +181,14 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRegions.addColumn (new NameColumn<ESM::Region>); mRegions.addColumn (new NameColumn<ESM::Region>);
mRegions.addColumn (new MapColourColumn<ESM::Region>); mRegions.addColumn (new MapColourColumn<ESM::Region>);
mRegions.addColumn (new SleepListColumn<ESM::Region>); mRegions.addColumn (new SleepListColumn<ESM::Region>);
// Region Weather
mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionWeather));
index = mRegions.getColumns()-1;
mRegions.addAdapter (std::make_pair(&mRegions.getColumn(index), new RegionWeatherAdapter ()));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_WeatherName, ColumnBase::Display_String, false));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_WeatherChance, ColumnBase::Display_UnsignedInteger8));
// Region Sounds // Region Sounds
mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionSounds)); mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionSounds));
index = mRegions.getColumns()-1; index = mRegions.getColumns()-1;
@ -271,7 +283,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc)); new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc));
// FIXME: don't have dynamic value enum delegate, use Display_String for now // FIXME: don't have dynamic value enum delegate, use Display_String for now
mTopicInfos.getNestableColumn(index)->addColumn( mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String)); new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_InfoCondVar));
mTopicInfos.getNestableColumn(index)->addColumn( mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp)); new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));
mTopicInfos.getNestableColumn(index)->addColumn( mTopicInfos.getNestableColumn(index)->addColumn(
@ -351,7 +363,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mBodyParts.addColumn (new FixedRecordTypeColumn<ESM::BodyPart> (UniversalId::Type_BodyPart)); mBodyParts.addColumn (new FixedRecordTypeColumn<ESM::BodyPart> (UniversalId::Type_BodyPart));
mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>); mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>);
mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>); mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>);
mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female)); mBodyParts.addColumn(new GenderNpcColumn<ESM::BodyPart>);
mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable, mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable,
ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true)); ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true));
@ -887,9 +899,11 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader = new ESM::ESMReader; mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder); mReader->setEncoder (&mEncoder);
mReader->setIndex(mReaderIndex++); mReader->setIndex((project || !base) ? 0 : mReaderIndex++);
mReader->open (path.string()); mReader->open (path.string());
mContentFileNames.insert(std::make_pair(path.filename().string(), mReader->getIndex()));
mBase = base; mBase = base;
mProject = project; mProject = project;
@ -902,6 +916,20 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData)); mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData));
} }
// Fix uninitialized master data index
for (std::vector<ESM::Header::MasterData>::const_iterator masterData = mReader->getGameFiles().begin();
masterData != mReader->getGameFiles().end(); ++masterData)
{
std::map<std::string, int>::iterator nameResult = mContentFileNames.find(masterData->name);
if (nameResult != mContentFileNames.end())
{
ESM::Header::MasterData& hackedMasterData = const_cast<ESM::Header::MasterData&>(*masterData);
hackedMasterData.index = nameResult->second;
}
}
return mReader->getRecordCount(); return mReader->getRecordCount();
} }
@ -934,7 +962,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
bool unhandledRecord = false; bool unhandledRecord = false;
switch (n.val) switch (n.intval)
{ {
case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break; case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break;
case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break; case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break;
@ -965,7 +993,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
if (index!=-1/* && !mBase*/) if (index!=-1/* && !mBase*/)
mLand.getRecord (index).get().loadData ( mLand.getRecord (index).get().loadData (
ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR |
ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM); ESM::Land::DATA_VTEX);
break; break;
} }
@ -1046,7 +1074,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
else else
{ {
mTopics.load (record, mBase); mTopics.load (record, mBase);
mDialogue = &mTopics.getRecord (record.mId).get(); mDialogue = &mTopics.getRecord (record.mId).get();
} }
} }

@ -123,6 +123,8 @@ namespace CSMWorld
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders; std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;
std::map<std::string, int> mContentFileNames;
// not implemented // not implemented
Data (const Data&); Data (const Data&);
Data& operator= (const Data&); Data& operator= (const Data&);
@ -138,7 +140,7 @@ namespace CSMWorld
public: public:
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback); Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir);
virtual ~Data(); virtual ~Data();

@ -1627,264 +1627,264 @@ const char * CSMWorld::DefaultGmsts::OptionalStrings[CSMWorld::DefaultGmsts::Opt
const float CSMWorld::DefaultGmsts::FloatsDefaultValues[CSMWorld::DefaultGmsts::FloatCount] = const float CSMWorld::DefaultGmsts::FloatsDefaultValues[CSMWorld::DefaultGmsts::FloatCount] =
{ {
0.3, // fAIFleeFleeMult 0.3f, // fAIFleeFleeMult
7.0, // fAIFleeHealthMult 7.0f, // fAIFleeHealthMult
3.0, // fAIMagicSpellMult 3.0f, // fAIMagicSpellMult
1.0, // fAIMeleeArmorMult 1.0f, // fAIMeleeArmorMult
1.0, // fAIMeleeSummWeaponMult 1.0f, // fAIMeleeSummWeaponMult
2.0, // fAIMeleeWeaponMult 2.0f, // fAIMeleeWeaponMult
5.0, // fAIRangeMagicSpellMult 5.0f, // fAIRangeMagicSpellMult
5.0, // fAIRangeMeleeWeaponMult 5.0f, // fAIRangeMeleeWeaponMult
2000.0, // fAlarmRadius 2000.0f, // fAlarmRadius
1.0, // fAthleticsRunBonus 1.0f, // fAthleticsRunBonus
40.0, // fAudioDefaultMaxDistance 40.0f, // fAudioDefaultMaxDistance
5.0, // fAudioDefaultMinDistance 5.0f, // fAudioDefaultMinDistance
50.0, // fAudioMaxDistanceMult 50.0f, // fAudioMaxDistanceMult
20.0, // fAudioMinDistanceMult 20.0f, // fAudioMinDistanceMult
60.0, // fAudioVoiceDefaultMaxDistance 60.0f, // fAudioVoiceDefaultMaxDistance
10.0, // fAudioVoiceDefaultMinDistance 10.0f, // fAudioVoiceDefaultMinDistance
50.0, // fAutoPCSpellChance 50.0f, // fAutoPCSpellChance
80.0, // fAutoSpellChance 80.0f, // fAutoSpellChance
50.0, // fBargainOfferBase 50.0f, // fBargainOfferBase
-4.0, // fBargainOfferMulti -4.0f, // fBargainOfferMulti
24.0, // fBarterGoldResetDelay 24.0f, // fBarterGoldResetDelay
1.75, // fBaseRunMultiplier 1.75f, // fBaseRunMultiplier
1.25, // fBlockStillBonus 1.25f, // fBlockStillBonus
150.0, // fBribe1000Mod 150.0f, // fBribe1000Mod
75.0, // fBribe100Mod 75.0f, // fBribe100Mod
35.0, // fBribe10Mod 35.0f, // fBribe10Mod
60.0, // fCombatAngleXY 60.0f, // fCombatAngleXY
60.0, // fCombatAngleZ 60.0f, // fCombatAngleZ
0.25, // fCombatArmorMinMult 0.25f, // fCombatArmorMinMult
-90.0, // fCombatBlockLeftAngle -90.0f, // fCombatBlockLeftAngle
30.0, // fCombatBlockRightAngle 30.0f, // fCombatBlockRightAngle
4.0, // fCombatCriticalStrikeMult 4.0f, // fCombatCriticalStrikeMult
0.1, // fCombatDelayCreature 0.1f, // fCombatDelayCreature
0.1, // fCombatDelayNPC 0.1f, // fCombatDelayNPC
128.0, // fCombatDistance 128.0f, // fCombatDistance
0.3, // fCombatDistanceWerewolfMod 0.3f, // fCombatDistanceWerewolfMod
30.0, // fCombatForceSideAngle 30.0f, // fCombatForceSideAngle
0.2, // fCombatInvisoMult 0.2f, // fCombatInvisoMult
1.5, // fCombatKODamageMult 1.5f, // fCombatKODamageMult
45.0, // fCombatTorsoSideAngle 45.0f, // fCombatTorsoSideAngle
0.3, // fCombatTorsoStartPercent 0.3f, // fCombatTorsoStartPercent
0.8, // fCombatTorsoStopPercent 0.8f, // fCombatTorsoStopPercent
15.0, // fConstantEffectMult 15.0f, // fConstantEffectMult
72.0, // fCorpseClearDelay 72.0f, // fCorpseClearDelay
72.0, // fCorpseRespawnDelay 72.0f, // fCorpseRespawnDelay
0.5, // fCrimeGoldDiscountMult 0.5f, // fCrimeGoldDiscountMult
0.9, // fCrimeGoldTurnInMult 0.9f, // fCrimeGoldTurnInMult
1.0, // fCrimeStealing 1.0f, // fCrimeStealing
0.5, // fDamageStrengthBase 0.5f, // fDamageStrengthBase
0.1, // fDamageStrengthMult 0.1f, // fDamageStrengthMult
5.0, // fDifficultyMult 5.0f, // fDifficultyMult
2.5, // fDiseaseXferChance 2.5f, // fDiseaseXferChance
-10.0, // fDispAttacking -10.0f, // fDispAttacking
-1.0, // fDispBargainFailMod -1.0f, // fDispBargainFailMod
1.0, // fDispBargainSuccessMod 1.0f, // fDispBargainSuccessMod
0.0, // fDispCrimeMod 0.0f, // fDispCrimeMod
-10.0, // fDispDiseaseMod -10.0f, // fDispDiseaseMod
3.0, // fDispFactionMod 3.0f, // fDispFactionMod
1.0, // fDispFactionRankBase 1.0f, // fDispFactionRankBase
0.5, // fDispFactionRankMult 0.5f, // fDispFactionRankMult
1.0, // fDispositionMod 1.0f, // fDispositionMod
50.0, // fDispPersonalityBase 50.0f, // fDispPersonalityBase
0.5, // fDispPersonalityMult 0.5f, // fDispPersonalityMult
-25.0, // fDispPickPocketMod -25.0f, // fDispPickPocketMod
5.0, // fDispRaceMod 5.0f, // fDispRaceMod
-0.5, // fDispStealing -0.5f, // fDispStealing
-5.0, // fDispWeaponDrawn -5.0f, // fDispWeaponDrawn
0.5, // fEffectCostMult 0.5f, // fEffectCostMult
0.1, // fElementalShieldMult 0.1f, // fElementalShieldMult
3.0, // fEnchantmentChanceMult 3.0f, // fEnchantmentChanceMult
0.5, // fEnchantmentConstantChanceMult 0.5f, // fEnchantmentConstantChanceMult
100.0, // fEnchantmentConstantDurationMult 100.0f, // fEnchantmentConstantDurationMult
0.1, // fEnchantmentMult 0.1f, // fEnchantmentMult
1000.0, // fEnchantmentValueMult 1000.0f, // fEnchantmentValueMult
0.3, // fEncumberedMoveEffect 0.3f, // fEncumberedMoveEffect
5.0, // fEncumbranceStrMult 5.0f, // fEncumbranceStrMult
0.04, // fEndFatigueMult 0.04f, // fEndFatigueMult
0.25, // fFallAcroBase 0.25f, // fFallAcroBase
0.01, // fFallAcroMult 0.01f, // fFallAcroMult
400.0, // fFallDamageDistanceMin 400.0f, // fFallDamageDistanceMin
0.0, // fFallDistanceBase 0.0f, // fFallDistanceBase
0.07, // fFallDistanceMult 0.07f, // fFallDistanceMult
2.0, // fFatigueAttackBase 2.0f, // fFatigueAttackBase
0.0, // fFatigueAttackMult 0.0f, // fFatigueAttackMult
1.25, // fFatigueBase 1.25f, // fFatigueBase
4.0, // fFatigueBlockBase 4.0f, // fFatigueBlockBase
0.0, // fFatigueBlockMult 0.0f, // fFatigueBlockMult
5.0, // fFatigueJumpBase 5.0f, // fFatigueJumpBase
0.0, // fFatigueJumpMult 0.0f, // fFatigueJumpMult
0.5, // fFatigueMult 0.5f, // fFatigueMult
2.5, // fFatigueReturnBase 2.5f, // fFatigueReturnBase
0.02, // fFatigueReturnMult 0.02f, // fFatigueReturnMult
5.0, // fFatigueRunBase 5.0f, // fFatigueRunBase
2.0, // fFatigueRunMult 2.0f, // fFatigueRunMult
1.5, // fFatigueSneakBase 1.5f, // fFatigueSneakBase
1.5, // fFatigueSneakMult 1.5f, // fFatigueSneakMult
0.0, // fFatigueSpellBase 0.0f, // fFatigueSpellBase
0.0, // fFatigueSpellCostMult 0.0f, // fFatigueSpellCostMult
0.0, // fFatigueSpellMult 0.0f, // fFatigueSpellMult
7.0, // fFatigueSwimRunBase 7.0f, // fFatigueSwimRunBase
0.0, // fFatigueSwimRunMult 0.0f, // fFatigueSwimRunMult
2.5, // fFatigueSwimWalkBase 2.5f, // fFatigueSwimWalkBase
0.0, // fFatigueSwimWalkMult 0.0f, // fFatigueSwimWalkMult
0.2, // fFightDispMult 0.2f, // fFightDispMult
0.005, // fFightDistanceMultiplier 0.005f, // fFightDistanceMultiplier
50.0, // fFightStealing 50.0f, // fFightStealing
3000.0, // fFleeDistance 3000.0f, // fFleeDistance
512.0, // fGreetDistanceReset 512.0f, // fGreetDistanceReset
0.1, // fHandtoHandHealthPer 0.1f, // fHandtoHandHealthPer
1.0, // fHandToHandReach 1.0f, // fHandToHandReach
0.5, // fHoldBreathEndMult 0.5f, // fHoldBreathEndMult
20.0, // fHoldBreathTime 20.0f, // fHoldBreathTime
0.75, // fIdleChanceMultiplier 0.75f, // fIdleChanceMultiplier
1.0, // fIngredientMult 1.0f, // fIngredientMult
0.5, // fInteriorHeadTrackMult 0.5f, // fInteriorHeadTrackMult
128.0, // fJumpAcrobaticsBase 128.0f, // fJumpAcrobaticsBase
4.0, // fJumpAcroMultiplier 4.0f, // fJumpAcroMultiplier
0.5, // fJumpEncumbranceBase 0.5f, // fJumpEncumbranceBase
1.0, // fJumpEncumbranceMultiplier 1.0f, // fJumpEncumbranceMultiplier
0.5, // fJumpMoveBase 0.5f, // fJumpMoveBase
0.5, // fJumpMoveMult 0.5f, // fJumpMoveMult
1.0, // fJumpRunMultiplier 1.0f, // fJumpRunMultiplier
0.5, // fKnockDownMult 0.5f, // fKnockDownMult
5.0, // fLevelMod 5.0f, // fLevelMod
0.1, // fLevelUpHealthEndMult 0.1f, // fLevelUpHealthEndMult
0.6, // fLightMaxMod 0.6f, // fLightMaxMod
10.0, // fLuckMod 10.0f, // fLuckMod
10.0, // fMagesGuildTravel 10.0f, // fMagesGuildTravel
1.5, // fMagicCreatureCastDelay 1.5f, // fMagicCreatureCastDelay
0.0167, // fMagicDetectRefreshRate 0.0167f, // fMagicDetectRefreshRate
1.0, // fMagicItemConstantMult 1.0f, // fMagicItemConstantMult
1.0, // fMagicItemCostMult 1.0f, // fMagicItemCostMult
1.0, // fMagicItemOnceMult 1.0f, // fMagicItemOnceMult
1.0, // fMagicItemPriceMult 1.0f, // fMagicItemPriceMult
0.05, // fMagicItemRechargePerSecond 0.05f, // fMagicItemRechargePerSecond
1.0, // fMagicItemStrikeMult 1.0f, // fMagicItemStrikeMult
1.0, // fMagicItemUsedMult 1.0f, // fMagicItemUsedMult
3.0, // fMagicStartIconBlink 3.0f, // fMagicStartIconBlink
0.5, // fMagicSunBlockedMult 0.5f, // fMagicSunBlockedMult
0.75, // fMajorSkillBonus 0.75f, // fMajorSkillBonus
300.0, // fMaxFlySpeed 300.0f, // fMaxFlySpeed
0.5, // fMaxHandToHandMult 0.5f, // fMaxHandToHandMult
400.0, // fMaxHeadTrackDistance 400.0f, // fMaxHeadTrackDistance
200.0, // fMaxWalkSpeed 200.0f, // fMaxWalkSpeed
300.0, // fMaxWalkSpeedCreature 300.0f, // fMaxWalkSpeedCreature
0.9, // fMedMaxMod 0.9f, // fMedMaxMod
0.1, // fMessageTimePerChar 0.1f, // fMessageTimePerChar
5.0, // fMinFlySpeed 5.0f, // fMinFlySpeed
0.1, // fMinHandToHandMult 0.1f, // fMinHandToHandMult
1.0, // fMinorSkillBonus 1.0f, // fMinorSkillBonus
100.0, // fMinWalkSpeed 100.0f, // fMinWalkSpeed
5.0, // fMinWalkSpeedCreature 5.0f, // fMinWalkSpeedCreature
1.25, // fMiscSkillBonus 1.25f, // fMiscSkillBonus
2.0, // fNPCbaseMagickaMult 2.0f, // fNPCbaseMagickaMult
0.5, // fNPCHealthBarFade 0.5f, // fNPCHealthBarFade
3.0, // fNPCHealthBarTime 3.0f, // fNPCHealthBarTime
1.0, // fPCbaseMagickaMult 1.0f, // fPCbaseMagickaMult
0.3, // fPerDieRollMult 0.3f, // fPerDieRollMult
5.0, // fPersonalityMod 5.0f, // fPersonalityMod
1.0, // fPerTempMult 1.0f, // fPerTempMult
-1.0, // fPickLockMult -1.0f, // fPickLockMult
0.3, // fPickPocketMod 0.3f, // fPickPocketMod
20.0, // fPotionMinUsefulDuration 20.0f, // fPotionMinUsefulDuration
0.5, // fPotionStrengthMult 0.5f, // fPotionStrengthMult
0.5, // fPotionT1DurMult 0.5f, // fPotionT1DurMult
1.5, // fPotionT1MagMult 1.5f, // fPotionT1MagMult
20.0, // fPotionT4BaseStrengthMult 20.0f, // fPotionT4BaseStrengthMult
12.0, // fPotionT4EquipStrengthMult 12.0f, // fPotionT4EquipStrengthMult
3000.0, // fProjectileMaxSpeed 3000.0f, // fProjectileMaxSpeed
400.0, // fProjectileMinSpeed 400.0f, // fProjectileMinSpeed
25.0, // fProjectileThrownStoreChance 25.0f, // fProjectileThrownStoreChance
3.0, // fRepairAmountMult 3.0f, // fRepairAmountMult
1.0, // fRepairMult 1.0f, // fRepairMult
1.0, // fReputationMod 1.0f, // fReputationMod
0.15, // fRestMagicMult 0.15f, // fRestMagicMult
0.0, // fSeriousWoundMult 0.0f, // fSeriousWoundMult
0.25, // fSleepRandMod 0.25f, // fSleepRandMod
0.3, // fSleepRestMod 0.3f, // fSleepRestMod
-1.0, // fSneakBootMult -1.0f, // fSneakBootMult
0.5, // fSneakDistanceBase 0.5f, // fSneakDistanceBase
0.002, // fSneakDistanceMultiplier 0.002f, // fSneakDistanceMultiplier
0.5, // fSneakNoViewMult 0.5f, // fSneakNoViewMult
1.0, // fSneakSkillMult 1.0f, // fSneakSkillMult
0.75, // fSneakSpeedMultiplier 0.75f, // fSneakSpeedMultiplier
1.0, // fSneakUseDelay 1.0f, // fSneakUseDelay
500.0, // fSneakUseDist 500.0f, // fSneakUseDist
1.5, // fSneakViewMult 1.5f, // fSneakViewMult
3.0, // fSoulGemMult 3.0f, // fSoulGemMult
0.8, // fSpecialSkillBonus 0.8f, // fSpecialSkillBonus
7.0, // fSpellMakingValueMult 7.0f, // fSpellMakingValueMult
2.0, // fSpellPriceMult 2.0f, // fSpellPriceMult
10.0, // fSpellValueMult 10.0f, // fSpellValueMult
0.25, // fStromWalkMult 0.25f, // fStromWalkMult
0.7, // fStromWindSpeed 0.7f, // fStromWindSpeed
3.0, // fSuffocationDamage 3.0f, // fSuffocationDamage
0.9, // fSwimHeightScale 0.9f, // fSwimHeightScale
0.1, // fSwimRunAthleticsMult 0.1f, // fSwimRunAthleticsMult
0.5, // fSwimRunBase 0.5f, // fSwimRunBase
0.02, // fSwimWalkAthleticsMult 0.02f, // fSwimWalkAthleticsMult
0.5, // fSwimWalkBase 0.5f, // fSwimWalkBase
1.0, // fSwingBlockBase 1.0f, // fSwingBlockBase
1.0, // fSwingBlockMult 1.0f, // fSwingBlockMult
1000.0, // fTargetSpellMaxSpeed 1000.0f, // fTargetSpellMaxSpeed
1000.0, // fThrownWeaponMaxSpeed 1000.0f, // fThrownWeaponMaxSpeed
300.0, // fThrownWeaponMinSpeed 300.0f, // fThrownWeaponMinSpeed
0.0, // fTrapCostMult 0.0f, // fTrapCostMult
4000.0, // fTravelMult 4000.0f, // fTravelMult
16000.0,// fTravelTimeMult 16000.0f,// fTravelTimeMult
0.1, // fUnarmoredBase1 0.1f, // fUnarmoredBase1
0.065, // fUnarmoredBase2 0.065f, // fUnarmoredBase2
30.0, // fVanityDelay 30.0f, // fVanityDelay
10.0, // fVoiceIdleOdds 10.0f, // fVoiceIdleOdds
0.0, // fWaterReflectUpdateAlways 0.0f, // fWaterReflectUpdateAlways
10.0, // fWaterReflectUpdateSeldom 10.0f, // fWaterReflectUpdateSeldom
0.1, // fWeaponDamageMult 0.1f, // fWeaponDamageMult
1.0, // fWeaponFatigueBlockMult 1.0f, // fWeaponFatigueBlockMult
0.25, // fWeaponFatigueMult 0.25f, // fWeaponFatigueMult
150.0, // fWereWolfAcrobatics 150.0f, // fWereWolfAcrobatics
150.0, // fWereWolfAgility 150.0f, // fWereWolfAgility
1.0, // fWereWolfAlchemy 1.0f, // fWereWolfAlchemy
1.0, // fWereWolfAlteration 1.0f, // fWereWolfAlteration
1.0, // fWereWolfArmorer 1.0f, // fWereWolfArmorer
150.0, // fWereWolfAthletics 150.0f, // fWereWolfAthletics
1.0, // fWereWolfAxe 1.0f, // fWereWolfAxe
1.0, // fWereWolfBlock 1.0f, // fWereWolfBlock
1.0, // fWereWolfBluntWeapon 1.0f, // fWereWolfBluntWeapon
1.0, // fWereWolfConjuration 1.0f, // fWereWolfConjuration
1.0, // fWereWolfDestruction 1.0f, // fWereWolfDestruction
1.0, // fWereWolfEnchant 1.0f, // fWereWolfEnchant
150.0, // fWereWolfEndurance 150.0f, // fWereWolfEndurance
400.0, // fWereWolfFatigue 400.0f, // fWereWolfFatigue
100.0, // fWereWolfHandtoHand 100.0f, // fWereWolfHandtoHand
2.0, // fWereWolfHealth 2.0f, // fWereWolfHealth
1.0, // fWereWolfHeavyArmor 1.0f, // fWereWolfHeavyArmor
1.0, // fWereWolfIllusion 1.0f, // fWereWolfIllusion
1.0, // fWereWolfIntellegence 1.0f, // fWereWolfIntellegence
1.0, // fWereWolfLightArmor 1.0f, // fWereWolfLightArmor
1.0, // fWereWolfLongBlade 1.0f, // fWereWolfLongBlade
1.0, // fWereWolfLuck 1.0f, // fWereWolfLuck
100.0, // fWereWolfMagicka 100.0f, // fWereWolfMagicka
1.0, // fWereWolfMarksman 1.0f, // fWereWolfMarksman
1.0, // fWereWolfMediumArmor 1.0f, // fWereWolfMediumArmor
1.0, // fWereWolfMerchantile 1.0f, // fWereWolfMerchantile
1.0, // fWereWolfMysticism 1.0f, // fWereWolfMysticism
1.0, // fWereWolfPersonality 1.0f, // fWereWolfPersonality
1.0, // fWereWolfRestoration 1.0f, // fWereWolfRestoration
1.5, // fWereWolfRunMult 1.5f, // fWereWolfRunMult
1.0, // fWereWolfSecurity 1.0f, // fWereWolfSecurity
1.0, // fWereWolfShortBlade 1.0f, // fWereWolfShortBlade
1.5, // fWereWolfSilverWeaponDamageMult 1.5f, // fWereWolfSilverWeaponDamageMult
1.0, // fWereWolfSneak 1.0f, // fWereWolfSneak
1.0, // fWereWolfSpear 1.0f, // fWereWolfSpear
1.0, // fWereWolfSpeechcraft 1.0f, // fWereWolfSpeechcraft
150.0, // fWereWolfSpeed 150.0f, // fWereWolfSpeed
150.0, // fWereWolfStrength 150.0f, // fWereWolfStrength
100.0, // fWereWolfUnarmored 100.0f, // fWereWolfUnarmored
1.0, // fWereWolfWillPower 1.0f, // fWereWolfWillPower
15.0 // fWortChanceValue 15.0f // fWortChanceValue
}; };
const int CSMWorld::DefaultGmsts::IntsDefaultValues[CSMWorld::DefaultGmsts::IntCount] = const int CSMWorld::DefaultGmsts::IntsDefaultValues[CSMWorld::DefaultGmsts::IntCount] =

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

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

Loading…
Cancel
Save