Merge pull request #172 from TES3MP/master

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

31
.gitignore vendored

@ -10,7 +10,6 @@ prebuilt
## doxygen
Doxygen
!docs/cs-manual/Makefile
## ides/editors
*~
@ -22,6 +21,8 @@ Doxygen
.project
.settings
.directory
.idea
cmake-build-*
## qt-creator
CMakeLists.txt.user*
@ -34,15 +35,36 @@ resources
## binaries
/esmtool
/mwiniimport
/omwlauncher
/openmw
/opencs
/niftest
/bsatool
/openmw-cs
/openmw-essimporter
/openmw-iniimporter
/openmw-launcher
/openmw-wizard
## generated objects
apps/openmw/config.hpp
apps/launcher/ui_contentselector.h
apps/launcher/ui_settingspage.h
apps/opencs/ui_contentselector.h
apps/opencs/ui_filedialog.h
apps/wizard/qrc_wizard.cxx
apps/wizard/ui_componentselectionpage.h
apps/wizard/ui_conclusionpage.h
apps/wizard/ui_existinginstallationpage.h
apps/wizard/ui_importpage.h
apps/wizard/ui_installationpage.h
apps/wizard/ui_installationtargetpage.h
apps/wizard/ui_intropage.h
apps/wizard/ui_languageselectionpage.h
apps/wizard/ui_methodselectionpage.h
components/ui_contentselector.h
docs/mainpage.hpp
docs/Doxyfile
docs/DoxyfilePages
moc_*.cxx
*.cxx_parameters
*qrc_launcher.cxx
@ -54,3 +76,6 @@ moc_*.cxx
*ui_playpage.h
*.[ao]
*.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:
- linux
# - osx
osx_image: xcode8.2
language: cpp
sudo: required
dist: trusty
@ -11,17 +12,37 @@ branches:
- /openmw-.*$/
env:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
- macos_qt_formula=qt@5.5
- secure: NZmvVuA0O9NJXVQ12tXQZHDJC2mbFgYNFcsicw0DgW1It2Nk5hxIkF0pfu4/Z59mhQuOPgRVjl5b0FKy2Axh0gkWc1DJEXGwNaiW5lpTMNWR1LJG5rxa8LrDUpFkycpbzfAFuTUZu5z3iYVv64XzELvBuqNGhPMu1LeBnrlech0jFNjkR9p5qtJGWb8zYcPMCC57rig8a9g1ABoVYS6UXjrKpx0946ZLRsE5ukc9pXsypGwPmOMyfzZkxxzIqFaxoE5JIEdaJTWba/6Za315ozYYIi/N35ROI1YAv5GHRe/Iw9XAa4vQpbDzjM7ZSsZdTvvQsSU598gD2xC6jFUKSrpW6GZKwM2x236fZLGnOk5Uw7DUbG+AwpcEmxBwoy9PjBl9ZF3tJykI0gROewCy8MODhdsVMKr1HGIMVBIJySm/RnNqtoDbYV8mYnSl5b8rwJiCajoiR8Zuv4CIfGneeH1a3DOQDPH/qkDsU6ilzF4ANsBlMUUpgY653KBMBmTlNuVZSH527tnD7Fg6JgHVuSQkTbRa1vSkR7Zcre604RZcAoaEdbX3bhVDasPPghU/I742L0RH3oQNlR09pPBDZ8kG7ydl4aPHwpCWnvXNM1vgxtGvnYLztwrse7IoaRXRYiMFmrso78WhMWUDKgvY4wV9aeUu0DtnMezZVIQwCKg=
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:
project:
name: "OpenMW/openmw"
name: "TES3MP/openmw-tes3mp"
description: "<Your project description here>"
notification_email: scrawl@baseoftrash.de
notification_email: stas5978@gmail.com
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE"
build_command: "make -j2"
build_command: "make -j3"
branch_pattern: coverity_scan
matrix:
include:
@ -32,27 +53,24 @@ matrix:
allow_failures:
- env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 "
before_install:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi
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
before_install:
- ./CI/before_install.${TRAVIS_OS_NAME}.sh
before_script: ./CI/before_script.${TRAVIS_OS_NAME}.sh
script:
- cd ./build
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
notifications:
recipients:
- corrmage+travis-ci@gmail.com
email:
on_success: change
on_failure: always
irc:
channels:
- "chat.freenode.net#openmw"
on_success: change
on_failure: always
use_notice: true
#notifications:
# email:
# recipients:
# - corrmage+travis-ci@gmail.com
# on_success: change
# on_failure: always
# irc:
# channels:
# - "chat.freenode.net#openmw"
# on_success: change
# on_failure: always
# use_notice: true

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

@ -1,21 +1,24 @@
#!/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
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
# build libgtest & libgtest_main
sudo mkdir /usr/src/gtest/build
cd /usr/src/gtest/build
sudo cmake .. -DBUILD_SHARED_LIBS=1
sudo make -j4
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
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
export CXX=clang++
export CC=clang
brew tap openmw/openmw
brew update
brew unlink boost
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg openmw/openmw/qt unshield
brew rm cmake || true
brew rm pkgconfig || true
brew rm qt5 || true
brew install cmake pkgconfig $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
cd build
export CODE_COVERAGE=1
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
export RAKNET_ROOT=~/RakNet
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
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
ARGSTR=$1
shift
@ -16,10 +32,6 @@ while [ $# -gt 0 ]; do
V )
VERBOSE=true ;;
v )
VS_VERSION=$1
shift ;;
d )
SKIP_DOWNLOAD=true ;;
@ -32,6 +44,10 @@ while [ $# -gt 0 ]; do
u )
UNITY_BUILD=true ;;
v )
VS_VERSION=$1
shift ;;
p )
PLATFORM=$1
shift ;;
@ -78,9 +94,6 @@ done
if [ -z $VERBOSE ]; then
STRIP="> /dev/null 2>&1"
fi
if [ -z $VS_VERSION ]; then
VS_VERSION="2013"
fi
if [ -z $APPVEYOR ]; then
echo "Running prebuild outside of Appveyor."
@ -90,9 +103,7 @@ if [ -z $APPVEYOR ]; then
else
echo "Running prebuild in Appveyor."
cd $APPVEYOR_BUILD_FOLDER
VERSION="$(cat README.md | grep Version: | awk '{ print $3; }')-$(git rev-parse --short HEAD)"
appveyor UpdateBuild -Version "$VERSION" > /dev/null &
cd "$APPVEYOR_BUILD_FOLDER"
fi
run_cmd() {
@ -105,7 +116,7 @@ run_cmd() {
if [ $RET -ne 0 ]; 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
echo
echo "Command $CMD failed;"
@ -184,44 +195,58 @@ add_osg_dlls() {
OSG_PLUGINS="$OSG_PLUGINS $@"
}
QT_PLATFORMS=""
add_qt_platform_dlls() {
QT_PLATFORMS="$QT_PLATFORMS $@"
}
if [ -z $PLATFORM ]; then
PLATFORM=`uname -m`
PLATFORM="$(uname -m)"
fi
if [ -z $CONFIGURATION ]; then
CONFIGURATION="Debug"
fi
if [ -z $VS_VERSION ]; then
VS_VERSION="2013"
fi
case $VS_VERSION in
14|2015 )
14|14.0|2015 )
GENERATOR="Visual Studio 14 2015"
XP_TOOLSET="v140_xp"
TOOLSET="v140"
MSVC_VER="14"
MSVC_YEAR="2015"
;;
# 12|2013|
* )
12|12.0|2013 )
GENERATOR="Visual Studio 12 2013"
XP_TOOLSET="v120_xp"
TOOLSET="v120"
MSVC_VER="12"
MSVC_YEAR="2013"
;;
esac
case $PLATFORM in
x64|x86_64|x86-64|win64|Win64 )
ARCHNAME=x86-64
ARCHSUFFIX=64
BITS=64
ARCHNAME="x86-64"
ARCHSUFFIX="64"
BITS="64"
BASE_OPTS="-G\"$GENERATOR Win64\""
add_cmake_opts "-G\"$GENERATOR Win64\""
;;
x32|x86|i686|i386|win32|Win32 )
ARCHNAME=x86
ARCHSUFFIX=86
BITS=32
ARCHNAME="x86"
ARCHSUFFIX="86"
BITS="32"
BASE_OPTS="-G\"$GENERATOR\" -T$XP_TOOLSET"
add_cmake_opts "-G\"$GENERATOR\"" -T$XP_TOOLSET
BASE_OPTS="-G\"$GENERATOR\""
add_cmake_opts "-G\"$GENERATOR\""
;;
* )
@ -230,35 +255,38 @@ case $PLATFORM in
;;
esac
if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi
case $CONFIGURATION in
debug|Debug|DEBUG )
CONFIGURATION=Debug
BUILD_CONFIG=Debug
;;
release|Release|RELEASE )
CONFIGURATION=Release
BUILD_CONFIG=Release
;;
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
CONFIGURATION=RelWithDebInfo
CONFIGURATION=Release
BUILD_CONFIG=RelWithDebInfo
;;
esac
if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi
echo
echo "=========================="
echo "Starting prebuild on win$BITS"
echo "=========================="
echo "==================================="
echo "Starting prebuild on MSVC${MSVC_YEAR} WIN${BITS}"
echo "==================================="
echo
# cd OpenMW/AppVeyor-test
mkdir -p deps
cd deps
DEPS="`pwd`"
DEPS="$(pwd)"
if [ -z $SKIP_DOWNLOAD ]; then
echo "Downloading dependency packages."
@ -266,162 +294,165 @@ if [ -z $SKIP_DOWNLOAD ]; then
# Boost
if [ -z $APPVEYOR ]; then
download "Boost 1.58.0" \
http://sourceforge.net/projects/boost/files/boost-binaries/1.58.0/boost_1_58_0-msvc-12.0-$BITS.exe \
boost-1.58.0-win$BITS.exe
download "Boost 1.61.0" \
"http://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \
"boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe"
fi
# Bullet
download "Bullet 2.83.5" \
http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.83.5-win$BITS.7z \
Bullet-2.83.5-win$BITS.7z
download "Bullet 2.86" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \
"Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z"
# FFmpeg
download "FFmpeg 2.5.2" \
http://ffmpeg.zeranoe.com/builds/win$BITS/shared/ffmpeg-2.5.2-win$BITS-shared.7z \
ffmpeg$BITS-2.5.2.7z \
http://ffmpeg.zeranoe.com/builds/win$BITS/dev/ffmpeg-2.5.2-win$BITS-dev.7z \
ffmpeg$BITS-2.5.2-dev.7z
download "FFmpeg 3.0.1" \
"http://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.0.1-win${BITS}-shared.7z" \
"ffmpeg-3.0.1-win${BITS}.7z" \
"http://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.0.1-win${BITS}-dev.7z" \
"ffmpeg-3.0.1-dev-win${BITS}.7z"
# MyGUI
download "MyGUI 3.2.2" \
http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-win$BITS.7z \
MyGUI-3.2.2-win$BITS.7z
download "MyGUI 3.2.3-git" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.3-git-msvc${MSVC_YEAR}-win${BITS}.7z" \
"MyGUI-3.2.3-git-msvc${MSVC_YEAR}-win${BITS}.7z"
# OpenAL
download "OpenAL-Soft 1.16.0" \
http://kcat.strangesoft.net/openal-binaries/openal-soft-1.16.0-bin.zip \
OpenAL-Soft-1.16.0.zip
download "OpenAL-Soft 1.17.2" \
"http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \
"OpenAL-Soft-1.17.2.zip"
# OSG
download "OpenSceneGraph 3.3.8" \
http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.3.8-win$BITS.7z \
OSG-3.3.8-win$BITS.7z
download "OpenSceneGraph 3.4.0-scrawl" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \
"OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z"
# Qt
if [ -z $APPVEYOR ]; then
download "Qt 4.8.6" \
http://sourceforge.net/projects/qt64ng/files/qt/$ARCHNAME/4.8.6/msvc2013/qt-4.8.6-x$ARCHSUFFIX-msvc2013.7z \
qt$BITS-4.8.6.7z
if [ $BITS == "64" ]; then
QT_SUFFIX="_64"
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
# SDL2
download "SDL 2.0.3" \
https://www.libsdl.org/release/SDL2-devel-2.0.3-VC.zip \
SDL2-2.0.3.zip
download "SDL 2.0.4" \
"https://www.libsdl.org/release/SDL2-devel-2.0.4-VC.zip" \
"SDL2-2.0.4.zip"
fi
cd .. #/..
# Set up dependencies
BUILD_DIR="MSVC${MSVC_YEAR}_${BITS}"
if [ -z $KEEP ]; then
echo
printf "Preparing build directory... "
rm -rf Build_$BITS
mkdir -p Build_$BITS/deps
echo "(Re)Creating build directory."
echo Done.
rm -rf "$BUILD_DIR"
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
echo
echo "Extracting dependencies..."
echo "Extracting dependencies, this might take a while..."
echo "---------------------------------------------------"
echo
# 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
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. "
elif [ -z $SKIP_EXTRACT ]; then
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
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.
else
# 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" \
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.0"
echo AppVeyor.
echo Done.
fi
}
cd $DEPS
echo
# Bullet
printf "Bullet 2.83.5... "
printf "Bullet 2.86... "
{
cd $DEPS_INSTALL
if [ -d Bullet ]; then
printf "Exists. (No version checking) "
printf -- "Exists. (No version checking) "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf Bullet
eval 7z x -y $DEPS/Bullet-2.83.5-win$BITS.7z $STRIP
mv Bullet-2.83.5-win$BITS Bullet
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
fi
BULLET_SDK="`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"
export BULLET_ROOT="$(real_pwd)/Bullet"
echo Done.
}
cd $DEPS
echo
# FFmpeg
printf "FFmpeg 2.5.2... "
printf "FFmpeg 3.0.1... "
{
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. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf FFmpeg
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2.7z $STRIP
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2-dev.7z $STRIP
eval 7z x -y "${DEPS}/ffmpeg-3.0.1-win${BITS}.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
cp -r ffmpeg-2.5.2-win$BITS-dev/* FFmpeg/
rm -rf ffmpeg-2.5.2-win$BITS-dev
mv "ffmpeg-3.0.1-win${BITS}-shared" FFmpeg
cp -r "ffmpeg-3.0.1-win${BITS}-dev/"* FFmpeg/
rm -rf "ffmpeg-3.0.1-win${BITS}-dev"
fi
FFMPEG_SDK="`real_pwd`/FFmpeg"
add_cmake_opts -DAVCODEC_INCLUDE_DIRS="$FFMPEG_SDK/include" \
-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
export FFMPEG_HOME="$(real_pwd)/FFmpeg"
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
if [ $BITS -eq 32 ]; then
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
@ -430,78 +461,79 @@ printf "FFmpeg 2.5.2... "
echo Done.
}
cd $DEPS
echo
# MyGUI
printf "MyGUI 3.2.2... "
printf "MyGUI 3.2.3-git... "
{
cd $DEPS_INSTALL
if [ -d MyGUI ] && \
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
grep "MYGUI_VERSION_PATCH 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf MyGUI
eval 7z x -y $DEPS/MyGUI-3.2.2-win$BITS.7z $STRIP
mv MyGUI-3.2.2-win$BITS MyGUI
eval 7z x -y "${DEPS}/MyGUI-3.2.3-git-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "MyGUI-3.2.3-git-msvc${MSVC_YEAR}-win${BITS}" MyGUI
fi
MYGUI_SDK="`real_pwd`/MyGUI"
add_cmake_opts -DMYGUISDK="$MYGUI_SDK" \
-DMYGUI_INCLUDE_DIRS="$MYGUI_SDK/include/MYGUI" \
-DMYGUI_PREQUEST_FILE="$MYGUI_SDK/include/MYGUI/MyGUI_Prerequest.h"
export MYGUI_HOME="$(real_pwd)/MyGUI"
if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="_d"
else
SUFFIX=""
fi
add_runtime_dlls `pwd`/MyGUI/bin/$CONFIGURATION/MyGUIEngine$SUFFIX.dll
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
echo Done.
}
cd $DEPS
echo
# 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. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf openal-soft-1.16.0-bin
eval 7z x -y OpenAL-Soft-1.16.0.zip $STRIP
rm -rf openal-soft-1.17.2-bin
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
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" \
-DOPENAL_LIBRARY="$OPENAL_SDK/libs/Win$BITS/OpenAL32.lib"
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
echo Done.
}
cd $DEPS
echo
# OSG
printf "OSG 3.3.8... "
printf "OSG 3.4.0-scrawl... "
{
cd $DEPS_INSTALL
if [ -d OSG ] && \
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_MINOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_PATCH_VERSION 8" OSG/include/osg/Version > /dev/null
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_PATCH_VERSION 0" OSG/include/osg/Version > /dev/null
then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf OSG
eval 7z x -y $DEPS/OSG-3.3.8-win$BITS.7z $STRIP
mv OSG-3.3.8-win$BITS OSG
eval 7z x -y "${DEPS}/OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "OSG-3.4.0-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
fi
OSG_SDK="`real_pwd`/OSG"
OSG_SDK="$(real_pwd)/OSG"
add_cmake_opts -DOSG_DIR="$OSG_SDK"
@ -511,85 +543,101 @@ printf "OSG 3.3.8... "
SUFFIX=""
fi
add_runtime_dlls `pwd`/OSG/bin/{OpenThreads,zlib}$SUFFIX.dll \
`pwd`/OSG/bin/osg{,Animation,DB,FX,GA,Particle,Qt,Text,Util,Viewer}$SUFFIX.dll
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${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.
}
cd $DEPS
echo
# Qt
if [ -z $APPVEYOR ]; then
printf "Qt 4.8.6... "
printf "Qt 5.7.0... "
else
printf "Qt 5.4... "
printf "Qt 5.7 AppVeyor... "
fi
{
if [ $BITS -eq 64 ]; then
SUFFIX="_64"
else
SUFFIX=""
fi
if [ -z $APPVEYOR ]; then
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. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf Qt
eval 7z x -y $DEPS/qt$BITS-4.8.6.7z $STRIP
mv qt-4.8.6-* Qt
cd Qt
eval ./qtbinpatcher.exe $STRIP
cp "${DEPS}/qt-5-install.qs" qt-install.qs
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
cd $QT_SDK
add_cmake_opts -DDESIRED_QT_VERSION=4 \
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe"
add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK"
if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d4"
SUFFIX="d"
else
SUFFIX="4"
SUFFIX=""
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.
else
if [ $BITS -eq 32 ]; then
QT_SDK="C:/Qt/5.4/msvc2013_opengl"
else
QT_SDK="C:/Qt/5.4/msvc2013_64_opengl"
fi
QT_SDK="C:/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
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"
echo AppVeyor.
echo Done.
fi
}
cd $DEPS
echo
# 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. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf SDL2-2.0.3
eval 7z x -y SDL2-2.0.3.zip $STRIP
rm -rf SDL2-2.0.4
eval 7z x -y SDL2-2.0.4.zip $STRIP
fi
SDL_SDK="`real_pwd`/SDL2-2.0.3"
add_cmake_opts -DSDL2_INCLUDE_DIR="$SDL_SDK/include" \
-DSDL2MAIN_LIBRARY="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2main.lib" \
-DSDL2_LIBRARY_PATH="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2.lib"
export SDL2DIR="$(real_pwd)/SDL2-2.0.4"
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
cd $DEPS_INSTALL/..
@ -602,12 +650,10 @@ add_cmake_opts -DBUILD_BSATOOL=no \
-DBUILD_MYGUI_PLUGIN=no \
-DOPENMW_MP_BUILD=on
if [ -z $CI ]; then
echo " (Outside of CI, doing full build.)"
else
if [ ! -z $CI ]; then
case $STEP in
components )
echo " Subproject: Components."
echo " Building subproject: Components."
add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \
@ -615,64 +661,98 @@ else
-DBUILD_OPENMW=no \
-DBUILD_WIZARD=no
;;
openmw )
echo " Subproject: OpenMW."
echo " Building subproject: OpenMW."
add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \
-DBUILD_OPENCS=no \
-DBUILD_WIZARD=no
;;
opencs )
echo " Subproject: OpenCS."
echo " Building subproject: OpenCS."
add_cmake_opts -DBUILD_ESSIMPORTER=no \
-DBUILD_LAUNCHER=no \
-DBUILD_MWINIIMPORTER=no \
-DBUILD_OPENMW=no \
-DBUILD_WIZARD=no
;;
misc )
echo " Subproject: Misc."
echo " Building subprojects: Misc."
add_cmake_opts -DBUILD_OPENCS=no \
-DBUILD_OPENMW=no
;;
* )
echo " Building everything."
;;
esac
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
printf " Configuring... "
printf -- "- Configuring... "
else
echo " cmake .. $CMAKE_OPTS"
echo "- cmake .. $CMAKE_OPTS"
fi
run_cmd cmake .. $CMAKE_OPTS
RET=$?
if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then echo Done.
else echo Failed.; fi
if [ $RET -eq 0 ]; then
echo Done.
else
echo Failed.
fi
fi
echo
# NOTE: Disable this when/if we want to run test cases
if [ -z $CI ]; then
echo "Copying Runtime DLLs..."
mkdir -p $CONFIGURATION
for DLL in $RUNTIME_DLLS; do
echo " `basename $DLL`."
cp "$DLL" $CONFIGURATION/
done
echo "OSG Plugin DLLs..."
mkdir -p $CONFIGURATION/osgPlugins-3.3.8
for DLL in $OSG_PLUGINS; do
echo " `basename $DLL`."
cp "$DLL" $CONFIGURATION/osgPlugins-3.3.8
done
echo "- Copying Runtime Resources/Config Files"
echo " gamecontrollerdb.txt"
cp gamecontrollerdb.txt $BUILD_CONFIG/gamecontrollerdb.txt
echo " openmw.cfg"
cp openmw.cfg.install $BUILD_CONFIG/openmw.cfg
echo " openmw-cs.cfg"
cp openmw-cs.cfg $BUILD_CONFIG/openmw-cs.cfg
echo " settings-default.cfg"
cp settings-default.cfg $BUILD_CONFIG/settings-default.cfg
echo " resources/"
cp -r resources $BUILD_CONFIG/resources
echo
fi
exit $RET
exit $RET

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

@ -25,7 +25,7 @@ endif()
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 38)
set(OPENMW_VERSION_MINOR 41)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
@ -46,7 +46,7 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
message(STATUS "Shallow Git clone detected, not attempting to retrieve version info")
endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
# Macros
include(OpenMWMacros)
@ -65,9 +65,11 @@ option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time"
# Apps and tools
option(BUILD_OPENMW "build OpenMW" ON)
option(BUILD_OPENMW_MP "build OpenMW-MP" ON)
option(BUILD_BSATOOL "build BSA extractor" ON)
option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON)
option(BUILD_BROWSER "build tes3mp Server Browser" ON)
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
@ -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_NIFTEST "build nif file tester" OFF)
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
option(BUILD_DOCS "build documentation." OFF )
# what is necessary to build documentation
IF( BUILD_DOCS )
# Builds the documentation.
FIND_PACKAGE( Sphinx REQUIRED )
FIND_PACKAGE( Doxygen REQUIRED )
ENDIF()
# OS X deployment
option(OPENMW_OSX_DEPLOYMENT OFF)
@ -88,7 +98,7 @@ endif()
# Set up common paths
if (APPLE)
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)
# Paths
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)
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)
else()
set(USE_QT TRUE)
@ -124,7 +137,7 @@ endif()
# Dependencies
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)
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
@ -140,41 +153,31 @@ if (USE_QT)
endif()
endif()
if (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows.
cmake_minimum_required(VERSION 2.8.11)
if (APPLE)
# OS X build process relies on this fix: https://github.com/Kitware/CMake/commit/3df5147043d83aa09acd5c9ce31d5c602efb99db
cmake_minimum_required(VERSION 3.1.0)
elseif (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows.
cmake_minimum_required(VERSION 2.8.11)
else()
# We probably support older versions than this.
cmake_minimum_required(VERSION 2.6)
# We probably support older versions than this.
cmake_minimum_required(VERSION 2.6)
endif()
# Sound setup
unset(FFMPEG_LIBRARIES CACHE)
find_package(FFmpeg REQUIRED)
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARIES})
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND OR NOT SWRESAMPLE_FOUND)
message(FATAL_ERROR "FFmpeg component required, but not found!")
endif()
# Required for building the FFmpeg headers
add_definitions(-D__STDC_CONSTANT_MACROS)
IF(BUILD_OPENMW OR BUILD_OPENCS)
# Sound setup
find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE)
# Required for building the FFmpeg headers
add_definitions(-D__STDC_CONSTANT_MACROS)
# TinyXML
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
if(USE_SYSTEM_TINYXML)
find_library(TINYXML_LIBRARIES tinyxml)
find_path(TINYXML_INCLUDE_DIR tinyxml.h)
message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}")
if (USE_SYSTEM_TINYXML)
find_package(TinyXML REQUIRED)
add_definitions (-DTIXML_USE_STL)
if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR)
include_directories(${TINYXML_INCLUDE_DIR})
message(STATUS "Using system TinyXML library.")
else()
message(FATAL_ERROR "Detection of system TinyXML incomplete.")
endif()
include_directories(SYSTEM ${TinyXML_INCLUDE_DIRS})
endif()
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
# Platform specific
if (WIN32)
@ -190,9 +193,10 @@ if (WIN32)
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
endif()
if (ANDROID)
set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE)
endif (ANDROID)
if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer
find_package(LIBUNSHIELD REQUIRED) # required only for non win32 when building openmw-wizard
set(OPENMW_USE_UNSHIELD TRUE)
endif()
option(OPENGL_ES "enable opengl es support" FALSE )
@ -213,108 +217,77 @@ if(NOT HAVE_STDINT_H)
message(FATAL_ERROR "stdint.h was not found" )
endif()
include (CheckIncludeFileCXX)
check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP)
if (HAVE_UNORDERED_MAP)
add_definitions(-DHAVE_UNORDERED_MAP)
endif ()
set(BOOST_COMPONENTS system filesystem program_options thread)
if(WIN32)
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 "")
message(FATAL_ERROR "Unable to find static OpenSceneGraph plugin: ${PLUGIN_NAME}")
endif()
IF(BUILD_OPENMW OR BUILD_OPENCS)
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${PLUGIN_NAME}_LIBRARY})
endmacro()
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgUtil osgFX)
macro(use_static_osg_plugin_dep DEPENDENCY)
find_package(${DEPENDENCY} REQUIRED)
set(USED_OSG_PLUGINS
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})
endmacro()
if(QT_STATIC)
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
osgdb_png # depends on libpng, zlib
osgdb_tga
osgdb_dds
osgdb_jpeg # depends on libjpeg
)
find_package(MyGUI 3.2.1 REQUIRED)
find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED)
find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)
foreach(PLUGIN ${PLUGIN_LIST})
use_static_osg_plugin_library(${PLUGIN})
endforeach()
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
# OSG static plugins need to linked against their respective dependencies
set(PLUGIN_DEPS_LIST
PNG # needed by osgdb_png
ZLIB # needed by osgdb_png
JPEG # needed by osgdb_jpeg
)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
foreach(DEPENDENCY ${PLUGIN_DEPS_LIST})
use_static_osg_plugin_dep(${DEPENDENCY})
endforeach()
endif()
if(QT_STATIC)
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()
set(BOOST_COMPONENTS system filesystem program_options)
if(WIN32)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
endif(WIN32)
find_package(MyGUI REQUIRED)
if (${MYGUI_VERSION} VERSION_LESS "3.2.1")
message(FATAL_ERROR "OpenMW requires MyGUI 3.2.1 or later, please install the latest version from http://mygui.info")
IF(BOOST_STATIC)
set(Boost_USE_STATIC_LIBS ON)
endif()
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED)
find_package(Bullet REQUIRED)
include_directories("."
SYSTEM
${SDL2_INCLUDE_DIR}
${Boost_INCLUDE_DIR}
${MYGUI_INCLUDE_DIRS}
${MyGUI_INCLUDE_DIRS}
${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)
add_definitions(-DMYGUI_STATIC)
@ -331,6 +304,11 @@ endif (APPLE)
# Set up DEBUG define
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/)
# Specify build paths
@ -349,14 +327,24 @@ endif (APPLE)
# 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
"${OpenMW_BINARY_DIR}/settings-default.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${OpenMW_BINARY_DIR}/openmw.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
if (NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${OpenMW_BINARY_DIR}/openmw.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${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
"${OpenMW_BINARY_DIR}/openmw-cs.cfg")
@ -378,21 +366,17 @@ endif()
# CXX Compiler settings
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long -Wno-variadic-macros")
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)
string(REGEX REPLACE ".*version ([0-9\\.]*).*" "\\1" CLANG_VERSION ${CLANG_VERSION})
if ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression")
endif ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6)
endif(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
endif ()
endif()
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
endif(CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
endif()
elseif (MSVC)
# Enable link-time code generation globally for all linking
if (OPENMW_LTO_BUILD)
@ -415,6 +399,9 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER)
IF(BUILD_BROWSER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-browser" DESTINATION "${BINDIR}" )
ENDIF(BUILD_BROWSER)
IF(BUILD_BSATOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
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}/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)
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS)
@ -468,8 +460,10 @@ IF(NOT WIN32 AND NOT APPLE)
ENDIF(NOT WIN32 AND NOT APPLE)
if(WIN32)
FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll")
INSTALL(FILES ${dll_files} DESTINATION ".")
FILE(GLOB dll_files_debug "${OpenMW_BINARY_DIR}/Debug/*.dll")
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_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.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/DejaVu Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/tes3mp-client-default.cfg"
"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt"
"${OpenMW_BINARY_DIR}/Release/openmw.exe"
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)
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)
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 ".")
FILE(GLOB plugin_dir "${OpenMW_BINARY_DIR}/Release/osgPlugins-*")
INSTALL(DIRECTORY ${plugin_dir} DESTINATION ".")
FILE(GLOB plugin_dir_debug "${OpenMW_BINARY_DIR}/Debug/osgPlugins-*")
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_PACKAGE_NAME "OpenMW")
@ -516,6 +502,9 @@ if(WIN32)
IF(BUILD_LAUNCHER)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher")
ENDIF(BUILD_LAUNCHER)
IF(BUILD_BROWSER)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};tes3mp-browser;tes3mp Launcher")
ENDIF(BUILD_BROWSER)
IF(BUILD_OPENCS)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set")
ENDIF(BUILD_OPENCS)
@ -534,8 +523,8 @@ if(WIN32)
SET(CPACK_NSIS_HELP_LINK "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_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp.ico")
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
@ -565,8 +554,13 @@ if(WIN32)
endif(WIN32)
# Extern
IF(BUILD_OPENMW OR BUILD_OPENCS)
add_subdirectory (extern/osg-ffmpeg-videoplayer)
add_subdirectory (extern/oics)
if (BUILD_OPENCS)
add_subdirectory (extern/osgQt)
endif()
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
# Components
add_subdirectory (components)
@ -577,6 +571,10 @@ add_subdirectory (components)
#endif()
# Apps and tools
if (BUILD_OPENMW_MP)
add_subdirectory( apps/openmw-mp )
endif()
if (BUILD_OPENMW)
add_subdirectory( apps/openmw )
endif()
@ -593,6 +591,10 @@ if (BUILD_LAUNCHER)
add_subdirectory( apps/launcher )
endif()
if (BUILD_BROWSER)
add_subdirectory( apps/browser )
endif()
if (BUILD_MWINIIMPORTER)
add_subdirectory( apps/mwiniimporter )
endif()
@ -631,20 +633,20 @@ if (WIN32)
endforeach( OUTPUTCONFIG )
if (USE_DEBUG_CONSOLE AND BUILD_OPENMW)
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
elseif (BUILD_OPENMW)
# 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(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
endif()
if (BUILD_OPENMW)
# Release builds use the debug console
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
endif()
# Play a bit with the warning levels
@ -667,9 +669,15 @@ if (WIN32)
4987 # nonstandard extension used (triggered by setjmp.h)
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
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
4099 # Type mismatch, declared class or struct is defined with other type
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
4351 # New behavior: elements of array 'array' will be default initialized (desired behavior)
4355 # Using 'this' in member initialization list
4464 # relative include path contains '..'
4505 # Unreferenced local function has been removed
4701 # Potentially uninitialized local variable used
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
)
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})
set(WARNINGS "${WARNINGS} /wd${d}")
endforeach(d)
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
# oics uses tinyxml, which has an initialized but unused variable
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${WARNINGS} /wd4189 ${MT_BUILD}")
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_BSATOOL)
@ -714,6 +728,10 @@ if (WIN32)
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
if (BUILD_BROWSER)
set_target_properties(tes3mp-browser PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
if (BUILD_MWINIIMPORTER)
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
@ -723,7 +741,12 @@ if (WIN32)
endif()
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()
if (BUILD_WIZARD)
@ -732,8 +755,8 @@ if (WIN32)
endif(MSVC)
# 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(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
#set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
#set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif()
# 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)
endif ()
set(INSTALL_SUBDIR OpenMW)
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)
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime)
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
@ -765,8 +781,8 @@ if (APPLE)
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}")
set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_NAME}")
set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${OPENCS_BUNDLE_NAME}")
install(CODE "
set(BU_CHMOD_BUNDLE_ITEMS ON)
@ -776,19 +792,16 @@ if (APPLE)
" COMPONENT Runtime)
set(ABSOLUTE_PLUGINS "")
set(USED_OSG_PLUGINS
osgdb_dds
osgdb_jpeg
osgdb_png
osgdb_tga
)
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})
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})
# and returns list of install paths for all installed plugins
@ -813,8 +826,8 @@ if (APPLE)
set(${plugins_var} ${PLUGINS} PARENT_SCOPE)
endfunction (install_plugins_for_bundle)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
install_plugins_for_bundle("${APP_BUNDLE_NAME}" 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(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}
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
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.38.0
* License: GPL (see docs/license/GPL3.txt for more information)
* Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net
* Version: 0.5.1
* License: GPLv3 (see docs/license/GPL3.txt for more information)
* Website: https://steamcommunity.com/groups/mwmulti
Font Licenses:
* 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.
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.
[Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
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/)
* [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)
Contributing
--------------
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>
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:
Getting Started
---------------
win1250 - Central and Eastern European
such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and
Albanian languages
* [Community forums](https://steamcommunity.com/groups/mwmulti)
* [Installation and build instructions](https://github.com/TES3MP/openmw-tes3mp/wiki/Installation-and-build-instructions)
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic
and other languages
Donations
---------------
win1252 - Western European (Latin)
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
You can benefit the project by supporting OpenMW and/or by [becoming Koncord's patron](https://www.patreon.com/Koncord).

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

@ -179,7 +179,7 @@ RecordBase::create(ESM::NAME type)
{
RecordBase *record = 0;
switch (type.val) {
switch (type.intval) {
case ESM::REC_ACTI:
{
record = new EsmTool::Record<ESM::Activator>;
@ -494,7 +494,7 @@ void Record<ESM::Book>::print()
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << 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;
if (mPrintPlain)
{

@ -42,3 +42,7 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(openmw-essimporter gcov)
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"
namespace ESSImport
@ -49,4 +55,49 @@ namespace ESSImport
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/npcstats.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/animationstate.hpp>
#include "importacdt.hpp"
@ -18,6 +19,8 @@ namespace ESSImport
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
void convertANIS (const ANIS& anis, ESM::AnimationState& state);
}
#endif

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

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

@ -5,7 +5,7 @@
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.mObject.mNpcStats.mBounty = pcdt.mBounty;
@ -17,24 +17,72 @@ namespace ESSImport
faction.mReputation = it->mReputation;
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
}
for (int i=0; i<3; ++i)
out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];
for (int i=0; i<8; ++i)
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
for (int i=0; i<27; ++i)
out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];
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;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell)
if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawn)
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();
it != pcdt.mKnownDialogueTopics.end(); ++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 <components/esm/player.hpp>
#include <components/esm/controlsstate.hpp>
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
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();
}
@ -123,8 +123,13 @@ namespace ESSImport
if (esm.isNextSub("ND3D"))
esm.skipHSub();
mHasANIS = false;
if (esm.isNextSub("ANIS"))
esm.skipHSub();
{
mHasANIS = true;
esm.getHT(mANIS);
}
}
}

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

@ -51,6 +51,7 @@ namespace
{
for (int x=0; x<128; ++x)
{
assert(image->data(x,y));
*(image->data(x,y)+2) = *it++;
*(image->data(x,y)+1) = *it++;
*image->data(x,y) = *it++;
@ -322,14 +323,14 @@ namespace ESSImport
ESM::NAME n = esm.getRecName();
esm.getRecHeader();
std::map<unsigned int, boost::shared_ptr<Converter> >::iterator it = converters.find(n.val);
std::map<unsigned int, boost::shared_ptr<Converter> >::iterator it = converters.find(n.intval);
if (it != converters.end())
{
it->second->read(esm);
}
else
{
if (unknownRecords.insert(n.val).second)
if (unknownRecords.insert(n.intval).second)
{
std::ios::fmtflags f(std::cerr.flags());
std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
@ -422,6 +423,10 @@ namespace ESSImport
writer.startRecord (ESM::REC_DIAS);
context.mDialogueState.save(writer);
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/loadcrea.hpp>
#include <components/esm/loadnpc.hpp>
#include <components/esm/controlsstate.hpp>
#include "importnpcc.hpp"
#include "importcrec.hpp"
@ -32,6 +33,8 @@ namespace ESSImport
ESM::DialogueState mDialogueState;
ESM::ControlsState mControlsState;
// cells which should show an explored overlay on the global map
std::set<std::pair<int, int> > mExploredCells;
@ -59,10 +62,14 @@ namespace ESSImport
playerCellId.mPaged = true;
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
mPlayer.mCellId = playerCellId;
//mPlayer.mLastKnownExteriorPosition
mPlayer.mHasMark = 0; // TODO
mPlayer.mLastKnownExteriorPosition[0]
= mPlayer.mLastKnownExteriorPosition[1]
= mPlayer.mLastKnownExteriorPosition[2]
= 0.0f;
mPlayer.mHasMark = 0;
mPlayer.mCurrentCrimeId = 0; // TODO
mPlayer.mObject.blank();
mPlayer.mObject.mEnabled = true;
mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame
mGlobalMapState.mBounds.mMinX = 0;

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

@ -23,9 +23,12 @@ namespace ESSImport
mKnownDialogueTopics.push_back(esm.getHString());
}
mHasMark = false;
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");
@ -34,6 +37,14 @@ namespace ESSImport
if (esm.isNextSub("NAM9"))
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;
esm.getHNOT(mBounty, "CNAM");
@ -50,8 +61,12 @@ namespace ESSImport
if (esm.isNextSub("NAM3"))
esm.skipHSub();
mHasENAM = false;
if (esm.isNextSub("ENAM"))
esm.skipHSub();
{
mHasENAM = true;
esm.getHT(mENAM);
}
if (esm.isNextSub("LNAM"))
esm.skipHSub();
@ -63,12 +78,19 @@ namespace ESSImport
mFactions.push_back(fnam);
}
if (esm.isNextSub("AADT"))
esm.skipHSub(); // 44 bytes, no clue
mHasAADT = false;
if (esm.isNextSub("AADT")) // Attack animation data?
{
mHasAADT = true;
esm.getHT(mAADT);
}
if (esm.isNextSub("KNAM"))
esm.skipHSub(); // assigned Quick Keys, I think
if (esm.isNextSub("ANIS"))
esm.skipHSub(); // 16 bytes
if (esm.isNextSub("WERE"))
{
// some werewolf data, 152 bytes
@ -76,10 +98,6 @@ namespace ESSImport
esm.getSubHeader();
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;
enum DrawState_
enum PlayerFlags
{
DrawState_Weapon = 0x80,
DrawState_Spell = 0x100
};
enum CameraState
{
CameraState_FirstPerson = 0x8,
CameraState_ThirdPerson = 0xa
PlayerFlags_ViewSwitchDisabled = 0x1,
PlayerFlags_ControlsDisabled = 0x4,
PlayerFlags_Sleeping = 0x10,
PlayerFlags_Waiting = 0x40,
PlayerFlags_WeaponDrawn = 0x80,
PlayerFlags_SpellDrawn = 0x100,
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)
@ -63,18 +71,53 @@ struct PCDT
struct PNAM
{
short mDrawState; // DrawState
short mCameraState; // CameraState
struct MarkLocation
{
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;
float mSkillProgress[27]; // skill progress, non-uniform scaled
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)
std::vector<FNAM> mFactions;
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);
};

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

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

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

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

@ -22,7 +22,8 @@ target_link_libraries(openmw-iniimporter
if (WIN32)
target_link_libraries(openmw-iniimporter
${Boost_LOCALE_LIBRARY})
endif()
INSTALL(TARGETS openmw-iniimporter RUNTIME DESTINATION ".")
endif(WIN32)
if (MINGW)
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
cfg["fallback-archive"].push_back("Morrowind.bsa");
for(std::vector<std::string>::const_iterator it=archives.begin(); it!=archives.end(); ++it) {
cfg["fallback-archive"].push_back(*it);
for(std::vector<std::string>::const_iterator iter=archives.begin(); iter!=archives.end(); ++iter) {
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.
sort(contentFiles.begin(), contentFiles.end());
for(std::vector<std::pair<std::time_t, std::string> >::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
cfg["content"].push_back(it->second);
for(std::vector<std::pair<std::time_t, std::string> >::const_iterator iter=contentFiles.begin(); iter!=contentFiles.end(); ++iter) {
cfg["content"].push_back(iter->second);
}
}

@ -26,7 +26,7 @@ opencs_units_noqt (model/world
universalid record commands columnbase columnimp scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager metadata defaultgmsts
idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro
)
opencs_hdrs_noqt (model/world
@ -35,14 +35,14 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools
tools reportmodel mergeoperation
tools reportmodel mergeoperation
)
opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages gmstcheck
mergestages gmstcheck topicinfocheck journalcheck
)
opencs_hdrs_noqt (model/tools
@ -66,8 +66,8 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator startscriptcreator referencecreator scenesubview
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator globalcreator
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
)
@ -85,12 +85,14 @@ opencs_units (view/widget
opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode
previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater
)
opencs_units_noqt (view/render
lighting lightingday lightingnight
lightingbright object cell terrainstorage tagbase cellarrow
lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase
cellarrow cellmarker cellborder pathgrid
)
opencs_hdrs_noqt (view/render
@ -107,11 +109,12 @@ opencs_units_noqt (view/tools
)
opencs_units (view/prefs
dialogue pagebase page
dialogue pagebase page keybindingpage
)
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
@ -159,9 +162,15 @@ endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
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()
set (OPENCS_MAC_ICON "")
set (OPENCS_CFG "")
set (OPENCS_DEFAULT_FILTERS_FILE "")
set (OPENCS_OPENMW_CFG "")
endif(APPLE)
add_executable(openmw-cs
@ -171,12 +180,23 @@ add_executable(openmw-cs
${OPENCS_MOC_SRC}
${OPENCS_RES_SRC}
${OPENCS_MAC_ICON}
${OPENCS_CFG}
${OPENCS_DEFAULT_FILTERS_FILE}
${OPENCS_OPENMW_CFG}
)
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
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
OUTPUT_NAME "OpenMW-CS"
OUTPUT_NAME ${OPENCS_BUNDLE_NAME}
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
@ -187,16 +207,27 @@ if(APPLE)
set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES
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)
target_link_libraries(openmw-cs
${OSG_LIBRARIES}
${OPENTHREADS_LIBRARIES}
${OSGTEXT_LIBRARIES}
${OSGUTIL_LIBRARIES}
${OSGVIEWER_LIBRARIES}
${OSGGA_LIBRARIES}
${OSGFX_LIBRARIES}
${OSGQT_LIBRARIES}
${EXTERN_OSGQT_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
@ -219,9 +250,18 @@ endif()
if (WIN32)
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
INSTALL(TARGETS openmw-cs RUNTIME DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".")
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)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT BUNDLE)
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
QDir dir(QCoreApplication::applicationDirPath());
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath());
#endif

@ -274,7 +274,7 @@ CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationM
const Fallback::Map* fallback,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
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),
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),

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

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

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

@ -81,7 +81,7 @@ void CSMDoc::Runner::start (bool delayed)
arguments << ("--script-run="+mStartup->fileName());;
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());
iter!=mContentFiles.end(); ++iter)

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

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

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

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

@ -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 "boolsetting.hpp"
#include "coloursetting.hpp"
#include "shortcutsetting.hpp"
#include "modifiersetting.hpp"
CSMPrefs::State *CSMPrefs::State::sThis = 0;
@ -136,6 +138,9 @@ void CSMPrefs::State::declare()
declareBool ("wrap-lines", "Wrap Lines", false).
setTooltip ("Wrap lines longer than width of script editor.");
declareBool ("mono-font", "Use monospace font", true);
declareInt ("tab-width", "Tab Width", 4).
setTooltip ("Number of characters for tab width").
setRange (1, 10);
EnumValue warningsNormal ("Normal", "Report warnings as warning");
declareEnum ("warnings", "Warning Mode", warningsNormal).
addValue ("Ignore", "Do not report warning").
@ -162,20 +167,17 @@ void CSMPrefs::State::declare()
"list go to the first/last item");
declareCategory ("3D Scene Input");
EnumValue left ("Left Mouse-Button");
EnumValue cLeft ("Ctrl-Left Mouse-Button");
EnumValue right ("Right Mouse-Button");
EnumValue cRight ("Ctrl-Right Mouse-Button");
EnumValue middle ("Middle Mouse-Button");
EnumValue cMiddle ("Ctrl-Middle Mouse-Button");
EnumValues inputButtons;
inputButtons.add (left).add (cLeft).add (right).add (cRight).add (middle).add (cMiddle);
declareEnum ("p-navi", "Primary Camera Navigation Button", left).addValues (inputButtons);
declareEnum ("s-navi", "Secondary Camera Navigation Button", cLeft).addValues (inputButtons);
declareEnum ("p-edit", "Primary Editing Button", right).addValues (inputButtons);
declareEnum ("s-edit", "Secondary Editing Button", cRight).addValues (inputButtons);
declareEnum ("p-select", "Primary Selection Button", middle).addValues (inputButtons);
declareEnum ("s-select", "Secondary Selection Button", cMiddle).addValues (inputButtons);
declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false);
declareDouble ("p-navi-orbit-sensitivity", "Orbit Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-orbit-invert", "Invert Orbit Camera Mouse Input", false);
declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0);
declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);
declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0);
declareDouble ("navi-orbit-rot-speed", "Orbital Camera Rotational Speed", 3.14 / 4).setRange(0.001, 6.28);
declareDouble ("navi-orbit-speed-mult", "Orbital Camera Speed Multiplier (from Modifier)", 4).setRange(0.001, 1000.0);
declareSeparator();
declareBool ("context-select", "Context Sensitive Selection", false);
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).
setTooltip ("Acceleration factor during drag operations while holding down shift").
setRange (0.001, 100.0);
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
declareCategory ("Tooltips");
declareBool ("scene", "Show Tooltips in 3D scenes", true);
@ -210,6 +213,119 @@ void CSMPrefs::State::declare()
addValues (insertOutsideCell);
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
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)
@ -326,6 +442,50 @@ CSMPrefs::ColourSetting& CSMPrefs::State::declareColour (const std::string& key,
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()
{
if (mCurrentCategory==mCategories.end())
@ -337,6 +497,17 @@ void CSMPrefs::State::declareSeparator()
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_)
{
Settings::CategorySetting fullKey (mCurrentCategory->second.getKey(), key);
@ -355,10 +526,10 @@ CSMPrefs::State::State (const Files::ConfigurationManager& configurationManager)
if (sThis)
throw std::logic_error ("An instance of CSMPRefs::State already exists");
sThis = this;
load();
declare();
sThis = this;
}
CSMPrefs::State::~State()
@ -382,6 +553,11 @@ CSMPrefs::State::Iterator CSMPrefs::State::end()
return mCategories.end();
}
CSMPrefs::ShortcutManager& CSMPrefs::State::getShortcutManager()
{
return mShortcutManager;
}
CSMPrefs::Category& CSMPrefs::State::operator[] (const std::string& 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
#include <map>
@ -16,6 +16,7 @@
#include "category.hpp"
#include "setting.hpp"
#include "enumsetting.hpp"
#include "shortcutmanager.hpp"
class QColor;
@ -25,6 +26,8 @@ namespace CSMPrefs
class DoubleSetting;
class BoolSetting;
class ColourSetting;
class ShortcutSetting;
class ModifierSetting;
/// \brief User settings state
///
@ -45,6 +48,7 @@ namespace CSMPrefs
const std::string mConfigFile;
const Files::ConfigurationManager& mConfigurationManager;
ShortcutManager mShortcutManager;
Settings::Manager mSettings;
Collection mCategories;
Iterator mCurrentCategory;
@ -71,8 +75,15 @@ namespace CSMPrefs
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 declareSubcategory(const std::string& label);
void setDefault (const std::string& key, const std::string& default_);
public:
@ -87,6 +98,8 @@ namespace CSMPrefs
Iterator end();
ShortcutManager& getShortcutManager();
Category& operator[](const std::string& key);
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.mContentFile = 0;
ref.mNew = false;
CSMWorld::Record<CSMWorld::CellRef> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref);
@ -128,7 +129,7 @@ void CSMTools::ListLandTexturesMergeStage::perform (int stage, CSMDoc::Messages&
// make sure record is loaded
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))
{
@ -183,7 +184,7 @@ void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& mes
CSMWorld::LandTexture texture =
mState.mSource.getData().getLandTextures().getRecord (index).get();
std::ostringstream stream;
stream.clear();
stream << mNext->second-1 << "_0";
texture.mIndex = mNext->second-1;
@ -220,7 +221,7 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
const CSMWorld::Land& land = record.get();
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);

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

@ -425,7 +425,7 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
//stats checks
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)
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
{
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;
}
@ -915,7 +915,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
id + " contains non-existing item (" + itemName + ")"));
else
{
// Needs to accomodate Containers, Creatures, and NPCs
// Needs to accommodate containers, creatures, and NPCs
switch (localIndex.second)
{
case CSMWorld::UniversalId::Type_Potion:

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

@ -37,14 +37,18 @@ int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const
QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
{
if (role!=Qt::DisplayRole)
if (role!=Qt::DisplayRole && role!=Qt::UserRole)
return QVariant();
switch (index.column())
{
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:
{

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

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

@ -3,6 +3,7 @@
#include <iosfwd>
#include <string>
#include <utility>
#include <QMetaType>
@ -19,6 +20,8 @@ namespace CSMWorld
CellCoordinates (int x, int y);
CellCoordinates (const std::pair<int, int>& coordinates);
int getX() const;
int getY() const;
@ -29,11 +32,16 @@ namespace CSMWorld
std::string getId (const std::string& worldspace) const;
///< 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),
/// second: is cell paged?
///
/// \note The worldspace part of \a id is ignored
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);

@ -85,6 +85,7 @@ namespace CSMWorld
Display_Enchantment,
//CONCRETE TYPES ENDS HERE
Display_UnsignedInteger8,
Display_Integer,
Display_Float,
Display_Var,
@ -130,10 +131,14 @@ namespace CSMWorld
Display_InfoCondComp,
Display_String32,
Display_LongString256,
Display_BookType,
Display_BloodType,
Display_EmitterType,
Display_EffectSkill, // must display at least one, unlike Display_Skill
Display_EffectAttribute, // must display at least one, unlike Display_Attribute
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
Display_NestedHeader

@ -1757,6 +1757,41 @@ namespace CSMWorld
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>
struct EnchantmentTypeColumn : public Column<ESXRecordT>

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

@ -92,7 +92,7 @@ namespace CSMWorld
ColumnId_ArmorType = 77,
ColumnId_Health = 78,
ColumnId_ArmorValue = 79,
ColumnId_Scroll = 80,
ColumnId_BookType = 80,
ColumnId_ClothingType = 81,
ColumnId_WeightCapacity = 82,
ColumnId_OrganicContainer = 83,
@ -107,8 +107,8 @@ namespace CSMWorld
ColumnId_Flies = 92,
ColumnId_Walks = 93,
ColumnId_Essential = 94,
ColumnId_SkeletonBlood = 95,
ColumnId_MetalBlood = 96,
ColumnId_BloodType = 95,
// unused
ColumnId_OpenSound = 97,
ColumnId_CloseSound = 98,
ColumnId_Duration = 99,
@ -118,10 +118,8 @@ namespace CSMWorld
ColumnId_Dynamic = 103,
ColumnId_Portable = 104,
ColumnId_NegativeLight = 105,
ColumnId_Flickering = 106,
ColumnId_SlowFlickering = 107,
ColumnId_Pulsing = 108,
ColumnId_SlowPulsing = 109,
ColumnId_EmitterType = 106,
// unused (3x)
ColumnId_Fire = 110,
ColumnId_OffByDefault = 111,
ColumnId_IsKey = 112,
@ -278,7 +276,7 @@ namespace CSMWorld
ColumnId_NpcMisc = 251,
ColumnId_Level = 252,
ColumnId_NpcFactionID = 253,
// unused
ColumnId_GenderNpc = 254,
ColumnId_Mana = 255,
ColumnId_Fatigue = 256,
ColumnId_NpcDisposition = 257,
@ -325,6 +323,12 @@ namespace CSMWorld
ColumnId_Idle7 = 292,
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
// to extend the number of use values.
ColumnId_UseValue1 = 0x10000,

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

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

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

@ -10,6 +10,8 @@
#include <components/esm/loadglob.hpp>
#include <components/esm/cellref.hpp>
#include <components/resource/scenemanager.hpp>
#include "idtable.hpp"
#include "idtree.hpp"
#include "columnimp.hpp"
@ -59,11 +61,13 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
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),
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;
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 MapColourColumn<ESM::Region>);
mRegions.addColumn (new SleepListColumn<ESM::Region>);
// Region Weather
mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionWeather));
index = mRegions.getColumns()-1;
mRegions.addAdapter (std::make_pair(&mRegions.getColumn(index), new RegionWeatherAdapter ()));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_WeatherName, ColumnBase::Display_String, false));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_WeatherChance, ColumnBase::Display_UnsignedInteger8));
// Region Sounds
mRegions.addColumn (new NestedParentColumn<ESM::Region> (Columns::ColumnId_RegionSounds));
index = mRegions.getColumns()-1;
@ -271,7 +283,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc));
// FIXME: don't have dynamic value enum delegate, use Display_String for now
mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String));
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_InfoCondVar));
mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));
mTopicInfos.getNestableColumn(index)->addColumn(
@ -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 BodyPartTypeColumn<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,
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->setEncoder (&mEncoder);
mReader->setIndex(mReaderIndex++);
mReader->setIndex((project || !base) ? 0 : mReaderIndex++);
mReader->open (path.string());
mContentFileNames.insert(std::make_pair(path.filename().string(), mReader->getIndex()));
mBase = base;
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));
}
// 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();
}
@ -934,7 +962,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
bool unhandledRecord = false;
switch (n.val)
switch (n.intval)
{
case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break;
case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break;
@ -965,7 +993,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
if (index!=-1/* && !mBase*/)
mLand.getRecord (index).get().loadData (
ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR |
ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM);
ESM::Land::DATA_VTEX);
break;
}
@ -1046,7 +1074,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
else
{
mTopics.load (record, mBase);
mDialogue = &mTopics.getRecord (record.mId).get();
mDialogue = &mTopics.getRecord (record.mId).get();
}
}

@ -123,6 +123,8 @@ namespace CSMWorld
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;
std::map<std::string, int> mContentFileNames;
// not implemented
Data (const Data&);
Data& operator= (const Data&);
@ -138,7 +140,7 @@ namespace CSMWorld
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();

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

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

Loading…
Cancel
Save